From 087cf0232864b60ce62550598f5903b766fe6c90 Mon Sep 17 00:00:00 2001 From: Kristian Nielsen Date: Thu, 9 Mar 2017 12:16:15 +0100 Subject: MDEV-12179: Per-engine mysql.gtid_slave_pos table Intermediate commit. Keep track of which mysql.gtid_slave_posXXX tables are available for each engine, by searching for all tables in the mysql schema with names that start with "gtid_slave_pos". The list is computed at server start when the GTID position is loaded, and it is re-computed on every START SLAVE command. This way, the DBA can manually add a table for a new engine, and it will be automatically picked up on next START SLAVE, so a full server restart is not needed. The list is not yet actually used in the code. --- sql/rpl_gtid.h | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'sql/rpl_gtid.h') diff --git a/sql/rpl_gtid.h b/sql/rpl_gtid.h index f638a084e38..c8af2c4946a 100644 --- a/sql/rpl_gtid.h +++ b/sql/rpl_gtid.h @@ -155,6 +155,13 @@ struct rpl_slave_state } }; + /* Descriptor for mysql.gtid_slave_posXXX table in specific engine. */ + struct gtid_pos_table { + struct gtid_pos_table *next; + handlerton *table_hton; + LEX_STRING table_name; + }; + /* Mapping from domain_id to its element. */ HASH hash; /* Mutex protecting access to the state. */ @@ -163,6 +170,7 @@ struct rpl_slave_state DYNAMIC_ARRAY gtid_sort_array; uint64 last_sub_id; + struct gtid_pos_table *gtid_pos_tables; bool loaded; rpl_slave_state(); @@ -192,6 +200,9 @@ struct rpl_slave_state int record_and_update_gtid(THD *thd, struct rpl_group_info *rgi); int check_duplicate_gtid(rpl_gtid *gtid, rpl_group_info *rgi); void release_domain_owner(rpl_group_info *rgi); + void set_gtid_pos_tables_list(struct gtid_pos_table *new_list); + struct gtid_pos_table *alloc_gtid_pos_table(LEX_STRING *table_name, handlerton *hton); + void free_gtid_pos_tables(struct gtid_pos_table *list); }; -- cgit v1.2.1 From c995ecbe9834bae31912e00cc98f7c872b63e1fb Mon Sep 17 00:00:00 2001 From: Kristian Nielsen Date: Thu, 9 Mar 2017 13:27:27 +0100 Subject: MDEV-12179: Per-engine mysql.gtid_slave_pos table Intermediate commit. For each GTID recorded in mysq.gtid_slave_pos, keep track of which engine the update was made in. This will be later used to know which rows can be deleted in the table of a given engine. --- sql/rpl_gtid.h | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) (limited to 'sql/rpl_gtid.h') diff --git a/sql/rpl_gtid.h b/sql/rpl_gtid.h index c8af2c4946a..ea278427061 100644 --- a/sql/rpl_gtid.h +++ b/sql/rpl_gtid.h @@ -112,6 +112,7 @@ struct rpl_slave_state uint64 sub_id; uint64 seq_no; uint32 server_id; + void *hton; }; /* Elements in the HASH that hold the state for one domain_id. */ @@ -158,7 +159,13 @@ struct rpl_slave_state /* Descriptor for mysql.gtid_slave_posXXX table in specific engine. */ struct gtid_pos_table { struct gtid_pos_table *next; - handlerton *table_hton; + /* + Use a void * here, rather than handlerton *, to make explicit that we + are not using the value to access any functionality in the engine. It + is just used as an opaque value to identify which engine we are using + for each GTID row. + */ + void *table_hton; LEX_STRING table_name; }; @@ -179,10 +186,10 @@ struct rpl_slave_state void truncate_hash(); ulong count() const { return hash.records; } int update(uint32 domain_id, uint32 server_id, uint64 sub_id, - uint64 seq_no, rpl_group_info *rgi); + uint64 seq_no, void *hton, rpl_group_info *rgi); int truncate_state_table(THD *thd); int record_gtid(THD *thd, const rpl_gtid *gtid, uint64 sub_id, - bool in_transaction, bool in_statement); + bool in_transaction, bool in_statement, void **out_hton); uint64 next_sub_id(uint32 domain_id); int iterate(int (*cb)(rpl_gtid *, void *), void *data, rpl_gtid *extra_gtids, uint32 num_extra, @@ -196,12 +203,13 @@ struct rpl_slave_state element *get_element(uint32 domain_id); int put_back_list(uint32 domain_id, list_element *list); - void update_state_hash(uint64 sub_id, rpl_gtid *gtid, rpl_group_info *rgi); + void update_state_hash(uint64 sub_id, rpl_gtid *gtid, void *hton, + rpl_group_info *rgi); int record_and_update_gtid(THD *thd, struct rpl_group_info *rgi); int check_duplicate_gtid(rpl_gtid *gtid, rpl_group_info *rgi); void release_domain_owner(rpl_group_info *rgi); void set_gtid_pos_tables_list(struct gtid_pos_table *new_list); - struct gtid_pos_table *alloc_gtid_pos_table(LEX_STRING *table_name, handlerton *hton); + struct gtid_pos_table *alloc_gtid_pos_table(LEX_STRING *table_name, void *hton); void free_gtid_pos_tables(struct gtid_pos_table *list); }; -- cgit v1.2.1 From 6a84473c28af10e072267bc811f280a49bdc8ca8 Mon Sep 17 00:00:00 2001 From: Kristian Nielsen Date: Tue, 14 Mar 2017 12:54:10 +0100 Subject: MDEV-12179: Per-engine mysql.gtid_slave_pos table Intermediate commit. This commit implements that record_gtid() selects a gtid_slave_posXXX table with a storage engine already in use by current transaction, if any. The default table mysql.gtid_slave_pos is used if no match can be found on storage engine, or for GTID position updates with no specific storage engine. Table discovery of mysql.gtid_slave_pos* happens on initial GTID state load as well as on every START SLAVE. Some effort is made to make this possible without additional locking. New tables are added using lock-free atomics. Removing tables requires stopping all slaves first. A warning is given in the error log when a table is removed but a non-stopped slave still has a reference to it. If multiple mysql.gtid_slave_posXXX tables with same storage engine exist, one is chosen arbitrarily to be used, with a warning in the error log. GTID data from all tables is still read, but only one among redundant tables with same storage engine will be updated. --- sql/rpl_gtid.h | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) (limited to 'sql/rpl_gtid.h') diff --git a/sql/rpl_gtid.h b/sql/rpl_gtid.h index ea278427061..b4f80fe3ddf 100644 --- a/sql/rpl_gtid.h +++ b/sql/rpl_gtid.h @@ -177,7 +177,27 @@ struct rpl_slave_state DYNAMIC_ARRAY gtid_sort_array; uint64 last_sub_id; + /* + List of tables available for durably storing the slave GTID position. + + Accesses to this table is protected by LOCK_slave_state. However for + efficiency, there is also a provision for read access to it from a running + slave without lock. + + An element can be added at the head of a list by storing the new + gtid_pos_tables pointer atomically with release semantics, to ensure that + the next pointer of the new element is visible to readers of the new list. + Other changes (like deleting or replacing elements) must happen only while + all SQL driver threads are stopped. LOCK_slave_state must be held in any + case. + + The list can be read without lock by an SQL driver thread or worker thread + by reading the gtid_pos_tables pointer atomically with acquire semantics, + to ensure that it will see the correct next pointer of a new head element. + */ struct gtid_pos_table *gtid_pos_tables; + /* The default entry in gtid_pos_tables, mysql.gtid_slave_pos. */ + struct gtid_pos_table *default_gtid_pos_table; bool loaded; rpl_slave_state(); @@ -188,6 +208,7 @@ struct rpl_slave_state int update(uint32 domain_id, uint32 server_id, uint64 sub_id, uint64 seq_no, void *hton, rpl_group_info *rgi); int truncate_state_table(THD *thd); + void select_gtid_pos_table(THD *thd, LEX_STRING *out_tablename); int record_gtid(THD *thd, const rpl_gtid *gtid, uint64 sub_id, bool in_transaction, bool in_statement, void **out_hton); uint64 next_sub_id(uint32 domain_id); @@ -208,7 +229,9 @@ struct rpl_slave_state int record_and_update_gtid(THD *thd, struct rpl_group_info *rgi); int check_duplicate_gtid(rpl_gtid *gtid, rpl_group_info *rgi); void release_domain_owner(rpl_group_info *rgi); - void set_gtid_pos_tables_list(struct gtid_pos_table *new_list); + void set_gtid_pos_tables_list(gtid_pos_table *new_list, + gtid_pos_table *default_entry); + void add_gtid_pos_table(gtid_pos_table *entry); struct gtid_pos_table *alloc_gtid_pos_table(LEX_STRING *table_name, void *hton); void free_gtid_pos_tables(struct gtid_pos_table *list); }; -- cgit v1.2.1 From fdf2d407707faf05b8b7d67662a70cc5537d15aa Mon Sep 17 00:00:00 2001 From: Kristian Nielsen Date: Fri, 24 Mar 2017 12:07:07 +0100 Subject: MDEV-12179: Per-engine mysql.gtid_slave_pos table Intermediate commit. Implement auto-creation of mysql.gtid_slave_pos* tables with needed engines, if listed in --gtid-pos-auto-engines. Uses an asynchronous approach to minimise locking overhead. The list of available tables is extended with a flag. Extra entries are added for --gtid-pos-auto-engines tables that do not exist yet, marked as not existing but ready for auto-creation. If record_gtid() needs a table marked for auto-creation, it sends a request to the slave background thread to create the table, and continues to use an existing table for the current and immediately coming transactions. As soon as the slave background thread has made the new table available, it will be used for all subsequent relevant transactions in record_gtid(). This asynchronous approach also avoids a lot of complex issues around trying to do DDL in the middle of an on-going transaction. --- sql/rpl_gtid.h | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'sql/rpl_gtid.h') diff --git a/sql/rpl_gtid.h b/sql/rpl_gtid.h index b4f80fe3ddf..e6a3ea1a667 100644 --- a/sql/rpl_gtid.h +++ b/sql/rpl_gtid.h @@ -157,6 +157,12 @@ struct rpl_slave_state }; /* Descriptor for mysql.gtid_slave_posXXX table in specific engine. */ + enum gtid_pos_table_state { + GTID_POS_AUTO_CREATE, + GTID_POS_CREATE_REQUESTED, + GTID_POS_CREATE_IN_PROGRESS, + GTID_POS_AVAILABLE + }; struct gtid_pos_table { struct gtid_pos_table *next; /* @@ -167,6 +173,7 @@ struct rpl_slave_state */ void *table_hton; LEX_STRING table_name; + uint8 state; }; /* Mapping from domain_id to its element. */ @@ -232,7 +239,8 @@ struct rpl_slave_state void set_gtid_pos_tables_list(gtid_pos_table *new_list, gtid_pos_table *default_entry); void add_gtid_pos_table(gtid_pos_table *entry); - struct gtid_pos_table *alloc_gtid_pos_table(LEX_STRING *table_name, void *hton); + struct gtid_pos_table *alloc_gtid_pos_table(LEX_STRING *table_name, + void *hton, rpl_slave_state::gtid_pos_table_state state); void free_gtid_pos_tables(struct gtid_pos_table *list); }; -- cgit v1.2.1 From 00eebb22435c871bbe9938582d96e6a3d1c00861 Mon Sep 17 00:00:00 2001 From: Kristian Nielsen Date: Thu, 20 Apr 2017 16:16:26 +0200 Subject: MDEV-12179: Per-engine mysql.gtid_slave_pos table Intermediate commit. Fix incorrect assertion. The hton in the list of pending GTIDs can be NULL, in the special case where we failed to load the mysql.gtid_slave_pos table at server startup, but nevertheless allow non-GTID replication to proceed. --- sql/rpl_gtid.h | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'sql/rpl_gtid.h') diff --git a/sql/rpl_gtid.h b/sql/rpl_gtid.h index e6a3ea1a667..4ff1ca563b8 100644 --- a/sql/rpl_gtid.h +++ b/sql/rpl_gtid.h @@ -112,6 +112,11 @@ struct rpl_slave_state uint64 sub_id; uint64 seq_no; uint32 server_id; + /* + hton of mysql.gtid_slave_pos* table used to record this GTID. + Can be NULL if the gtid table failed to load (eg. missing + mysql.gtid_slave_pos table following an upgrade). + */ void *hton; }; -- cgit v1.2.1 From 0db2cd7c7600c8cd03a1005d299cd0112d80b4ad Mon Sep 17 00:00:00 2001 From: Kristian Nielsen Date: Wed, 10 May 2017 09:56:31 +0200 Subject: MDEV-12179: Per-engine mysql.gtid_slave_pos table Intermediate commit. Fix compilation failure with different my_atomic implementation. The my_atomic_loadptr* takes void ** as first argument, so variables updated with it needs to be void * (it is not legal C to cast some_type ** to void **). --- sql/rpl_gtid.h | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'sql/rpl_gtid.h') diff --git a/sql/rpl_gtid.h b/sql/rpl_gtid.h index 4ff1ca563b8..452f3676704 100644 --- a/sql/rpl_gtid.h +++ b/sql/rpl_gtid.h @@ -206,10 +206,13 @@ struct rpl_slave_state The list can be read without lock by an SQL driver thread or worker thread by reading the gtid_pos_tables pointer atomically with acquire semantics, to ensure that it will see the correct next pointer of a new head element. + + The type is struct gtid_pos_table *, but needs to be void * to allow using + my_atomic operations without violating C strict aliasing semantics. */ - struct gtid_pos_table *gtid_pos_tables; + void * volatile gtid_pos_tables; /* The default entry in gtid_pos_tables, mysql.gtid_slave_pos. */ - struct gtid_pos_table *default_gtid_pos_table; + void * volatile default_gtid_pos_table; bool loaded; rpl_slave_state(); -- cgit v1.2.1