From: Andrei Elkin Date: November 30 2010 2:40pm Subject: bzr push into mysql-next-mr.crash-safe branch (andrei.elkin:3222) WL#5569 WL#5599 List-Archive: http://lists.mysql.com/commits/125514 Message-Id: <201011301440.oAUEenba018057@mysql1000.dsl.inet.fi> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="===============1514489534==" --===============1514489534== MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Content-Disposition: inline 3222 Andrei Elkin 2010-11-30 [merge] merging from from wl#5569 repo containing wl#5599 integration modified: sql/binlog.cc sql/log_event.cc sql/rpl_constants.h sql/rpl_info.cc sql/rpl_info.h sql/rpl_info_dummy.cc sql/rpl_info_dummy.h sql/rpl_info_factory.cc sql/rpl_info_factory.h sql/rpl_mi.cc sql/rpl_mi.h sql/rpl_rli.cc sql/rpl_rli.h sql/rpl_rli_pdb.cc sql/rpl_rli_pdb.h sql/rpl_slave.cc === modified file 'sql/log_event.cc' --- a/sql/log_event.cc 2010-11-30 02:08:01 +0000 +++ b/sql/log_event.cc 2010-11-30 14:39:40 +0000 @@ -2411,13 +2411,32 @@ void append_item_to_jobs(slave_job_item Slave_worker *w, Relay_log_info *rli) { THD *thd= rli->info_thd; + DBUG_ASSERT(thd == current_thd); thd_proc_info(thd, "Feeding an event to a worker thread"); mysql_mutex_lock(&rli->pending_jobs_lock); - // todo: modify condition for waiting basing on sizes of worker' queues + // C waits basing on *data* sizes in the queues + while (rli->mts_pending_jobs_size + + ((Log_event*) (job_item->data))->data_written + > rli->mts_pending_jobs_size_max) + { + const char *old_msg; + rli->mts_wqs_oversize= TRUE; + rli->wait_jobs++; + old_msg= thd->enter_cond(&rli->pending_jobs_cond, + &rli->pending_jobs_lock, + "Waiting for Workers to unload queues"); + mysql_cond_wait(&rli->pending_jobs_cond, &rli->pending_jobs_lock); + thd->exit_cond(old_msg); + mysql_mutex_lock(&rli->pending_jobs_lock); + if (thd->killed) + return; + } + rli->mts_pending_jobs_size += ((Log_event*) (job_item->data))->data_written; +#if 0 while (rli->pending_jobs >= rli->slave_pending_jobs_max) { const char *old_msg; @@ -2432,25 +2451,47 @@ void append_item_to_jobs(slave_job_item if (thd->killed) return; } +#endif + rli->stmt_jobs++; rli->pending_jobs++; mysql_mutex_unlock(&rli->pending_jobs_lock); + // sleep while all queue lengths are gt Underrun + // sleep time lasts the longer the further WQ:s shift to Overrun + // Workers report their U,O status + + if (rli->mts_wqs_underrun_w_id != (ulong) -1) + { + // todo: experiment with weight to get a good approximation formula + ulong nap_weight= rli->mts_wqs_overrun + 1; + my_sleep(nap_weight * rli->mts_coordinator_basic_nap); + } + if (!w->info_thd->killed) { int ret; mysql_mutex_lock(&w->jobs_lock); - ret= en_queue(&w->jobs, job_item); - - DBUG_ASSERT(ret >= 0); + // possible WQ overfill + while (!thd->killed && (ret= en_queue(&w->jobs, job_item)) == -1) + { + const char *old_msg; + old_msg= thd->enter_cond(&w->jobs_cond, &w->jobs_lock, + "Waiting for an event from sql thread"); + w->jobs.overfill= TRUE; + w->jobs.waited_overfill++; + mysql_cond_wait(&w->jobs_cond, &w->jobs_lock); + thd->exit_cond(old_msg); + mysql_mutex_lock(&w->jobs_lock); + } w->curr_jobs++; if (w->jobs.len == 1) mysql_cond_signal(&w->jobs_cond); - + mysql_mutex_unlock(&w->jobs_lock); } else @@ -2664,6 +2705,14 @@ int slave_worker_exec_job(Slave_worker * mysql_mutex_lock(&w->jobs_lock); de_queue(&w->jobs, job_item); + + /* possible overfill */ + if (w->jobs.len == w->jobs.s - 1 && w->jobs.overfill == TRUE) + { + w->jobs.overfill= FALSE; + mysql_cond_signal(&w->jobs_cond); + } + /* preserving signatures of existing methods. todo: convert update_pos(w->w_rli) -> update_pos(w) @@ -2672,7 +2721,6 @@ int slave_worker_exec_job(Slave_worker * if (!error) ev->update_pos(w->w_rli); - // delete ev; // after ev->update_pos() event is garbage mysql_mutex_unlock(&w->jobs_lock); @@ -2680,18 +2728,57 @@ int slave_worker_exec_job(Slave_worker * mysql_mutex_lock(&rli->pending_jobs_lock); rli->pending_jobs--; - DBUG_ASSERT(rli->pending_jobs < rli->slave_pending_jobs_max); + rli->mts_pending_jobs_size -= ev->data_written; + DBUG_ASSERT(rli->mts_pending_jobs_size < rli->mts_pending_jobs_size_max); + + // underrun + if ((rli->mts_worker_underrun_level * w->jobs.s) / 100 > w->jobs.len) + { + rli-> mts_wqs_underrun_w_id= w->id; + // todo: + // w->underrun_cnt++; + } else if (rli->mts_wqs_underrun_w_id == w->id) + { + rli->mts_wqs_underrun_w_id= (ulong) -1; + } - /* coordinator can be waiting */ + // overrun exploits the underrun level param + if (((100 - rli->mts_worker_underrun_level) * w->jobs.s) / 100 < w->jobs.len) + { + rli->mts_wqs_overrun++; + w->wq_overrun_set= TRUE; + // todo: + // w->underrun_cnt++; + } + else if (w->wq_overrun_set == TRUE) + { + rli->mts_wqs_overrun--; + w->wq_overrun_set= FALSE; + } + + DBUG_ASSERT(rli->mts_wqs_overrun >= 0); - if (rli->pending_jobs == rli->slave_pending_jobs_max - 1 || - rli->pending_jobs == 0) + /* coordinator can be waiting */ + if (rli->mts_pending_jobs_size < rli->mts_pending_jobs_size_max && + rli->mts_wqs_oversize) // TODO: unit/general test wqs_oversize + { + rli->mts_wqs_oversize= FALSE; mysql_cond_signal(&rli->pending_jobs_cond); + } + + //DBUG_ASSERT(rli->pending_jobs < rli->slave_pending_jobs_max); + // if (rli->pending_jobs == rli->slave_pending_jobs_max - 1 || + // rli->pending_jobs == 0) + // mysql_cond_signal(&rli->pending_jobs_cond); + mysql_mutex_unlock(&rli->pending_jobs_lock); w->stmt_jobs++; err: + // if (!ev) + // delete ev; // after ev->update_pos() event is garbage + DBUG_RETURN(error); } === modified file 'sql/mysqld.cc' --- a/sql/mysqld.cc 2010-11-26 21:08:30 +0000 +++ b/sql/mysqld.cc 2010-11-30 14:02:15 +0000 @@ -464,6 +464,10 @@ ulong slave_parallel_workers; ulong slave_max_pending_jobs; my_bool slave_local_timestamp_opt; my_bool opt_slave_run_query_in_parallel; +ulong opt_mts_partition_hash_soft_max; +ulonglong opt_mts_pending_jobs_size_max; +ulong opt_mts_coordinator_basic_nap; +ulong opt_mts_worker_underrun_level; ulong thread_cache_size=0; ulong binlog_cache_size=0; ulonglong max_binlog_cache_size=0; === modified file 'sql/mysqld.h' --- a/sql/mysqld.h 2010-11-26 21:08:30 +0000 +++ b/sql/mysqld.h 2010-11-30 14:02:15 +0000 @@ -176,6 +176,11 @@ extern ulong slave_parallel_workers; extern ulong slave_max_pending_jobs; extern my_bool slave_local_timestamp_opt; extern my_bool opt_slave_run_query_in_parallel; +extern ulong opt_mts_partition_hash_soft_max; +extern ulonglong opt_mts_pending_jobs_size_max; +extern ulong opt_mts_coordinator_basic_nap; +extern ulong opt_mts_worker_underrun_level; + extern uint max_user_connections; extern ulong what_to_log,flush_time; extern ulong max_prepared_stmt_count, prepared_stmt_count; === modified file 'sql/rpl_rli.cc' --- a/sql/rpl_rli.cc 2010-11-30 02:08:01 +0000 +++ b/sql/rpl_rli.cc 2010-11-30 14:39:40 +0000 @@ -162,9 +162,8 @@ Slave_worker* Relay_log_info::get_curren return this_worker; // can be asserted: !this_worker => C for (i= 0; i< workers.elements; i++) { - Slave_worker* w_i; - // todo: optimaze/replace the loop - get_dynamic(const_cast(&workers), (uchar*) &w_i, i); + Slave_worker* w_i= *(Slave_worker**) + dynamic_array_ptr(const_cast(&workers), i); if (w_i->info_thd == current_thd) { return w_i; === modified file 'sql/rpl_rli.h' --- a/sql/rpl_rli.h 2010-11-30 02:08:01 +0000 +++ b/sql/rpl_rli.h 2010-11-30 14:39:40 +0000 @@ -425,13 +425,19 @@ public: mysql_mutex_t pending_jobs_lock; mysql_cond_t pending_jobs_cond; ulong slave_pending_jobs_max; + ulonglong mts_pending_jobs_size; // actual mem usage by WQ:s + ulonglong mts_pending_jobs_size_max; // the max forcing to wait by C + bool mts_wqs_oversize; // C raises flag to wait some memory's released Slave_worker *last_assigned_worker; // a hint to partitioning func for some events Slave_committed_queue *gaq; DYNAMIC_ARRAY curr_group_assigned_parts; // CGAP DYNAMIC_ARRAY curr_group_da; // deferred array to hold part-info-free events bool curr_group_seen_begin; // current group started with B-event or not bool run_query_in_parallel; // Query's default db not the actual db as part - + volatile ulong mts_wqs_underrun_w_id; // Id of a Worker whose queue is getting empty + volatile long mts_wqs_overrun; // W to incr and decr + long mts_worker_underrun_level; // percent of WQ size at which Worker claims hungry + ulong mts_coordinator_basic_nap; // C sleeps to avoid WQs overrun Slave_worker* get_current_worker() const; Slave_worker* set_this_worker(Slave_worker *w) { return this_worker= w; } Slave_worker* this_worker; // used by w_rli. The cental rli has it as NULL. === modified file 'sql/rpl_rli_pdb.cc' --- a/sql/rpl_rli_pdb.cc 2010-11-30 02:08:01 +0000 +++ b/sql/rpl_rli_pdb.cc 2010-11-30 14:39:40 +0000 @@ -323,6 +323,19 @@ Slave_worker *get_slave_worker(const cha hash map. */ my_bool ret; + char *db= NULL; + + if (mapping_db_to_worker.records > opt_mts_partition_hash_soft_max) + { + /* remove zero-usage (todo: relatively rare scheduled) records */ + for (uint i= 0; i < mapping_db_to_worker.records; i++) + { + db_worker *entry= (db_worker*) my_hash_element(&mapping_db_to_worker, i); + if (entry->usage == 0) + my_hash_delete(&mapping_db_to_worker, (uchar*) entry); + } + } + mysql_mutex_unlock(&slave_worker_hash_lock); DBUG_PRINT("info", ("Inserting %s, %d", dbname, dblength)); @@ -330,7 +343,6 @@ Slave_worker *get_slave_worker(const cha Allocate an entry to be inserted and if the operation fails an error is returned. */ - char *db= NULL; if (!(db= (char *) my_malloc((size_t)dblength, MYF(0)))) goto err; if (!(entry= (db_worker *) my_malloc(sizeof(db_worker), MYF(0)))) === modified file 'sql/rpl_rli_pdb.h' --- a/sql/rpl_rli_pdb.h 2010-11-30 02:08:01 +0000 +++ b/sql/rpl_rli_pdb.h 2010-11-30 14:39:40 +0000 @@ -152,6 +152,11 @@ public: class Slave_jobs_queue : public circular_buffer_queue { +public: + + /* C marks with true, W signals back at queue back to available */ + bool overfill; + ulonglong waited_overfill; }; class Slave_worker : public Rpl_info_worker @@ -195,7 +200,7 @@ public: volatile int curr_jobs; // the current assignments ulong usage_partition; // number of different partitions handled by this worker volatile bool relay_log_change_notified; // Coord sets and resets, W can read - + bool wq_overrun_set; // W monitors its queue usage to incr/decr rli->mts_wqs_overrun /* We need to make this a dynamic field. /Alfranio */ === modified file 'sql/rpl_slave.cc' --- a/sql/rpl_slave.cc 2010-11-30 02:08:01 +0000 +++ b/sql/rpl_slave.cc 2010-11-30 14:39:40 +0000 @@ -2607,15 +2607,10 @@ int apply_event_and_update_pos(Log_event if (!ev->when) ev->when= my_time(0); ev->thd = thd; // because up to this point, ev->thd == 0 - /* - mts-II: - exec mode can change dynamicaly e.g SEQUENTIAL default -> PARALLEL - but only when the last group has ended - */ if (!rli->is_in_group() && rli->slave_exec_mode != slave_exec_mode_options) rli->slave_exec_mode= slave_exec_mode_options; - int reason= ev->shall_skip(rli); // TODO: MTS skip handling + int reason= ev->shall_skip(rli); if (reason == Log_event::EVENT_SKIP_COUNT) { sql_slave_skip_counter= --rli->slave_skip_counter; @@ -2623,7 +2618,13 @@ int apply_event_and_update_pos(Log_event } if (reason == Log_event::EVENT_SKIP_NOT) { + /* + MTS-todo: to test neither skipping nor delayed-exec logics + are affected by parallel exec mode. + */ + // Sleeps if needed, and unlocks rli->data_lock. + if (sql_delay_event(ev, thd, rli)) DBUG_RETURN(0); exec_res= ev->apply_event(rli); @@ -2812,11 +2813,17 @@ static int exec_relay_log_event(THD* thd /* This tests if the position of the beginning of the current event hits the UNTIL barrier. + MTS: since master,relay-group coordinates change per checkpoint + at the end of the checkpoint interval UNTIL can be left far behind. + Hence, UNTIL forces the sequential applying. + + TODO: to not let to start with UNTIL whenever @@global.max_slave_workers>0. */ if (rli->until_condition != Relay_log_info::UNTIL_NONE && rli->is_until_satisfied(thd, ev)) { char buf[22]; + sql_print_information("Slave SQL thread stopped because it reached its" " UNTIL position %s", llstr(rli->until_pos(), buf)); /* @@ -2875,6 +2882,13 @@ static int exec_relay_log_event(THD* thd else */ + /* MTS: Observation/todo. + + ROWS_QUERY_LOG_EVENT could be supported easier if + destructing part of handle_rows_query_log_event would be merged + with rli->cleanup_context() and the rest move into + ROWS...::do_apply_event + */ if (thd->variables.binlog_rows_query_log_events) handle_rows_query_log_event(ev, rli); @@ -3517,6 +3531,7 @@ pthread_handler_t handle_slave_worker(vo Slave_worker *w= (Slave_worker *) arg; Relay_log_info* rli= w->c_rli; ulong purge_cnt= 0; + ulonglong purge_size= 0; struct slave_job_item _item, *job_item= &_item; my_thread_init(); @@ -3574,6 +3589,7 @@ pthread_handler_t handle_slave_worker(vo while(de_queue(&w->jobs, job_item)) { purge_cnt++; + purge_size += ((Log_event*) (job_item->data))->data_written; DBUG_ASSERT(job_item->data); delete static_cast(job_item->data); } @@ -3584,6 +3600,7 @@ pthread_handler_t handle_slave_worker(vo mysql_mutex_lock(&rli->pending_jobs_lock); rli->pending_jobs -= purge_cnt; + rli->mts_pending_jobs_size -= purge_size; mysql_mutex_unlock(&rli->pending_jobs_lock); mysql_mutex_lock(&w->jobs_lock); @@ -3644,8 +3661,6 @@ int slave_start_single_worker(Relay_log_ w->usage_partition= 0; w->last_group_done_index= rli->gaq->s; // out of range - // Queue initialization - rli->slave_pending_jobs_max= ::slave_max_pending_jobs; // may change while offline w->jobs.s= rli->slave_pending_jobs_max + 1; my_init_dynamic_array(&w->jobs.Q, sizeof(Slave_job_item), w->jobs.s, 0); // todo: implement increment e.g n * 10; for (k= 0; k < w->jobs.s; k++) @@ -3656,7 +3671,9 @@ int slave_start_single_worker(Relay_log_ w->jobs.e= w->jobs.s; w->jobs.a= 0; w->jobs.len= rli->slave_pending_jobs_max + 1; // to first handshake - + w->jobs.overfill= FALSE; // todo: move into Slave_jobs_queue constructor + w->jobs.waited_overfill= 0; + w->wq_overrun_set= FALSE; set_dynamic(&rli->workers, (uchar*) &w, i); mysql_mutex_init(key_mutex_slave_parallel_worker[i], &w->jobs_lock, MY_MUTEX_INIT_FAST); @@ -3709,6 +3726,15 @@ int slave_start_workers(Relay_log_info * rli->gaq= new Slave_committed_queue(rli->get_group_master_log_name(), sizeof(Slave_job_group), ::slave_max_pending_jobs, n); + + // size of WQ stays fixed in one slave session + rli->slave_pending_jobs_max= ::slave_max_pending_jobs; + rli->mts_pending_jobs_size= 0; + rli->mts_pending_jobs_size_max= ::opt_mts_pending_jobs_size_max; + rli->mts_wqs_underrun_w_id= (ulong) -1; + rli->mts_wqs_overrun= 0; + rli->mts_coordinator_basic_nap= ::opt_mts_coordinator_basic_nap; + rli->mts_worker_underrun_level= ::opt_mts_worker_underrun_level; rli->mts_total_groups= 0; rli->slave_worker_is_error= NULL; rli->curr_group_seen_begin= NULL; @@ -3790,6 +3816,7 @@ void slave_stop_workers(Relay_log_info * } DBUG_ASSERT(rli->pending_jobs == 0); + DBUG_ASSERT(rli->mts_pending_jobs_size == 0); destroy_hash_workers(); delete rli->gaq; @@ -5353,9 +5380,9 @@ static Log_event* next_event(Relay_log_i } /* Reset the relay-log-change-notified status of Slave Workers */ - for (uint i; i < rli->workers.elements; i++) + for (uint i= 0; i < rli->workers.elements; i++) { - Slave_worker *w= (Slave_worker *) dynamic_array_ptr(&rli->workers, i); + Slave_worker *w= *(Slave_worker **) dynamic_array_ptr(&rli->workers, i); w->relay_log_change_notified= FALSE; } === modified file 'sql/sys_vars.cc' --- a/sql/sys_vars.cc 2010-11-26 21:08:30 +0000 +++ b/sql/sys_vars.cc 2010-11-30 14:02:15 +0000 @@ -3109,6 +3109,9 @@ static Sys_var_ulong Sys_slave_parallel_ "Number of worker threads for executing events in parallel ", GLOBAL_VAR(slave_parallel_workers), CMD_LINE(REQUIRED_ARG), VALID_RANGE(0, ULONG_MAX), DEFAULT(4), BLOCK_SIZE(1)); + +// TODO: redefine slave_max_pending_jobs + static Sys_var_ulong Sys_slave_max_pending_jobs( "slave_max_pending_jobs", "Number of replication events read out of Relay log and still not applied. " @@ -3119,7 +3122,7 @@ static Sys_var_ulong Sys_slave_max_pendi static Sys_var_mybool Sys_slave_local_timestamp( "slave_local_timestamp", "if enabled slave computes the event appying " "time value to implicitly affected timestamp columms. Otherwise (default) " - "installs prescribed by the master value.", + "installs prescribed by the master value", GLOBAL_VAR(slave_local_timestamp_opt), CMD_LINE(OPT_ARG), DEFAULT(FALSE)); static Sys_var_mybool Sys_slave_run_query_in_parallel( "slave_run_query_in_parallel", @@ -3127,6 +3130,32 @@ static Sys_var_mybool Sys_slave_run_quer "for parallel execution of Query_log_event ", GLOBAL_VAR(opt_slave_run_query_in_parallel), CMD_LINE(OPT_ARG), DEFAULT(FALSE)); +static Sys_var_ulong Sys_mts_partition_hash_soft_max( + "opt_mts_partition_hash_soft_max", + "Number of records in the mts partition hash below which " + "entries with zero usage are tolerated", + GLOBAL_VAR(opt_mts_partition_hash_soft_max), CMD_LINE(REQUIRED_ARG), + VALID_RANGE(0, ULONG_MAX), DEFAULT(16), BLOCK_SIZE(1)); +static Sys_var_ulonglong Sys_mts_pending_jobs_size_max( + "opt_mts_pending_jobs_size_max", + "Max size of Slave Worker queues holding yet not applied events." + "The least possible value must be not less than the master size " + "max_allowed_packet.", + GLOBAL_VAR(opt_mts_pending_jobs_size_max), CMD_LINE(REQUIRED_ARG), + VALID_RANGE(1024, (ulonglong)~(intptr)0), DEFAULT(16 * 1024*1024), + BLOCK_SIZE(1024), ON_CHECK(0)); +static Sys_var_ulong Sys_mts_coordinator_basic_nap( + "opt_mts_coordinator_basic_nap", + "Time in msec to sleep by MTS Coordinator to avoid the Worker queues " + "room overrun", + GLOBAL_VAR(opt_mts_coordinator_basic_nap), CMD_LINE(REQUIRED_ARG), + VALID_RANGE(0, ULONG_MAX), DEFAULT(0), BLOCK_SIZE(1)); +static Sys_var_ulong Sys_mts_worker_underrun_level( + "opt_mts_worker_underrun_level", + "percent of Worker queue size at which Worker is considered to become " + "hungry", + GLOBAL_VAR(opt_mts_worker_underrun_level), CMD_LINE(REQUIRED_ARG), + VALID_RANGE(0, 100), DEFAULT(0), BLOCK_SIZE(1)); #endif static bool check_locale(sys_var *self, THD *thd, set_var *var) --===============1514489534== 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: 6d42ff2471267b9fd2e73523183bdad217d0d589 # timestamp: 2010-11-30 16:40:49 +0200 # base_revision_id: alfranio.correia@stripped\ # a4b6giisgzl8pdiu # # Begin bundle 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 woSDvFLRAA== --===============1514489534==--