From: Andrei Elkin Date: February 3 2011 9:28am Subject: bzr commit into mysql-next-mr-wl5569 branch (andrei.elkin:3271) WL#5754 List-Archive: http://lists.mysql.com/commits/130305 Message-Id: <201102030928.p139SWJw015252@mysql1000.dsl.inet.fi> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="===============0208423017==" --===============0208423017== MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Content-Disposition: inline #At file:///home/andrei/MySQL/BZR/2a-23May/WL/mysql-next-mr-wl5569/ based on revid:andrei.elkin@stripped 3271 Andrei Elkin 2011-02-03 wl#5754 Parallel Query replication This patch implements a core of the design that is the DML queries. Queries can contain arbitrary features including temp tables. Todo: 1. Extend the framework to capture the remained DDL:s 2. fix the server startup opt (--mts-exp-run-query-in-parallel). 3. Refine only_sequential() to return TRUE in case the list of the updated db:s is bigger than the max, *and* in case the query is from the Old master. @ mysql-test/suite/rpl/r/rpl_parallel_multi_db.result new result file is added. @ mysql-test/suite/rpl/r/rpl_parallel_query.result new result file is added. @ mysql-test/suite/rpl/t/rpl_parallel_multi_db.test multi-db DML query test is added. todo: add triggers, sf(), SP. @ mysql-test/suite/rpl/t/rpl_parallel_query.test query with temporary tables testing. @ sql/binlog.cc gathering to be updated db in the DML case. Over-MAX_DBS_IN_QUERY_MTS-sized list won't be shipped to the slave. @ sql/log_event.cc master and slave (Coord's and Worker's) handling of updated db:s. The Coordinator's distribution changed to involve a loop per db, similaraly for the Worker at applying. @ sql/log_event.h Hardcoding the max updated db:s. Static allocation for updated db:s in Query log event is motivated by the fact of the event is shared by both C and W and the standard malloc/free can't be a reasonble choice either. Added a new status and changed dependent info. Added a new method to return the *list* of updated db:s which in all but Query case is just a wrapper over get_db(). @ sql/rpl_rli.cc fixing an error. @ sql/rpl_slave.cc Added a work-around/cleanup needed by the standard temp table closing algorithm. @ sql/sql_class.cc master side gathering updated db:s new memeber initializations. @ sql/sql_class.h master side gathering updated db:s list and accessor members. added: mysql-test/suite/rpl/r/rpl_parallel_multi_db.result mysql-test/suite/rpl/t/rpl_parallel_multi_db.test mysql-test/suite/rpl/t/rpl_parallel_query.test modified: mysql-test/suite/rpl/r/rpl_parallel_query.result sql/binlog.cc sql/log_event.cc sql/log_event.h sql/rpl_rli.cc sql/rpl_slave.cc sql/sql_class.cc sql/sql_class.h === added file 'mysql-test/suite/rpl/r/rpl_parallel_multi_db.result' --- a/mysql-test/suite/rpl/r/rpl_parallel_multi_db.result 1970-01-01 00:00:00 +0000 +++ b/mysql-test/suite/rpl/r/rpl_parallel_multi_db.result 2011-02-03 09:28:27 +0000 @@ -0,0 +1,141 @@ +include/master-slave.inc +[connection master] +include/stop_slave.inc +set @save.mts_slave_parallel_workers= @@global.mts_slave_parallel_workers; +set @@global.mts_slave_parallel_workers= 4; +include/start_slave.inc +Warnings: +Note 1726 Temporary failed transaction retry is not supported in Parallel Slave. Such failure will force the slave to stop. +create database d4; +create table d4.t8 (a int); +select round(rand()*8) into @var; +insert into d4.t8 values (@var); +create table d4.t7 (a int); +select round(rand()*8) into @var; +insert into d4.t7 values (@var); +create table d4.t6 (a int); +select round(rand()*8) into @var; +insert into d4.t6 values (@var); +create table d4.t5 (a int); +select round(rand()*8) into @var; +insert into d4.t5 values (@var); +create table d4.t4 (a int); +select round(rand()*8) into @var; +insert into d4.t4 values (@var); +create table d4.t3 (a int); +select round(rand()*8) into @var; +insert into d4.t3 values (@var); +create table d4.t2 (a int); +select round(rand()*8) into @var; +insert into d4.t2 values (@var); +create table d4.t1 (a int); +select round(rand()*8) into @var; +insert into d4.t1 values (@var); +create database d3; +create table d3.t8 (a int); +select round(rand()*8) into @var; +insert into d3.t8 values (@var); +create table d3.t7 (a int); +select round(rand()*8) into @var; +insert into d3.t7 values (@var); +create table d3.t6 (a int); +select round(rand()*8) into @var; +insert into d3.t6 values (@var); +create table d3.t5 (a int); +select round(rand()*8) into @var; +insert into d3.t5 values (@var); +create table d3.t4 (a int); +select round(rand()*8) into @var; +insert into d3.t4 values (@var); +create table d3.t3 (a int); +select round(rand()*8) into @var; +insert into d3.t3 values (@var); +create table d3.t2 (a int); +select round(rand()*8) into @var; +insert into d3.t2 values (@var); +create table d3.t1 (a int); +select round(rand()*8) into @var; +insert into d3.t1 values (@var); +create database d2; +create table d2.t8 (a int); +select round(rand()*8) into @var; +insert into d2.t8 values (@var); +create table d2.t7 (a int); +select round(rand()*8) into @var; +insert into d2.t7 values (@var); +create table d2.t6 (a int); +select round(rand()*8) into @var; +insert into d2.t6 values (@var); +create table d2.t5 (a int); +select round(rand()*8) into @var; +insert into d2.t5 values (@var); +create table d2.t4 (a int); +select round(rand()*8) into @var; +insert into d2.t4 values (@var); +create table d2.t3 (a int); +select round(rand()*8) into @var; +insert into d2.t3 values (@var); +create table d2.t2 (a int); +select round(rand()*8) into @var; +insert into d2.t2 values (@var); +create table d2.t1 (a int); +select round(rand()*8) into @var; +insert into d2.t1 values (@var); +create database d1; +create table d1.t8 (a int); +select round(rand()*8) into @var; +insert into d1.t8 values (@var); +create table d1.t7 (a int); +select round(rand()*8) into @var; +insert into d1.t7 values (@var); +create table d1.t6 (a int); +select round(rand()*8) into @var; +insert into d1.t6 values (@var); +create table d1.t5 (a int); +select round(rand()*8) into @var; +insert into d1.t5 values (@var); +create table d1.t4 (a int); +select round(rand()*8) into @var; +insert into d1.t4 values (@var); +create table d1.t3 (a int); +select round(rand()*8) into @var; +insert into d1.t3 values (@var); +create table d1.t2 (a int); +select round(rand()*8) into @var; +insert into d1.t2 values (@var); +create table d1.t1 (a int); +select round(rand()*8) into @var; +insert into d1.t1 values (@var); +include/diff_tables.inc [master:d4.t8, slave:d4.t8] +include/diff_tables.inc [master:d4.t7, slave:d4.t7] +include/diff_tables.inc [master:d4.t6, slave:d4.t6] +include/diff_tables.inc [master:d4.t5, slave:d4.t5] +include/diff_tables.inc [master:d4.t4, slave:d4.t4] +include/diff_tables.inc [master:d4.t3, slave:d4.t3] +include/diff_tables.inc [master:d4.t2, slave:d4.t2] +include/diff_tables.inc [master:d4.t1, slave:d4.t1] +include/diff_tables.inc [master:d3.t8, slave:d3.t8] +include/diff_tables.inc [master:d3.t7, slave:d3.t7] +include/diff_tables.inc [master:d3.t6, slave:d3.t6] +include/diff_tables.inc [master:d3.t5, slave:d3.t5] +include/diff_tables.inc [master:d3.t4, slave:d3.t4] +include/diff_tables.inc [master:d3.t3, slave:d3.t3] +include/diff_tables.inc [master:d3.t2, slave:d3.t2] +include/diff_tables.inc [master:d3.t1, slave:d3.t1] +include/diff_tables.inc [master:d2.t8, slave:d2.t8] +include/diff_tables.inc [master:d2.t7, slave:d2.t7] +include/diff_tables.inc [master:d2.t6, slave:d2.t6] +include/diff_tables.inc [master:d2.t5, slave:d2.t5] +include/diff_tables.inc [master:d2.t4, slave:d2.t4] +include/diff_tables.inc [master:d2.t3, slave:d2.t3] +include/diff_tables.inc [master:d2.t2, slave:d2.t2] +include/diff_tables.inc [master:d2.t1, slave:d2.t1] +include/diff_tables.inc [master:d1.t8, slave:d1.t8] +include/diff_tables.inc [master:d1.t7, slave:d1.t7] +include/diff_tables.inc [master:d1.t6, slave:d1.t6] +include/diff_tables.inc [master:d1.t5, slave:d1.t5] +include/diff_tables.inc [master:d1.t4, slave:d1.t4] +include/diff_tables.inc [master:d1.t3, slave:d1.t3] +include/diff_tables.inc [master:d1.t2, slave:d1.t2] +include/diff_tables.inc [master:d1.t1, slave:d1.t1] +set @@global.mts_slave_parallel_workers= @save.mts_slave_parallel_workers; === modified file 'mysql-test/suite/rpl/r/rpl_parallel_query.result' --- a/mysql-test/suite/rpl/r/rpl_parallel_query.result 2011-01-20 13:39:00 +0000 +++ b/mysql-test/suite/rpl/r/rpl_parallel_query.result 2011-02-03 09:28:27 +0000 @@ -3,28 +3,49 @@ include/master-slave.inc call mtr.add_suppression('Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT. Statement accesses nontransactional table as well as transactional or temporary table.*'); include/stop_slave.inc set @save.mts_slave_parallel_workers= @@global.mts_slave_parallel_workers; -set @@global.mts_slave_parallel_workers= 2; +set @@global.mts_slave_parallel_workers= 4; include/start_slave.inc Warnings: Note 1726 Temporary failed transaction retry is not supported in Parallel Slave. Such failure will force the slave to stop. -create database d1; -use d1; -create table d1.t1 (a int auto_increment primary key, b int) engine=innodb; -create temporary table tt_1 (a int auto_increment primary key); -insert into tt_1 values (null); -insert into tt_1 values (null); -insert into d1.t1 (b) select count(*) from tt_1; create database d2; use d2; create table d2.t1 (a int auto_increment primary key, b int) engine=innodb; -create temporary table tt_1 (a int auto_increment primary key); -insert into tt_1 values (null); -insert into tt_1 values (null); -insert into d2.t1 (b) select count(*) from tt_1; +insert into d2.t1 (b) select count(*) from tt_##; +create database d1; +use d1; +create table d1.t1 (a int auto_increment primary key, b int) engine=innodb; +insert into d1.t1 (b) select count(*) from tt_##; +create database d4; +use d4; +create table d4.t1 (a int auto_increment primary key, b int) engine=innodb; +insert into d4.t1 (b) select count(*) from tt_##; +create database d3; +use d3; +create table d3.t1 (a int auto_increment primary key, b int) engine=innodb; +insert into d3.t1 (b) select count(*) from tt_##; +include/diff_tables.inc [master:d4.t1, slave:d4.t1] +include/diff_tables.inc [master:d3.t1, slave:d3.t1] include/diff_tables.inc [master:d2.t1, slave:d2.t1] include/diff_tables.inc [master:d1.t1, slave:d1.t1] +drop temporary table tt_8; +drop temporary table tt_7; +drop temporary table tt_6; +drop temporary table tt_5; +drop temporary table tt_4; +drop temporary table tt_3; +drop temporary table tt_2; drop temporary table tt_1; +drop database d2; drop database d1; +drop temporary table tt_8; +drop temporary table tt_7; +drop temporary table tt_6; +drop temporary table tt_5; +drop temporary table tt_4; +drop temporary table tt_3; +drop temporary table tt_2; drop temporary table tt_1; +drop database d4; +drop database d3; include/stop_slave.inc set @@global.mts_slave_parallel_workers= @save.mts_slave_parallel_workers; === added file 'mysql-test/suite/rpl/t/rpl_parallel_multi_db.test' --- a/mysql-test/suite/rpl/t/rpl_parallel_multi_db.test 1970-01-01 00:00:00 +0000 +++ b/mysql-test/suite/rpl/t/rpl_parallel_multi_db.test 2011-02-03 09:28:27 +0000 @@ -0,0 +1,123 @@ +# +# WL#5569 MTS +# +# The test verifies correctness of Query event parallelization when +# a DML Query modifies multiple databases. +# + +--source include/master-slave.inc +--source include/have_binlog_format_statement.inc + +--disable_query_log +call mtr.add_suppression('.*Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT.*'); +--enable_query_log + +# restart in Parallel + +let $workers= 4; + +connection slave; + +source include/stop_slave.inc; +set @save.mts_slave_parallel_workers= @@global.mts_slave_parallel_workers; +eval set @@global.mts_slave_parallel_workers= $workers; + +source include/start_slave.inc; + +let $dbs= 4; +let $tables= 8; +let $queries= `select $dbs*$tables * 8`; + +# +# 1. Case of multi-update +# + +connection master; + +# create & populate + +let $n= $dbs; +while ($n) +{ + eval create database d$n; + let $m= $tables; + while ($m) + { + eval create table d$n.t$m (a int); + eval select round(rand()*$tables) into @var; + eval insert into d$n.t$m values (@var); + dec $m; + } + dec $n; +} + + +# operate to check consistency in the end + +let $k= $queries; + +--disable_query_log +--disable_warnings +while ($k) +{ + let $n1= `select floor(rand()*$dbs + 1)`; + let $m1= `select floor(rand()*$tables + 1)`; + let $n2= `select floor(rand()*$dbs + 1)`; + let $m2= `select floor(rand()*$tables + 1)`; + let $n3= `select floor(rand()*$dbs + 1)`; + let $m3= `select floor(rand()*$tables + 1)`; + let $n4= `select floor(rand()*$dbs + 1)`; + let $m4= `select floor(rand()*$tables + 1)`; + eval update d$n1.t$m1 as t_1, d$n2.t$m2 as t_2, d$n3.t$m3, d$n4.t$m4 as t_3 set t_1.a=t_2.a+ round(rand(10)), t_2.a=t_3.a+ round(rand(10)), t_3.a=t_1.a+ round(rand(10)), t_3.a=t_1.a+ round(rand(10)); + dec $k; +} +--enable_warnings +--enable_query_log + +sync_slave_with_master; + +# +# Consistency check +# + +let $n = $dbs; +while($n) +{ + let $m= $tables; + while ($m) + { + let $diff_tables=master:d$n.t$m, slave:d$n.t$m; + source include/diff_tables.inc; + dec $m; + } + dec $n; +} + + +# +# 2. Case of invoked routines (TODO: add parallel support to CREATE trig,sf(). +# + + +# +# Clean-up +# + +connection master; + +--disable_query_log + +let $n= $dbs; +while ($n) +{ + eval drop database d$n; + dec $n; +} + +--enable_query_log + +sync_slave_with_master; + +set @@global.mts_slave_parallel_workers= @save.mts_slave_parallel_workers; + +### TODO: --source include/rpl_end.inc === added file 'mysql-test/suite/rpl/t/rpl_parallel_query.test' --- a/mysql-test/suite/rpl/t/rpl_parallel_query.test 1970-01-01 00:00:00 +0000 +++ b/mysql-test/suite/rpl/t/rpl_parallel_query.test 2011-02-03 09:28:27 +0000 @@ -0,0 +1,169 @@ +# +# WL#5569 MTS +# +# The test verifies correctness of Query events parallelization. +# + +--source include/master-slave.inc +--source include/have_binlog_format_statement.inc + +call mtr.add_suppression('Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT. Statement accesses nontransactional table as well as transactional or temporary table.*'); + +let $temp_tables= 16; +let $workers= 4; + +connection slave; + +# restart in Parallel +source include/stop_slave.inc; +set @save.mts_slave_parallel_workers= @@global.mts_slave_parallel_workers; +eval set @@global.mts_slave_parallel_workers= $workers; + +source include/start_slave.inc; + +# Two connections each create 2 db:s a regular table and a set of temp tables. +# The temp tables contribute content to the regular tables. +# In the end there must be consistent data on both sides. + +connection master; + +let $n= `select round($workers/2)`; +let $n1= `select $n`; +while ($n) +{ + eval create database d$n1; + eval use d$n1; + eval create table d$n1.t1 (a int auto_increment primary key, b int) engine=innodb; + let $i= $temp_tables; + +--disable_query_log + while($i) + { + let $temp_rows= `select round(rand()*$temp_tables) + 1`; + let $k= $temp_rows; + eval create temporary table tt_$i (a int auto_increment primary key); + while($k) + { + eval insert into tt_$i values (null); + dec $k; + } + dec $i; + } +--enable_query_log + + let $j= `select floor(rand()*$temp_tables) + 1`; +--replace_regex /tt_.*/tt_##/ + eval insert into d$n1.t1 (b) select count(*) from tt_$j; + dec $n; + dec $n1; +} + +connection master1; + +let $n= `select round($workers/2)`; +let $n1= `select 2*$n`; +while ($n) +{ + eval create database d$n1; + eval use d$n1; + eval create table d$n1.t1 (a int auto_increment primary key, b int) engine=innodb; + let $i= $temp_tables; + +--disable_query_log + while($i) + { + let $temp_rows= `select round(rand()*$temp_tables) + 1`; + let $k= $temp_rows; + eval create temporary table tt_$i (a int auto_increment primary key); + while($k) + { + eval insert into tt_$i values (null); + dec $k; + } + dec $i; + } +--enable_query_log + + let $j= `select floor(rand()*$temp_tables) + 1`; +--replace_regex /tt_.*/tt_##/ + eval insert into d$n1.t1 (b) select count(*) from tt_$j; + dec $n; + dec $n1; +} + +sync_slave_with_master; + +# +# Consistency check +# + +let $n = $workers; +while($n) +{ + let $diff_tables=master:d$n.t1, slave:d$n.t1; + source include/diff_tables.inc; + + dec $n; +} + +# +# cleanup +# +# Temp tables are removed two ways explicitly and implicitly by disconnecting. +# + +connection master; + +let $i= `select round($temp_tables/2)`; +while($i) +{ + eval drop temporary table tt_$i; + dec $i; +} + +let $n= `select round($workers/2)`; +let $n1= `select $n`; +while ($n) +{ + eval drop database d$n1; + dec $n; + dec $n1; +} + + +connection master1; + +let $i= `select round($temp_tables/2)`; +while($i) +{ + eval drop temporary table tt_$i; + dec $i; +} + +sync_slave_with_master; +#connection slave; + +connection master1; +disconnect master1; + +# +# Clean-up +# + +connection master; + +let $n= `select round($workers/2)`; +let $n1= `select 2*$n`; +while ($n) +{ + eval drop database d$n1; + dec $n; + dec $n1; +} + +sync_slave_with_master; +source include/stop_slave.inc; + +set @@global.mts_slave_parallel_workers= @save.mts_slave_parallel_workers; + +### TODO: --source include/rpl_end.inc === modified file 'sql/binlog.cc' --- a/sql/binlog.cc 2010-12-27 18:54:41 +0000 +++ b/sql/binlog.cc 2011-02-03 09:28:27 +0000 @@ -4733,7 +4733,56 @@ int THD::decide_logging_format(TABLE_LIS is_write= TRUE; prev_write_table= table->table; + + if (variables.binlog_format != BINLOG_FORMAT_ROW) + { + /* + Master side of the STMT format events parallelization. + Write-locked table's db:s are stored in a abc-ordered name list. + The list can remain empty if the only database that + is updated is the default one. + In case the number of databases exceeds MAX_DBS_IN_QUERY_MTS + the list won't be sent to the slave either. + */ + + if (!binlog_updated_db_names) + { + binlog_updated_db_names= new List; /* thd->mem_root is used */ + } + if (binlog_updated_db_names->elements < MAX_DBS_IN_QUERY_MTS + 1) + { + char *after_db= strdup(table->db); + if (binlog_updated_db_names->elements != 0) + { + List_iterator it(*get_binlog_updated_db_names()); + + while (it++) + { + char *swap= NULL; + char **ref_cur_db= it.ref(); + int cmp= strcmp(after_db, *ref_cur_db); + + DBUG_ASSERT(!swap || cmp < 0); + + if (cmp == 0) + { + after_db= NULL; /* dup to ignore */ + break; + } + else if (swap || cmp > 0) + { + swap= *ref_cur_db; + *ref_cur_db= after_db; + after_db= swap; + } + } + } + if (after_db) + binlog_updated_db_names->push_back(after_db); + } + } } + flags_access_some_set |= flags; if (lex->sql_command != SQLCOM_CREATE_TABLE || === modified file 'sql/log_event.cc' --- a/sql/log_event.cc 2011-01-11 23:01:02 +0000 +++ b/sql/log_event.cc 2011-02-03 09:28:27 +0000 @@ -2489,17 +2489,25 @@ Slave_worker *Log_event::get_slave_worke if (contains_partition_info()) { - // a lot of things inside `get_slave_worker_id' - const_cast(rli)->last_assigned_worker= - worker= get_slave_worker(get_db(), const_cast(rli)); - get_dynamic(&rli->gaq->Q, (uchar*) &g, rli->gaq->assigned_group_index); - if (g.worker_id == (ulong) -1) // assign "offically" the current group + List_iterator it(*mts_get_dbs()); + while (it++) { - g.worker_id= worker->id; // todo/fixme: think of Slave_worker* here - set_dynamic(&rli->gaq->Q, (uchar*) &g, rli->gaq->assigned_group_index); - - DBUG_ASSERT(g.group_relay_log_name == NULL); + char **ref_cur_db= it.ref(); + // a lot of things inside `get_slave_worker_id' + const_cast(rli)->last_assigned_worker= + worker= get_slave_worker(*ref_cur_db, const_cast(rli)); + get_dynamic(&rli->gaq->Q, (uchar*) &g, rli->gaq->assigned_group_index); + if (g.worker_id == (ulong) -1) // assign "offically" the current group + { + g.worker_id= worker->id; // todo/fixme: think of Slave_worker* here + set_dynamic(&rli->gaq->Q, (uchar*) &g, rli->gaq->assigned_group_index); + + DBUG_ASSERT(g.group_relay_log_name == NULL); + } } + // releasing the Coord's mem-root from the updated dbs + // if (get_type_code() == QUERY_EVENT) + free_root(rli->info_thd->mem_root, MYF(MY_KEEP_PREALLOC)); } else // r if (rli->last_assigned_worker) @@ -2987,25 +2995,29 @@ int slave_worker_exec_job(Slave_worker * { if (ev->contains_partition_info()) { + List_iterator it(*ev->mts_get_dbs()); DYNAMIC_ARRAY *ep= &(w->curr_group_exec_parts->dynamic_ids); - uint i; - char key[NAME_LEN + 2]; - bool found= FALSE; - const char *dbname= ev->get_db(); - uchar dblength= (uint) strlen(dbname); - for (i= 0; i < ep->elements && !found; i++) + while (it++) { - get_dynamic(ep, (uchar*) key, i); - found= - (key[0] == dblength) && - (strncmp(key + 1, const_cast(dbname), dblength) == 0); - } - if (!found) - { - key[0]= dblength; - memcpy(key + 1, dbname, dblength + 1); - insert_dynamic(ep, (uchar*) key); + bool found= FALSE; + char key[NAME_LEN + 2]; + const char *dbname= *it.ref(); + uchar dblength= (uint) strlen(dbname); + + for (uint i= 0; i < ep->elements && !found; i++) + { + get_dynamic(ep, (uchar*) key, i); + found= + (key[0] == dblength) && + (strncmp(key + 1, const_cast(dbname), dblength) == 0); + } + if (!found) + { + key[0]= dblength; + memcpy(key + 1, dbname, dblength + 1); + insert_dynamic(ep, (uchar*) key); + } } } } @@ -3089,6 +3101,8 @@ err: if (ev && ev->get_type_code() != ROWS_QUERY_LOG_EVENT) delete ev; // after ev->update_pos() event is garbage + free_root(thd->mem_root, MYF(MY_KEEP_PREALLOC)); + DBUG_RETURN(error); } @@ -3354,6 +3368,30 @@ bool Query_log_event::write(IO_CACHE* fi start+= host.length; } } + + if (thd->get_binlog_updated_db_names() != NULL) + { + uchar dbs; + *start++= Q_UPDATED_DB_NAMES; + dbs= *start++= thd->get_binlog_updated_db_names()->elements; + + DBUG_ASSERT(dbs != 0); + /* + MAX_DBS_IN_QUERY_MTS + 1 is special no db:s is written + and event requires the sequential applying on slave. + */ + if (dbs != MAX_DBS_IN_QUERY_MTS + 1) + { + List_iterator_fast it(*thd->get_binlog_updated_db_names()); + char *db_name; + while ((db_name= it++)) + { + strcpy((char*) start, db_name); + start += strlen(db_name) + 1; + } + } + } + /* NOTE: When adding new status vars, please don't forget to update the MAX_SIZE_LOG_EVENT_STATUS in log_event.h and update the function @@ -3437,7 +3475,7 @@ Query_log_event::Query_log_event(THD* th lc_time_names_number(thd_arg->variables.lc_time_names->number), charset_database_number(0), table_map_for_update((ulonglong)thd_arg->table_map_for_update), - master_data_written(0) + master_data_written(0), mts_updated_dbs(0) { time_t end_time; @@ -3638,6 +3676,7 @@ code_name(int code) case Q_CHARSET_DATABASE_CODE: return "Q_CHARSET_DATABASE_CODE"; case Q_TABLE_MAP_FOR_UPDATE_CODE: return "Q_TABLE_MAP_FOR_UPDATE_CODE"; case Q_MASTER_DATA_WRITTEN_CODE: return "Q_MASTER_DATA_WRITTEN_CODE"; + case Q_UPDATED_DB_NAMES: return "Q_UPDATED_DB_NAMES"; } sprintf(buf, "CODE#%d", code); return buf; @@ -3675,7 +3714,8 @@ Query_log_event::Query_log_event(const c flags2_inited(0), sql_mode_inited(0), charset_inited(0), auto_increment_increment(1), auto_increment_offset(1), time_zone_len(0), lc_time_names_number(0), charset_database_number(0), - table_map_for_update(0), master_data_written(0) + table_map_for_update(0), master_data_written(0), + mts_updated_dbs(MAX_DBS_IN_QUERY_MTS + 1) { ulong data_len; uint32 tmp; @@ -3683,6 +3723,7 @@ Query_log_event::Query_log_event(const c Log_event::Byte *start; const Log_event::Byte *end; bool catalog_nz= 1; + DBUG_ENTER("Query_log_event::Query_log_event(char*,...)"); memset(&user, 0, sizeof(user)); @@ -3854,6 +3895,23 @@ Query_log_event::Query_log_event(const c CHECK_SPACE(pos, end, host.length); host.str= (char *)pos; pos+= host.length; + break; + } + case Q_UPDATED_DB_NAMES: + { + CHECK_SPACE(pos, end, 1); + mts_updated_dbs= *pos++; + if (mts_updated_dbs == MAX_DBS_IN_QUERY_MTS + 1) + break; + + DBUG_ASSERT(mts_updated_dbs != 0); + + for (uchar i= 0; i < mts_updated_dbs; i++) + { + strcpy(mts_updated_db_names[i], (char*) pos); + pos+= 1 + strlen((const char*) pos); + } + break; } default: /* That's why you must write status vars in growing order of code */ === modified file 'sql/log_event.h' --- a/sql/log_event.h 2011-01-11 23:01:02 +0000 +++ b/sql/log_event.h 2011-02-03 09:28:27 +0000 @@ -258,6 +258,14 @@ struct sql_ex_info #define INCIDENT_HEADER_LEN 2 #define HEARTBEAT_HEADER_LEN 0 #define IGNORABLE_HEADER_LEN 0 + +/** + MTS parameter. The maximum number of databases that a query modifies + and be logged in the binarry log with a special status variable containing + the database names to facilitate the parallel applying of the Query-event. +*/ +#define MAX_DBS_IN_QUERY_MTS 16 + /* Max number of possible extra bytes in a replication event compared to a packet (i.e. a query) sent from client to master; @@ -273,6 +281,8 @@ struct sql_ex_info 1 + 2 /* type, charset_database_number */ + \ 1 + 8 /* type, table_map_for_update */ + \ 1 + 4 /* type, master_data_written */ + \ + /* type, db_1, db_2, ... */ \ + 1 + (MAX_DBS_IN_QUERY_MTS * (1 + NAME_LEN)) + \ 1 + 16 + 1 + 60/* type, user_len, user, host_len, host */) #define MAX_LOG_EVENT_HEADER ( /* in order of Query_log_event::write */ \ LOG_EVENT_HEADER_LEN + /* write_header */ \ @@ -344,6 +354,8 @@ struct sql_ex_info #define Q_INVOKER 11 +#define Q_UPDATED_DB_NAMES 12 + /* Intvar event post-header */ /* Intvar event data */ @@ -1069,6 +1081,14 @@ public: { return thd ? thd->db : 0; } + + virtual List* mts_get_dbs() + { + List *res= new List; + res->push_back(strdup(get_db())); + return res; + } + #else Log_event() : temp_buf(0) {} /* avoid having to link mysqlbinlog against libpthread */ @@ -1851,12 +1871,26 @@ public: Q_MASTER_DATA_WRITTEN_CODE to the slave's server binlog. */ uint32 master_data_written; + /* + number of updated db:s by the query and their names. This info + is requested by both Coordinator and Worker. + */ + uchar mts_updated_dbs; + char mts_updated_db_names[MAX_DBS_IN_QUERY_MTS][NAME_LEN]; #ifdef MYSQL_SERVER Query_log_event(THD* thd_arg, const char* query_arg, ulong query_length, bool using_trans, bool direct, bool suppress_use, int error); const char* get_db() { return db; } + virtual List* mts_get_dbs() + { + DBUG_ASSERT(mts_updated_dbs > 0 && mts_updated_dbs < MAX_DBS_IN_QUERY_MTS + 1); + List *res= new List; + for (uchar i= 0; i < mts_updated_dbs; i++) + res->push_back(mts_updated_db_names[i]); + return res; + } #ifdef HAVE_REPLICATION void pack_info(Protocol* protocol); #endif /* HAVE_REPLICATION */ === modified file 'sql/rpl_rli.cc' --- a/sql/rpl_rli.cc 2011-01-20 13:39:00 +0000 +++ b/sql/rpl_rli.cc 2011-02-03 09:28:27 +0000 @@ -223,11 +223,13 @@ THD* mts_get_worker_thd() NULL : w->w_rli->info_thd; } +// TODO: remove the query in parallel option, rename mts_is_worker and +// s/ SYSTEM_THREAD_SLAVE_SQL/SYSTEM_THREAD_SLAVE_WORKER/ bool mts_is_coord_or_worker(THD *thd) { return thd->slave_thread && - thd->system_thread != SYSTEM_THREAD_SLAVE_IO && + thd->system_thread == SYSTEM_THREAD_SLAVE_SQL && (mts_get_worker_thd() != NULL); } === modified file 'sql/rpl_slave.cc' --- a/sql/rpl_slave.cc 2011-01-20 13:39:00 +0000 +++ b/sql/rpl_slave.cc 2011-02-03 09:28:27 +0000 @@ -3736,7 +3736,7 @@ pthread_handler_t handle_slave_worker(vo thd->thread_stack = (char*)&thd; pthread_detach_this_thread(); - if (init_slave_thread(thd, SLAVE_THD_SQL)) + if (init_slave_thread(thd, SLAVE_THD_SQL)) // todo: make thd->sys_thr= worker { // todo make SQL thread killed sql_print_error("Failed during slave worker initialization"); @@ -3804,6 +3804,7 @@ err: { mysql_mutex_lock(&LOCK_thread_count); THD_CHECK_SENTRY(thd); + thd->system_thread= NON_SYSTEM_THREAD; // tt closing work/around delete thd; mysql_mutex_unlock(&LOCK_thread_count); } === modified file 'sql/sql_class.cc' --- a/sql/sql_class.cc 2010-12-17 16:14:15 +0000 +++ b/sql/sql_class.cc 2011-02-03 09:28:27 +0000 @@ -503,6 +503,7 @@ THD::THD() user_time(0), in_sub_stmt(0), binlog_unsafe_warning_flags(0), binlog_table_maps(0), + binlog_updated_db_names(NULL), table_map_for_update(0), arg_of_last_insert_id_function(FALSE), first_successful_insert_id_in_prev_stmt(0), @@ -981,7 +982,6 @@ void THD::init_for_queries() transaction.xid_state.in_thd=1; } - /* Do what's needed when one invokes change user @@ -1069,7 +1069,6 @@ void THD::cleanup(void) mysql_mutex_unlock(&LOCK_user_locks); ull= NULL; } - cleanup_done=1; DBUG_VOID_RETURN; } @@ -1414,6 +1413,7 @@ void THD::cleanup_after_query() /* reset table map for multi-table update */ table_map_for_update= 0; m_binlog_invoker= FALSE; + binlog_updated_db_names= NULL; } === modified file 'sql/sql_class.h' --- a/sql/sql_class.h 2011-01-11 23:01:02 +0000 +++ b/sql/sql_class.h 2011-02-03 09:28:27 +0000 @@ -1721,6 +1721,11 @@ private: transaction cache. */ uint binlog_table_maps; + /* + String space-separated db names listing to be updated by the query databases + */ + List *binlog_updated_db_names; + public: void issue_unsafe_warnings(); @@ -1730,6 +1735,9 @@ public: void clear_binlog_table_maps() { binlog_table_maps= 0; } + List * get_binlog_updated_db_names() { + return binlog_updated_db_names; + } #endif /* MYSQL_CLIENT */ public: --===============0208423017== MIME-Version: 1.0 Content-Type: text/bzr-bundle; charset="us-ascii"; name="bzr/andrei.elkin@stripped" Content-Transfer-Encoding: 7bit Content-Disposition: inline # Bazaar merge directive format 2 (Bazaar 0.90) # revision_id: andrei.elkin@stripped # target_branch: file:///home/andrei/MySQL/BZR/2a-23May/WL/mysql-next-\ # mr-wl5569/ # testament_sha1: b007188f10ec2d4b11281412f80ebe5fe4571180 # timestamp: 2011-02-03 11:28:32 +0200 # source_branch: file:///home/andrei/MySQL/BZR/2a-23May/mysql-trunk/ # base_revision_id: andrei.elkin@stripped\ # ygoyseep8798hy84 # # Begin bundle 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 --===============0208423017==--