From: Pekka Nousiainen Date: May 29 2011 1:01pm Subject: bzr commit into mysql-5.1-telco-7.0-wl4124 branch (pekka.nousiainen:4386) WL#4124 List-Archive: http://lists.mysql.com/commits/138420 Message-Id: <20110529130150.8F1F15586A@sama.localdomain> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="===============0257905011==" --===============0257905011== MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Content-Disposition: inline #At file:///export/space/pekka/ms/ms-wl4124-70/ based on revid:pekka.nousiainen@stripped 4386 Pekka Nousiainen 2011-05-29 wl#4124 b06_handler.diff handler, simple MTR fixes modified: mysql-test/suite/ndb/r/ndb_basic.result mysql-test/suite/ndb/r/ndb_index_ordered.result mysql-test/suite/ndb/t/ndb_basic.test mysql-test/suite/ndb/t/ndb_index_ordered.test sql/ha_ndbcluster.cc sql/ha_ndbcluster.h sql/ha_ndbcluster_binlog.cc sql/ha_ndbcluster_binlog.h === modified file 'mysql-test/suite/ndb/r/ndb_basic.result' --- a/mysql-test/suite/ndb/r/ndb_basic.result 2011-04-28 07:47:53 +0000 +++ b/mysql-test/suite/ndb/r/ndb_basic.result 2011-05-29 13:01:46 +0000 @@ -69,6 +69,8 @@ Ndb_conflict_fn_max # Ndb_conflict_fn_old # Ndb_connect_count # Ndb_execute_count # +Ndb_index_stat_cache_clean # +Ndb_index_stat_cache_query # Ndb_number_of_data_nodes # Ndb_number_of_ready_data_nodes # Ndb_pruned_scan_count # @@ -88,6 +90,7 @@ ndb_extra_logging # ndb_force_send # ndb_index_stat_cache_entries # ndb_index_stat_enable # +ndb_index_stat_option # ndb_index_stat_update_freq # ndb_log_apply_status # ndb_log_bin # @@ -898,8 +901,8 @@ drop table t10,t20; # # bug #39872 - explain causes segv # (ndb_index_stat_enable=1 must be set to trigger bug) +# index stats v4: do not set, the v2 setting was local # -set ndb_index_stat_enable=1; CREATE TABLE `t1` ( `id` int(11) NOT NULL AUTO_INCREMENT, PRIMARY KEY (`id`) @@ -913,8 +916,8 @@ KEY `obj_id` (`obj_id`) # here we used to segv explain SELECT t1.id FROM t1 INNER JOIN t2 ON t1.id = t2.id WHERE t2.obj_id=1; id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t2 ref id,obj_id obj_id 5 const 1 Using where with pushed condition -1 SIMPLE t1 eq_ref PRIMARY PRIMARY 4 test.t2.id 1 +1 SIMPLE t2 ref id,obj_id obj_id 5 const # Using where with pushed condition +1 SIMPLE t1 eq_ref PRIMARY PRIMARY 4 test.t2.id # drop table t1, t2; # # Bug #47054 Cluster only deletes first matched row in delete with left join === modified file 'mysql-test/suite/ndb/r/ndb_index_ordered.result' --- a/mysql-test/suite/ndb/r/ndb_index_ordered.result 2010-10-20 17:39:53 +0000 +++ b/mysql-test/suite/ndb/r/ndb_index_ordered.result 2011-05-29 13:01:46 +0000 @@ -659,143 +659,6 @@ select count(*) from t1 where c<'bbb'; count(*) 1 drop table t1; -set autocommit=1; -show session variables like 'ndb_index_stat_%'; -Variable_name Value -ndb_index_stat_cache_entries 32 -ndb_index_stat_enable OFF -ndb_index_stat_update_freq 20 -set ndb_index_stat_enable = off; -show session variables like 'ndb_index_stat_%'; -Variable_name Value -ndb_index_stat_cache_entries 32 -ndb_index_stat_enable OFF -ndb_index_stat_update_freq 20 -create table t1 (a int, b int, c varchar(10) not null, -primary key using hash (a), index(b,c)) engine=ndb; -insert into t1 values -(1,10,'aaa'),(2,10,'bbb'),(3,10,'ccc'), -(4,20,'aaa'),(5,20,'bbb'),(6,20,'ccc'), -(7,30,'aaa'),(8,30,'bbb'),(9,30,'ccc'); -select count(*) from t1 where b < 10; -count(*) -0 -select count(*) from t1 where b >= 10 and c >= 'bbb'; -count(*) -6 -select count(*) from t1 where b > 10; -count(*) -6 -select count(*) from t1 where b <= 20 and c < 'ccc'; -count(*) -4 -select count(*) from t1 where b = 20 and c = 'ccc'; -count(*) -1 -select count(*) from t1 where b > 20; -count(*) -3 -select count(*) from t1 where b = 30 and c > 'aaa'; -count(*) -2 -select count(*) from t1 where b <= 20; -count(*) -6 -select count(*) from t1 where b >= 20 and c > 'aaa'; -count(*) -4 -drop table t1; -set ndb_index_stat_enable = on; -set ndb_index_stat_cache_entries = 0; -show session variables like 'ndb_index_stat_%'; -Variable_name Value -ndb_index_stat_cache_entries 0 -ndb_index_stat_enable ON -ndb_index_stat_update_freq 20 -create table t1 (a int, b int, c varchar(10) not null, -primary key using hash (a), index(b,c)) engine=ndb; -insert into t1 values -(1,10,'aaa'),(2,10,'bbb'),(3,10,'ccc'), -(4,20,'aaa'),(5,20,'bbb'),(6,20,'ccc'), -(7,30,'aaa'),(8,30,'bbb'),(9,30,'ccc'); -select count(*) from t1 where b < 10; -count(*) -0 -select count(*) from t1 where b >= 10 and c >= 'bbb'; -count(*) -6 -select count(*) from t1 where b > 10; -count(*) -6 -select count(*) from t1 where b <= 20 and c < 'ccc'; -count(*) -4 -select count(*) from t1 where b = 20 and c = 'ccc'; -count(*) -1 -select count(*) from t1 where b > 20; -count(*) -3 -select count(*) from t1 where b = 30 and c > 'aaa'; -count(*) -2 -select count(*) from t1 where b <= 20; -count(*) -6 -select count(*) from t1 where b >= 20 and c > 'aaa'; -count(*) -4 -drop table t1; -set ndb_index_stat_enable = on; -set ndb_index_stat_cache_entries = 4; -set ndb_index_stat_update_freq = 2; -show session variables like 'ndb_index_stat_%'; -Variable_name Value -ndb_index_stat_cache_entries 4 -ndb_index_stat_enable ON -ndb_index_stat_update_freq 2 -create table t1 (a int, b int, c varchar(10) not null, -primary key using hash (a), index(b,c)) engine=ndb; -insert into t1 values -(1,10,'aaa'),(2,10,'bbb'),(3,10,'ccc'), -(4,20,'aaa'),(5,20,'bbb'),(6,20,'ccc'), -(7,30,'aaa'),(8,30,'bbb'),(9,30,'ccc'); -select count(*) from t1 where b < 10; -count(*) -0 -select count(*) from t1 where b >= 10 and c >= 'bbb'; -count(*) -6 -select count(*) from t1 where b > 10; -count(*) -6 -select count(*) from t1 where b <= 20 and c < 'ccc'; -count(*) -4 -select count(*) from t1 where b = 20 and c = 'ccc'; -count(*) -1 -select count(*) from t1 where b > 20; -count(*) -3 -select count(*) from t1 where b = 30 and c > 'aaa'; -count(*) -2 -select count(*) from t1 where b <= 20; -count(*) -6 -select count(*) from t1 where b >= 20 and c > 'aaa'; -count(*) -4 -drop table t1; -set ndb_index_stat_enable = @@global.ndb_index_stat_enable; -set ndb_index_stat_cache_entries = @@global.ndb_index_stat_cache_entries; -set ndb_index_stat_update_freq = @@global.ndb_index_stat_update_freq; -show session variables like 'ndb_index_stat_%'; -Variable_name Value -ndb_index_stat_cache_entries 32 -ndb_index_stat_enable OFF -ndb_index_stat_update_freq 20 create table t1 (a int primary key) engine = ndb; insert into t1 values (1), (2), (3); begin; === modified file 'mysql-test/suite/ndb/t/ndb_basic.test' --- a/mysql-test/suite/ndb/t/ndb_basic.test 2011-04-11 13:36:12 +0000 +++ b/mysql-test/suite/ndb/t/ndb_basic.test 2011-05-29 13:01:46 +0000 @@ -758,8 +758,9 @@ drop table t10,t20; --echo # --echo # bug #39872 - explain causes segv --echo # (ndb_index_stat_enable=1 must be set to trigger bug) +--echo # index stats v4: do not set, the v2 setting was local --echo # -set ndb_index_stat_enable=1; +# set ndb_index_stat_enable=1; CREATE TABLE `t1` ( `id` int(11) NOT NULL AUTO_INCREMENT, PRIMARY KEY (`id`) @@ -771,6 +772,7 @@ CREATE TABLE `t2` ( KEY `obj_id` (`obj_id`) ) ENGINE=ndbcluster DEFAULT CHARSET=utf8; --echo # here we used to segv +--replace_column 9 # explain SELECT t1.id FROM t1 INNER JOIN t2 ON t1.id = t2.id WHERE t2.obj_id=1; drop table t1, t2; === modified file 'mysql-test/suite/ndb/t/ndb_index_ordered.test' --- a/mysql-test/suite/ndb/t/ndb_index_ordered.test 2010-10-20 17:39:53 +0000 +++ b/mysql-test/suite/ndb/t/ndb_index_ordered.test 2011-05-29 13:01:46 +0000 @@ -355,78 +355,7 @@ insert into t1 (a, c) values (1,'aaa'),( select count(*) from t1 where c<'bbb'; drop table t1; -# -- index statistics -- - -set autocommit=1; -show session variables like 'ndb_index_stat_%'; - -set ndb_index_stat_enable = off; -show session variables like 'ndb_index_stat_%'; - -create table t1 (a int, b int, c varchar(10) not null, - primary key using hash (a), index(b,c)) engine=ndb; -insert into t1 values - (1,10,'aaa'),(2,10,'bbb'),(3,10,'ccc'), - (4,20,'aaa'),(5,20,'bbb'),(6,20,'ccc'), - (7,30,'aaa'),(8,30,'bbb'),(9,30,'ccc'); -select count(*) from t1 where b < 10; -select count(*) from t1 where b >= 10 and c >= 'bbb'; -select count(*) from t1 where b > 10; -select count(*) from t1 where b <= 20 and c < 'ccc'; -select count(*) from t1 where b = 20 and c = 'ccc'; -select count(*) from t1 where b > 20; -select count(*) from t1 where b = 30 and c > 'aaa'; -select count(*) from t1 where b <= 20; -select count(*) from t1 where b >= 20 and c > 'aaa'; -drop table t1; - -set ndb_index_stat_enable = on; -set ndb_index_stat_cache_entries = 0; -show session variables like 'ndb_index_stat_%'; - -create table t1 (a int, b int, c varchar(10) not null, - primary key using hash (a), index(b,c)) engine=ndb; -insert into t1 values - (1,10,'aaa'),(2,10,'bbb'),(3,10,'ccc'), - (4,20,'aaa'),(5,20,'bbb'),(6,20,'ccc'), - (7,30,'aaa'),(8,30,'bbb'),(9,30,'ccc'); -select count(*) from t1 where b < 10; -select count(*) from t1 where b >= 10 and c >= 'bbb'; -select count(*) from t1 where b > 10; -select count(*) from t1 where b <= 20 and c < 'ccc'; -select count(*) from t1 where b = 20 and c = 'ccc'; -select count(*) from t1 where b > 20; -select count(*) from t1 where b = 30 and c > 'aaa'; -select count(*) from t1 where b <= 20; -select count(*) from t1 where b >= 20 and c > 'aaa'; -drop table t1; - -set ndb_index_stat_enable = on; -set ndb_index_stat_cache_entries = 4; -set ndb_index_stat_update_freq = 2; -show session variables like 'ndb_index_stat_%'; - -create table t1 (a int, b int, c varchar(10) not null, - primary key using hash (a), index(b,c)) engine=ndb; -insert into t1 values - (1,10,'aaa'),(2,10,'bbb'),(3,10,'ccc'), - (4,20,'aaa'),(5,20,'bbb'),(6,20,'ccc'), - (7,30,'aaa'),(8,30,'bbb'),(9,30,'ccc'); -select count(*) from t1 where b < 10; -select count(*) from t1 where b >= 10 and c >= 'bbb'; -select count(*) from t1 where b > 10; -select count(*) from t1 where b <= 20 and c < 'ccc'; -select count(*) from t1 where b = 20 and c = 'ccc'; -select count(*) from t1 where b > 20; -select count(*) from t1 where b = 30 and c > 'aaa'; -select count(*) from t1 where b <= 20; -select count(*) from t1 where b >= 20 and c > 'aaa'; -drop table t1; - -set ndb_index_stat_enable = @@global.ndb_index_stat_enable; -set ndb_index_stat_cache_entries = @@global.ndb_index_stat_cache_entries; -set ndb_index_stat_update_freq = @@global.ndb_index_stat_update_freq; -show session variables like 'ndb_index_stat_%'; +# index stats v4: old v2 tests are not meaningful and are removed # End of 4.1 tests === modified file 'sql/ha_ndbcluster.cc' --- a/sql/ha_ndbcluster.cc 2011-04-29 11:45:56 +0000 +++ b/sql/ha_ndbcluster.cc 2011-05-29 13:01:46 +0000 @@ -65,6 +65,7 @@ static ulong opt_ndb_wait_connected; ulong opt_ndb_wait_setup; static ulong opt_ndb_cache_check_time; static uint opt_ndb_cluster_connection_pool; +static char* opt_ndb_index_stat_option; static char* opt_ndb_connectstring; static uint opt_ndb_nodeid; @@ -178,7 +179,7 @@ static MYSQL_THDVAR_BOOL( static MYSQL_THDVAR_ULONG( index_stat_cache_entries, /* name */ PLUGIN_VAR_NOCMDARG, - "", + "Obsolete (ignored and will be removed later).", NULL, /* check func. */ NULL, /* update func. */ 32, /* default */ @@ -191,7 +192,7 @@ static MYSQL_THDVAR_ULONG( static MYSQL_THDVAR_ULONG( index_stat_update_freq, /* name */ PLUGIN_VAR_NOCMDARG, - "", + "Obsolete (ignored and will be removed later).", NULL, /* check func. */ NULL, /* update func. */ 20, /* default */ @@ -404,6 +405,21 @@ pthread_cond_t COND_ndb_util_thread; pthread_cond_t COND_ndb_util_ready; pthread_handler_t ndb_util_thread_func(void *arg); +// Index stats thread variables +pthread_t ndb_index_stat_thread; +int ndb_index_stat_thread_running= 0; +pthread_mutex_t LOCK_ndb_index_stat_thread; +pthread_cond_t COND_ndb_index_stat_thread; +pthread_cond_t COND_ndb_index_stat_ready; +pthread_mutex_t ndb_index_stat_glob_mutex; +pthread_mutex_t ndb_index_stat_list_mutex; +pthread_mutex_t ndb_index_stat_stat_mutex; +pthread_cond_t ndb_index_stat_stat_cond; +pthread_handler_t ndb_index_stat_thread_func(void *arg); + +extern void ndb_index_stat_free(NDB_SHARE *share); +extern void ndb_index_stat_end(); + /* Status variables shown with 'show status like 'Ndb%' */ struct st_ndb_status { @@ -427,6 +443,9 @@ static struct st_ndb_status g_ndb_status static long g_ndb_status_conflict_fn_max= 0; static long g_ndb_status_conflict_fn_old= 0; +static long g_ndb_status_index_stat_cache_query = 0; +static long g_ndb_status_index_stat_cache_clean = 0; + long long g_event_data_count = 0; long long g_event_nondata_count = 0; long long g_event_bytes_count = 0; @@ -604,6 +623,11 @@ static int show_ndb_server_api_stats(THD return 0; } +SHOW_VAR ndb_status_index_stat_variables[]= { + {"cache_query", (char*) &g_ndb_status_index_stat_cache_query, SHOW_LONG}, + {"cache_clean", (char*) &g_ndb_status_index_stat_cache_clean, SHOW_LONG}, + {NullS, NullS, SHOW_LONG} +}; /* Error handling functions @@ -1124,7 +1148,19 @@ void ha_ndbcluster::set_rec_per_key() case ORDERED_INDEX: // 'Records pr. key' are unknown for non-unique indexes. // (May change when we get better index statistics.) + { + THD *thd= current_thd; + const bool index_stat_enable= THDVAR(NULL, index_stat_enable) && + THDVAR(thd, index_stat_enable); + if (index_stat_enable) + { + int err= ndb_index_stat_set_rpk(i); + if (err == 0) + break; + } + // no fallback method... break; + } default: DBUG_ASSERT(false); } @@ -2152,10 +2188,6 @@ static void ndb_init_index(NDB_INDEX_DAT data.unique_index= NULL; data.index= NULL; data.unique_index_attrid_map= NULL; - data.index_stat=NULL; - data.index_stat_cache_entries=0; - data.index_stat_update_freq=0; - data.index_stat_query_count=0; data.ndb_record_key= NULL; data.ndb_unique_record_key= NULL; data.ndb_unique_record_row= NULL; @@ -2167,10 +2199,6 @@ static void ndb_clear_index(NDBDICT *dic { my_free((char*)data.unique_index_attrid_map, MYF(0)); } - if (data.index_stat) - { - delete data.index_stat; - } if (data.ndb_unique_record_key) dict->releaseRecord(data.ndb_unique_record_key); if (data.ndb_unique_record_row) @@ -2242,25 +2270,6 @@ int ha_ndbcluster::add_index_handle(THD break; } while (1); m_index[index_no].index= index; - // ordered index - add stats - NDB_INDEX_DATA& d=m_index[index_no]; - delete d.index_stat; - d.index_stat=NULL; - if (THDVAR(thd, index_stat_enable)) - { - d.index_stat=new NdbIndexStat(index); - d.index_stat_cache_entries= THDVAR(thd, index_stat_cache_entries); - d.index_stat_update_freq= THDVAR(thd, index_stat_update_freq); - d.index_stat_query_count=0; - d.index_stat->alloc_cache(d.index_stat_cache_entries); - DBUG_PRINT("info", ("index %s stat=on cache_entries=%u update_freq=%u", - index->getName(), - d.index_stat_cache_entries, - d.index_stat_update_freq)); - } else - { - DBUG_PRINT("info", ("index %s stat=off", index->getName())); - } } if (idx_type == UNIQUE_ORDERED_INDEX || idx_type == UNIQUE_INDEX) { @@ -3530,6 +3539,11 @@ compute_index_bounds(NdbIndexScanOperati bound.high_key= NULL; bound.high_key_count= 0; } + DBUG_PRINT("info", ("start_flag=0x%x end_flag=0x%x" + " lo_keys=%d lo_incl=%d hi_keys=%d hi_incl=%d", + start_key?start_key->flag:0, end_key?end_key->flag:0, + bound.low_key_count, bound.low_inclusive, + bound.high_key_count, bound.high_inclusive)); } /** @@ -6068,9 +6082,11 @@ int ha_ndbcluster::info(uint flag) result= update_stats(thd, 1); break; } - if (flag & HA_STATUS_CONST) + /* RPK moved to variable part */ + if (flag & HA_STATUS_VARIABLE) { - DBUG_PRINT("info", ("HA_STATUS_CONST")); + /* No meaningful way to return error */ + DBUG_PRINT("info", ("rec_per_key")); set_rec_per_key(); } if (flag & HA_STATUS_ERRKEY) @@ -9864,7 +9880,51 @@ int ha_ndbcluster::ndb_optimize_table(TH int ha_ndbcluster::analyze(THD* thd, HA_CHECK_OPT* check_opt) { - return update_stats(thd, 1); + int err; + if ((err= update_stats(thd, 1)) != 0) + return err; + const bool index_stat_enable= THDVAR(NULL, index_stat_enable) && + THDVAR(thd, index_stat_enable); + if (index_stat_enable) + { + if ((err= analyze_index(thd)) != 0) + return err; + } + return 0; +} + +int +ha_ndbcluster::analyze_index(THD *thd) +{ + DBUG_ENTER("ha_ndbcluster::analyze_index"); + + Thd_ndb *thd_ndb= get_thd_ndb(thd); + Ndb *ndb= thd_ndb->ndb; + + uint inx_list[MAX_INDEXES]; + uint inx_count= 0; + + uint inx; + for (inx= 0; inx < table_share->keys; inx++) + { + NDB_INDEX_TYPE idx_type= get_index_type(inx); + + if ((idx_type == PRIMARY_KEY_ORDERED_INDEX || + idx_type == UNIQUE_ORDERED_INDEX || + idx_type == ORDERED_INDEX)) + { + if (inx_count < MAX_INDEXES) + inx_list[inx_count++]= inx; + } + } + + if (inx_count != 0) + { + int err= ndb_index_stat_analyze(ndb, inx_list, inx_count); + if (err != 0) + DBUG_RETURN(err); + } + DBUG_RETURN(0); } /* @@ -10868,6 +10928,14 @@ static int ndbcluster_init(void *p) pthread_cond_init(&COND_ndb_util_ready, NULL); pthread_cond_init(&COND_ndb_setup_complete, NULL); ndb_util_thread_running= -1; + pthread_mutex_init(&LOCK_ndb_index_stat_thread, MY_MUTEX_INIT_FAST); + pthread_cond_init(&COND_ndb_index_stat_thread, NULL); + pthread_cond_init(&COND_ndb_index_stat_ready, NULL); + pthread_mutex_init(&ndb_index_stat_glob_mutex, MY_MUTEX_INIT_FAST); + pthread_mutex_init(&ndb_index_stat_list_mutex, MY_MUTEX_INIT_FAST); + pthread_mutex_init(&ndb_index_stat_stat_mutex, MY_MUTEX_INIT_FAST); + pthread_cond_init(&ndb_index_stat_stat_cond, NULL); + ndb_index_stat_thread_running= -1; ndbcluster_terminating= 0; ndb_dictionary_is_mysqld= 1; ndb_setup_complete= 0; @@ -10964,6 +11032,44 @@ static int ndbcluster_init(void *p) goto ndbcluster_init_error; } + // Create index statistics thread + pthread_t tmp2; + if (pthread_create(&tmp2, &connection_attrib, ndb_index_stat_thread_func, 0)) + { + DBUG_PRINT("error", ("Could not create ndb index statistics thread")); + hash_free(&ndbcluster_open_tables); + pthread_mutex_destroy(&ndbcluster_mutex); + pthread_mutex_destroy(&LOCK_ndb_index_stat_thread); + pthread_cond_destroy(&COND_ndb_index_stat_thread); + pthread_cond_destroy(&COND_ndb_index_stat_ready); + pthread_mutex_destroy(&ndb_index_stat_glob_mutex); + pthread_mutex_destroy(&ndb_index_stat_list_mutex); + pthread_mutex_destroy(&ndb_index_stat_stat_mutex); + pthread_cond_destroy(&ndb_index_stat_stat_cond); + goto ndbcluster_init_error; + } + + /* Wait for the index statistics thread to start */ + pthread_mutex_lock(&LOCK_ndb_index_stat_thread); + while (ndb_index_stat_thread_running < 0) + pthread_cond_wait(&COND_ndb_index_stat_ready, &LOCK_ndb_index_stat_thread); + pthread_mutex_unlock(&LOCK_ndb_index_stat_thread); + + if (!ndb_index_stat_thread_running) + { + DBUG_PRINT("error", ("ndb index statistics thread exited prematurely")); + hash_free(&ndbcluster_open_tables); + pthread_mutex_destroy(&ndbcluster_mutex); + pthread_mutex_destroy(&LOCK_ndb_index_stat_thread); + pthread_cond_destroy(&COND_ndb_index_stat_thread); + pthread_cond_destroy(&COND_ndb_index_stat_ready); + pthread_mutex_destroy(&ndb_index_stat_glob_mutex); + pthread_mutex_destroy(&ndb_index_stat_list_mutex); + pthread_mutex_destroy(&ndb_index_stat_stat_mutex); + pthread_cond_destroy(&ndb_index_stat_stat_cond); + goto ndbcluster_init_error; + } + #ifndef NDB_NO_WAIT_SETUP ndb_wait_setup_func= ndb_wait_setup_func_impl; #endif @@ -10991,6 +11097,15 @@ static int ndbcluster_end(handlerton *ht DBUG_RETURN(0); ndbcluster_inited= 0; + /* wait for index stat thread to finish */ + sql_print_information("Stopping Cluster Index Statistics thread"); + pthread_mutex_lock(&LOCK_ndb_index_stat_thread); + ndbcluster_terminating= 1; + pthread_cond_signal(&COND_ndb_index_stat_thread); + while (ndb_index_stat_thread_running > 0) + pthread_cond_wait(&COND_ndb_index_stat_ready, &LOCK_ndb_index_stat_thread); + pthread_mutex_unlock(&LOCK_ndb_index_stat_thread); + /* wait for util and binlog thread to finish */ ndbcluster_binlog_end(NULL); @@ -11010,6 +11125,7 @@ static int ndbcluster_end(handlerton *ht } my_hash_free(&ndbcluster_open_tables); + ndb_index_stat_end(); ndbcluster_disconnect(); // cleanup ndb interface @@ -11020,6 +11136,9 @@ static int ndbcluster_end(handlerton *ht pthread_cond_destroy(&COND_ndb_util_thread); pthread_cond_destroy(&COND_ndb_util_ready); pthread_cond_destroy(&COND_ndb_setup_complete); + pthread_mutex_destroy(&LOCK_ndb_index_stat_thread); + pthread_cond_destroy(&COND_ndb_index_stat_thread); + pthread_cond_destroy(&COND_ndb_index_stat_ready); ndbcluster_global_schema_lock_deinit(); DBUG_RETURN(0); } @@ -11129,6 +11248,12 @@ void ha_ndbcluster::set_tabname(const ch } +/* + If there are no stored stats, should we do a tree-dive on all db + nodes. The result is fairly good but does mean a round-trip. + */ +static const bool g_ndb_records_in_range_tree_dive= false; + /* Determine roughly how many records are in the range specified */ ha_rows ha_ndbcluster::records_in_range(uint inx, key_range *min_key, @@ -11154,92 +11279,73 @@ ha_ndbcluster::records_in_range(uint inx memcmp(min_key->key, max_key->key, key_length)==0))) DBUG_RETURN(1); + // XXX why this if if ((idx_type == PRIMARY_KEY_ORDERED_INDEX || idx_type == UNIQUE_ORDERED_INDEX || - idx_type == ORDERED_INDEX) && - m_index[inx].index_stat != NULL) // --ndb-index-stat-enable=1 + idx_type == ORDERED_INDEX)) { THD *thd= current_thd; - NDB_INDEX_DATA& d=m_index[inx]; - const NDBINDEX* index= d.index; - Ndb *ndb= get_ndb(thd); - NdbTransaction* active_trans= m_thd_ndb ? m_thd_ndb->trans : 0; - NdbTransaction* trans=NULL; - int res=0; - Uint64 rows; + const bool index_stat_enable= THDVAR(NULL, index_stat_enable) && + THDVAR(thd, index_stat_enable); - do + if (index_stat_enable) { - // We must provide approx table rows - Uint64 table_rows=0; - if (stats.records != ~(ha_rows)0 && stats.records != 0) - { - table_rows = stats.records; - DBUG_PRINT("info", ("use info->records: %lu", (ulong) table_rows)); - } - else - { - if (update_stats(thd, 1)) - break; - table_rows= stats.records; - DBUG_PRINT("info", ("use db row_count: %lu", (ulong) table_rows)); - if (table_rows == 0) { - // Problem if autocommit=0 -#ifdef ndb_get_table_statistics_uses_active_trans - rows=0; - break; -#endif - } - } + ha_rows rows= HA_POS_ERROR; + int err= ndb_index_stat_get_rir(inx, min_key, max_key, &rows); + if (err == 0) + DBUG_RETURN(rows); + /*fall through*/ + } - /* - Query the index statistics for our range. - */ - if ((trans=active_trans) == NULL || - trans->commitStatus() != NdbTransaction::Started) - { - DBUG_PRINT("info", ("no active trans")); - if (! (trans=ndb->startTransaction())) - ERR_BREAK(ndb->getNdbError(), res); - } - - /* Create an IndexBound struct for the keys */ - NdbIndexScanOperation::IndexBound ib; - compute_index_bounds(ib, - key_info, - min_key, - max_key); - - ib.range_no= 0; - - // Decide if db should be contacted - int flags=0; - if (d.index_stat_query_count < d.index_stat_cache_entries || - (d.index_stat_update_freq != 0 && - d.index_stat_query_count % d.index_stat_update_freq == 0)) - { - DBUG_PRINT("info", ("force stat from db")); - flags|=NdbIndexStat::RR_UseDb; - } - if (d.index_stat->records_in_range(index, - trans, - d.ndb_record_key, - m_ndb_record, - &ib, - table_rows, - &rows, - flags) == -1) - ERR_BREAK(d.index_stat->getNdbError(), res); - d.index_stat_query_count++; - } while (0); + if (g_ndb_records_in_range_tree_dive) + { + NDB_INDEX_DATA& d=m_index[inx]; + const NDBINDEX* index= d.index; + Ndb *ndb= get_ndb(thd); + NdbTransaction* active_trans= m_thd_ndb ? m_thd_ndb->trans : 0; + NdbTransaction* trans=NULL; + int res=0; + Uint64 rows; - if (trans != active_trans && rows == 0) - rows = 1; - if (trans != active_trans && trans != NULL) - ndb->closeTransaction(trans); - if (res != 0) - DBUG_RETURN(HA_POS_ERROR); - DBUG_RETURN(rows); + do + { + if ((trans=active_trans) == NULL || + trans->commitStatus() != NdbTransaction::Started) + { + DBUG_PRINT("info", ("no active trans")); + if (! (trans=ndb->startTransaction())) + ERR_BREAK(ndb->getNdbError(), res); + } + + /* Create an IndexBound struct for the keys */ + NdbIndexScanOperation::IndexBound ib; + compute_index_bounds(ib, + key_info, + min_key, + max_key); + + ib.range_no= 0; + + NdbIndexStat is; + if (is.records_in_range(index, + trans, + d.ndb_record_key, + m_ndb_record, + &ib, + 0, + &rows, + 0) == -1) + ERR_BREAK(is.getNdbError(), res); + } while (0); + + if (trans != active_trans && rows == 0) + rows = 1; + if (trans != active_trans && trans != NULL) + ndb->closeTransaction(trans); + if (res == 0) + DBUG_RETURN(rows); + /*fall through*/ + } } /* Use simple heuristics to estimate fraction @@ -12119,6 +12225,8 @@ void ndbcluster_real_free_share(NDB_SHAR if (opt_ndb_extra_logging > 9) sql_print_information ("ndbcluster_real_free_share: %s use_count: %u", (*share)->key, (*share)->use_count); + ndb_index_stat_free(*share); + my_hash_delete(&ndbcluster_open_tables, (uchar*) *share); thr_lock_delete(&(*share)->lock); pthread_mutex_destroy(&(*share)->mutex); @@ -13469,188 +13577,2043 @@ ndb_util_thread_fail: } /* - Condition pushdown -*/ -/** - Push a condition to ndbcluster storage engine for evaluation - during table and index scans. The conditions will be stored on a stack - for possibly storing several conditions. The stack can be popped - by calling cond_pop, handler::extra(HA_EXTRA_RESET) (handler::reset()) - will clear the stack. - The current implementation supports arbitrary AND/OR nested conditions - with comparisons between columns and constants (including constant - expressions and function calls) and the following comparison operators: - =, !=, >, >=, <, <=, "is null", and "is not null". - - @retval - NULL The condition was supported and will be evaluated for each - row found during the scan - @retval - cond The condition was not supported and all rows will be returned from - the scan for evaluation (and thus not saved on stack) + Ordered index stats */ -const -Item* -ha_ndbcluster::cond_push(const Item *cond) -{ - DBUG_ENTER("cond_push"); - if (cond->used_tables() & ~table->map) - { - /** - * 'cond' refers fields from other tables, or other instances - * of this table, -> reject it. - * (Optimizer need to have a better understanding of what is - * pushable by each handler.) - */ - DBUG_EXECUTE("where",print_where((Item *)cond, "Rejected cond_push", QT_ORDINARY);); - DBUG_RETURN(cond); - } - if (!m_cond) - m_cond= new ha_ndbcluster_cond; - if (!m_cond) +time_t ndb_index_stat_time_now= 0; + +time_t +ndb_index_stat_time() +{ + time_t now= time(0); + + if (unlikely(ndb_index_stat_time_now == 0)) + ndb_index_stat_time_now= now; + + if (unlikely(now < ndb_index_stat_time_now)) { - my_errno= HA_ERR_OUT_OF_MEM; - DBUG_RETURN(cond); + DBUG_PRINT("index_stat", ("time moved backwards %d seconds", + int(ndb_index_stat_time_now - now))); + now= ndb_index_stat_time_now; } - DBUG_EXECUTE("where",print_where((Item *)cond, m_tabname, QT_ORDINARY);); - DBUG_RETURN(m_cond->cond_push(cond, table, (NDBTAB *)m_table)); -} -/** - Pop the top condition from the condition stack of the handler instance. -*/ -void -ha_ndbcluster::cond_pop() -{ - if (m_cond) - m_cond->cond_pop(); + ndb_index_stat_time_now= now; + return now; } +bool ndb_index_stat_allow_flag= false; -/* - Implements the SHOW NDB STATUS command. -*/ bool -ndbcluster_show_status(handlerton *hton, THD* thd, stat_print_fn *stat_print, - enum ha_stat_type stat_type) +ndb_index_stat_allow(int flag= -1) { - char name[16]; - char buf[IO_SIZE]; - uint buflen; - DBUG_ENTER("ndbcluster_show_status"); - - if (stat_type != HA_ENGINE_STATUS) - { - DBUG_RETURN(FALSE); + if (flag != -1) { + pthread_mutex_lock(&ndb_index_stat_list_mutex); + ndb_index_stat_allow_flag= (bool)flag; + pthread_mutex_unlock(&ndb_index_stat_list_mutex); } + return ndb_index_stat_allow_flag; +} - Ndb* ndb= check_ndb_in_thd(thd); - Thd_ndb *thd_ndb= get_thd_ndb(thd); - struct st_ndb_status ns; - if (ndb) - update_status_variables(thd_ndb, &ns, thd_ndb->connection); - else - update_status_variables(NULL, &ns, g_ndb_cluster_connection); - - buflen= - my_snprintf(buf, sizeof(buf), - "cluster_node_id=%ld, " - "connected_host=%s, " - "connected_port=%ld, " - "number_of_data_nodes=%ld, " - "number_of_ready_data_nodes=%ld, " - "connect_count=%ld", - ns.cluster_node_id, - ns.connected_host, - ns.connected_port, - ns.number_of_data_nodes, - ns.number_of_ready_data_nodes, - ns.connect_count); - if (stat_print(thd, ndbcluster_hton_name, ndbcluster_hton_name_length, - STRING_WITH_LEN("connection"), buf, buflen)) - DBUG_RETURN(TRUE); +/* Options */ - for (int i= 0; i < MAX_NDB_NODES; i++) - { - if (ns.transaction_hint_count[i] > 0 || - ns.transaction_no_hint_count[i] > 0) - { - uint namelen= my_snprintf(name, sizeof(name), "node[%d]", i); - buflen= my_snprintf(buf, sizeof(buf), - "transaction_hint=%ld, transaction_no_hint=%ld", - ns.transaction_hint_count[i], - ns.transaction_no_hint_count[i]); - if (stat_print(thd, ndbcluster_hton_name, ndbcluster_hton_name_length, - name, namelen, buf, buflen)) - DBUG_RETURN(TRUE); - } - } +/* Options in string format buffer size */ +static const uint ndb_index_stat_option_sz= 512; +void ndb_index_stat_opt2str(const class Ndb_index_stat_opt&, char*); - if (ndb) - { - Ndb::Free_list_usage tmp; - tmp.m_name= 0; - while (ndb->get_free_list_usage(&tmp)) - { - buflen= - my_snprintf(buf, sizeof(buf), - "created=%u, free=%u, sizeof=%u", - tmp.m_created, tmp.m_free, tmp.m_sizeof); - if (stat_print(thd, ndbcluster_hton_name, ndbcluster_hton_name_length, - tmp.m_name, strlen(tmp.m_name), buf, buflen)) - DBUG_RETURN(TRUE); - } +struct Ndb_index_stat_opt { + enum Unit { + Ubool = 1, + Usize = 2, + Utime = 3, + Umsec = 4 + }; + enum Flag { + Freadonly = (1 << 0) + }; + struct Val { + const char* name; + uint val; + uint minval; + uint maxval; + Unit unit; + uint flag; + }; + enum Idx { + Iloop_checkon = 0, + Iloop_idle = 1, + Iloop_busy = 2, + Iupdate_batch = 3, + Iread_batch = 4, + Iidle_batch = 5, + Icheck_batch = 6, + Icheck_delay = 7, + Idelete_batch = 8, + Iclean_delay = 9, + Ierror_batch = 10, + Ierror_delay = 11, + Ievict_batch = 12, + Ievict_delay = 13, + Icache_limit = 14, + Icache_lowpct = 15, + Imax = 16 + }; + Val val[Imax]; + /* Options in string format (SYSVAR ndb_index_stat_option) */ + char option[ndb_index_stat_option_sz]; + Ndb_index_stat_opt(); + uint get(Idx i) const { + assert(i < Imax); + return val[i].val; } - ndbcluster_show_status_binlog(thd, stat_print, stat_type); +}; - DBUG_RETURN(FALSE); +Ndb_index_stat_opt::Ndb_index_stat_opt() +{ +#define ival(aname, aval, aminval, amaxval, aunit, aflag) \ + val[I##aname].name = #aname; \ + val[I##aname].val = aval; \ + val[I##aname].minval = aminval; \ + val[I##aname].maxval = amaxval; \ + val[I##aname].unit = aunit; \ + val[I##aname].flag = aflag + ival(loop_checkon, 1000, 0, ~0, Umsec, 0); + ival(loop_idle, 1000, 0, ~0, Umsec, 0); + ival(loop_busy, 100, 0, ~0, Umsec, 0); + ival(update_batch, 1, 1, ~0, Usize, 0); + ival(read_batch, 4, 1, ~0, Usize, 0); + ival(idle_batch, 32, 1, ~0, Usize, 0); + ival(check_batch, 32, 1, ~0, Usize, 0); + ival(check_delay, 60, 0, ~0, Utime, 0); + ival(clean_delay, 0, 0, ~0, Utime, 0); + ival(delete_batch, 8, 1, ~0, Usize, 0); + ival(error_batch, 4, 1, ~0, Usize, 0); + ival(error_delay, 60, 0, ~0, Utime, 0); + ival(evict_batch, 8, 1, ~0, Usize, 0); + ival(evict_delay, 60, 0, ~0, Utime, 0); + ival(cache_limit, 32*1024*1024, 1024*1024, ~0, Usize, 0); + ival(cache_lowpct, 90, 0, 100, Usize, 0); +#undef ival + + ndb_index_stat_opt2str(*this, option); } +/* Hard limits */ +static const uint ndb_index_stat_max_evict_batch = 32; -int ha_ndbcluster::get_default_no_partitions(HA_CREATE_INFO *create_info) +Ndb_index_stat_opt ndb_index_stat_opt; + +/* Copy option struct to string buffer */ +void +ndb_index_stat_opt2str(const Ndb_index_stat_opt& opt, char* str) { - if (unlikely(g_ndb_cluster_connection->get_no_ready() <= 0)) - { -err: - my_error(HA_ERR_NO_CONNECTION, MYF(0)); - return -1; - } + DBUG_ENTER("ndb_index_stat_opt2str"); - THD* thd = current_thd; - if (thd == 0) - goto err; - Thd_ndb * thd_ndb = get_thd_ndb(thd); - if (thd_ndb == 0) - goto err; + char buf[ndb_index_stat_option_sz]; + char *const end= &buf[sizeof(buf)]; + char* ptr= buf; + *ptr= 0; - ha_rows max_rows, min_rows; - if (create_info) - { - max_rows= create_info->max_rows; - min_rows= create_info->min_rows; - } - else + const uint imax= Ndb_index_stat_opt::Imax; + for (uint i= 0; i < imax; i++) { - max_rows= table_share->max_rows; - min_rows= table_share->min_rows; - } - uint no_fragments= get_no_fragments(max_rows >= min_rows ? - max_rows : min_rows); - uint reported_frags; - adjusted_frag_count(thd_ndb->ndb, - no_fragments, - reported_frags); - return reported_frags; -} + const Ndb_index_stat_opt::Val& v= opt.val[i]; + ptr+= strlen(ptr); + const char* sep= (ptr == buf ? "" : ","); + const uint sz= ptr < end ? end - ptr : 0; -uint32 ha_ndbcluster::calculate_key_hash_value(Field **field_array) -{ - Uint32 hash_value; - struct Ndb::Key_part_ptr key_data[MAX_REF_PARTS]; - struct Ndb::Key_part_ptr *key_data_ptr= &key_data[0]; - Uint32 i= 0; - int ret_val; + switch (v.unit) { + case Ndb_index_stat_opt::Ubool: + { + DBUG_ASSERT(v.val == 0 || v.val == 1); + if (v.val == 0) + my_snprintf(ptr, sz, "%s%s=OFF", sep, v.name); + else + my_snprintf(ptr, sz, "%s%s=ON", sep, v.name); + } + break; + + case Ndb_index_stat_opt::Usize: + { + uint m; + if (v.val == 0) + my_snprintf(ptr, sz, "%s%s=0", sep, v.name); + else if (v.val % (m= 1024*1024*1024) == 0) + my_snprintf(ptr, sz, "%s%s=%uG", sep, v.name, v.val / m); + else if (v.val % (m= 1024*1024) == 0) + my_snprintf(ptr, sz, "%s%s=%uM", sep, v.name, v.val / m); + else if (v.val % (m= 1024) == 0) + my_snprintf(ptr, sz, "%s%s=%uK", sep, v.name, v.val / m); + else + my_snprintf(ptr, sz, "%s%s=%u", sep, v.name, v.val); + } + break; + + case Ndb_index_stat_opt::Utime: + { + uint m; + if (v.val == 0) + my_snprintf(ptr, sz, "%s%s=0", sep, v.name); + else if (v.val % (m= 60*60*24) == 0) + snprintf(ptr, sz, "%s%s=%ud", sep, v.name, v.val / m); + else if (v.val % (m= 60*60) == 0) + snprintf(ptr, sz, "%s%s=%uh", sep, v.name, v.val / m); + else if (v.val % (m= 60) == 0) + snprintf(ptr, sz, "%s%s=%um", sep, v.name, v.val / m); + else + snprintf(ptr, sz, "%s%s=%us", sep, v.name, v.val); + } + break; + + case Ndb_index_stat_opt::Umsec: + { + if (v.val == 0) + my_snprintf(ptr, sz, "%s%s=0", sep, v.name); + else + snprintf(ptr, sz, "%s%s=%ums", sep, v.name, v.val); + } + break; + + default: + DBUG_ASSERT(false); + break; + } + } + + memset(str, 0, ndb_index_stat_option_sz); + strcpy(str, buf); + DBUG_PRINT("index_stat", ("str: \"%s\"", str)); + DBUG_VOID_RETURN; +} + +int +ndb_index_stat_option_parse(char* p, Ndb_index_stat_opt& opt) +{ + DBUG_ENTER("ndb_index_stat_option_parse"); + + char *r= strchr(p, '='); + if (r == 0) + DBUG_RETURN(-1); + *r++= 0; + + while (isspace(*r)) + *r++= 0; + if (*r == 0) + DBUG_RETURN(-1); + + const uint imax= Ndb_index_stat_opt::Imax; + for (uint i= 0; i < imax; i++) + { + Ndb_index_stat_opt::Val& v= opt.val[i]; + if (strcmp(p, v.name) != 0) + continue; + + char *s; + for (s= r; *s != 0; s++) + *s= tolower(*s); + ulonglong val= strtoull(r, &s, 10); + + switch (v.unit) { + case Ndb_index_stat_opt::Ubool: + { + if ((s > r && *s == 0 && val == 0) || + strcmp(r, "off") == 0 || + strcmp(r, "false") == 0) + val= 0; + else if ((s > r && *s == 0 && val == 1) || + strcmp(r, "on") == 0 || + strcmp(r, "true") == 0) + val= 1; + else + DBUG_RETURN(-1); + v.val= val; + } + break; + + case Ndb_index_stat_opt::Usize: + { + if (s == r) + DBUG_RETURN(-1); + if (strcmp(s, "") == 0) + ; + else if (strcmp(s, "k") == 0) + val*= 1024; + else if (strcmp(s, "m") == 0) + val*= 1024*1024; + else if (strcmp(s, "g") == 0) + val*= 1024*1024*1024; + else + DBUG_RETURN(-1); + if (val < v.minval || val > v.maxval) + DBUG_RETURN(-1); + v.val= val; + } + break; + + case Ndb_index_stat_opt::Utime: + { + if (s == r) + DBUG_RETURN(-1); + if (strcmp(s, "") == 0) + ; + else if (strcmp(s, "s") == 0) + ; + else if (strcmp(s, "m") == 0) + val*= 60; + else if (strcmp(s, "h") == 0) + val*= 60*60; + else if (strcmp(s, "d") == 0) + val*= 24*60*60; + else + DBUG_RETURN(-1); + if (val < v.minval || val > v.maxval) + DBUG_RETURN(-1); + v.val= val; + } + break; + + case Ndb_index_stat_opt::Umsec: + { + if (s == r) + DBUG_RETURN(-1); + if (strcmp(s, "") == 0) + ; + else if (strcmp(s, "ms") == 0) + ; + else + DBUG_RETURN(-1); + if (val < v.minval || val > v.maxval) + DBUG_RETURN(-1); + v.val= val; + } + break; + + default: + DBUG_ASSERT(false); + break; + } + } + DBUG_RETURN(0); +} + +/* Copy option string to option struct */ +int +ndb_index_stat_str2opt(const char *str, Ndb_index_stat_opt& opt) +{ + DBUG_ENTER("ndb_index_stat_str2opt"); + DBUG_PRINT("index_stat", ("str: \"%s\"", str)); + + char buf[ndb_index_stat_option_sz]; + + assert(str != 0); + if (strlen(str) >= sizeof(buf)) + DBUG_RETURN(-1); + strcpy(buf, str); + + char *p= buf; + while (1) + { + while (isspace(*p)) + p++; + if (*p == 0) + break; + + char *q= strchr(p, ','); + if (q == p) + DBUG_RETURN(-1); + if (q != 0) + *q= 0; + + DBUG_PRINT("index_stat", ("parse: %s", p)); + if (ndb_index_stat_option_parse(p, opt) == -1) + DBUG_RETURN(-1); + + if (q == 0) + break; + p= q + 1; + } + + ndb_index_stat_opt2str(opt, opt.option); + DBUG_RETURN(0); +} + +/* Thanks to ha_innodb.cc */ + +/* Need storage between check and update (assume locked) */ +char ndb_index_stat_option_tmp[ndb_index_stat_option_sz]; + +int +ndb_index_stat_option_check(MYSQL_THD, + struct st_mysql_sys_var *var, + void *save, + struct st_mysql_value *value) +{ + DBUG_ENTER("ndb_index_stat_option_check"); + char buf[ndb_index_stat_option_sz]; + int len= sizeof(buf); + const char *str= value->val_str(value, buf, &len); + if (str != 0) + { + /* Seems to be nothing in buf */ + DBUG_PRINT("index_stat", ("str: %s len: %d", str, len)); + Ndb_index_stat_opt opt; + if (ndb_index_stat_str2opt(str, opt) == 0) + { + /* Passed to update */ + strcpy(ndb_index_stat_option_tmp, str); + *(const char**)save= ndb_index_stat_option_tmp; + DBUG_RETURN(0); + } + } + DBUG_RETURN(1); +} + +void +ndb_index_stat_option_update(MYSQL_THD, + struct st_mysql_sys_var *var, + void *var_ptr, + const void *save) +{ + DBUG_ENTER("ndb_index_stat_option_update"); + const char *str= *(const char**)save; + DBUG_PRINT("index_stat", ("str: %s", str)); + Ndb_index_stat_opt& opt= ndb_index_stat_opt; + int ret= ndb_index_stat_str2opt(str, opt); + assert(ret == 0); + *(const char**)var_ptr= ndb_index_stat_opt.option; + DBUG_VOID_RETURN; +} + +/* Global stuff */ + +struct Ndb_index_stat_glob { + uint list_count[Ndb_index_stat::LT_Count]; /* Temporary use */ + uint total_count; + uint force_update; + uint wait_update; + uint cache_query_bytes; /* In use */ + uint cache_clean_bytes; /* Obsolete versions not yet removed */ + bool is_locked; + Ndb_index_stat_glob() : + total_count(0), + force_update(0), + wait_update(0), + cache_query_bytes(0), + cache_clean_bytes(0), + is_locked(false) + { + } + void set_list_count() + { + int lt; + for (lt= 0; lt < Ndb_index_stat::LT_Count; lt++) + { + const Ndb_index_stat_list &list= ndb_index_stat_list[lt]; + list_count[lt]= list.count; + } + } + void lock() + { + pthread_mutex_lock(&ndb_index_stat_glob_mutex); + assert(!is_locked); + is_locked= true; + } + void unlock() + { + assert(is_locked); + g_ndb_status_index_stat_cache_query= cache_query_bytes; + g_ndb_status_index_stat_cache_clean= cache_clean_bytes; + is_locked= false; + pthread_mutex_unlock(&ndb_index_stat_glob_mutex); + } +}; + +Ndb_index_stat_glob ndb_index_stat_glob; + +/* Shared index entries */ + +Ndb_index_stat::Ndb_index_stat() +{ + is= 0; + index_id= 0; + index_version= 0; +#ifndef DBUG_OFF + memset(id, 0, sizeof(id)); +#endif + access_time= 0; + load_time= 0; + read_time= 0; + sample_version= 0; + check_time= 0; + cache_clean= false; + force_update= 0; + error_time= 0; + error_count= 0; + share_next= 0; + lt= 0; + lt_old= 0; + list_next= 0; + list_prev= 0; + share= 0; +} + +void +ndb_index_stat_error(Ndb_index_stat *st, const char* place, int line) +{ + pthread_mutex_lock(&ndb_index_stat_stat_mutex); + time_t now= ndb_index_stat_time(); + NdbIndexStat::Error error= st->is->getNdbError(); + if (error.code == 0) + { + // XXX why this if + NdbIndexStat::Error error2; + error= error2; + error.code= NdbIndexStat::InternalError; + error.status= NdbError::TemporaryError; + } + st->error= error; + st->error_time= now; + st->error_count++; + pthread_cond_broadcast(&ndb_index_stat_stat_cond); + pthread_mutex_unlock(&ndb_index_stat_stat_mutex); + + DBUG_PRINT("index_stat", ("%s line %d: error %d line %d extra %d", + place, line, error.code, error.line, error.extra)); +} + +/* Lists across shares */ + +Ndb_index_stat_list::Ndb_index_stat_list(int the_lt, const char* the_name) +{ + lt= the_lt; + name= the_name; + head= 0; + tail= 0; + count= 0; +} + +Ndb_index_stat_list ndb_index_stat_list[Ndb_index_stat::LT_Count] = { + Ndb_index_stat_list(0, 0), + Ndb_index_stat_list(Ndb_index_stat::LT_New, "New"), + Ndb_index_stat_list(Ndb_index_stat::LT_Update, "Update"), + Ndb_index_stat_list(Ndb_index_stat::LT_Read, "Read"), + Ndb_index_stat_list(Ndb_index_stat::LT_Idle, "Idle"), + Ndb_index_stat_list(Ndb_index_stat::LT_Check, "Check"), + Ndb_index_stat_list(Ndb_index_stat::LT_Delete, "Delete"), + Ndb_index_stat_list(Ndb_index_stat::LT_Error, "Error") +}; + +void +ndb_index_stat_list_add(Ndb_index_stat* st, int lt, int place= +1) +{ + Ndb_index_stat_glob &glob= ndb_index_stat_glob; + assert(st != 0 && st->lt == 0); + assert(st->list_next == 0 && st->list_prev == 0); + assert(1 <= lt && lt < Ndb_index_stat::LT_Count); + Ndb_index_stat_list &list= ndb_index_stat_list[lt]; + + DBUG_PRINT("index_stat", ("st %s -> %s", st->id, list.name)); + + if (list.count == 0) + { + assert(list.head == 0 && list.tail == 0); + list.head= st; + list.tail= st; + } + else if (place < 0) + { + assert(list.head != 0 && list.head->list_prev == 0); + st->list_next= list.head; + list.head->list_prev= st; + list.head= st; + } + else + { + assert(list.tail != 0 && list.tail->list_next == 0); + st->list_prev= list.tail; + list.tail->list_next= st; + list.tail= st; + } + list.count++; + glob.lock(); + glob.total_count++; + glob.unlock(); + + st->lt= lt; +} + +void +ndb_index_stat_list_remove(Ndb_index_stat* st) +{ + Ndb_index_stat_glob &glob= ndb_index_stat_glob; + assert(st != 0); + int lt= st->lt; + assert(1 <= lt && lt < Ndb_index_stat::LT_Count); + Ndb_index_stat_list &list= ndb_index_stat_list[lt]; + + DBUG_PRINT("index_stat", ("st %s <- %s", st->id, list.name)); + + Ndb_index_stat* next= st->list_next; + Ndb_index_stat* prev= st->list_prev; + + if (list.head == st) + list.head= next; + if (list.tail == st) + list.tail= prev; + assert(list.count != 0); + list.count--; + glob.lock(); + assert(glob.total_count != 0); + glob.total_count--; + glob.unlock(); + + if (next != 0) + next->list_prev= prev; + if (prev != 0) + prev->list_next= next; + + st->lt= 0; + st->lt_old= 0; + st->list_next= 0; + st->list_prev= 0; +} + +void +ndb_index_stat_list_move(Ndb_index_stat *st, int lt, int place= +1) +{ + assert(st != 0); + ndb_index_stat_list_remove(st); + ndb_index_stat_list_add(st, lt, place); +} + +/* Move entry in / out error list */ + +void +ndb_index_stat_list_to_error(Ndb_index_stat *st) +{ + Ndb_index_stat_glob &glob= ndb_index_stat_glob; + + assert(st != 0); + const int lt= st->lt; + assert(1 <= lt && lt < Ndb_index_stat::LT_Count); + assert(lt != Ndb_index_stat::LT_Error); + + if (st->force_update != 0) + { + glob.lock(); + assert(glob.force_update >= st->force_update); + glob.force_update-= st->force_update; + glob.unlock(); + st->force_update= 0; + } + + time_t now= ndb_index_stat_time(); + st->error_time= now; + ndb_index_stat_list_move(st, Ndb_index_stat::LT_Error); +} + +void +ndb_index_stat_list_from_error(Ndb_index_stat *st) +{ + assert(st != 0); + assert(st->lt == Ndb_index_stat::LT_Error); + if (st->force_update) + ndb_index_stat_list_move(st, Ndb_index_stat::LT_Update); + else + ndb_index_stat_list_move(st, Ndb_index_stat::LT_Read); + st->error.code= 0; + st->error.status= NdbError::Success; +} + +/* Find or add entry under the share */ + +Ndb_index_stat* +ndb_index_stat_alloc() +{ + Ndb_index_stat *st= new Ndb_index_stat; + NdbIndexStat *is= new NdbIndexStat; + if (st != 0 && is != 0) + { + st->is= is; + return st; + } + delete is; + delete st; + return 0; +} + +/* Subroutine, have lock */ +Ndb_index_stat* +ndb_index_stat_find_share(NDB_SHARE *share, + const NDBINDEX *index, + Ndb_index_stat *&st_last) +{ + struct Ndb_index_stat *st= share->index_stat_list; + st_last= 0; + while (st != 0) + { + assert(st->share == share); + assert(st->is != 0); + NdbIndexStat::Head head; + st->is->get_head(head); + if (head.m_indexId == (uint)index->getObjectId() && + head.m_indexVersion == (uint)index->getObjectVersion()) + break; + st_last= st; + st= st->share_next; + } + return st; +} + +/* Subroutine, have lock */ +Ndb_index_stat* +ndb_index_stat_add_share(NDB_SHARE *share, + const NDBINDEX *index, + const NDBTAB *table, + Ndb_index_stat *st_last) +{ + struct Ndb_index_stat *st= ndb_index_stat_alloc(); + if (st != 0) + { + st->share= share; + if (st_last == 0) + share->index_stat_list= st; + else + st_last->share_next= st; + st->index_id= index->getObjectId(); + st->index_version= index->getObjectVersion(); +#ifndef DBUG_OFF + snprintf(st->id, sizeof(st->id), "%d.%d", st->index_id, st->index_version); +#endif + if (st->is->set_index(*index, *table) == -1) + { + ndb_index_stat_error(st, "set_index", __LINE__); + /* Caller assigns list */ + } + } + return st; +} + +Ndb_index_stat* +ndb_index_stat_get_share(NDB_SHARE *share, + const NDBINDEX *index, + const NDBTAB *table, + bool allow_add, + bool force_update) +{ + pthread_mutex_lock(&share->mutex); + pthread_mutex_lock(&ndb_index_stat_list_mutex); + time_t now= ndb_index_stat_time(); + + struct Ndb_index_stat *st= 0; + struct Ndb_index_stat *st_last= 0; + if (ndb_index_stat_allow()) + { + st= ndb_index_stat_find_share(share, index, st_last); + if (st == 0 && allow_add) + { + st= ndb_index_stat_add_share(share, index, table, st_last); + if (st != 0) + ndb_index_stat_list_add(st, Ndb_index_stat::LT_New); + } + if (st != 0) + { + if (force_update != 0) + { + st->force_update++; + Ndb_index_stat_glob &glob= ndb_index_stat_glob; + glob.lock(); + glob.force_update++; + glob.unlock(); + } + st->access_time= now; + } + } + + pthread_mutex_unlock(&ndb_index_stat_list_mutex); + pthread_mutex_unlock(&share->mutex); + return st; +} +void +ndb_index_stat_free(Ndb_index_stat *st) +{ + pthread_mutex_lock(&ndb_index_stat_list_mutex); + NDB_SHARE *share= st->share; + assert(share != 0); + + Ndb_index_stat *st_head= 0; + Ndb_index_stat *st_tail= 0; + Ndb_index_stat *st_loop= share->index_stat_list; + bool found= false; + while (st_loop != 0) { + if (st == st_loop) { + st->share= 0; + assert(st->lt != 0); + assert(st->lt != Ndb_index_stat::LT_Delete); + ndb_index_stat_list_move(st, Ndb_index_stat::LT_Delete); + st_loop= st_loop->share_next; + assert(!found); + found++; + } else { + if (st_head == 0) + st_head= st_loop; + else + st_tail->share_next= st_loop; + st_tail= st_loop; + st_loop= st_loop->share_next; + st_tail->share_next= 0; + } + } + assert(found); + share->index_stat_list= st_head; + pthread_mutex_unlock(&ndb_index_stat_list_mutex); +} + +void +ndb_index_stat_free(NDB_SHARE *share) +{ + pthread_mutex_lock(&ndb_index_stat_list_mutex); + Ndb_index_stat *st; + while ((st= share->index_stat_list) != 0) + { + share->index_stat_list= st->share_next; + st->share= 0; + assert(st->lt != 0); + assert(st->lt != Ndb_index_stat::LT_Delete); + ndb_index_stat_list_move(st, Ndb_index_stat::LT_Delete); + } + pthread_mutex_unlock(&ndb_index_stat_list_mutex); +} + +/* Statistics thread sub-routines */ + +void +ndb_index_stat_cache_move(Ndb_index_stat *st) +{ + Ndb_index_stat_glob &glob= ndb_index_stat_glob; + NdbIndexStat::CacheInfo infoBuild; + NdbIndexStat::CacheInfo infoQuery; + + st->is->get_cache_info(infoBuild, NdbIndexStat::CacheBuild); + st->is->get_cache_info(infoQuery, NdbIndexStat::CacheQuery); + const uint new_query_bytes= infoBuild.m_totalBytes; + const uint old_query_bytes= infoQuery.m_totalBytes; + DBUG_PRINT("index_stat", ("st %s cache move: query:%u clean:%u", + st->id, new_query_bytes, old_query_bytes)); + st->is->move_cache(); + glob.lock(); + assert(glob.cache_query_bytes >= old_query_bytes); + glob.cache_query_bytes-= old_query_bytes; + glob.cache_query_bytes+= new_query_bytes; + glob.cache_clean_bytes+= old_query_bytes; + glob.unlock(); +} + +void +ndb_index_stat_cache_clean(Ndb_index_stat *st) +{ + Ndb_index_stat_glob &glob= ndb_index_stat_glob; + NdbIndexStat::CacheInfo infoClean; + + st->is->get_cache_info(infoClean, NdbIndexStat::CacheClean); + const uint old_clean_bytes= infoClean.m_totalBytes; + DBUG_PRINT("index_stat", ("st %s cache clean: clean:%u", + st->id, old_clean_bytes)); + st->is->clean_cache(); + glob.lock(); + assert(glob.cache_clean_bytes >= old_clean_bytes); + glob.cache_clean_bytes-= old_clean_bytes; + glob.unlock(); +} + +/* Misc in/out parameters for process steps */ +struct Ndb_index_stat_proc { + Ndb *ndb; + time_t now; + int lt; + bool busy; + bool end; + Ndb_index_stat_proc() : + ndb(0), + now(0), + lt(0), + busy(false), + end(false) + {} +}; + +void +ndb_index_stat_proc_new(Ndb_index_stat_proc &pr, Ndb_index_stat *st) +{ + if (st->error.code != 0) + pr.lt= Ndb_index_stat::LT_Error; + else if (st->force_update) + pr.lt= Ndb_index_stat::LT_Update; + else + pr.lt= Ndb_index_stat::LT_Read; +} + +void +ndb_index_stat_proc_new(Ndb_index_stat_proc &pr) +{ + pthread_mutex_lock(&ndb_index_stat_list_mutex); + const int lt= Ndb_index_stat::LT_New; + Ndb_index_stat_list &list= ndb_index_stat_list[lt]; + + Ndb_index_stat *st_loop= list.head; + while (st_loop != 0) + { + Ndb_index_stat *st= st_loop; + st_loop= st_loop->list_next; + DBUG_PRINT("index_stat", ("st %s proc %s", st->id, list.name)); + ndb_index_stat_proc_new(pr, st); + ndb_index_stat_list_move(st, pr.lt); + } + pthread_mutex_unlock(&ndb_index_stat_list_mutex); +} + +void +ndb_index_stat_proc_update(Ndb_index_stat_proc &pr, Ndb_index_stat *st) +{ + if (st->is->update_stat(pr.ndb) == -1) + { + ndb_index_stat_error(st, "update_stat", __LINE__); + pr.lt= Ndb_index_stat::LT_Error; + return; + } + pr.lt= Ndb_index_stat::LT_Read; +} + +void +ndb_index_stat_proc_update(Ndb_index_stat_proc &pr) +{ + const int lt= Ndb_index_stat::LT_Update; + Ndb_index_stat_list &list= ndb_index_stat_list[lt]; + const Ndb_index_stat_opt &opt= ndb_index_stat_opt; + const uint batch= opt.get(Ndb_index_stat_opt::Iupdate_batch); + + Ndb_index_stat *st_loop= list.head; + uint cnt= 0; + while (st_loop != 0 && cnt < batch) + { + Ndb_index_stat *st= st_loop; + st_loop= st_loop->list_next; + DBUG_PRINT("index_stat", ("st %s proc %s", st->id, list.name)); + ndb_index_stat_proc_update(pr, st); + ndb_index_stat_list_move(st, pr.lt); + cnt++; + } + if (cnt == batch) + pr.busy= true; +} + +void +ndb_index_stat_proc_read(Ndb_index_stat_proc &pr, Ndb_index_stat *st) +{ + NdbIndexStat::Head head; + if (st->is->read_stat(pr.ndb) == -1) + { + ndb_index_stat_error(st, "read_stat", __LINE__); + pr.lt= Ndb_index_stat::LT_Error; + return; + } + + pthread_mutex_lock(&ndb_index_stat_stat_mutex); + pr.now= ndb_index_stat_time(); + st->is->get_head(head); + st->load_time= head.m_loadTime; + st->read_time= pr.now; + st->sample_version= head.m_sampleVersion; + + if (st->force_update != 0) + { + Ndb_index_stat_glob &glob= ndb_index_stat_glob; + glob.lock(); + assert(glob.force_update >= st->force_update); + glob.force_update-= st->force_update; + glob.unlock(); + st->force_update= 0; + } + + ndb_index_stat_cache_move(st); + st->cache_clean= false; + pr.lt= Ndb_index_stat::LT_Idle; + pthread_cond_broadcast(&ndb_index_stat_stat_cond); + pthread_mutex_unlock(&ndb_index_stat_stat_mutex); +} + +void +ndb_index_stat_proc_read(Ndb_index_stat_proc &pr) +{ + const int lt= Ndb_index_stat::LT_Read; + Ndb_index_stat_list &list= ndb_index_stat_list[lt]; + const Ndb_index_stat_opt &opt= ndb_index_stat_opt; + const uint batch= opt.get(Ndb_index_stat_opt::Iread_batch); + + Ndb_index_stat *st_loop= list.head; + uint cnt= 0; + while (st_loop != 0 && cnt < batch) + { + Ndb_index_stat *st= st_loop; + st_loop= st_loop->list_next; + DBUG_PRINT("index_stat", ("st %s proc %s", st->id, list.name)); + ndb_index_stat_proc_read(pr, st); + ndb_index_stat_list_move(st, pr.lt); + cnt++; + } + if (cnt == batch) + pr.busy= true; +} + +void +ndb_index_stat_proc_idle(Ndb_index_stat_proc &pr, Ndb_index_stat *st) +{ + const Ndb_index_stat_opt &opt= ndb_index_stat_opt; + const int clean_delay= opt.get(Ndb_index_stat_opt::Iclean_delay); + const int check_delay= opt.get(Ndb_index_stat_opt::Icheck_delay); + const time_t clean_wait= + st->cache_clean ? 0 : st->read_time + clean_delay - pr.now; + const time_t check_wait= + st->check_time == 0 ? 0 : st->check_time + check_delay - pr.now; + + DBUG_PRINT("index_stat", ("st %s check wait:%ds force update:%u" + " clean wait:%ds cache clean:%d", + st->id, check_wait, st->force_update, + clean_wait, st->cache_clean)); + + if (!st->cache_clean && clean_wait <= 0) + { + ndb_index_stat_cache_clean(st); + st->cache_clean= true; + } + if (st->force_update) + { + pr.lt= Ndb_index_stat::LT_Update; + return; + } + if (check_wait <= 0) + { + pr.lt= Ndb_index_stat::LT_Check; + return; + } + pr.lt= Ndb_index_stat::LT_Idle; +} + +void +ndb_index_stat_proc_idle(Ndb_index_stat_proc &pr) +{ + const int lt= Ndb_index_stat::LT_Idle; + Ndb_index_stat_list &list= ndb_index_stat_list[lt]; + const Ndb_index_stat_opt &opt= ndb_index_stat_opt; + const uint batch= opt.get(Ndb_index_stat_opt::Iidle_batch); + pr.now= ndb_index_stat_time(); + + Ndb_index_stat *st_loop= list.head; + uint cnt= 0; + while (st_loop != 0 && cnt < batch) + { + Ndb_index_stat *st= st_loop; + st_loop= st_loop->list_next; + DBUG_PRINT("index_stat", ("st %s proc %s", st->id, list.name)); + ndb_index_stat_proc_idle(pr, st); + if (pr.lt != lt) + { + ndb_index_stat_list_move(st, pr.lt); + cnt++; + } + } + if (cnt == batch) + pr.busy= true; +} + +void +ndb_index_stat_proc_check(Ndb_index_stat_proc &pr, Ndb_index_stat *st) +{ + pr.now= ndb_index_stat_time(); + st->check_time= pr.now; + const Ndb_index_stat_opt &opt= ndb_index_stat_opt; + NdbIndexStat::Head head; + if (st->is->read_head(pr.ndb) == -1) + { + ndb_index_stat_error(st, "read_head", __LINE__); + pr.lt= Ndb_index_stat::LT_Error; + return; + } + st->is->get_head(head); + const uint version_old= st->sample_version; + const uint version_new= head.m_sampleVersion; + if (version_old != version_new) + { + DBUG_PRINT("index_stat", ("st %s sample version old:%u new:%u", + st->id, version_old, version_new)); + pr.lt= Ndb_index_stat::LT_Read; + return; + } + pr.lt= Ndb_index_stat::LT_Idle; +} + +void +ndb_index_stat_proc_check(Ndb_index_stat_proc &pr) +{ + const int lt= Ndb_index_stat::LT_Check; + Ndb_index_stat_list &list= ndb_index_stat_list[lt]; + const Ndb_index_stat_opt &opt= ndb_index_stat_opt; + const uint batch= opt.get(Ndb_index_stat_opt::Icheck_batch); + + Ndb_index_stat *st_loop= list.head; + uint cnt= 0; + while (st_loop != 0 && cnt < batch) + { + Ndb_index_stat *st= st_loop; + st_loop= st_loop->list_next; + DBUG_PRINT("index_stat", ("st %s proc %s", st->id, list.name)); + ndb_index_stat_proc_check(pr, st); + ndb_index_stat_list_move(st, pr.lt); + cnt++; + } + if (cnt == batch) + pr.busy= true; +} + +void +ndb_index_stat_proc_evict(Ndb_index_stat_proc &pr, Ndb_index_stat *st) +{ + const Ndb_index_stat_opt &opt= ndb_index_stat_opt; + + NdbIndexStat::Head head; + NdbIndexStat::CacheInfo infoBuild; + NdbIndexStat::CacheInfo infoQuery; + NdbIndexStat::CacheInfo infoClean; + st->is->get_head(head); + st->is->get_cache_info(infoBuild, NdbIndexStat::CacheBuild); + st->is->get_cache_info(infoQuery, NdbIndexStat::CacheQuery); + st->is->get_cache_info(infoClean, NdbIndexStat::CacheClean); + + DBUG_PRINT("index_stat", + ("evict table: %u index: %u version: %u" + " sample version: %u" + " cache bytes build:%u query:%u clean:%u", + head.m_tableId, head.m_indexId, head.m_indexVersion, + head.m_sampleVersion, + infoBuild.m_totalBytes, infoQuery.m_totalBytes, infoClean.m_totalBytes)); + + /* Twice to move all caches to clean */ + ndb_index_stat_cache_move(st); + ndb_index_stat_cache_move(st); + ndb_index_stat_cache_clean(st); +} + +bool +ndb_index_stat_proc_evict() +{ + const Ndb_index_stat_opt &opt= ndb_index_stat_opt; + Ndb_index_stat_glob &glob= ndb_index_stat_glob; + glob.lock(); + uint curr_size= glob.cache_query_bytes + glob.cache_clean_bytes; + glob.unlock(); + const uint cache_lowpct= opt.get(Ndb_index_stat_opt::Icache_lowpct); + const uint cache_limit= opt.get(Ndb_index_stat_opt::Icache_limit); + if (100 * curr_size <= cache_lowpct * cache_limit) + return false; + return true; +} + +void +ndb_index_stat_proc_evict(Ndb_index_stat_proc &pr, int lt) +{ + Ndb_index_stat_list &list= ndb_index_stat_list[lt]; + const Ndb_index_stat_opt &opt= ndb_index_stat_opt; + const uint batch= opt.get(Ndb_index_stat_opt::Ievict_batch); + const int evict_delay= opt.get(Ndb_index_stat_opt::Ievict_delay); + pr.now= ndb_index_stat_time(); + + if (!ndb_index_stat_proc_evict()) + return; + + /* Create a LRU batch */ + Ndb_index_stat* st_lru_arr[ndb_index_stat_max_evict_batch + 1]; + uint st_lru_cnt= 0; + Ndb_index_stat *st_loop= list.head; + while (st_loop != 0 && st_lru_cnt < batch) + { + Ndb_index_stat *st= st_loop; + st_loop= st_loop->list_next; + if (st->read_time + evict_delay <= pr.now) + { + /* Insertion sort into the batch from the end */ + if (st_lru_cnt == 0) + st_lru_arr[st_lru_cnt++]= st; + else + { + uint i= st_lru_cnt; + while (i != 0) + { + if (st_lru_arr[i-1]->access_time < st->access_time) + break; + i--; + } + if (i < st_lru_cnt) + { + uint j= st_lru_cnt; /* There is place for one more at end */ + while (j > i) + { + st_lru_arr[j]= st_lru_arr[j-1]; + j--; + } + st_lru_arr[i]= st; + if (st_lru_cnt < batch) + st_lru_cnt++; + } + } + } + } + + /* Process the LRU batch */ + uint cnt= 0; + while (cnt < st_lru_cnt) + { + if (!ndb_index_stat_proc_evict()) + break; + + Ndb_index_stat *st= st_lru_arr[cnt]; + DBUG_PRINT("index_stat", ("st %s proc evict %s", st->id, list.name)); + ndb_index_stat_proc_evict(pr, st); + ndb_index_stat_free(st); + cnt++; + } + if (cnt == batch) + pr.busy= true; +} + +void +ndb_index_stat_proc_evict(Ndb_index_stat_proc &pr) +{ + ndb_index_stat_proc_evict(pr, Ndb_index_stat::LT_Error); + ndb_index_stat_proc_evict(pr, Ndb_index_stat::LT_Idle); +} + +void +ndb_index_stat_proc_delete(Ndb_index_stat_proc &pr) +{ + const int lt= Ndb_index_stat::LT_Delete; + Ndb_index_stat_list &list= ndb_index_stat_list[lt]; + const Ndb_index_stat_opt &opt= ndb_index_stat_opt; + const uint delete_batch= opt.get(Ndb_index_stat_opt::Idelete_batch); + const uint batch= !pr.end ? delete_batch : 0xFFFFFFFF; + + Ndb_index_stat *st_loop= list.head; + uint cnt= 0; + while (st_loop != 0 && cnt < batch) + { + Ndb_index_stat *st= st_loop; + st_loop= st_loop->list_next; + DBUG_PRINT("index_stat", ("st %s proc %s", st->id, list.name)); + ndb_index_stat_proc_evict(pr, st); + ndb_index_stat_list_remove(st); + delete st->is; + delete st; + cnt++; + } + if (cnt == batch) + pr.busy= true; +} + +void +ndb_index_stat_proc_error(Ndb_index_stat_proc &pr, Ndb_index_stat *st) +{ + const Ndb_index_stat_opt &opt= ndb_index_stat_opt; + const int error_delay= opt.get(Ndb_index_stat_opt::Ierror_delay); + const time_t error_wait= st->error_time + error_delay - pr.now; + + if (error_wait <= 0) + { + ndb_index_stat_list_from_error(st); + DBUG_PRINT("index_stat", ("st %s error wait:%ds error count:%u", + st->id, (int)error_wait, st->error_count)); + if (st->force_update) + pr.lt= Ndb_index_stat::LT_Update; + else + pr.lt= Ndb_index_stat::LT_Read; + return; + } + pr.lt= Ndb_index_stat::LT_Error; +} + +void +ndb_index_stat_proc_error(Ndb_index_stat_proc &pr) +{ + const int lt= Ndb_index_stat::LT_Error; + Ndb_index_stat_list &list= ndb_index_stat_list[lt]; + const Ndb_index_stat_opt &opt= ndb_index_stat_opt; + const uint batch= opt.get(Ndb_index_stat_opt::Ierror_batch); + pr.now= ndb_index_stat_time(); + + Ndb_index_stat *st_loop= list.head; + uint cnt= 0; + while (st_loop != 0 && cnt < batch) + { + Ndb_index_stat *st= st_loop; + st_loop= st_loop->list_next; + DBUG_PRINT("index_stat", ("st %s proc %s", st->id, list.name)); + ndb_index_stat_proc_error(pr, st); + if (pr.lt != lt) + { + ndb_index_stat_list_move(st, pr.lt); + cnt++; + } + } + if (cnt == batch) + pr.busy= true; +} + +#ifndef DBUG_OFF +void +ndb_index_stat_report(const Ndb_index_stat_glob& old_glob) +{ + Ndb_index_stat_glob new_glob= ndb_index_stat_glob; + new_glob.set_list_count(); + + /* List counts */ + { + const uint (&old_count)[Ndb_index_stat::LT_Count]= old_glob.list_count; + const uint (&new_count)[Ndb_index_stat::LT_Count]= new_glob.list_count; + bool any= false; + int lt; + for (lt=1; lt < Ndb_index_stat::LT_Count; lt++) + { + const Ndb_index_stat_list &list= ndb_index_stat_list[lt]; + const char* name= list.name; + if (old_count[lt] != new_count[lt]) + { + DBUG_PRINT("index_stat", ("%s: %u -> %u", + name, old_count[lt], new_count[lt])); + any= true; + } + } + if (any) + { + const uint bufsz= 20 * Ndb_index_stat::LT_Count; + char buf[bufsz]; + char *ptr= buf; + for (lt= 1; lt < Ndb_index_stat::LT_Count; lt++) + { + const Ndb_index_stat_list &list= ndb_index_stat_list[lt]; + const char* name= list.name; + sprintf(ptr, " %s:%u", name, new_count[lt]); + ptr+= strlen(ptr); + } + DBUG_PRINT("index_stat", ("list:%s", buf)); + } + } + + /* Cache summary */ + { + const Ndb_index_stat_opt &opt= ndb_index_stat_opt; + uint query_size= new_glob.cache_query_bytes; + uint clean_size= new_glob.cache_clean_bytes; + uint total_size= query_size + clean_size; + const uint limit= opt.get(Ndb_index_stat_opt::Icache_limit); + double pct= 100.0; + if (limit != 0) + pct= 100.0 * (double)total_size / (double)limit; + DBUG_PRINT("index_stat", ("cache query:%u clean:%u (%.2f pct)", + query_size, clean_size, pct)); + } + + /* Updates waited for and forced updates */ + { + pthread_mutex_lock(&ndb_index_stat_list_mutex); + uint wait_update= new_glob.wait_update; + uint force_update= new_glob.force_update; + pthread_mutex_unlock(&ndb_index_stat_list_mutex); + if (wait_update != 0 || force_update != 0) + { + DBUG_PRINT("index_stat", ("wait update:%u force update:%u", + wait_update, force_update)); + } + } +} +#endif + +void +ndb_index_stat_proc(Ndb_index_stat_proc &pr) +{ +#ifndef DBUG_OFF + Ndb_index_stat_glob old_glob= ndb_index_stat_glob; + old_glob.set_list_count(); +#endif + + DBUG_ENTER("ndb_index_stat_proc"); + + ndb_index_stat_proc_new(pr); + ndb_index_stat_proc_update(pr); + ndb_index_stat_proc_read(pr); + ndb_index_stat_proc_idle(pr); + ndb_index_stat_proc_check(pr); + ndb_index_stat_proc_evict(pr); + ndb_index_stat_proc_delete(pr); + ndb_index_stat_proc_error(pr); + +#ifndef DBUG_OFF + ndb_index_stat_report(old_glob); +#endif + DBUG_VOID_RETURN; +} + +void +ndb_index_stat_end() +{ + DBUG_ENTER("ndb_index_stat_end"); + Ndb_index_stat_proc pr; + pr.end= true; + + /* + * Shares have been freed so any index stat entries left should be + * in LT_Delete. The first two steps here should be unnecessary. + */ + + ndb_index_stat_allow(0); + + int lt; + for (lt= 1; lt < Ndb_index_stat::LT_Count; lt++) + { + if (lt == (int)Ndb_index_stat::LT_Delete) + continue; + Ndb_index_stat_list &list= ndb_index_stat_list[lt]; + Ndb_index_stat *st_loop= list.head; + while (st_loop != 0) + { + Ndb_index_stat *st= st_loop; + st_loop= st_loop->list_next; + DBUG_PRINT("index_stat", ("st %s end %s", st->id, list.name)); + pr.lt= Ndb_index_stat::LT_Delete; + ndb_index_stat_list_move(st, pr.lt); + } + } + + /* Real free */ + ndb_index_stat_proc_delete(pr); + DBUG_VOID_RETURN; +} + +/* Index stats thread */ + +pthread_handler_t +ndb_index_stat_thread_func(void *arg __attribute__((unused))) +{ + THD *thd; /* needs to be first for thread_stack */ + struct timespec abstime; + Thd_ndb *thd_ndb; + + my_thread_init(); + DBUG_ENTER("ndb_index_stat_thread_func"); + + // wl4124_todo remove useless stuff copied from utility thread + + pthread_mutex_lock(&LOCK_ndb_index_stat_thread); + + thd= new THD; /* note that contructor of THD uses DBUG_ */ + if (thd == NULL) + { + my_errno= HA_ERR_OUT_OF_MEM; + DBUG_RETURN(NULL); + } + THD_CHECK_SENTRY(thd); + pthread_detach_this_thread(); + ndb_index_stat_thread= pthread_self(); + + thd->thread_stack= (char*)&thd; /* remember where our stack is */ + if (thd->store_globals()) + goto ndb_index_stat_thread_fail; + lex_start(thd); + thd->init_for_queries(); +#ifndef NDB_THD_HAS_NO_VERSION + thd->version=refresh_version; +#endif + thd->client_capabilities = 0; + thd->security_ctx->skip_grants(); + my_net_init(&thd->net, 0); + + CHARSET_INFO *charset_connection; + charset_connection= get_charset_by_csname("utf8", + MY_CS_PRIMARY, MYF(MY_WME)); + thd->variables.character_set_client= charset_connection; + thd->variables.character_set_results= charset_connection; + thd->variables.collation_connection= charset_connection; + thd->update_charset(); + + /* Signal successful initialization */ + ndb_index_stat_thread_running= 1; + pthread_cond_signal(&COND_ndb_index_stat_ready); + pthread_mutex_unlock(&LOCK_ndb_index_stat_thread); + + /* + wait for mysql server to start + */ + pthread_mutex_lock(&LOCK_server_started); + while (!mysqld_server_started) + { + set_timespec(abstime, 1); + pthread_cond_timedwait(&COND_server_started, &LOCK_server_started, + &abstime); + if (ndbcluster_terminating) + { + pthread_mutex_unlock(&LOCK_server_started); + pthread_mutex_lock(&LOCK_ndb_index_stat_thread); + goto ndb_index_stat_thread_end; + } + } + pthread_mutex_unlock(&LOCK_server_started); + + /* + Wait for cluster to start + */ + pthread_mutex_lock(&LOCK_ndb_index_stat_thread); + while (!g_ndb_status.cluster_node_id && (ndbcluster_hton->slot != ~(uint)0)) + { + /* ndb not connected yet */ + pthread_cond_wait(&COND_ndb_index_stat_thread, &LOCK_ndb_index_stat_thread); + if (ndbcluster_terminating) + goto ndb_index_stat_thread_end; + } + pthread_mutex_unlock(&LOCK_ndb_index_stat_thread); + + /* Get thd_ndb for this thread */ + if (!(thd_ndb= ha_ndbcluster::seize_thd_ndb())) + { + sql_print_error("Could not allocate Thd_ndb object"); + pthread_mutex_lock(&LOCK_ndb_index_stat_thread); + goto ndb_index_stat_thread_end; + } + set_thd_ndb(thd, thd_ndb); + thd_ndb->options|= TNO_NO_LOG_SCHEMA_OP; + if (thd_ndb->ndb->setDatabaseName("sys") == -1) + { + sql_print_error("Could not change index stats thd_ndb database to sys"); + pthread_mutex_lock(&LOCK_ndb_index_stat_thread); + goto ndb_index_stat_thread_end; + } + + ndb_index_stat_allow(1); + bool enable_ok; + enable_ok= false; + + set_timespec(abstime, 0); + for (;;) + { + pthread_mutex_lock(&LOCK_ndb_index_stat_thread); + if (!ndbcluster_terminating) { + int ret= pthread_cond_timedwait(&COND_ndb_index_stat_thread, + &LOCK_ndb_index_stat_thread, + &abstime); + const char* reason= ret == ETIMEDOUT ? "timed out" : "wake up"; + DBUG_PRINT("index_stat", ("loop: %s", reason)); + } + if (ndbcluster_terminating) /* Shutting down server */ + goto ndb_index_stat_thread_end; + pthread_mutex_unlock(&LOCK_ndb_index_stat_thread); + + pthread_mutex_lock(&LOCK_global_system_variables); + const bool enable_ok_new= THDVAR(NULL, index_stat_enable); + pthread_mutex_unlock(&LOCK_global_system_variables); + + Ndb_index_stat_proc pr; + pr.ndb= thd_ndb->ndb; + + do + { + if (enable_ok != enable_ok_new) + { + DBUG_PRINT("index_stat", ("global enable: %d -> %d", + enable_ok, enable_ok_new)); + enable_ok= enable_ok_new; + } + + if (!enable_ok) + break; + + pr.busy= false; + ndb_index_stat_proc(pr); + } while (0); + + /* Calculate new time to wake up */ + + const Ndb_index_stat_opt &opt= ndb_index_stat_opt; + int msecs= 0; + if (!enable_ok) + msecs= opt.get(Ndb_index_stat_opt::Iloop_checkon); + else if (!pr.busy) + msecs= opt.get(Ndb_index_stat_opt::Iloop_idle); + else + msecs= opt.get(Ndb_index_stat_opt::Iloop_busy); + DBUG_PRINT("index_stat", ("sleep %dms", msecs)); + + struct timeval tick_time; + gettimeofday(&tick_time, 0); + abstime.tv_sec= tick_time.tv_sec; + abstime.tv_nsec= tick_time.tv_usec * 1000; + + int secs= 0; + if (msecs >= 1000) + { + secs= msecs / 1000; + msecs= msecs % 1000; + } + + abstime.tv_sec+= secs; + abstime.tv_nsec+= msecs * 1000000; + if (abstime.tv_nsec >= 1000000000) + { + abstime.tv_sec+= 1; + abstime.tv_nsec-= 1000000000; + } + } + +ndb_index_stat_thread_end: + net_end(&thd->net); + +ndb_index_stat_thread_fail: + if (thd_ndb) + { + ha_ndbcluster::release_thd_ndb(thd_ndb); + set_thd_ndb(thd, NULL); + } + thd->cleanup(); + delete thd; + + /* signal termination */ + ndb_index_stat_thread_running= 0; + pthread_cond_signal(&COND_ndb_index_stat_ready); + pthread_mutex_unlock(&LOCK_ndb_index_stat_thread); + DBUG_PRINT("exit", ("ndb_index_stat_thread")); + + DBUG_LEAVE; + my_thread_end(); + pthread_exit(0); + return NULL; +} + +/* Optimizer queries */ + +static ulonglong +ndb_index_stat_round(double x) +{ + char buf[100]; + if (x < 0.0) + x= 0.0; + snprintf(buf, sizeof(buf), "%.0f", x); + /* mysql provides strtoull */ + ulonglong n= strtoull(buf, 0, 10); + return n; +} + +int +ha_ndbcluster::ndb_index_stat_wait(Ndb_index_stat *st, + uint sample_version) +{ + DBUG_ENTER("ha_ndbcluster::ndb_index_stat_wait"); + + pthread_mutex_lock(&ndb_index_stat_stat_mutex); + int err= 0; + NdbIndexStat::Head head; + uint count= 0; + struct timespec abstime; + while (true) { + int ret= 0; + if (st->error.code != 0 && + (st->error.code != NdbIndexStat::NoIndexStats || + st->force_update == 0)) + { + err= st->error.code; + break; + } + if (st->sample_version > sample_version) + break; + DBUG_PRINT("index_stat", ("st %s wait count:%u", + st->id, ++count)); + pthread_mutex_lock(&LOCK_ndb_index_stat_thread); + pthread_cond_signal(&COND_ndb_index_stat_thread); + pthread_mutex_unlock(&LOCK_ndb_index_stat_thread); + set_timespec(abstime, 1); + ret= pthread_cond_timedwait(&ndb_index_stat_stat_cond, + &ndb_index_stat_stat_mutex, + &abstime); + if (ret != 0 && ret != ETIMEDOUT) + { + err= ret; + break; + } + } + pthread_mutex_unlock(&ndb_index_stat_stat_mutex); + if (err != 0) { + DBUG_PRINT("index_stat", ("st %s wait error: %d", + st->id, err)); + DBUG_RETURN(err); + } + DBUG_PRINT("index_stat", ("st %s wait ok: sample_version %u -> %u", + st->id, sample_version, st->sample_version)); + DBUG_RETURN(0); +} + +int +ha_ndbcluster::ndb_index_stat_query(uint inx, + const key_range *min_key, + const key_range *max_key, + NdbIndexStat::Stat& stat) +{ + DBUG_ENTER("ha_ndbcluster::ndb_index_stat_query"); + + const KEY *key_info= table->key_info + inx; + const NDB_INDEX_DATA &data= m_index[inx]; + const NDBINDEX *index= data.index; + DBUG_PRINT("index_stat", ("index: %s", index->getName())); + + THD *thd= current_thd; + Ndb *ndb= get_ndb(thd); + int err= 0; + + /* Create an IndexBound struct for the keys */ + NdbIndexScanOperation::IndexBound ib; + compute_index_bounds(ib, key_info, min_key, max_key); + ib.range_no= 0; + + Ndb_index_stat *st= + ndb_index_stat_get_share(m_share, index, m_table, true, false); + if (st == 0) + { + DBUG_PRINT("index_stat", ("failed to add index stat share")); + DBUG_RETURN(HA_ERR_OUT_OF_MEM); + } + + err= ndb_index_stat_wait(st, 0); + if (err != 0) + DBUG_RETURN(err); + + if (st->read_time == 0) + { + DBUG_PRINT("index_stat", ("no index stats")); + pthread_mutex_lock(&LOCK_ndb_index_stat_thread); + pthread_cond_signal(&COND_ndb_index_stat_thread); + pthread_mutex_unlock(&LOCK_ndb_index_stat_thread); + DBUG_RETURN(NdbIndexStat::NoIndexStats); + } + + uint8 bound_lo_buffer[NdbIndexStat::BoundBufferBytes]; + uint8 bound_hi_buffer[NdbIndexStat::BoundBufferBytes]; + NdbIndexStat::Bound bound_lo(st->is, bound_lo_buffer); + NdbIndexStat::Bound bound_hi(st->is, bound_hi_buffer); + NdbIndexStat::Range range(bound_lo, bound_hi); + + const NdbRecord* key_record= data.ndb_record_key; + if (st->is->convert_range(range, key_record, &ib) == -1) + { + ndb_index_stat_error(st, "convert_range", __LINE__); + DBUG_RETURN(st->error.code); + } + if (st->is->query_stat(range, stat) == -1) + { + /* Invalid cache - should remove the entry */ + ndb_index_stat_error(st, "query_stat", __LINE__); + DBUG_RETURN(st->error.code); + } + + DBUG_RETURN(0); +} + +int +ha_ndbcluster::ndb_index_stat_get_rir(uint inx, + key_range *min_key, + key_range *max_key, + ha_rows *rows_out) +{ + DBUG_ENTER("ha_ndbcluster::ndb_index_stat_get_rir"); + uint8 stat_buffer[NdbIndexStat::StatBufferBytes]; + NdbIndexStat::Stat stat(stat_buffer); + int err= ndb_index_stat_query(inx, min_key, max_key, stat); + if (err == 0) + { + double rir= -1.0; + NdbIndexStat::get_rir(stat, &rir); + DBUG_PRINT("index_stat", ("stat rir: %.2f", rir)); + ha_rows rows= ndb_index_stat_round(rir); + /* Estimate only so cannot return exact zero */ + if (rows == 0) + rows= 1; + *rows_out= rows; + DBUG_PRINT("index_stat", ("rir: %u", (uint)rows)); + DBUG_RETURN(0); + } + DBUG_RETURN(err); +} + +int +ha_ndbcluster::ndb_index_stat_set_rpk(uint inx) +{ + DBUG_ENTER("ha_ndbcluster::ndb_index_stat_set_rpk"); + + KEY *key_info= table->key_info + inx; + int err= 0; + + uint8 stat_buffer[NdbIndexStat::StatBufferBytes]; + NdbIndexStat::Stat stat(stat_buffer); + const key_range *min_key= 0; + const key_range *max_key= 0; + err= ndb_index_stat_query(inx, min_key, max_key, stat); + if (err == 0) + { + uint k; + for (k= 0; k < key_info->key_parts; k++) + { + double rpk= -1.0; + NdbIndexStat::get_rpk(stat, k, &rpk); + ulonglong recs= ndb_index_stat_round(rpk); + key_info->rec_per_key[k]= recs; + DBUG_PRINT("index_stat", ("rpk[%u]: %u", k, (uint)recs)); + } + DBUG_RETURN(0); + } + DBUG_RETURN(err); +} + +int +ha_ndbcluster::ndb_index_stat_analyze(Ndb *ndb, + uint *inx_list, + uint inx_count) +{ + DBUG_ENTER("ha_ndbcluster::ndb_index_stat_analyze"); + const Ndb_index_stat_opt &opt= ndb_index_stat_opt; + + struct { + uint sample_version; + uint error_count; + } old[MAX_INDEXES]; + + int err= 0; + uint i; + + /* Force stats update on each index */ + for (i= 0; i < inx_count; i++) + { + uint inx= inx_list[i]; + const NDB_INDEX_DATA &data= m_index[inx]; + const NDBINDEX *index= data.index; + DBUG_PRINT("index_stat", ("force update: %s", index->getName())); + + Ndb_index_stat *st= + ndb_index_stat_get_share(m_share, index, m_table, true, true); + + if (st == 0) + DBUG_RETURN(HA_ERR_OUT_OF_MEM); + + old[i].sample_version= st->sample_version; + old[i].error_count= st->error_count; + } + + /* Wait for each update (or error) */ + for (i = 0; i < inx_count; i++) + { + uint inx= inx_list[i]; + const NDB_INDEX_DATA &data= m_index[inx]; + const NDBINDEX *index= data.index; + DBUG_PRINT("index_stat", ("wait for update: %s", index->getName())); + + Ndb_index_stat *st= + ndb_index_stat_get_share(m_share, index, m_table, false, false); + if (st == 0) + DBUG_RETURN(HA_ERR_NO_SUCH_TABLE); + + err= ndb_index_stat_wait(st, old[i].sample_version); + if (err != 0) + DBUG_RETURN(err); + } + + DBUG_RETURN(0); +} + +/* END: Index statistcs */ + +/* + Condition pushdown +*/ +/** + Push a condition to ndbcluster storage engine for evaluation + during table and index scans. The conditions will be stored on a stack + for possibly storing several conditions. The stack can be popped + by calling cond_pop, handler::extra(HA_EXTRA_RESET) (handler::reset()) + will clear the stack. + The current implementation supports arbitrary AND/OR nested conditions + with comparisons between columns and constants (including constant + expressions and function calls) and the following comparison operators: + =, !=, >, >=, <, <=, "is null", and "is not null". + + @retval + NULL The condition was supported and will be evaluated for each + row found during the scan + @retval + cond The condition was not supported and all rows will be returned from + the scan for evaluation (and thus not saved on stack) +*/ +const +Item* +ha_ndbcluster::cond_push(const Item *cond) +{ + DBUG_ENTER("cond_push"); + + if (cond->used_tables() & ~table->map) + { + /** + * 'cond' refers fields from other tables, or other instances + * of this table, -> reject it. + * (Optimizer need to have a better understanding of what is + * pushable by each handler.) + */ + DBUG_EXECUTE("where",print_where((Item *)cond, "Rejected cond_push", QT_ORDINARY);); + DBUG_RETURN(cond); + } + if (!m_cond) + m_cond= new ha_ndbcluster_cond; + if (!m_cond) + { + my_errno= HA_ERR_OUT_OF_MEM; + DBUG_RETURN(cond); + } + DBUG_EXECUTE("where",print_where((Item *)cond, m_tabname, QT_ORDINARY);); + DBUG_RETURN(m_cond->cond_push(cond, table, (NDBTAB *)m_table)); +} + +/** + Pop the top condition from the condition stack of the handler instance. +*/ +void +ha_ndbcluster::cond_pop() +{ + if (m_cond) + m_cond->cond_pop(); +} + + +/* + Implements the SHOW NDB STATUS command. +*/ +bool +ndbcluster_show_status(handlerton *hton, THD* thd, stat_print_fn *stat_print, + enum ha_stat_type stat_type) +{ + char name[16]; + char buf[IO_SIZE]; + uint buflen; + DBUG_ENTER("ndbcluster_show_status"); + + if (stat_type != HA_ENGINE_STATUS) + { + DBUG_RETURN(FALSE); + } + + Ndb* ndb= check_ndb_in_thd(thd); + Thd_ndb *thd_ndb= get_thd_ndb(thd); + struct st_ndb_status ns; + if (ndb) + update_status_variables(thd_ndb, &ns, thd_ndb->connection); + else + update_status_variables(NULL, &ns, g_ndb_cluster_connection); + + buflen= + my_snprintf(buf, sizeof(buf), + "cluster_node_id=%ld, " + "connected_host=%s, " + "connected_port=%ld, " + "number_of_data_nodes=%ld, " + "number_of_ready_data_nodes=%ld, " + "connect_count=%ld", + ns.cluster_node_id, + ns.connected_host, + ns.connected_port, + ns.number_of_data_nodes, + ns.number_of_ready_data_nodes, + ns.connect_count); + if (stat_print(thd, ndbcluster_hton_name, ndbcluster_hton_name_length, + STRING_WITH_LEN("connection"), buf, buflen)) + DBUG_RETURN(TRUE); + + for (int i= 0; i < MAX_NDB_NODES; i++) + { + if (ns.transaction_hint_count[i] > 0 || + ns.transaction_no_hint_count[i] > 0) + { + uint namelen= my_snprintf(name, sizeof(name), "node[%d]", i); + buflen= my_snprintf(buf, sizeof(buf), + "transaction_hint=%ld, transaction_no_hint=%ld", + ns.transaction_hint_count[i], + ns.transaction_no_hint_count[i]); + if (stat_print(thd, ndbcluster_hton_name, ndbcluster_hton_name_length, + name, namelen, buf, buflen)) + DBUG_RETURN(TRUE); + } + } + + if (ndb) + { + Ndb::Free_list_usage tmp; + tmp.m_name= 0; + while (ndb->get_free_list_usage(&tmp)) + { + buflen= + my_snprintf(buf, sizeof(buf), + "created=%u, free=%u, sizeof=%u", + tmp.m_created, tmp.m_free, tmp.m_sizeof); + if (stat_print(thd, ndbcluster_hton_name, ndbcluster_hton_name_length, + tmp.m_name, strlen(tmp.m_name), buf, buflen)) + DBUG_RETURN(TRUE); + } + } + ndbcluster_show_status_binlog(thd, stat_print, stat_type); + + DBUG_RETURN(FALSE); +} + + +int ha_ndbcluster::get_default_no_partitions(HA_CREATE_INFO *create_info) +{ + if (unlikely(g_ndb_cluster_connection->get_no_ready() <= 0)) + { +err: + my_error(HA_ERR_NO_CONNECTION, MYF(0)); + return -1; + } + + THD* thd = current_thd; + if (thd == 0) + goto err; + Thd_ndb * thd_ndb = get_thd_ndb(thd); + if (thd_ndb == 0) + goto err; + + ha_rows max_rows, min_rows; + if (create_info) + { + max_rows= create_info->max_rows; + min_rows= create_info->min_rows; + } + else + { + max_rows= table_share->max_rows; + min_rows= table_share->min_rows; + } + uint no_fragments= get_no_fragments(max_rows >= min_rows ? + max_rows : min_rows); + uint reported_frags; + adjusted_frag_count(thd_ndb->ndb, + no_fragments, + reported_frags); + return reported_frags; +} + +uint32 ha_ndbcluster::calculate_key_hash_value(Field **field_array) +{ + Uint32 hash_value; + struct Ndb::Key_part_ptr key_data[MAX_REF_PARTS]; + struct Ndb::Key_part_ptr *key_data_ptr= &key_data[0]; + Uint32 i= 0; + int ret_val; Uint64 tmp[(MAX_KEY_SIZE_IN_WORDS*MAX_XFRM_MULTIPLY) >> 1]; void *buf= (void*)&tmp[0]; Ndb *ndb= m_thd_ndb->ndb; @@ -15353,6 +17316,7 @@ SHOW_VAR ndb_status_variables_export[]= {"Ndb", (char*) &ndb_status_injector_variables, SHOW_ARRAY}, {"Ndb", (char*) &ndb_status_slave_variables, SHOW_ARRAY}, {"Ndb", (char*) &show_ndb_server_api_stats, SHOW_FUNC}, + {"Ndb_index_stat", (char*) &ndb_status_index_stat_variables, SHOW_ARRAY}, {NullS, NullS, SHOW_LONG} }; @@ -15432,6 +17396,17 @@ static MYSQL_SYSVAR_UINT( ); +static MYSQL_SYSVAR_STR( + index_stat_option, /* name */ + opt_ndb_index_stat_option, /* var */ + PLUGIN_VAR_RQCMDARG, + "Comma-separated tunable options for ndb index statistics", + ndb_index_stat_option_check, /* check func. */ + ndb_index_stat_option_update, /* update func. */ + ndb_index_stat_opt.option +); + + ulong opt_ndb_report_thresh_binlog_epoch_slip; static MYSQL_SYSVAR_ULONG( report_thresh_binlog_epoch_slip, /* name */ @@ -15624,6 +17599,7 @@ static struct st_mysql_sys_var* system_v MYSQL_SYSVAR(batch_size), MYSQL_SYSVAR(optimization_delay), MYSQL_SYSVAR(index_stat_enable), + MYSQL_SYSVAR(index_stat_option), MYSQL_SYSVAR(index_stat_cache_entries), MYSQL_SYSVAR(index_stat_update_freq), MYSQL_SYSVAR(table_no_logging), === modified file 'sql/ha_ndbcluster.h' --- a/sql/ha_ndbcluster.h 2011-04-29 09:06:21 +0000 +++ b/sql/ha_ndbcluster.h 2011-05-29 13:01:46 +0000 @@ -71,12 +71,6 @@ typedef struct ndb_index_data { const NdbDictionary::Index *unique_index; unsigned char *unique_index_attrid_map; bool null_in_unique_index; - // In this version stats are not shared between threads - NdbIndexStat* index_stat; - uint index_stat_cache_entries; - // Simple counter mechanism to decide when to connect to db - uint index_stat_update_freq; - uint index_stat_query_count; /* In mysqld, keys and rows are stored differently (using KEY_PART_INFO for keys and Field for rows). @@ -168,6 +162,54 @@ struct Ndb_statistics { Uint64 fragment_extent_free_space; }; +struct Ndb_index_stat { + enum { + LT_Undef= 0, + LT_New= 1, /* new entry added by a table handler */ + LT_Update = 2, /* force kernel update from analyze table */ + LT_Read= 3, /* read or reread stats into new query cache */ + LT_Idle= 4, /* stats exist */ + LT_Check= 5, /* check for new stats */ + LT_Delete= 6, /* delete the entry */ + LT_Error= 7, /* error, on hold for a while */ + LT_Count= 8 + }; + NdbIndexStat* is; + int index_id; + int index_version; +#ifndef DBUG_OFF + char id[32]; +#endif + time_t access_time; /* by any table handler */ + time_t load_time; /* when stats were created by kernel */ + time_t read_time; /* when stats were read by us (>= load_time) */ + uint sample_version; /* goes with read_time */ + time_t check_time; /* when checked for updated stats (>= read_time) */ + bool cache_clean; /* old caches have been deleted */ + uint force_update; /* one-time force update from analyze table */ + NdbIndexStat::Error error; + time_t error_time; + int error_count; + struct Ndb_index_stat *share_next; /* per-share list */ + int lt; + int lt_old; /* for info only */ + struct Ndb_index_stat *list_next; + struct Ndb_index_stat *list_prev; + struct st_ndbcluster_share *share; + Ndb_index_stat(); +}; + +struct Ndb_index_stat_list { + const char *name; + int lt; + struct Ndb_index_stat *head; + struct Ndb_index_stat *tail; + uint count; + Ndb_index_stat_list(int the_lt, const char* the_name); +}; + +extern Ndb_index_stat_list ndb_index_stat_list[]; + typedef struct st_ndbcluster_share { NDB_SHARE_STATE state; MEM_ROOT mem_root; @@ -183,6 +225,7 @@ typedef struct st_ndbcluster_share { char *table_name; Ndb::TupleIdRange tuple_id_range; struct Ndb_statistics stat; + struct Ndb_index_stat *index_stat_list; bool util_thread; // if opened by util thread uint32 connect_count; uint32 flags; @@ -382,6 +425,7 @@ class ha_ndbcluster: public handler int optimize(THD* thd, HA_CHECK_OPT* check_opt); int analyze(THD* thd, HA_CHECK_OPT* check_opt); + int analyze_index(THD* thd); int write_row(uchar *buf); int update_row(const uchar *old_data, uchar *new_data); @@ -749,6 +793,21 @@ private: void no_uncommitted_rows_update(int); void no_uncommitted_rows_reset(THD *); + /* Ordered index statistics v4 */ + int ndb_index_stat_get_rir(uint inx, + key_range *min_key, + key_range *max_key, + ha_rows *rows_out); + int ndb_index_stat_set_rpk(uint inx); + int ndb_index_stat_wait(Ndb_index_stat *st, + uint sample_version); + int ndb_index_stat_query(uint inx, + const key_range *min_key, + const key_range *max_key, + NdbIndexStat::Stat& stat); + int ndb_index_stat_analyze(Ndb *ndb, + uint *inx_list, + uint inx_count); NdbTransaction *start_transaction_part_id(uint32 part_id, int &error); inline NdbTransaction *get_transaction_part_id(uint32 part_id, int &error) @@ -896,3 +955,5 @@ static const int ndbcluster_hton_name_le extern int ndbcluster_terminating; extern int ndb_util_thread_running; extern pthread_cond_t COND_ndb_util_ready; +extern int ndb_index_stat_thread_running; +extern pthread_cond_t COND_ndb_index_stat_ready; === modified file 'sql/ha_ndbcluster_binlog.cc' --- a/sql/ha_ndbcluster_binlog.cc 2011-03-22 08:32:32 +0000 +++ b/sql/ha_ndbcluster_binlog.cc 2011-05-29 13:01:46 +0000 @@ -831,6 +831,24 @@ int ndbcluster_binlog_end(THD *thd) pthread_mutex_unlock(&LOCK_ndb_util_thread); } + if (ndb_index_stat_thread_running > 0) + { + /* + Index stats thread blindly imitates util thread. Following actually + fixes some "[Warning] Plugin 'ndbcluster' will be forced to shutdown". + */ + sql_print_information("Stopping Cluster Index Stats thread"); + pthread_mutex_lock(&LOCK_ndb_index_stat_thread); + /* Ensure mutex are not freed if ndb_cluster_end is running at same time */ + ndb_index_stat_thread_running++; + ndbcluster_terminating= 1; + pthread_cond_signal(&COND_ndb_index_stat_thread); + while (ndb_index_stat_thread_running > 1) + pthread_cond_wait(&COND_ndb_index_stat_ready, &LOCK_ndb_index_stat_thread); + ndb_index_stat_thread_running--; + pthread_mutex_unlock(&LOCK_ndb_index_stat_thread); + } + if (ndbcluster_binlog_inited) { ndbcluster_binlog_inited= 0; === modified file 'sql/ha_ndbcluster_binlog.h' --- a/sql/ha_ndbcluster_binlog.h 2011-02-18 13:55:27 +0000 +++ b/sql/ha_ndbcluster_binlog.h 2011-05-29 13:01:46 +0000 @@ -195,6 +195,8 @@ void ndbcluster_global_schema_lock_deini extern unsigned char g_node_id_map[max_ndb_nodes]; extern pthread_mutex_t LOCK_ndb_util_thread; extern pthread_cond_t COND_ndb_util_thread; +extern pthread_mutex_t LOCK_ndb_index_stat_thread; +extern pthread_cond_t COND_ndb_index_stat_thread; extern pthread_mutex_t ndbcluster_mutex; extern HASH ndbcluster_open_tables; --===============0257905011== MIME-Version: 1.0 Content-Type: text/bzr-bundle; charset="us-ascii"; name="bzr/pekka.nousiainen@stripped" Content-Transfer-Encoding: 7bit Content-Disposition: inline # Bazaar merge directive format 2 (Bazaar 0.90) # revision_id: pekka.nousiainen@stripped\ # 90a2rq09ifje98db # target_branch: file:///export/space/pekka/ms/ms-wl4124-70/ # testament_sha1: 7045fd9f239d0b72a3cda3c1b482f71ee1a04128 # timestamp: 2011-05-29 16:01:50 +0300 # source_branch: bzr+ssh://pnousiainen@stripped/bzrroot\ # /server/mysql-5.1-telco-7.0/ # base_revision_id: pekka.nousiainen@stripped\ # l80j5vq3giohzkee # # Begin bundle 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 mf14Mlwif/F3JFOFCQUFAojA --===============0257905011==--