From: Pekka Nousiainen Date: June 15 2011 10:44am Subject: bzr push into mysql-5.1-telco-7.0-wl4124-new1 branch (pekka.nousiainen:4397 to 4399) WL#4124 List-Archive: http://lists.mysql.com/commits/139226 Message-Id: <20110615104451.D58905586E@sama.localdomain> MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit 4399 Pekka Nousiainen 2011-06-15 wl#4124 b06_handler.diff handler (+ start on modularize), simple MTR fixes added: sql/ha_ndb_index_stat.cc sql/ha_ndb_index_stat.h 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/Makefile.am sql/ha_ndbcluster.cc sql/ha_ndbcluster.h sql/ha_ndbcluster_binlog.cc sql/ha_ndbcluster_binlog.h storage/ndb/CMakeLists.txt 4398 Pekka Nousiainen 2011-06-15 treename wl4124-new1 modified: .bzr-mysql/default.conf 4397 Pekka Nousiainen 2011-06-14 wl#4124 x07_fix.diff build fixes modified: storage/ndb/src/ndbapi/CMakeLists.txt storage/ndb/src/ndbapi/NdbIndexStatImpl.cpp storage/ndb/tools/CMakeLists.txt storage/ndb/tools/ndb_dump_frm_data.cpp storage/ndb/tools/ndb_index_stat.cpp === modified file '.bzr-mysql/default.conf' --- a/.bzr-mysql/default.conf 2011-06-07 11:44:29 +0000 +++ b/.bzr-mysql/default.conf 2011-06-15 09:15:33 +0000 @@ -1,4 +1,4 @@ [MYSQL] post_commit_to = commits@stripped post_push_to = commits@stripped -tree_name = mysql-5.1-telco-7.0-wl4124-new0 +tree_name = mysql-5.1-telco-7.0-wl4124-new1 === 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-06-15 10:37:56 +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-06-15 10:37:56 +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-06-15 10:37:56 +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-06-15 10:37:56 +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/Makefile.am' --- a/sql/Makefile.am 2011-04-08 11:06:53 +0000 +++ b/sql/Makefile.am 2011-06-15 10:37:56 +0000 @@ -62,6 +62,7 @@ noinst_HEADERS = item.h item_func.h item ha_ndbcluster_connection.h ha_ndbcluster_connection.h \ ha_ndbcluster_lock_ext.h ha_ndbinfo.h \ ha_ndbcluster_glue.h \ + ha_ndb_index_stat.h \ ha_partition.h rpl_constants.h \ debug_sync.h \ opt_range.h protocol.h rpl_tblmap.h rpl_utility.h \ @@ -136,6 +137,7 @@ libndb_la_SOURCES= ha_ndbcluster.cc \ ha_ndbcluster_binlog.cc \ ha_ndbcluster_connection.cc \ ha_ndbcluster_cond.cc \ + ha_ndb_index_stat.cc \ ha_ndbinfo.cc gen_lex_hash_SOURCES = gen_lex_hash.cc === added file 'sql/ha_ndb_index_stat.cc' --- a/sql/ha_ndb_index_stat.cc 1970-01-01 00:00:00 +0000 +++ b/sql/ha_ndb_index_stat.cc 2011-06-15 10:37:56 +0000 @@ -0,0 +1,1975 @@ +/* + Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "ha_ndbcluster_glue.h" + +#ifdef WITH_NDBCLUSTER_STORAGE_ENGINE + +#include "ha_ndbcluster.h" +#include "ha_ndbcluster_binlog.h" +#include "ha_ndb_index_stat.h" +#include +#include + +// Typedefs for long names +typedef NdbDictionary::Table NDBTAB; +typedef NdbDictionary::Index NDBINDEX; + +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[]; + +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)) + { + DBUG_PRINT("index_stat", ("time moved backwards %d seconds", + int(ndb_index_stat_time_now - now))); + now= ndb_index_stat_time_now; + } + + ndb_index_stat_time_now= now; + return now; +} + +bool ndb_index_stat_allow_flag= false; + +bool +ndb_index_stat_allow(int flag= -1) +{ + 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; +} + +/* Options */ + +/* 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*); + +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_opt(char* buf); + uint get(Idx i) const { + assert(i < Imax); + return val[i].val; + } +}; + +Ndb_index_stat_opt::Ndb_index_stat_opt(char* buf) : + option(buf) +{ +#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; + +char ndb_index_stat_option_buf[ndb_index_stat_option_sz]; +Ndb_index_stat_opt ndb_index_stat_opt(ndb_index_stat_option_buf); + +/* Copy option struct to string buffer */ +void +ndb_index_stat_opt2str(const Ndb_index_stat_opt& opt, char* str) +{ + DBUG_ENTER("ndb_index_stat_opt2str"); + + char buf[ndb_index_stat_option_sz]; + char *const end= &buf[sizeof(buf)]; + char* ptr= buf; + *ptr= 0; + + const uint imax= Ndb_index_stat_opt::Imax; + for (uint i= 0; i < imax; i++) + { + 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; + + 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)); + char buf2[ndb_index_stat_option_sz]; + Ndb_index_stat_opt opt(buf2); + 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 */ + +static int +ndb_index_stat_check_or_create_systables(NdbIndexStat* is, Ndb* ndb) +{ + DBUG_ENTER("ndb_index_stat_check_or_create_systables"); + + if (is->check_systables(ndb) == 0) + { + DBUG_PRINT("index_stat", ("using existing index stats tables")); + DBUG_RETURN(0); + } + + if (is->create_systables(ndb) == 0) + { + DBUG_PRINT("index_stat", ("created index stats tables")); + DBUG_RETURN(0); + } + + if (is->getNdbError().code == 721) + { + // race between mysqlds, maybe + DBUG_PRINT("index_stat", ("create index stats tables failed: error %d line %d", + is->getNdbError().code, is->getNdbError().line)); + DBUG_RETURN(-1); + } + + sql_print_warning("create index stats tables failed: error %d line %d", + is->getNdbError().code, is->getNdbError().line); + DBUG_RETURN(-1); +} + +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(NDB_INDEX_STAT_DB) == -1) + { + sql_print_error("Could not change index stats thd_ndb database to %s", + NDB_INDEX_STAT_DB); + 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); */ + const bool enable_ok_new= ndb_index_stat_get_enable(NULL); + 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)); + + if (enable_ok_new) + { + // at enable check or create stats tables + NdbIndexStat is; + if (ndb_index_stat_check_or_create_systables(&is, thd_ndb->ndb) == -1) + { + // try again in next loop + break; + } + } + 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); +} + +#endif === added file 'sql/ha_ndb_index_stat.h' --- a/sql/ha_ndb_index_stat.h 1970-01-01 00:00:00 +0000 +++ b/sql/ha_ndb_index_stat.h 2011-06-15 10:37:56 +0000 @@ -0,0 +1,38 @@ +/* + Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +/* provides declarations only to index_stat.cc */ + +extern struct st_ndb_status g_ndb_status; + +extern pthread_t ndb_index_stat_thread; +extern pthread_cond_t COND_ndb_index_stat_thread; +extern pthread_mutex_t LOCK_ndb_index_stat_thread; +extern pthread_mutex_t ndb_index_stat_glob_mutex; +extern pthread_mutex_t ndb_index_stat_list_mutex; +extern pthread_mutex_t ndb_index_stat_stat_mutex; +extern pthread_cond_t ndb_index_stat_stat_cond; + +extern bool ndb_index_stat_get_enable(THD *thd); + +extern long g_ndb_status_index_stat_cache_query; +extern long g_ndb_status_index_stat_cache_clean; + +void +compute_index_bounds(NdbIndexScanOperation::IndexBound & bound, + const KEY *key_info, + const key_range *start_key, const key_range *end_key); === modified file 'sql/ha_ndbcluster.cc' --- a/sql/ha_ndbcluster.cc 2011-06-06 12:18:27 +0000 +++ b/sql/ha_ndbcluster.cc 2011-06-15 10:37:56 +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 */ @@ -259,6 +260,15 @@ static MYSQL_THDVAR_UINT( ); /* + Required in index_stat.cc but available only from here + thanks to use of top level anonymous structs. +*/ +bool ndb_index_stat_get_enable(THD *thd) +{ + return THDVAR(thd, index_stat_enable); +} + +/* Default value for max number of transactions createable against NDB from the handler. Should really be 2 but there is a transaction to much allocated when lock table is used, and one extra to used for global schema lock. @@ -404,29 +414,30 @@ pthread_cond_t COND_ndb_util_thread; pthread_cond_t COND_ndb_util_ready; pthread_handler_t ndb_util_thread_func(void *arg); -/* Status variables shown with 'show status like 'Ndb%' */ +// 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); -struct st_ndb_status { - st_ndb_status() { bzero(this, sizeof(struct st_ndb_status)); } - long cluster_node_id; - const char * connected_host; - long connected_port; - long number_of_replicas; - long number_of_data_nodes; - long number_of_ready_data_nodes; - long connect_count; - long execute_count; - long scan_count; - long pruned_scan_count; - long transaction_no_hint_count[MAX_NDB_NODES]; - long transaction_hint_count[MAX_NDB_NODES]; - long long api_client_stats[Ndb::NumClientStatistics]; -}; +extern void ndb_index_stat_free(NDB_SHARE *share); +extern void ndb_index_stat_end(); + +/* Status variables shown with 'show status like 'Ndb%' */ -static struct st_ndb_status g_ndb_status; +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; +long g_ndb_status_index_stat_cache_query = 0; +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 +615,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 +1140,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 +2180,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 +2191,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 +2262,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(); - 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) { @@ -3479,7 +3480,7 @@ count_key_columns(const KEY *key_info, c } /* Helper method to compute NDB index bounds. Note: does not set range_no. */ -static void +void compute_index_bounds(NdbIndexScanOperation::IndexBound & bound, const KEY *key_info, const key_range *start_key, const key_range *end_key) @@ -3530,6 +3531,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 +6074,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 +9872,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 +10920,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 +11024,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 +11089,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 +11117,7 @@ static int ndbcluster_end(handlerton *ht } my_hash_free(&ndbcluster_open_tables); + ndb_index_stat_end(); ndbcluster_disconnect(); // cleanup ndb interface @@ -11020,6 +11128,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 +11240,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 +11271,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) + 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; + + do { - 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 (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); + 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 +12217,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); @@ -15353,6 +15453,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} }; @@ -15431,6 +15532,31 @@ static MYSQL_SYSVAR_UINT( 0 /* block */ ); +/* should be in index_stat.h */ + +extern int +ndb_index_stat_option_check(MYSQL_THD, + struct st_mysql_sys_var *var, + void *save, + struct st_mysql_value *value); +extern void +ndb_index_stat_option_update(MYSQL_THD, + struct st_mysql_sys_var *var, + void *var_ptr, + const void *save); + +extern char ndb_index_stat_option_buf[]; + +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_option_buf +); + ulong opt_ndb_report_thresh_binlog_epoch_slip; static MYSQL_SYSVAR_ULONG( @@ -15624,6 +15750,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-06-15 10:37:56 +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). @@ -183,6 +177,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; @@ -366,6 +361,23 @@ class Thd_ndb bool recycle_ndb(THD* thd); }; +struct st_ndb_status { + st_ndb_status() { bzero(this, sizeof(struct st_ndb_status)); } + long cluster_node_id; + const char * connected_host; + long connected_port; + long number_of_replicas; + long number_of_data_nodes; + long number_of_ready_data_nodes; + long connect_count; + long execute_count; + long scan_count; + long pruned_scan_count; + long transaction_no_hint_count[MAX_NDB_NODES]; + long transaction_hint_count[MAX_NDB_NODES]; + long long api_client_stats[Ndb::NumClientStatistics]; +}; + int ndbcluster_commit(handlerton *hton, THD *thd, bool all); class ha_ndbcluster: public handler { @@ -382,6 +394,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 +762,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 +924,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-06-15 10:37:56 +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-06-15 10:37:56 +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; === modified file 'storage/ndb/CMakeLists.txt' --- a/storage/ndb/CMakeLists.txt 2011-05-19 09:05:45 +0000 +++ b/storage/ndb/CMakeLists.txt 2011-06-15 10:37:56 +0000 @@ -146,6 +146,7 @@ SET(NDBCLUSTER_SOURCES ../../sql/ha_ndbcluster_cond.cc ../../sql/ha_ndbcluster_connection.cc ../../sql/ha_ndbcluster_binlog.cc + ../../sql/ha_ndb_index_stat.cc ../../sql/ha_ndbinfo.cc) INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/storage/ndb/include) No bundle (reason: useless for push emails).