548 lines
15 KiB
C++
548 lines
15 KiB
C++
/*
|
|
Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved.
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; version 2 of the License.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
#ifndef NDB_CONFLICT_H
|
|
#define NDB_CONFLICT_H
|
|
|
|
#include "ndb_conflict_trans.h"
|
|
#include <ndbapi/NdbDictionary.hpp>
|
|
#include <ndbapi/NdbTransaction.hpp>
|
|
|
|
#include <mysql_com.h> // NAME_CHAR_LEN
|
|
#include <sql_const.h> // MAX_REF_PARTS
|
|
#include <mysql/plugin.h> // SHOW_VAR
|
|
|
|
enum enum_conflict_fn_type
|
|
{
|
|
CFT_NDB_UNDEF = 0
|
|
,CFT_NDB_MAX
|
|
,CFT_NDB_OLD
|
|
,CFT_NDB_MAX_DEL_WIN
|
|
,CFT_NDB_EPOCH
|
|
,CFT_NDB_EPOCH_TRANS
|
|
,CFT_NDB_EPOCH2
|
|
,CFT_NDB_EPOCH2_TRANS
|
|
,CFT_NUMBER_OF_CFTS /* End marker */
|
|
};
|
|
|
|
/**
|
|
* Definitions used when setting the conflict flags
|
|
* member of the 'extra row info' on a Binlog row
|
|
* event
|
|
*/
|
|
enum enum_binlog_extra_info_conflict_flags
|
|
{
|
|
NDB_ERIF_CFT_REFLECT_OP = 0x1,
|
|
NDB_ERIF_CFT_REFRESH_OP = 0x2,
|
|
NDB_ERIF_CFT_READ_OP = 0x4
|
|
};
|
|
|
|
#ifdef HAVE_NDB_BINLOG
|
|
static const Uint32 MAX_CONFLICT_ARGS= 8;
|
|
|
|
enum enum_conflict_fn_arg_type
|
|
{
|
|
CFAT_END
|
|
,CFAT_COLUMN_NAME
|
|
,CFAT_EXTRA_GCI_BITS
|
|
};
|
|
|
|
struct st_conflict_fn_arg
|
|
{
|
|
enum_conflict_fn_arg_type type;
|
|
union
|
|
{
|
|
char resolveColNameBuff[ NAME_CHAR_LEN + 1 ]; // CFAT_COLUMN_NAME
|
|
uint32 extraGciBits; // CFAT_EXTRA_GCI_BITS
|
|
};
|
|
};
|
|
|
|
struct st_conflict_fn_arg_def
|
|
{
|
|
enum enum_conflict_fn_arg_type arg_type;
|
|
bool optional;
|
|
};
|
|
|
|
/* What type of operation was issued */
|
|
enum enum_conflicting_op_type
|
|
{ /* NdbApi */
|
|
WRITE_ROW = 1, /* insert (!write) */
|
|
UPDATE_ROW = 2, /* update */
|
|
DELETE_ROW = 3, /* delete */
|
|
REFRESH_ROW = 4, /* refresh */
|
|
READ_ROW = 5 /* read tracking */
|
|
};
|
|
|
|
/*
|
|
Room for 10 instruction words, two labels (@ 2words/label)
|
|
+ 2 extra words for the case of resolve_size == 8
|
|
*/
|
|
#define MAX_CONFLICT_INTERPRETED_PROG_SIZE 16
|
|
|
|
/*
|
|
prepare_detect_func
|
|
|
|
Type of function used to prepare for conflict detection on
|
|
an NdbApi operation
|
|
*/
|
|
typedef int (* prepare_detect_func) (struct NDB_CONFLICT_FN_SHARE* cfn_share,
|
|
enum_conflicting_op_type op_type,
|
|
const NdbRecord* data_record,
|
|
const uchar* old_data,
|
|
const uchar* new_data,
|
|
/* Before image columns bitmap */
|
|
const MY_BITMAP* bi_cols,
|
|
/* After image columns bitmap */
|
|
const MY_BITMAP* ai_cols,
|
|
class NdbInterpretedCode* code);
|
|
|
|
/**
|
|
* enum_conflict_fn_flags
|
|
*
|
|
* These are 'features' of a particular conflict resolution algorithm, not
|
|
* controlled on a per-table basis.
|
|
* TODO : Encapsulate all these per-algorithm details inside the algorithm
|
|
*/
|
|
enum enum_conflict_fn_flags
|
|
{
|
|
CF_TRANSACTIONAL = 0x1, /* Conflicts are handled per transaction */
|
|
CF_REFLECT_SEC_OPS = 0x2, /* Secondary operations are reflected back */
|
|
CF_USE_ROLE_VAR = 0x4 /* Functionality controlled by role variable */
|
|
};
|
|
|
|
struct st_conflict_fn_def
|
|
{
|
|
const char *name;
|
|
enum_conflict_fn_type type;
|
|
const st_conflict_fn_arg_def* arg_defs;
|
|
prepare_detect_func prep_func;
|
|
uint8 flags; /* enum_conflict_fn_flags */
|
|
};
|
|
|
|
/* What sort of conflict was found */
|
|
enum enum_conflict_cause
|
|
{
|
|
ROW_ALREADY_EXISTS = 1, /* On insert */
|
|
ROW_DOES_NOT_EXIST = 2, /* On Update, Delete */
|
|
ROW_IN_CONFLICT = 3, /* On Update, Delete */
|
|
TRANS_IN_CONFLICT = 4 /* Any of above, or implied by transaction */
|
|
};
|
|
|
|
/* NdbOperation custom data which points out handler and record. */
|
|
struct Ndb_exceptions_data {
|
|
struct NDB_SHARE* share;
|
|
const NdbRecord* key_rec;
|
|
const NdbRecord* data_rec;
|
|
const uchar* old_row;
|
|
const uchar* new_row;
|
|
my_bitmap_map* bitmap_buf; /* Buffer for write_set */
|
|
MY_BITMAP* write_set;
|
|
enum_conflicting_op_type op_type;
|
|
bool reflected_operation;
|
|
Uint64 trans_id;
|
|
};
|
|
|
|
enum enum_conflict_fn_table_flags
|
|
{
|
|
CFF_NONE = 0,
|
|
CFF_REFRESH_ROWS = 1
|
|
};
|
|
|
|
/*
|
|
Maximum supported key parts (16)
|
|
(Ndb supports 32, but MySQL has a lower limit)
|
|
*/
|
|
static const int NDB_MAX_KEY_PARTS = MAX_REF_PARTS;
|
|
|
|
/**
|
|
ExceptionsTableWriter
|
|
|
|
Helper class for inserting entries into an exceptions
|
|
table
|
|
*/
|
|
class ExceptionsTableWriter
|
|
{
|
|
enum COLUMN_VERSION {
|
|
DEFAULT = 0,
|
|
OLD = 1,
|
|
NEW = 2
|
|
};
|
|
|
|
public:
|
|
ExceptionsTableWriter()
|
|
: m_pk_cols(0), m_cols(0), m_xcols(0), m_ex_tab(NULL),
|
|
m_count(0), m_extended(false), m_op_type_pos(0), m_conflict_cause_pos(0),
|
|
m_orig_transid_pos(0)
|
|
{};
|
|
|
|
~ExceptionsTableWriter()
|
|
{};
|
|
|
|
/**
|
|
hasTable
|
|
|
|
Returns true if there is an Exceptions table
|
|
*/
|
|
bool hasTable() const
|
|
{
|
|
return m_ex_tab != NULL;
|
|
};
|
|
|
|
/**
|
|
init
|
|
|
|
Initialise ExceptionsTableWriter with main and exceptions
|
|
tables.
|
|
|
|
May set a warning message on success or error.
|
|
*/
|
|
int init(const NdbDictionary::Table* mainTable,
|
|
const NdbDictionary::Table* exceptionsTable,
|
|
char* msg_buf,
|
|
uint msg_buf_len,
|
|
const char** msg);
|
|
|
|
/**
|
|
free
|
|
|
|
Release reference to exceptions table
|
|
*/
|
|
void mem_free(Ndb* ndb);
|
|
|
|
/**
|
|
writeRow
|
|
|
|
Write a row to the Exceptions Table for the given
|
|
key
|
|
*/
|
|
int writeRow(NdbTransaction* trans,
|
|
const NdbRecord* keyRecord,
|
|
const NdbRecord* dataRecord,
|
|
uint32 server_id,
|
|
uint32 master_server_id,
|
|
uint64 master_epoch,
|
|
const uchar* oldRowPtr,
|
|
const uchar* newRowPtr,
|
|
enum_conflicting_op_type op_type,
|
|
enum_conflict_cause conflict_cause,
|
|
uint64 orig_transid,
|
|
const MY_BITMAP *write_set,
|
|
NdbError& err);
|
|
|
|
private:
|
|
/* Help methods for checking exception table definition */
|
|
bool check_mandatory_columns(const NdbDictionary::Table* exceptionsTable);
|
|
bool check_pk_columns(const NdbDictionary::Table* mainTable,
|
|
const NdbDictionary::Table* exceptionsTable,
|
|
int &k);
|
|
bool check_optional_columns(const NdbDictionary::Table* mainTable,
|
|
const NdbDictionary::Table* exceptionsTable,
|
|
char* msg_buf,
|
|
uint msg_buf_len,
|
|
const char** msg,
|
|
int &k,
|
|
char *error_details,
|
|
uint error_details_len);
|
|
|
|
/* info about original table */
|
|
uint8 m_pk_cols;
|
|
uint16 m_cols;
|
|
/* Specifies if a column in the original table is nullable */
|
|
bool m_col_nullable[ NDB_MAX_ATTRIBUTES_IN_TABLE ];
|
|
|
|
/* info about exceptions table */
|
|
uint16 m_xcols;
|
|
const NdbDictionary::Table *m_ex_tab;
|
|
uint32 m_count;
|
|
/*
|
|
Extension tables can be extended with optional fields
|
|
NDB@OPT_TYPE
|
|
*/
|
|
bool m_extended;
|
|
uint32 m_op_type_pos;
|
|
uint32 m_conflict_cause_pos;
|
|
uint32 m_orig_transid_pos;
|
|
|
|
/*
|
|
Mapping of where the referenced primary key fields are
|
|
in the original table. Doesn't have to include all fields.
|
|
*/
|
|
uint16 m_key_attrids[ NDB_MAX_KEY_PARTS ];
|
|
/* Mapping of pk columns in original table to conflict table */
|
|
int m_key_data_pos[ NDB_MAX_KEY_PARTS ];
|
|
/* Mapping of non-pk columns in original table to conflict table */
|
|
int m_data_pos[ NDB_MAX_ATTRIBUTES_IN_TABLE ];
|
|
/* Specifies what version of a column is reference (before- or after-image) */
|
|
COLUMN_VERSION m_column_version[ NDB_MAX_ATTRIBUTES_IN_TABLE ];
|
|
|
|
/*
|
|
has_prefix_ci
|
|
|
|
Return true if a column has a specific prefix.
|
|
*/
|
|
bool has_prefix_ci(const char *col_name, const char *prefix, CHARSET_INFO *cs);
|
|
|
|
/*
|
|
has_suffix_ci
|
|
|
|
Return true if a column has a specific suffix
|
|
and sets the column_real_name to the column name
|
|
without the suffix.
|
|
*/
|
|
bool has_suffix_ci(const char *col_name,
|
|
const char *suffix,
|
|
CHARSET_INFO *cs,
|
|
char *col_name_real);
|
|
|
|
/*
|
|
find_column_name_ci
|
|
|
|
Search for column_name in table and
|
|
return true if found. Also return what
|
|
position column was found in pos and possible
|
|
position in the primary key in key_pos.
|
|
*/
|
|
bool find_column_name_ci(CHARSET_INFO *cs,
|
|
const char *col_name,
|
|
const NdbDictionary::Table* table,
|
|
int *pos,
|
|
int *no_key_cols);
|
|
};
|
|
|
|
struct NDB_CONFLICT_FN_SHARE{
|
|
const st_conflict_fn_def* m_conflict_fn;
|
|
|
|
/* info about original table */
|
|
uint16 m_resolve_column;
|
|
uint8 m_resolve_size;
|
|
uint8 m_flags;
|
|
|
|
ExceptionsTableWriter m_ex_tab_writer;
|
|
};
|
|
|
|
|
|
/* HAVE_NDB_BINLOG */
|
|
#endif
|
|
|
|
/**
|
|
* enum_slave_conflict_role
|
|
*
|
|
* These are the roles the Slave can play
|
|
* in asymmetric conflict algorithms
|
|
*/
|
|
|
|
enum enum_slave_conflict_role
|
|
{
|
|
SCR_NONE = 0,
|
|
SCR_SECONDARY = 1,
|
|
SCR_PRIMARY = 2,
|
|
SCR_PASS = 3
|
|
};
|
|
|
|
enum enum_slave_trans_conflict_apply_state
|
|
{
|
|
/* Normal with optional row-level conflict detection */
|
|
SAS_NORMAL,
|
|
|
|
/*
|
|
SAS_TRACK_TRANS_DEPENDENCIES
|
|
Track inter-transaction dependencies
|
|
*/
|
|
SAS_TRACK_TRANS_DEPENDENCIES,
|
|
|
|
/*
|
|
SAS_APPLY_TRANS_DEPENDENCIES
|
|
Apply only non conflicting transactions
|
|
*/
|
|
SAS_APPLY_TRANS_DEPENDENCIES
|
|
};
|
|
|
|
enum enum_slave_conflict_flags
|
|
{
|
|
/* Conflict detection Ops defined */
|
|
SCS_OPS_DEFINED = 1,
|
|
/* Conflict detected on table with transactional resolution */
|
|
SCS_TRANS_CONFLICT_DETECTED_THIS_PASS = 2
|
|
};
|
|
|
|
/*
|
|
State associated with the Slave thread
|
|
(From the Ndb handler's point of view)
|
|
*/
|
|
struct st_ndb_slave_state
|
|
{
|
|
/* Counter values for current slave transaction */
|
|
Uint32 current_violation_count[CFT_NUMBER_OF_CFTS];
|
|
|
|
/**
|
|
* Number of delete-delete conflicts detected
|
|
* (delete op is applied, and row does not exist)
|
|
*/
|
|
Uint32 current_delete_delete_count;
|
|
|
|
/**
|
|
* Number of reflected operations received that have been
|
|
* prepared (defined) to be executed.
|
|
*/
|
|
Uint32 current_reflect_op_prepare_count;
|
|
|
|
/**
|
|
* Number of reflected operations that were not applied as
|
|
* they hit some error during execution
|
|
*/
|
|
Uint32 current_reflect_op_discard_count;
|
|
|
|
/**
|
|
* Number of refresh operations that have been prepared
|
|
*/
|
|
Uint32 current_refresh_op_count;
|
|
|
|
/* Track the current epoch from the immediate master,
|
|
* and whether we've committed it
|
|
*/
|
|
Uint64 current_master_server_epoch;
|
|
bool current_master_server_epoch_committed;
|
|
|
|
Uint64 current_max_rep_epoch;
|
|
uint8 conflict_flags; /* enum_slave_conflict_flags */
|
|
/* Transactional conflict detection */
|
|
Uint32 retry_trans_count;
|
|
Uint32 current_trans_row_conflict_count;
|
|
Uint32 current_trans_row_reject_count;
|
|
Uint32 current_trans_in_conflict_count;
|
|
|
|
/* Last conflict epoch */
|
|
Uint64 last_conflicted_epoch;
|
|
|
|
/* Last stable epoch */
|
|
Uint64 last_stable_epoch;
|
|
|
|
/* Cumulative counter values */
|
|
Uint64 total_violation_count[CFT_NUMBER_OF_CFTS];
|
|
Uint64 total_delete_delete_count;
|
|
Uint64 total_reflect_op_prepare_count;
|
|
Uint64 total_reflect_op_discard_count;
|
|
Uint64 total_refresh_op_count;
|
|
Uint64 max_rep_epoch;
|
|
Uint32 sql_run_id;
|
|
/* Transactional conflict detection */
|
|
Uint64 trans_row_conflict_count;
|
|
Uint64 trans_row_reject_count;
|
|
Uint64 trans_detect_iter_count;
|
|
Uint64 trans_in_conflict_count;
|
|
Uint64 trans_conflict_commit_count;
|
|
|
|
static const Uint32 MAX_RETRY_TRANS_COUNT = 100;
|
|
|
|
/*
|
|
Slave Apply State
|
|
|
|
State of Binlog application from Ndb point of view.
|
|
*/
|
|
enum_slave_trans_conflict_apply_state trans_conflict_apply_state;
|
|
|
|
MEM_ROOT conflict_mem_root;
|
|
class DependencyTracker* trans_dependency_tracker;
|
|
|
|
/* Methods */
|
|
void atStartSlave();
|
|
int atPrepareConflictDetection(const NdbDictionary::Table* table,
|
|
const NdbRecord* key_rec,
|
|
const uchar* row_data,
|
|
Uint64 transaction_id,
|
|
bool& handle_conflict_now);
|
|
int atTransConflictDetected(Uint64 transaction_id);
|
|
int atConflictPreCommit(bool& retry_slave_trans);
|
|
|
|
void atBeginTransConflictHandling();
|
|
void atEndTransConflictHandling();
|
|
|
|
void atTransactionCommit(Uint64 epoch);
|
|
void atTransactionAbort();
|
|
void atResetSlave();
|
|
|
|
int atApplyStatusWrite(Uint32 master_server_id,
|
|
Uint32 row_server_id,
|
|
Uint64 row_epoch,
|
|
bool is_row_server_id_local);
|
|
bool verifyNextEpoch(Uint64 next_epoch,
|
|
Uint32 master_server_id) const;
|
|
|
|
void resetPerAttemptCounters();
|
|
|
|
static
|
|
bool checkSlaveConflictRoleChange(enum_slave_conflict_role old_role,
|
|
enum_slave_conflict_role new_role,
|
|
const char** failure_cause);
|
|
|
|
st_ndb_slave_state();
|
|
~st_ndb_slave_state();
|
|
};
|
|
|
|
#ifdef HAVE_NDB_BINLOG
|
|
|
|
const uint error_conflict_fn_violation= 9999;
|
|
|
|
/**
|
|
* Conflict function setup infrastructure
|
|
*/
|
|
int
|
|
parse_conflict_fn_spec(const char* conflict_fn_spec,
|
|
const st_conflict_fn_def** conflict_fn,
|
|
st_conflict_fn_arg* args,
|
|
Uint32* max_args,
|
|
char *msg, uint msg_len);
|
|
int
|
|
setup_conflict_fn(Ndb* ndb,
|
|
NDB_CONFLICT_FN_SHARE** ppcfn_share,
|
|
const char* dbName,
|
|
const char* tabName,
|
|
bool tableUsesBlobs,
|
|
bool tableBinlogUseUpdate,
|
|
const NdbDictionary::Table *ndbtab,
|
|
char *msg, uint msg_len,
|
|
const st_conflict_fn_def* conflict_fn,
|
|
const st_conflict_fn_arg* args,
|
|
const Uint32 num_args);
|
|
|
|
void
|
|
teardown_conflict_fn(Ndb* ndb,
|
|
NDB_CONFLICT_FN_SHARE* cfn_share);
|
|
|
|
void
|
|
slave_reset_conflict_fn(NDB_CONFLICT_FN_SHARE *cfn_share);
|
|
|
|
bool
|
|
is_exceptions_table(const char *table_name);
|
|
|
|
#endif /* HAVE_NDB_BINLOG */
|
|
|
|
|
|
/**
|
|
* show_ndb_conflict_status_vars
|
|
*
|
|
* Function called as part of SHOW STATUS / INFORMATION_SCHEMA
|
|
* tables.
|
|
* This function returns info about ndb_conflict related status
|
|
* vars
|
|
*/
|
|
int
|
|
show_ndb_conflict_status_vars(THD *thd, struct st_mysql_show_var *var, char *buff);
|
|
|
|
/* NDB_CONFLICT_H */
|
|
#endif
|