#At file:///export/space/pekka/ms/ms-wl4124-70/ based on revid:pekka.nousiainen@stripped
4397 Pekka Nousiainen 2011-06-12
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-06-12 16:54:59 +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-12 16:54:59 +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-12 16:54:59 +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-12 16:54:59 +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-06-06 12:18:27 +0000
+++ b/sql/ha_ndbcluster.cc 2011-06-12 16:54:59 +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();
- 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,2076 @@ 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 */
+
+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);
+ }
+
+ 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);
+ 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
+ sql_print_warning("create stats tables failed: error code %d",
+ is.getNdbError().code);
+ 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);
+}
+
+/* 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 +17349,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 +17429,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 +17632,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-12 16:54:59 +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-06-12 16:54:59 +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-12 16:54:59 +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;
Attachment: [text/bzr-bundle] bzr/pekka.nousiainen@oracle.com-20110612165459-1e4b24mixhcfa17g.bundle
| Thread |
|---|
| • bzr commit into mysql-5.1-telco-7.0-wl4124-new0 branch(pekka.nousiainen:4397) WL#4124 | Pekka Nousiainen | 13 Jun |