/* 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 #include #include // NAME_CHAR_LEN #include // MAX_REF_PARTS #include // 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