From 33c05ce98dbb6ab8ceef68a6dadd8eaa51ff0645 Mon Sep 17 00:00:00 2001 From: "tomas@poseidon.ndb.mysql.com" <> Date: Tue, 23 Nov 2004 09:50:33 +0000 Subject: [PATCH 01/10] enabling control of query cache for ndb --- sql/ha_ndbcluster.cc | 2 +- sql/mysqld.cc | 13 ++++++++++++- sql/set_var.cc | 6 ++++++ sql/sql_class.h | 1 + 4 files changed, 20 insertions(+), 2 deletions(-) diff --git a/sql/ha_ndbcluster.cc b/sql/ha_ndbcluster.cc index 6f7940caf75..db031c632ff 100644 --- a/sql/ha_ndbcluster.cc +++ b/sql/ha_ndbcluster.cc @@ -3004,7 +3004,7 @@ int ha_ndbcluster::external_lock(THD *thd, int lock_type) m_transaction_on= FALSE; else m_transaction_on= thd->variables.ndb_use_transactions; - // m_use_local_query_cache= thd->variables.ndb_use_local_query_cache; + m_use_local_query_cache= thd->variables.ndb_use_local_query_cache; m_active_trans= thd->transaction.all.ndb_tid ? (NdbConnection*)thd->transaction.all.ndb_tid: diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 5033c42ac69..f7c89f6dde3 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -2227,7 +2227,11 @@ extern "C" pthread_handler_decl(handle_shutdown,arg) #endif -const char *load_default_groups[]= { "mysqld","server",MYSQL_BASE_VERSION,0,0}; +const char *load_default_groups[]= { +#ifdef HAVE_NDBCLUSTER_DB + "mysql_cluster", +#endif + "mysqld","server",MYSQL_BASE_VERSION,0,0}; bool open_log(MYSQL_LOG *log, const char *hostname, const char *opt_name, const char *extension, @@ -3950,6 +3954,7 @@ enum options_mysqld OPT_INNODB, OPT_ISAM, OPT_NDBCLUSTER, OPT_NDB_CONNECTSTRING, OPT_NDB_USE_EXACT_COUNT, OPT_NDB_FORCE_SEND, OPT_NDB_AUTOINCREMENT_PREFETCH_SZ, + OPT_NDB_USE_LOCAL_QUERY_CACHE, OPT_SKIP_SAFEMALLOC, OPT_TEMP_POOL, OPT_TX_ISOLATION, OPT_SKIP_STACK_TRACE, OPT_SKIP_SYMLINKS, @@ -4409,6 +4414,12 @@ Disable with --skip-ndbcluster (will save memory).", (gptr*) &global_system_variables.ndb_use_exact_count, (gptr*) &global_system_variables.ndb_use_exact_count, 0, GET_BOOL, OPT_ARG, 1, 0, 0, 0, 0, 0}, + {"ndb_use_local_query_cache", OPT_NDB_USE_LOCAL_QUERY_CACHE, + "Use local query cache, note that this cache will _not_ " + "be invalidated if data is updated through other mysql servers", + (gptr*) &global_system_variables.ndb_use_local_query_cache, + (gptr*) &global_system_variables.ndb_use_local_query_cache, + 0, GET_BOOL, OPT_ARG, 0, 0, 0, 0, 0, 0}, #endif {"new", 'n', "Use very new possible 'unsafe' functions.", (gptr*) &global_system_variables.new_mode, diff --git a/sql/set_var.cc b/sql/set_var.cc index f1973b53e49..6bf151be83f 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -371,6 +371,9 @@ sys_var_thd_bool sys_ndb_use_exact_count("ndb_use_exact_count", &SV::ndb_use_exact_count); sys_var_thd_bool +sys_ndb_use_local_query_cache("ndb_use_local_query_cache", + &SV::ndb_use_local_query_cache); +sys_var_thd_bool sys_ndb_use_transactions("ndb_use_transactions", &SV::ndb_use_transactions); // ndb server global variable settings @@ -634,6 +637,7 @@ sys_var *sys_variables[]= &sys_ndb_autoincrement_prefetch_sz, &sys_ndb_force_send, &sys_ndb_use_exact_count, + &sys_ndb_use_local_query_cache, &sys_ndb_use_transactions, #endif &sys_unique_checks, @@ -801,6 +805,8 @@ struct show_var_st init_vars[]= { (char*) &sys_ndb_autoincrement_prefetch_sz, SHOW_SYS}, {sys_ndb_force_send.name, (char*) &sys_ndb_force_send, SHOW_SYS}, {sys_ndb_use_exact_count.name,(char*) &sys_ndb_use_exact_count, SHOW_SYS}, + {sys_ndb_use_local_query_cache.name, + (char*) &sys_ndb_use_local_query_cache, SHOW_SYS}, {sys_ndb_use_transactions.name,(char*) &sys_ndb_use_transactions, SHOW_SYS}, #endif {sys_net_buffer_length.name,(char*) &sys_net_buffer_length, SHOW_SYS}, diff --git a/sql/sql_class.h b/sql/sql_class.h index d0d9afc7746..06975730195 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -403,6 +403,7 @@ struct system_variables ulong ndb_autoincrement_prefetch_sz; my_bool ndb_force_send; my_bool ndb_use_exact_count; + my_bool ndb_use_local_query_cache; my_bool ndb_use_transactions; #endif /* HAVE_NDBCLUSTER_DB */ my_bool old_passwords; From ca61e54cd0ce37613aee72d94e4aea1553beabd4 Mon Sep 17 00:00:00 2001 From: "tomas@poseidon.ndb.mysql.com" <> Date: Tue, 23 Nov 2004 16:38:00 +0000 Subject: [PATCH 02/10] changed query cache type variable for ndb --- sql/ha_ndbcluster.cc | 21 +++++++++++++++------ sql/ha_ndbcluster.h | 2 +- sql/mysqld.cc | 18 +++++++++++------- sql/set_var.cc | 18 ++++++++++++------ sql/sql_cache.cc | 6 ++++++ sql/sql_cache.h | 1 + sql/sql_class.h | 2 +- 7 files changed, 47 insertions(+), 21 deletions(-) diff --git a/sql/ha_ndbcluster.cc b/sql/ha_ndbcluster.cc index 8d82f60ae85..eea051be9e4 100644 --- a/sql/ha_ndbcluster.cc +++ b/sql/ha_ndbcluster.cc @@ -3025,7 +3025,9 @@ int ha_ndbcluster::external_lock(THD *thd, int lock_type) m_transaction_on= FALSE; else m_transaction_on= thd->variables.ndb_use_transactions; - m_use_local_query_cache= thd->variables.ndb_use_local_query_cache; +#ifdef HAVE_QUERY_CACHE + m_query_cache_type= thd->variables.ndb_query_cache_type; +#endif m_active_trans= thd->transaction.all.ndb_tid ? (NdbConnection*)thd->transaction.all.ndb_tid: @@ -3751,8 +3753,8 @@ ha_ndbcluster::ha_ndbcluster(TABLE *table_arg): m_force_send(TRUE), m_autoincrement_prefetch(32), m_transaction_on(TRUE), - m_use_local_query_cache(FALSE) -{ + m_query_cache_type(0) +{ int i; DBUG_ENTER("ha_ndbcluster"); @@ -4455,10 +4457,17 @@ const char* ha_ndbcluster::index_type(uint key_number) } uint8 ha_ndbcluster::table_cache_type() { - if (m_use_local_query_cache) - return HA_CACHE_TBL_TRANSACT; - else + switch (m_query_cache_type) + { + case 0: return HA_CACHE_TBL_NOCACHE; + case 1: + return HA_CACHE_TBL_ASKTRANSACT; + case 2: + return HA_CACHE_TBL_TRANSACT; + default: + return HA_CACHE_TBL_NOCACHE; + } } /* diff --git a/sql/ha_ndbcluster.h b/sql/ha_ndbcluster.h index f6c712620c1..baf4a7480ac 100644 --- a/sql/ha_ndbcluster.h +++ b/sql/ha_ndbcluster.h @@ -244,7 +244,7 @@ class ha_ndbcluster: public handler bool m_force_send; ha_rows m_autoincrement_prefetch; bool m_transaction_on; - bool m_use_local_query_cache; + ulong m_query_cache_type; void set_rec_per_key(); void records_update(); diff --git a/sql/mysqld.cc b/sql/mysqld.cc index f7c89f6dde3..51e12ff23f9 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -3954,7 +3954,7 @@ enum options_mysqld OPT_INNODB, OPT_ISAM, OPT_NDBCLUSTER, OPT_NDB_CONNECTSTRING, OPT_NDB_USE_EXACT_COUNT, OPT_NDB_FORCE_SEND, OPT_NDB_AUTOINCREMENT_PREFETCH_SZ, - OPT_NDB_USE_LOCAL_QUERY_CACHE, + OPT_NDB_QUERY_CACHE_TYPE, OPT_SKIP_SAFEMALLOC, OPT_TEMP_POOL, OPT_TX_ISOLATION, OPT_SKIP_STACK_TRACE, OPT_SKIP_SYMLINKS, @@ -4414,12 +4414,16 @@ Disable with --skip-ndbcluster (will save memory).", (gptr*) &global_system_variables.ndb_use_exact_count, (gptr*) &global_system_variables.ndb_use_exact_count, 0, GET_BOOL, OPT_ARG, 1, 0, 0, 0, 0, 0}, - {"ndb_use_local_query_cache", OPT_NDB_USE_LOCAL_QUERY_CACHE, - "Use local query cache, note that this cache will _not_ " - "be invalidated if data is updated through other mysql servers", - (gptr*) &global_system_variables.ndb_use_local_query_cache, - (gptr*) &global_system_variables.ndb_use_local_query_cache, - 0, GET_BOOL, OPT_ARG, 0, 0, 0, 0, 0, 0}, +#ifdef HAVE_QUERY_CACHE + {"ndb_query_cache_type", OPT_NDB_QUERY_CACHE_TYPE, + "0 = OFF = Don't cache or retrieve results. 1 = ON = Cache as query_cache_type states and " + "invalidate cache if tables are updated by other mysql servers. " + "2 = LOCAL = Cache as query_cache_type states and don't bother about what's happening on other " + "mysql servers.", + (gptr*) &global_system_variables.ndb_query_cache_type, + (gptr*) &global_system_variables.ndb_query_cache_type, + 0, GET_ULONG, REQUIRED_ARG, 0, 0, 2, 0, 0, 0}, +#endif #endif {"new", 'n', "Use very new possible 'unsafe' functions.", (gptr*) &global_system_variables.new_mode, diff --git a/sql/set_var.cc b/sql/set_var.cc index a040e8b2ba4..c51bfce7a43 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -370,9 +370,12 @@ sys_ndb_force_send("ndb_force_send", sys_var_thd_bool sys_ndb_use_exact_count("ndb_use_exact_count", &SV::ndb_use_exact_count); -sys_var_thd_bool -sys_ndb_use_local_query_cache("ndb_use_local_query_cache", - &SV::ndb_use_local_query_cache); +#ifdef HAVE_QUERY_CACHE +sys_var_thd_enum +sys_ndb_query_cache_type("ndb_query_cache_type", + &SV::ndb_query_cache_type, + &ndb_query_cache_type_typelib); +#endif sys_var_thd_bool sys_ndb_use_transactions("ndb_use_transactions", &SV::ndb_use_transactions); @@ -637,7 +640,9 @@ sys_var *sys_variables[]= &sys_ndb_autoincrement_prefetch_sz, &sys_ndb_force_send, &sys_ndb_use_exact_count, - &sys_ndb_use_local_query_cache, +#ifdef HAVE_QUERY_CACHE + &sys_ndb_query_cache_type, +#endif &sys_ndb_use_transactions, #endif &sys_unique_checks, @@ -805,8 +810,9 @@ struct show_var_st init_vars[]= { (char*) &sys_ndb_autoincrement_prefetch_sz, SHOW_SYS}, {sys_ndb_force_send.name, (char*) &sys_ndb_force_send, SHOW_SYS}, {sys_ndb_use_exact_count.name,(char*) &sys_ndb_use_exact_count, SHOW_SYS}, - {sys_ndb_use_local_query_cache.name, - (char*) &sys_ndb_use_local_query_cache, SHOW_SYS}, +#ifdef HAVE_QUERY_CACHE + {sys_ndb_query_cache_type.name,(char*) &sys_ndb_query_cache_type, SHOW_SYS}, +#endif {sys_ndb_use_transactions.name,(char*) &sys_ndb_use_transactions, SHOW_SYS}, #endif {sys_net_buffer_length.name,(char*) &sys_net_buffer_length, SHOW_SYS}, diff --git a/sql/sql_cache.cc b/sql/sql_cache.cc index 1bf8d179770..da6998ded47 100644 --- a/sql/sql_cache.cc +++ b/sql/sql_cache.cc @@ -366,6 +366,12 @@ TYPELIB query_cache_type_typelib= array_elements(query_cache_type_names)-1,"", query_cache_type_names, NULL }; +const char *ndb_query_cache_type_names[]= { "OFF", "ON", "LOCAL",NullS }; +TYPELIB ndb_query_cache_type_typelib= +{ + array_elements(ndb_query_cache_type_names)-1,"", ndb_query_cache_type_names, NULL +}; + /***************************************************************************** Query_cache_block_table method(s) *****************************************************************************/ diff --git a/sql/sql_cache.h b/sql/sql_cache.h index c933a2349af..b8cbc7953b8 100644 --- a/sql/sql_cache.h +++ b/sql/sql_cache.h @@ -412,6 +412,7 @@ protected: }; extern Query_cache query_cache; +extern TYPELIB ndb_query_cache_type_typelib; extern TYPELIB query_cache_type_typelib; void query_cache_end_of_result(THD *thd); void query_cache_abort(NET *net); diff --git a/sql/sql_class.h b/sql/sql_class.h index 06975730195..2ebad4b466a 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -403,7 +403,7 @@ struct system_variables ulong ndb_autoincrement_prefetch_sz; my_bool ndb_force_send; my_bool ndb_use_exact_count; - my_bool ndb_use_local_query_cache; + ulong ndb_query_cache_type; my_bool ndb_use_transactions; #endif /* HAVE_NDBCLUSTER_DB */ my_bool old_passwords; From ae471974ac158133976ce3510afe16db75fda798 Mon Sep 17 00:00:00 2001 From: "tomas@poseidon.ndb.mysql.com" <> Date: Wed, 24 Nov 2004 11:56:51 +0000 Subject: [PATCH 03/10] sql/ha_innodb.cc enabled query cache for ndb modified engine interface somewhat sql/ha_innodb.h enabled query cache for ndb modified engine interface somewhat sql/ha_ndbcluster.cc enabled query cache for ndb modified engine interface somewhat ndb will only allow caching and retrieval if running autocommit - return false, but do not invalidate commit count is used as engine data, i.e. - store commit count before store of cache - allow retrieval if commit count has not changed on a table - invalidate if commit count has changed sql/ha_ndbcluster.h enabled query cache for ndb modified engine interface somewhat sql/handler.cc enabled query cache for ndb modified engine interface somewhat sql/handler.h enabled query cache for ndb modified engine interface somewhat new virtual handler method cached_table_registration called on each table before alowing store in query cache - return TRUE - ok to cache, FALSE - not allowed to cache, invalidate queries if engine_data below has changed - sets ulonglong (engine_data) that is stored in query cache for each table - sets callback to be called for each table before usage of cached query, callback = 0 -> no check later sql/mysql_priv.h enabled query cache for ndb modified engine interface somewhat callcack prototype for callback to engine before query cache retrieval sql/sql_cache.cc enabled query cache for ndb modified engine interface somewhat if callback is set on table in cache, do callback to check if allowed to use cache if not allowed to use cache, check if engine_data has changed, if so, invalidate all queries with that table + changes to store and pass callback and engine_data around sql/sql_cache.h enabled query cache for ndb modified engine interface somewhat changes to store callback and engine_data sql/table.h enabled query cache for ndb modified engine interface somewhat changes to store callback and engine_data --- sql/ha_innodb.cc | 3 +- sql/ha_innodb.h | 20 ++++++- sql/ha_ndbcluster.cc | 132 ++++++++++++++++++++++++++++++++++++++++++- sql/ha_ndbcluster.h | 5 +- sql/handler.cc | 9 --- sql/handler.h | 15 +++-- sql/mysql_priv.h | 3 + sql/sql_cache.cc | 64 ++++++++++++++++----- sql/sql_cache.h | 12 +++- sql/table.h | 4 ++ 10 files changed, 232 insertions(+), 35 deletions(-) diff --git a/sql/ha_innodb.cc b/sql/ha_innodb.cc index 07d8da63733..b5c94386677 100644 --- a/sql/ha_innodb.cc +++ b/sql/ha_innodb.cc @@ -655,8 +655,9 @@ innobase_query_caching_of_table_permitted( char* full_name, /* in: concatenation of database name, the null character '\0', and the table name */ - uint full_name_len) /* in: length of the full name, i.e. + uint full_name_len, /* in: length of the full name, i.e. len(dbname) + len(tablename) + 1 */ + ulonglong *unused) /* unused for this engine */ { ibool is_autocommit; trx_t* trx; diff --git a/sql/ha_innodb.h b/sql/ha_innodb.h index e76a966c6b9..e3b058d0b42 100644 --- a/sql/ha_innodb.h +++ b/sql/ha_innodb.h @@ -33,6 +33,10 @@ typedef struct st_innobase_share { } INNOBASE_SHARE; +my_bool innobase_query_caching_of_table_permitted(THD* thd, char* full_name, + uint full_name_len, + ulonglong *unused); + /* The class defining a handle to an Innodb table */ class ha_innobase: public handler { @@ -168,6 +172,20 @@ class ha_innobase: public handler void init_table_handle_for_HANDLER(); longlong get_auto_increment(); uint8 table_cache_type() { return HA_CACHE_TBL_ASKTRANSACT; } + /* + ask handler about permission to cache table during query registration + */ + my_bool cached_table_registration(THD *thd, char *table_key, + uint key_length, + qc_engine_callback *call_back, + ulonglong *engine_data) + { + *call_back= innobase_query_caching_of_table_permitted; + *engine_data= 0; + return innobase_query_caching_of_table_permitted(thd, table_key, + key_length, + engine_data); + } static char *get_mysql_bin_log_name(); static ulonglong get_mysql_bin_log_pos(); @@ -233,8 +251,6 @@ int innobase_close_connection(THD *thd); int innobase_drop_database(char *path); int innodb_show_status(THD* thd); -my_bool innobase_query_caching_of_table_permitted(THD* thd, char* full_name, - uint full_name_len); void innobase_release_temporary_latches(void* innobase_tid); void innobase_store_binlog_offset_and_flush_log(char *binlog_name,longlong offset); diff --git a/sql/ha_ndbcluster.cc b/sql/ha_ndbcluster.cc index 8d82f60ae85..8621fb89563 100644 --- a/sql/ha_ndbcluster.cc +++ b/sql/ha_ndbcluster.cc @@ -4455,10 +4455,138 @@ const char* ha_ndbcluster::index_type(uint key_number) } uint8 ha_ndbcluster::table_cache_type() { + DBUG_ENTER("ha_ndbcluster::table_cache_type"); if (m_use_local_query_cache) - return HA_CACHE_TBL_TRANSACT; + { + DBUG_PRINT("exit",("HA_CACHE_TBL_ASKTRANSACT")); + DBUG_RETURN(HA_CACHE_TBL_ASKTRANSACT); //HA_CACHE_TBL_TRANSACT; + } else - return HA_CACHE_TBL_NOCACHE; + { + DBUG_PRINT("exit",("HA_CACHE_TBL_NOCACHE")); + DBUG_RETURN(HA_CACHE_TBL_NOCACHE); + } +} + +static +my_bool +ndbcluster_cache_retrieval_allowed( +/*======================================*/ + /* out: TRUE if permitted, FALSE if not; + note that the value FALSE means invalidation + of query cache if *engine_data is changed */ + THD* thd, /* in: thd of the user who is trying to + store a result to the query cache or + retrieve it */ + char* full_name, /* in: concatenation of database name, + the null character '\0', and the table + name */ + uint full_name_len, /* in: length of the full name, i.e. + len(dbname) + len(tablename) + 1 */ + ulonglong *engine_data) /* in: value set in call to + ha_ndbcluster::cached_table_registration + out: if return FALSE this is used to invalidate + all cached queries with this table*/ +{ + DBUG_ENTER("ndbcluster_cache_retrieval_allowed"); + char tabname[128]; + char *dbname= full_name; + my_bool is_autocommit; + { + int dbname_len= strlen(full_name); + int tabname_len= full_name_len-dbname_len-1; + memcpy(tabname, full_name+dbname_len+1, tabname_len); + tabname[tabname_len]= '\0'; + } + if (thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)) + is_autocommit = FALSE; + else + is_autocommit = TRUE; + DBUG_PRINT("enter",("dbname=%s, tabname=%s, autocommit=%d", + dbname,tabname,is_autocommit)); + if (!is_autocommit) + { + DBUG_PRINT("info",("OPTION_NOT_AUTOCOMMIT=%d OPTION_BEGIN=%d", + thd->options & OPTION_NOT_AUTOCOMMIT, + thd->options & OPTION_BEGIN)); + // ToDo enable cache inside a transaction + // no need to invalidate though so leave *engine_data + DBUG_RETURN(FALSE); + } + { + Ndb *ndb; + Uint64 commit_count; + if (!(ndb= check_ndb_in_thd(thd))) + { + *engine_data= *engine_data+1; // invalidate + DBUG_RETURN(FALSE); + } + ndb->setDatabaseName(dbname); + if (ndb_get_table_statistics(ndb, tabname, 0, &commit_count)) + { + *engine_data= *engine_data+1; // invalidate + DBUG_RETURN(FALSE); + } + if (*engine_data != commit_count) + { + *engine_data= commit_count; // invalidate + DBUG_RETURN(FALSE); + } + } + DBUG_PRINT("exit",("*engine_data=%d ok, use cache",*engine_data)); + DBUG_RETURN(TRUE); +} + +my_bool +ha_ndbcluster::cached_table_registration( +/*======================================*/ + /* out: TRUE if permitted, FALSE if not; + note that the value FALSE means invalidation + of query cache if *engine_data is changed */ + THD* thd, /* in: thd of the user who is trying to + store a result to the query cache or + retrieve it */ + char* full_name, /* in: concatenation of database name, + the null character '\0', and the table + name */ + uint full_name_len, /* in: length of the full name, i.e. + len(dbname) + len(tablename) + 1 */ + qc_engine_callback + *engine_callback, /* out: function to be called before using + cache on this table */ + ulonglong *engine_data) /* out: if return FALSE this is used to + invalidate all cached queries with this table*/ +{ + DBUG_ENTER("ha_ndbcluster::cached_table_registration"); + my_bool is_autocommit; + if (thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)) + is_autocommit = FALSE; + else + is_autocommit = TRUE; + DBUG_PRINT("enter",("dbname=%s, tabname=%s, is_autocommit=%d", + m_dbname,m_tabname,is_autocommit)); + if (!is_autocommit) + { + DBUG_PRINT("info",("OPTION_NOT_AUTOCOMMIT=%d OPTION_BEGIN=%d", + thd->options & OPTION_NOT_AUTOCOMMIT, + thd->options & OPTION_BEGIN)); + // ToDo enable cache inside a transaction + // no need to invalidate though so leave *engine_data + DBUG_RETURN(FALSE); + } + { + Uint64 commit_count; + m_ndb->setDatabaseName(m_dbname); + if (ndb_get_table_statistics(m_ndb, m_tabname, 0, &commit_count)) + { + *engine_data= 0; + DBUG_RETURN(FALSE); + } + *engine_data= commit_count; + } + *engine_callback= ndbcluster_cache_retrieval_allowed; + DBUG_PRINT("exit",("*engine_data=%d", *engine_data)); + DBUG_RETURN(TRUE); } /* diff --git a/sql/ha_ndbcluster.h b/sql/ha_ndbcluster.h index f6c712620c1..7b3b5658175 100644 --- a/sql/ha_ndbcluster.h +++ b/sql/ha_ndbcluster.h @@ -146,7 +146,10 @@ class ha_ndbcluster: public handler static Thd_ndb* seize_thd_ndb(); static void release_thd_ndb(Thd_ndb* thd_ndb); uint8 table_cache_type(); - + my_bool cached_table_registration(THD *thd, char *table_key, + uint key_length, + qc_engine_callback *engine_callback, + ulonglong *engine_data); private: int alter_table_name(const char *from, const char *to); int drop_table(); diff --git a/sql/handler.cc b/sql/handler.cc index 7ddd7b80a34..9e781817c02 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -229,15 +229,6 @@ handler *get_new_handler(TABLE *table, enum db_type db_type) } } -bool ha_caching_allowed(THD* thd, char* table_key, - uint key_length, uint8 cache_type) -{ -#ifdef HAVE_INNOBASE_DB - if (cache_type == HA_CACHE_TBL_ASKTRANSACT) - return innobase_query_caching_of_table_permitted(thd, table_key, key_length); -#endif - return 1; -} int ha_init() { diff --git a/sql/handler.h b/sql/handler.h index 252861e5c37..31710ec728c 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -506,10 +506,15 @@ public: /* Type of table for caching query */ virtual uint8 table_cache_type() { return HA_CACHE_TBL_NONTRANSACT; } - /* - Is query with this table cachable (have sense only for ASKTRANSACT - tables) - */ + /* ask handler about permission to cache table during query registration */ + virtual my_bool cached_table_registration(THD *thd, char *table_key, + uint key_length, + qc_engine_callback *engine_callback, + ulonglong *engine_data) + { + *engine_callback= 0; + return 1; + } }; /* Some extern variables used with handlers */ @@ -528,8 +533,6 @@ extern TYPELIB tx_isolation_typelib; T != DB_TYPE_BERKELEY_DB && \ T != DB_TYPE_NDBCLUSTER) -bool ha_caching_allowed(THD* thd, char* table_key, - uint key_length, uint8 cache_type); enum db_type ha_resolve_by_name(const char *name, uint namelen); const char *ha_get_storage_engine(enum db_type db_type); handler *get_new_handler(TABLE *table, enum db_type db_type); diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 3f55a88b262..77703c2b390 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -340,6 +340,9 @@ inline THD *_current_thd(void) } #define current_thd _current_thd() +typedef my_bool (*qc_engine_callback)(THD *thd, char *table_key, + uint key_length, + ulonglong *engine_data); #include "sql_string.h" #include "sql_list.h" #include "sql_map.h" diff --git a/sql/sql_cache.cc b/sql/sql_cache.cc index 1bf8d179770..5c2698bcef2 100644 --- a/sql/sql_cache.cc +++ b/sql/sql_cache.cc @@ -912,12 +912,12 @@ end: int Query_cache::send_result_to_client(THD *thd, char *sql, uint query_length) { + ulonglong engine_data; Query_cache_query *query; Query_cache_block *first_result_block, *result_block; Query_cache_block_table *block_table, *block_table_end; ulong tot_length; Query_cache_query_flags flags; - bool check_tables; DBUG_ENTER("Query_cache::send_result_to_client"); if (query_cache_size == 0 || thd->variables.query_cache_type == 0) @@ -1018,7 +1018,6 @@ Query_cache::send_result_to_client(THD *thd, char *sql, uint query_length) goto err_unlock; } - check_tables= query->tables_type() & HA_CACHE_TBL_ASKTRANSACT; // Check access; block_table= query_block->table(0); block_table_end= block_table+query_block->n_tables; @@ -1079,19 +1078,29 @@ Query_cache::send_result_to_client(THD *thd, char *sql, uint query_length) goto err_unlock; // Parse query } #endif /*!NO_EMBEDDED_ACCESS_CHECKS*/ - if (check_tables && !ha_caching_allowed(thd, table->db(), - table->key_length(), - table->type())) + engine_data= table->engine_data(); + if (table->callback() && + !(*table->callback())(thd, table->db(), + table->key_length(), + &engine_data)) { DBUG_PRINT("qcache", ("Handler does not allow caching for %s.%s", table_list.db, table_list.alias)); BLOCK_UNLOCK_RD(query_block); thd->lex->safe_to_cache_query= 0; // Don't try to cache this + if (engine_data != table->engine_data()) + { + DBUG_PRINT("qcache", + ("Handler require invalidation queries of %s.%s %lld-%lld", + table_list.db, table_list.alias, + engine_data, table->engine_data())); + invalidate_table(table->db(), table->key_length()); + } goto err_unlock; // Parse query } else - DBUG_PRINT("qcache", ("handler allow caching (%d) %s,%s", - check_tables, table_list.db, table_list.alias)); + DBUG_PRINT("qcache", ("handler allow caching %s,%s", + table_list.db, table_list.alias)); } move_to_query_list_end(query_block); hits++; @@ -2116,7 +2125,9 @@ my_bool Query_cache::register_all_tables(Query_cache_block *block, if (!insert_table(tables_used->table->key_length, tables_used->table->table_cache_key, block_table, tables_used->db_length, - tables_used->table->file->table_cache_type())) + tables_used->table->file->table_cache_type(), + tables_used->callback_func, + tables_used->engine_data)) break; if (tables_used->table->db_type == DB_TYPE_MRG_MYISAM) @@ -2132,9 +2143,13 @@ my_bool Query_cache::register_all_tables(Query_cache_block *block, uint key_length= filename_2_table_key(key, table->table->filename, &db_length); (++block_table)->n= ++n; + /* + There are not callback function for for MyISAM, and engine data + */ if (!insert_table(key_length, key, block_table, db_length, - tables_used->table->file->table_cache_type())) + tables_used->table->file->table_cache_type(), + 0, 0)) goto err; } } @@ -2161,7 +2176,9 @@ err: my_bool Query_cache::insert_table(uint key_len, char *key, Query_cache_block_table *node, - uint32 db_length, uint8 cache_type) + uint32 db_length, uint8 cache_type, + qc_engine_callback callback, + ulonglong engine_data) { DBUG_ENTER("Query_cache::insert_table"); DBUG_PRINT("qcache", ("insert table node 0x%lx, len %d", @@ -2171,6 +2188,23 @@ Query_cache::insert_table(uint key_len, char *key, hash_search(&tables, (byte*) key, key_len)); + if (table_block && + table_block->table()->engine_data() != engine_data) + { + DBUG_PRINT("qcache", + ("Handler require invalidation queries of %s.%s %lld-%lld", + table_block->table()->db(), + table_block->table()->table(), + engine_data, + table_block->table()->engine_data())); + /* + as far as we delete all queries with this table, table block will be + deleted, too + */ + invalidate_table(table_block); + table_block= 0; + } + if (table_block == 0) { DBUG_PRINT("qcache", ("new table block from 0x%lx (%u)", @@ -2201,6 +2235,8 @@ Query_cache::insert_table(uint key_len, char *key, header->table(db + db_length + 1); header->key_length(key_len); header->type(cache_type); + header->callback(callback); + header->engine_data(engine_data); } Query_cache_block_table *list_root = table_block->table(0); @@ -2721,9 +2757,11 @@ my_bool Query_cache::ask_handler_allowance(THD *thd, for (; tables_used; tables_used= tables_used->next) { TABLE *table= tables_used->table; - if (!ha_caching_allowed(thd, table->table_cache_key, - table->key_length, - table->file->table_cache_type())) + handler *handler= table->file; + if (!handler->cached_table_registration(thd, table->table_cache_key, + table->key_length, + &tables_used->callback_func, + &tables_used->engine_data)) { DBUG_PRINT("qcache", ("Handler does not allow caching for %s.%s", tables_used->db, tables_used->alias)); diff --git a/sql/sql_cache.h b/sql/sql_cache.h index c933a2349af..7595bfbbd54 100644 --- a/sql/sql_cache.h +++ b/sql/sql_cache.h @@ -145,6 +145,10 @@ struct Query_cache_table char *tbl; uint32 key_len; uint8 table_type; + /* unique for every engine reference */ + qc_engine_callback callback_func; + /* data need by some engines */ + ulonglong engine_data_buff; inline char *db() { return (char *) data(); } inline char *table() { return tbl; } @@ -153,6 +157,10 @@ struct Query_cache_table inline void key_length(uint32 len) { key_len= len; } inline uint8 type() { return table_type; } inline void type(uint8 t) { table_type= t; } + inline qc_engine_callback callback() { return callback_func; } + inline void callback(qc_engine_callback fn){ callback_func= fn; } + inline ulonglong engine_data() { return engine_data_buff; } + inline void engine_data(ulonglong data) { engine_data_buff= data; } inline gptr data() { return (gptr)(((byte*)this)+ @@ -281,7 +289,9 @@ protected: TABLE_COUNTER_TYPE tables); my_bool insert_table(uint key_len, char *key, Query_cache_block_table *node, - uint32 db_length, uint8 cache_type); + uint32 db_length, uint8 cache_type, + qc_engine_callback callback, + ulonglong engine_data); void unlink_table(Query_cache_block_table *node); Query_cache_block *get_free_block (ulong len, my_bool not_less, ulong min); diff --git a/sql/table.h b/sql/table.h index 2eb854f553d..c3945ac5d79 100644 --- a/sql/table.h +++ b/sql/table.h @@ -207,6 +207,10 @@ typedef struct st_table_list TABLE *table; /* opened table */ st_table_list *table_list; /* pointer to node of list of all tables */ class st_select_lex_unit *derived; /* SELECT_LEX_UNIT of derived table */ + /* data need by some engines in query cache*/ + ulonglong engine_data; + /* call back function for asking handler about caching in query cache */ + qc_engine_callback callback_func; GRANT_INFO grant; thr_lock_type lock_type; uint outer_join; /* Which join type */ From 468dd0869b5166829de2f6551d670c83e1923019 Mon Sep 17 00:00:00 2001 From: "bell@sanja.is.com.ua" <> Date: Thu, 25 Nov 2004 19:49:50 +0200 Subject: [PATCH 04/10] new NDB test with QC --- mysql-test/r/ndb_cache.result | 43 ----------------------------------- mysql-test/t/ndb_cache.test | 38 +++++++++++++++++++++++-------- sql/sql_cache.cc | 3 ++- 3 files changed, 30 insertions(+), 54 deletions(-) diff --git a/mysql-test/r/ndb_cache.result b/mysql-test/r/ndb_cache.result index 714e1831267..e69de29bb2d 100644 --- a/mysql-test/r/ndb_cache.result +++ b/mysql-test/r/ndb_cache.result @@ -1,43 +0,0 @@ -set GLOBAL query_cache_size=1355776; -reset query cache; -flush status; -drop table if exists t1,t2; -CREATE TABLE t1 (a int) ENGINE=ndbcluster; -CREATE TABLE t2 (a int); -select * from t1; -a -show status like "Qcache_queries_in_cache"; -Variable_name Value -Qcache_queries_in_cache 0 -show status like "Qcache_inserts"; -Variable_name Value -Qcache_inserts 0 -show status like "Qcache_hits"; -Variable_name Value -Qcache_hits 0 -select * from t2; -a -show status like "Qcache_queries_in_cache"; -Variable_name Value -Qcache_queries_in_cache 1 -show status like "Qcache_inserts"; -Variable_name Value -Qcache_inserts 1 -show status like "Qcache_hits"; -Variable_name Value -Qcache_hits 0 -select * from t1; -a -select * from t2; -a -show status like "Qcache_queries_in_cache"; -Variable_name Value -Qcache_queries_in_cache 1 -show status like "Qcache_inserts"; -Variable_name Value -Qcache_inserts 1 -show status like "Qcache_hits"; -Variable_name Value -Qcache_hits 1 -drop table t1, t2; -SET GLOBAL query_cache_size=0; diff --git a/mysql-test/t/ndb_cache.test b/mysql-test/t/ndb_cache.test index abd09424f64..bd368105a84 100644 --- a/mysql-test/t/ndb_cache.test +++ b/mysql-test/t/ndb_cache.test @@ -1,31 +1,49 @@ -- source include/have_query_cache.inc -- source include/have_ndb.inc - +set GLOBAL ndb_query_cache_type=on; +# following line have to be removed when ndb_query_cache_type will made +# global only +set ndb_query_cache_type=on; set GLOBAL query_cache_size=1355776; reset query cache; flush status; --disable_warnings -drop table if exists t1,t2; +drop table if exists t1; --enable_warnings CREATE TABLE t1 (a int) ENGINE=ndbcluster; -CREATE TABLE t2 (a int); - +insert into t1 value (2); select * from t1; show status like "Qcache_queries_in_cache"; show status like "Qcache_inserts"; show status like "Qcache_hits"; -select * from t2; -show status like "Qcache_queries_in_cache"; -show status like "Qcache_inserts"; -show status like "Qcache_hits"; +connect (con1,localhost,root,,); +connection con1; +use test; +set autocommit=0; +update t1 set a=3; +connect (con2,localhost,root,,); +connection con2; +select * from t1; select * from t1; -select * from t2; show status like "Qcache_queries_in_cache"; show status like "Qcache_inserts"; show status like "Qcache_hits"; +connection con1; +select * from t1; +select * from t1; +show status like "Qcache_queries_in_cache"; +show status like "Qcache_inserts"; +show status like "Qcache_hits"; +commit; +connection con2; +select * from t1; +select * from t1; +show status like "Qcache_queries_in_cache"; +show status like "Qcache_inserts"; +show status like "Qcache_hits"; +drop table t1; -drop table t1, t2; SET GLOBAL query_cache_size=0; diff --git a/sql/sql_cache.cc b/sql/sql_cache.cc index 105dcae0319..28e814a2d62 100644 --- a/sql/sql_cache.cc +++ b/sql/sql_cache.cc @@ -1093,7 +1093,6 @@ Query_cache::send_result_to_client(THD *thd, char *sql, uint query_length) DBUG_PRINT("qcache", ("Handler does not allow caching for %s.%s", table_list.db, table_list.alias)); BLOCK_UNLOCK_RD(query_block); - thd->lex->safe_to_cache_query= 0; // Don't try to cache this if (engine_data != table->engine_data()) { DBUG_PRINT("qcache", @@ -1102,6 +1101,8 @@ Query_cache::send_result_to_client(THD *thd, char *sql, uint query_length) engine_data, table->engine_data())); invalidate_table(table->db(), table->key_length()); } + else + thd->lex->safe_to_cache_query= 0; // Don't try to cache this goto err_unlock; // Parse query } else From e57726b98edda0fb151a38ea083ab9a1ffbd6266 Mon Sep 17 00:00:00 2001 From: "tomas@poseidon.ndb.mysql.com" <> Date: Thu, 25 Nov 2004 18:56:10 +0000 Subject: [PATCH 05/10] removed special ndb query cache variable ndb will always return query cache type ASKTRANSACT --- sql/ha_ndbcluster.cc | 24 +++--------------------- sql/ha_ndbcluster.h | 1 - sql/mysqld.cc | 11 ----------- sql/set_var.cc | 12 ------------ sql/sql_cache.cc | 6 ------ sql/sql_cache.h | 1 - sql/sql_class.h | 1 - 7 files changed, 3 insertions(+), 53 deletions(-) diff --git a/sql/ha_ndbcluster.cc b/sql/ha_ndbcluster.cc index 5dd977dc95b..3a1dde2e4d4 100644 --- a/sql/ha_ndbcluster.cc +++ b/sql/ha_ndbcluster.cc @@ -3025,9 +3025,6 @@ int ha_ndbcluster::external_lock(THD *thd, int lock_type) m_transaction_on= FALSE; else m_transaction_on= thd->variables.ndb_use_transactions; -#ifdef HAVE_QUERY_CACHE - m_query_cache_type= thd->variables.ndb_query_cache_type; -#endif m_active_trans= thd->transaction.all.ndb_tid ? (NdbConnection*)thd->transaction.all.ndb_tid: @@ -3752,8 +3749,7 @@ ha_ndbcluster::ha_ndbcluster(TABLE *table_arg): m_ha_not_exact_count(FALSE), m_force_send(TRUE), m_autoincrement_prefetch(32), - m_transaction_on(TRUE), - m_query_cache_type(0) + m_transaction_on(TRUE) { int i; @@ -4457,22 +4453,8 @@ const char* ha_ndbcluster::index_type(uint key_number) } uint8 ha_ndbcluster::table_cache_type() { - DBUG_ENTER("ha_ndbcluster::table_cache_type"); - switch (m_query_cache_type) - { - case 0: - DBUG_PRINT("exit",("HA_CACHE_TBL_NOCACHE")); - DBUG_RETURN(HA_CACHE_TBL_NOCACHE); - case 1: - DBUG_PRINT("exit",("HA_CACHE_TBL_ASKTRANSACT")); - DBUG_RETURN(HA_CACHE_TBL_ASKTRANSACT); - case 2: - DBUG_PRINT("exit",("HA_CACHE_TBL_TRANSACT")); - DBUG_RETURN(HA_CACHE_TBL_TRANSACT); - default: - DBUG_PRINT("exit",("HA_CACHE_TBL_NOCACHE")); - DBUG_RETURN(HA_CACHE_TBL_NOCACHE); - } + DBUG_ENTER("ha_ndbcluster::table_cache_type=HA_CACHE_TBL_ASKTRANSACT"); + DBUG_RETURN(HA_CACHE_TBL_ASKTRANSACT); } static diff --git a/sql/ha_ndbcluster.h b/sql/ha_ndbcluster.h index 33f06dd9092..ab39993c8e6 100644 --- a/sql/ha_ndbcluster.h +++ b/sql/ha_ndbcluster.h @@ -247,7 +247,6 @@ class ha_ndbcluster: public handler bool m_force_send; ha_rows m_autoincrement_prefetch; bool m_transaction_on; - ulong m_query_cache_type; void set_rec_per_key(); void records_update(); diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 2778bdedee6..14fd267e85d 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -3969,7 +3969,6 @@ enum options_mysqld OPT_INNODB, OPT_ISAM, OPT_NDBCLUSTER, OPT_NDB_CONNECTSTRING, OPT_NDB_USE_EXACT_COUNT, OPT_NDB_FORCE_SEND, OPT_NDB_AUTOINCREMENT_PREFETCH_SZ, - OPT_NDB_QUERY_CACHE_TYPE, OPT_SKIP_SAFEMALLOC, OPT_TEMP_POOL, OPT_TX_ISOLATION, OPT_SKIP_STACK_TRACE, OPT_SKIP_SYMLINKS, @@ -4429,16 +4428,6 @@ Disable with --skip-ndbcluster (will save memory).", (gptr*) &global_system_variables.ndb_use_exact_count, (gptr*) &global_system_variables.ndb_use_exact_count, 0, GET_BOOL, OPT_ARG, 1, 0, 0, 0, 0, 0}, -#ifdef HAVE_QUERY_CACHE - {"ndb_query_cache_type", OPT_NDB_QUERY_CACHE_TYPE, - "0 = OFF = Don't cache or retrieve results. 1 = ON = Cache as query_cache_type states and " - "invalidate cache if tables are updated by other mysql servers. " - "2 = LOCAL = Cache as query_cache_type states and don't bother about what's happening on other " - "mysql servers.", - (gptr*) &global_system_variables.ndb_query_cache_type, - (gptr*) &global_system_variables.ndb_query_cache_type, - 0, GET_ULONG, REQUIRED_ARG, 0, 0, 2, 0, 0, 0}, -#endif #endif {"new", 'n', "Use very new possible 'unsafe' functions.", (gptr*) &global_system_variables.new_mode, diff --git a/sql/set_var.cc b/sql/set_var.cc index c51bfce7a43..2031ac15412 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -370,12 +370,6 @@ sys_ndb_force_send("ndb_force_send", sys_var_thd_bool sys_ndb_use_exact_count("ndb_use_exact_count", &SV::ndb_use_exact_count); -#ifdef HAVE_QUERY_CACHE -sys_var_thd_enum -sys_ndb_query_cache_type("ndb_query_cache_type", - &SV::ndb_query_cache_type, - &ndb_query_cache_type_typelib); -#endif sys_var_thd_bool sys_ndb_use_transactions("ndb_use_transactions", &SV::ndb_use_transactions); @@ -640,9 +634,6 @@ sys_var *sys_variables[]= &sys_ndb_autoincrement_prefetch_sz, &sys_ndb_force_send, &sys_ndb_use_exact_count, -#ifdef HAVE_QUERY_CACHE - &sys_ndb_query_cache_type, -#endif &sys_ndb_use_transactions, #endif &sys_unique_checks, @@ -810,9 +801,6 @@ struct show_var_st init_vars[]= { (char*) &sys_ndb_autoincrement_prefetch_sz, SHOW_SYS}, {sys_ndb_force_send.name, (char*) &sys_ndb_force_send, SHOW_SYS}, {sys_ndb_use_exact_count.name,(char*) &sys_ndb_use_exact_count, SHOW_SYS}, -#ifdef HAVE_QUERY_CACHE - {sys_ndb_query_cache_type.name,(char*) &sys_ndb_query_cache_type, SHOW_SYS}, -#endif {sys_ndb_use_transactions.name,(char*) &sys_ndb_use_transactions, SHOW_SYS}, #endif {sys_net_buffer_length.name,(char*) &sys_net_buffer_length, SHOW_SYS}, diff --git a/sql/sql_cache.cc b/sql/sql_cache.cc index 105dcae0319..5c2698bcef2 100644 --- a/sql/sql_cache.cc +++ b/sql/sql_cache.cc @@ -366,12 +366,6 @@ TYPELIB query_cache_type_typelib= array_elements(query_cache_type_names)-1,"", query_cache_type_names, NULL }; -const char *ndb_query_cache_type_names[]= { "OFF", "ON", "LOCAL",NullS }; -TYPELIB ndb_query_cache_type_typelib= -{ - array_elements(ndb_query_cache_type_names)-1,"", ndb_query_cache_type_names, NULL -}; - /***************************************************************************** Query_cache_block_table method(s) *****************************************************************************/ diff --git a/sql/sql_cache.h b/sql/sql_cache.h index 9ab9b7cc549..7595bfbbd54 100644 --- a/sql/sql_cache.h +++ b/sql/sql_cache.h @@ -422,7 +422,6 @@ protected: }; extern Query_cache query_cache; -extern TYPELIB ndb_query_cache_type_typelib; extern TYPELIB query_cache_type_typelib; void query_cache_end_of_result(THD *thd); void query_cache_abort(NET *net); diff --git a/sql/sql_class.h b/sql/sql_class.h index 2ebad4b466a..d0d9afc7746 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -403,7 +403,6 @@ struct system_variables ulong ndb_autoincrement_prefetch_sz; my_bool ndb_force_send; my_bool ndb_use_exact_count; - ulong ndb_query_cache_type; my_bool ndb_use_transactions; #endif /* HAVE_NDBCLUSTER_DB */ my_bool old_passwords; From fcd69f0098b9460598760335e54863d8cebb12b3 Mon Sep 17 00:00:00 2001 From: "magnus@neptunus.(none)" <> Date: Thu, 9 Dec 2004 12:55:17 +0100 Subject: [PATCH 06/10] Test for query cache in combination with NDB --- mysql-test/r/ndb_cache.result | 191 ++++++++++++++++++++++++++++++++++ mysql-test/t/ndb_cache.test | 102 +++++++++++++++--- 2 files changed, 278 insertions(+), 15 deletions(-) diff --git a/mysql-test/r/ndb_cache.result b/mysql-test/r/ndb_cache.result index e69de29bb2d..7423771e026 100644 --- a/mysql-test/r/ndb_cache.result +++ b/mysql-test/r/ndb_cache.result @@ -0,0 +1,191 @@ +drop table if exists t1; +set GLOBAL query_cache_type=on; +set GLOBAL query_cache_size=1355776; +reset query cache; +flush status; +CREATE TABLE t1 ( pk int not null primary key, +a int, b int not null, c varchar(20)) ENGINE=ndbcluster; +insert into t1 value (1, 2, 3, 'First row'); +select * from t1; +pk a b c +1 2 3 First row +show status like "Qcache_queries_in_cache"; +Variable_name Value +Qcache_queries_in_cache 1 +show status like "Qcache_inserts"; +Variable_name Value +Qcache_inserts 1 +show status like "Qcache_hits"; +Variable_name Value +Qcache_hits 0 +select * from t1; +pk a b c +1 2 3 First row +show status like "Qcache_hits"; +Variable_name Value +Qcache_hits 1 +update t1 set a=3 where pk=1; +select * from t1; +pk a b c +1 3 3 First row +show status like "Qcache_inserts"; +Variable_name Value +Qcache_inserts 2 +show status like "Qcache_hits"; +Variable_name Value +Qcache_hits 1 +insert into t1 value (2, 7, 8, 'Second row'); +insert into t1 value (4, 5, 6, 'Fourth row'); +select * from t1; +pk a b c +2 7 8 Second row +4 5 6 Fourth row +1 3 3 First row +show status like "Qcache_inserts"; +Variable_name Value +Qcache_inserts 3 +show status like "Qcache_hits"; +Variable_name Value +Qcache_hits 1 +select * from t1; +pk a b c +2 7 8 Second row +4 5 6 Fourth row +1 3 3 First row +show status like "Qcache_hits"; +Variable_name Value +Qcache_hits 2 +select * from t1 where b=3; +pk a b c +1 3 3 First row +show status like "Qcache_queries_in_cache"; +Variable_name Value +Qcache_queries_in_cache 2 +show status like "Qcache_hits"; +Variable_name Value +Qcache_hits 2 +select * from t1 where b=3; +pk a b c +1 3 3 First row +show status like "Qcache_hits"; +Variable_name Value +Qcache_hits 3 +delete from t1 where c='Fourth row'; +show status like "Qcache_queries_in_cache"; +Variable_name Value +Qcache_queries_in_cache 0 +select * from t1 where b=3; +pk a b c +1 3 3 First row +show status like "Qcache_hits"; +Variable_name Value +Qcache_hits 3 +use test; +select * from t1; +pk a b c +2 7 8 Second row +1 3 3 First row +select * from t1 where b=3; +pk a b c +1 3 3 First row +show status like "Qcache_hits"; +Variable_name Value +Qcache_hits 4 +update t1 set a=4 where b=3; +use test; +show status like "Qcache_queries_in_cache"; +Variable_name Value +Qcache_queries_in_cache 0 +select * from t1; +pk a b c +2 7 8 Second row +1 4 3 First row +select * from t1; +pk a b c +2 7 8 Second row +1 4 3 First row +show status like "Qcache_inserts"; +Variable_name Value +Qcache_inserts 7 +show status like "Qcache_hits"; +Variable_name Value +Qcache_hits 5 +select * from t1; +pk a b c +2 7 8 Second row +1 4 3 First row +select * from t1; +pk a b c +2 7 8 Second row +1 4 3 First row +show status like "Qcache_queries_in_cache"; +Variable_name Value +Qcache_queries_in_cache 1 +show status like "Qcache_inserts"; +Variable_name Value +Qcache_inserts 7 +show status like "Qcache_hits"; +Variable_name Value +Qcache_hits 7 +begin; +update t1 set a=5 where pk=1; +show status like "Qcache_queries_in_cache"; +Variable_name Value +Qcache_queries_in_cache 0 +show status like "Qcache_inserts"; +Variable_name Value +Qcache_inserts 7 +show status like "Qcache_hits"; +Variable_name Value +Qcache_hits 7 +select * from t1; +pk a b c +2 7 8 Second row +1 4 3 First row +show status like "Qcache_queries_in_cache"; +Variable_name Value +Qcache_queries_in_cache 1 +show status like "Qcache_inserts"; +Variable_name Value +Qcache_inserts 8 +show status like "Qcache_hits"; +Variable_name Value +Qcache_hits 7 +commit; +show status like "Qcache_queries_in_cache"; +Variable_name Value +Qcache_queries_in_cache 1 +show status like "Qcache_inserts"; +Variable_name Value +Qcache_inserts 8 +show status like "Qcache_hits"; +Variable_name Value +Qcache_hits 7 +select * from t1; +pk a b c +2 7 8 Second row +1 5 3 First row +show status like "Qcache_inserts"; +Variable_name Value +Qcache_inserts 9 +show status like "Qcache_hits"; +Variable_name Value +Qcache_hits 7 +select * from t1; +pk a b c +2 7 8 Second row +1 5 3 First row +show status like "Qcache_queries_in_cache"; +Variable_name Value +Qcache_queries_in_cache 1 +show status like "Qcache_inserts"; +Variable_name Value +Qcache_inserts 9 +show status like "Qcache_hits"; +Variable_name Value +Qcache_hits 8 +drop table t1; +show status like "Qcache_queries_in_cache"; +Variable_name Value +Qcache_queries_in_cache 0 +SET GLOBAL query_cache_size=0; diff --git a/mysql-test/t/ndb_cache.test b/mysql-test/t/ndb_cache.test index bd368105a84..8bdcbe17728 100644 --- a/mysql-test/t/ndb_cache.test +++ b/mysql-test/t/ndb_cache.test @@ -1,33 +1,77 @@ -- source include/have_query_cache.inc -- source include/have_ndb.inc -set GLOBAL ndb_query_cache_type=on; -# following line have to be removed when ndb_query_cache_type will made -# global only -set ndb_query_cache_type=on; -set GLOBAL query_cache_size=1355776; -reset query cache; -flush status; --disable_warnings drop table if exists t1; --enable_warnings -CREATE TABLE t1 (a int) ENGINE=ndbcluster; -insert into t1 value (2); +# Turn on and reset query cache +set GLOBAL query_cache_type=on; +set GLOBAL query_cache_size=1355776; +reset query cache; +flush status; + +# Create test table in NDB +CREATE TABLE t1 ( pk int not null primary key, + a int, b int not null, c varchar(20)) ENGINE=ndbcluster; +insert into t1 value (1, 2, 3, 'First row'); + +# Perform one query which should be inerted in query cache select * from t1; show status like "Qcache_queries_in_cache"; show status like "Qcache_inserts"; show status like "Qcache_hits"; + +# Perform the same query and make sure the query cache is hit +select * from t1; +show status like "Qcache_hits"; + +# Update the table and make sure the correct data is returned +update t1 set a=3 where pk=1; +select * from t1; +show status like "Qcache_inserts"; +show status like "Qcache_hits"; + +# Insert a new record and make sure the correct data is returned +insert into t1 value (2, 7, 8, 'Second row'); +insert into t1 value (4, 5, 6, 'Fourth row'); +select * from t1; +show status like "Qcache_inserts"; +show status like "Qcache_hits"; +select * from t1; +show status like "Qcache_hits"; + +# Perform a "new" query and make sure the query cache is not hit +select * from t1 where b=3; +show status like "Qcache_queries_in_cache"; +show status like "Qcache_hits"; + +# Same query again... +select * from t1 where b=3; +show status like "Qcache_hits"; + +# Delete from the table +delete from t1 where c='Fourth row'; +show status like "Qcache_queries_in_cache"; +select * from t1 where b=3; +show status like "Qcache_hits"; + +# Start another connection and check that the query cache is hit connect (con1,localhost,root,,); connection con1; use test; -set autocommit=0; -update t1 set a=3; +select * from t1; +select * from t1 where b=3; +show status like "Qcache_hits"; + +# Update the table and switch to other connection +update t1 set a=4 where b=3; connect (con2,localhost,root,,); connection con2; -select * from t1; -select * from t1; +use test; show status like "Qcache_queries_in_cache"; +select * from t1; +select * from t1; show status like "Qcache_inserts"; show status like "Qcache_hits"; connection con1; @@ -36,14 +80,42 @@ select * from t1; show status like "Qcache_queries_in_cache"; show status like "Qcache_inserts"; show status like "Qcache_hits"; -commit; + +# Use transactions and make sure the query cache is not updated until +# transaction is commited +begin; +update t1 set a=5 where pk=1; +# Note!! the below test shows that table is invalidated +# before transaction is committed +# TODO Fix so that cache is not invalidated HERE! +show status like "Qcache_queries_in_cache"; +show status like "Qcache_inserts"; +show status like "Qcache_hits"; connection con2; select * from t1; -select * from t1; show status like "Qcache_queries_in_cache"; show status like "Qcache_inserts"; show status like "Qcache_hits"; +connection con1; +commit; +# TODO Here query is invalidated once again, commit count in NDB has changed +show status like "Qcache_queries_in_cache"; +show status like "Qcache_inserts"; +show status like "Qcache_hits"; +connection con2; +select * from t1; +show status like "Qcache_inserts"; +show status like "Qcache_hits"; +connection con1; +select * from t1; +show status like "Qcache_queries_in_cache"; +show status like "Qcache_inserts"; +show status like "Qcache_hits"; + drop table t1; +show status like "Qcache_queries_in_cache"; SET GLOBAL query_cache_size=0; + + From c30ddab38706a37b335ed345f9a445dfb2f7efa6 Mon Sep 17 00:00:00 2001 From: "magnus@neptunus.(none)" <> Date: Wed, 15 Dec 2004 12:09:19 +0100 Subject: [PATCH 07/10] Added test case for multiple MySQL Servers connected to one cluster Added test for cache in combination with multiple MySQL Servers --- mysql-test/include/have_ndb.inc | 33 +++++++++++-- mysql-test/r/ndb_cache_multi.result | 72 +++++++++++++++++++++++++++++ mysql-test/r/ndb_multi.result | 49 ++++++++++++++++++++ mysql-test/r/server_id.require | 2 + mysql-test/r/server_id1.require | 2 + mysql-test/t/ndb_cache_multi.test | 63 +++++++++++++++++++++++++ mysql-test/t/ndb_multi.test | 42 +++++++++++++++++ 7 files changed, 260 insertions(+), 3 deletions(-) create mode 100644 mysql-test/r/ndb_cache_multi.result create mode 100644 mysql-test/r/ndb_multi.result create mode 100644 mysql-test/r/server_id.require create mode 100644 mysql-test/r/server_id1.require create mode 100644 mysql-test/t/ndb_cache_multi.test create mode 100644 mysql-test/t/ndb_multi.test diff --git a/mysql-test/include/have_ndb.inc b/mysql-test/include/have_ndb.inc index 84e60657876..249f3362bce 100644 --- a/mysql-test/include/have_ndb.inc +++ b/mysql-test/include/have_ndb.inc @@ -2,6 +2,33 @@ disable_query_log; show variables like "have_ndbcluster"; enable_query_log; -#connect (server1,127.0.0.1,root,,test,$MASTER_MYPORT,$MASTER_MYSOCK); -#connect (server2,127.0.0.1,root,,test,$MASTER_MYPORT1,$MASTER_MYSOCK1); -#connection server1; + +# Setup connections to both MySQL Servers connected to the cluster +connect (server1,127.0.0.1,root,,test,$MASTER_MYPORT,); +connect (server2,127.0.0.1,root,,test,$MASTER_MYPORT1,); + +# Check that server1 has NDB support +connection server1; +disable_query_log; +--disable_warnings +drop table if exists t1, t2; +--enable_warnings +flush tables; +@r/have_ndb.require show variables like "have_ndbcluster"; +@r/server_id.require show variables like "server_id"; +enable_query_log; + +# Check that server2 has NDB support +connection server2; +disable_query_log; +--disable_warnings +drop table if exists t1, t2; +--enable_warnings +flush tables; +@r/have_ndb.require show variables like "have_ndbcluster"; +@r/server_id1.require show variables like "server_id"; +enable_query_log; + +# Set the default connection to 'server1' +connection server1; + diff --git a/mysql-test/r/ndb_cache_multi.result b/mysql-test/r/ndb_cache_multi.result new file mode 100644 index 00000000000..c7135ed9e8a --- /dev/null +++ b/mysql-test/r/ndb_cache_multi.result @@ -0,0 +1,72 @@ +drop table if exists t1, t2; +set GLOBAL query_cache_type=on; +set GLOBAL query_cache_size=1355776; +reset query cache; +flush status; +set GLOBAL query_cache_type=on; +set GLOBAL query_cache_size=1355776; +reset query cache; +flush status; +create table t1 (a int) engine=ndbcluster; +create table t2 (a int) engine=ndbcluster; +insert into t1 value (2); +insert into t2 value (3); +select * from t1; +a +2 +select * from t2; +a +3 +show status like "Qcache_queries_in_cache"; +Variable_name Value +Qcache_queries_in_cache 2 +show status like "Qcache_inserts"; +Variable_name Value +Qcache_inserts 2 +show status like "Qcache_hits"; +Variable_name Value +Qcache_hits 0 +show status like "Qcache_queries_in_cache"; +Variable_name Value +Qcache_queries_in_cache 0 +show status like "Qcache_inserts"; +Variable_name Value +Qcache_inserts 0 +show status like "Qcache_hits"; +Variable_name Value +Qcache_hits 0 +select * from t1; +a +2 +show status like "Qcache_queries_in_cache"; +Variable_name Value +Qcache_queries_in_cache 1 +show status like "Qcache_inserts"; +Variable_name Value +Qcache_inserts 1 +show status like "Qcache_hits"; +Variable_name Value +Qcache_hits 0 +update t1 set a=3 where a=2; +show status like "Qcache_queries_in_cache"; +Variable_name Value +Qcache_queries_in_cache 2 +show status like "Qcache_inserts"; +Variable_name Value +Qcache_inserts 2 +show status like "Qcache_hits"; +Variable_name Value +Qcache_hits 0 +select * from t1; +a +3 +show status like "Qcache_queries_in_cache"; +Variable_name Value +Qcache_queries_in_cache 2 +show status like "Qcache_inserts"; +Variable_name Value +Qcache_inserts 3 +show status like "Qcache_hits"; +Variable_name Value +Qcache_hits 0 +drop table t1, t2; diff --git a/mysql-test/r/ndb_multi.result b/mysql-test/r/ndb_multi.result new file mode 100644 index 00000000000..4a2389cd1ff --- /dev/null +++ b/mysql-test/r/ndb_multi.result @@ -0,0 +1,49 @@ +drop table if exists t1, t2, t3, t4; +flush status; +create table t1 (a int) engine=ndbcluster; +create table t2 (a int) engine=ndbcluster; +insert into t1 value (2); +insert into t2 value (3); +select * from t1; +a +2 +select * from t2; +a +3 +show status like 'handler_discover%'; +Variable_name Value +Handler_discover 0 +flush status; +select * from t1; +a +2 +update t1 set a=3 where a=2; +show status like 'handler_discover%'; +Variable_name Value +Handler_discover 1 +create table t3 (a int not null primary key, b varchar(22), +c int, last_col text) engine=ndb; +insert into t3 values(1, 'Hi!', 89, 'Longtext column'); +create table t4 (pk int primary key, b int) engine=ndb; +select * from t1; +a +3 +select * from t3; +a b c last_col +1 Hi! 89 Longtext column +show status like 'handler_discover%'; +Variable_name Value +Handler_discover 1 +show tables like 't4'; +Tables_in_test (t4) +t4 +show status like 'handler_discover%'; +Variable_name Value +Handler_discover 2 +show tables; +Tables_in_test +t1 +t2 +t3 +t4 +drop table t1, t2, t3, t4; diff --git a/mysql-test/r/server_id.require b/mysql-test/r/server_id.require new file mode 100644 index 00000000000..adffcc483b1 --- /dev/null +++ b/mysql-test/r/server_id.require @@ -0,0 +1,2 @@ +Variable_name Value +server_id 1 diff --git a/mysql-test/r/server_id1.require b/mysql-test/r/server_id1.require new file mode 100644 index 00000000000..666c94ef633 --- /dev/null +++ b/mysql-test/r/server_id1.require @@ -0,0 +1,2 @@ +Variable_name Value +server_id 102 diff --git a/mysql-test/t/ndb_cache_multi.test b/mysql-test/t/ndb_cache_multi.test new file mode 100644 index 00000000000..ac4a80cee30 --- /dev/null +++ b/mysql-test/t/ndb_cache_multi.test @@ -0,0 +1,63 @@ +-- source include/have_query_cache.inc +-- source include/have_ndb.inc + +--disable_warnings +drop table if exists t1, t2; +--enable_warnings + + +# Turn on and reset query cache on server1 +connection server1; +set GLOBAL query_cache_type=on; +set GLOBAL query_cache_size=1355776; +reset query cache; +flush status; + +# Turn on and reset query cache on server2 +connection server2; +set GLOBAL query_cache_type=on; +set GLOBAL query_cache_size=1355776; +reset query cache; +flush status; + + + +# Create test tables in NDB and load them into cache +# on server1 +connection server1; +create table t1 (a int) engine=ndbcluster; +create table t2 (a int) engine=ndbcluster; +insert into t1 value (2); +insert into t2 value (3); +select * from t1; +select * from t2; +show status like "Qcache_queries_in_cache"; +show status like "Qcache_inserts"; +show status like "Qcache_hits"; + + +# Connect server2, load table in to cache, then update the table +connection server2; +show status like "Qcache_queries_in_cache"; +show status like "Qcache_inserts"; +show status like "Qcache_hits"; +select * from t1; +show status like "Qcache_queries_in_cache"; +show status like "Qcache_inserts"; +show status like "Qcache_hits"; +update t1 set a=3 where a=2; + +# Connect to server1 and check that cache is invalidated +# and correct data is returned +connection server1; +show status like "Qcache_queries_in_cache"; +show status like "Qcache_inserts"; +show status like "Qcache_hits"; +select * from t1; +show status like "Qcache_queries_in_cache"; +show status like "Qcache_inserts"; +show status like "Qcache_hits"; + +drop table t1, t2; + + diff --git a/mysql-test/t/ndb_multi.test b/mysql-test/t/ndb_multi.test new file mode 100644 index 00000000000..65b1c7b2db4 --- /dev/null +++ b/mysql-test/t/ndb_multi.test @@ -0,0 +1,42 @@ +-- source include/have_ndb.inc + +--disable_warnings +drop table if exists t1, t2, t3, t4; +--enable_warnings + +flush status; + +# Create test tables on server1 +create table t1 (a int) engine=ndbcluster; +create table t2 (a int) engine=ndbcluster; +insert into t1 value (2); +insert into t2 value (3); +select * from t1; +select * from t2; +show status like 'handler_discover%'; + +# Connect to server2 and use the tables from there +connection server2; +flush status; +select * from t1; +update t1 set a=3 where a=2; +show status like 'handler_discover%'; + +# Create a new table on server2 +create table t3 (a int not null primary key, b varchar(22), +c int, last_col text) engine=ndb; +insert into t3 values(1, 'Hi!', 89, 'Longtext column'); +create table t4 (pk int primary key, b int) engine=ndb; + +# Check that the tables are accessible from server1 +connection server1; +select * from t1; +select * from t3; +show status like 'handler_discover%'; +show tables like 't4'; +show status like 'handler_discover%'; +show tables; + +drop table t1, t2, t3, t4; + + From 6f557367998f9749f44cff218aa2c1ea74f4274b Mon Sep 17 00:00:00 2001 From: "magnus@neptunus.(none)" <> Date: Thu, 16 Dec 2004 11:09:06 +0100 Subject: [PATCH 08/10] Moved test for multi ndb to have_ndb_multi --- mysql-test/include/have_multi_ndb.inc | 28 ++++++++++++++++++++++++++ mysql-test/include/have_ndb.inc | 29 --------------------------- mysql-test/t/ndb_cache_multi.test | 1 + mysql-test/t/ndb_multi.test | 2 ++ 4 files changed, 31 insertions(+), 29 deletions(-) create mode 100644 mysql-test/include/have_multi_ndb.inc diff --git a/mysql-test/include/have_multi_ndb.inc b/mysql-test/include/have_multi_ndb.inc new file mode 100644 index 00000000000..d0c083cab86 --- /dev/null +++ b/mysql-test/include/have_multi_ndb.inc @@ -0,0 +1,28 @@ +# Setup connections to both MySQL Servers connected to the cluster +connect (server1,127.0.0.1,root,,test,$MASTER_MYPORT,); +connect (server2,127.0.0.1,root,,test,$MASTER_MYPORT1,); + +# Check that server1 has NDB support +connection server1; +disable_query_log; +--disable_warnings +drop table if exists t1, t2; +--enable_warnings +flush tables; +@r/have_ndb.require show variables like "have_ndbcluster"; +@r/server_id.require show variables like "server_id"; +enable_query_log; + +# Check that server2 has NDB support +connection server2; +disable_query_log; +--disable_warnings +drop table if exists t1, t2; +--enable_warnings +flush tables; +@r/have_ndb.require show variables like "have_ndbcluster"; +@r/server_id1.require show variables like "server_id"; +enable_query_log; + +# Set the default connection to 'server1' +connection server1; diff --git a/mysql-test/include/have_ndb.inc b/mysql-test/include/have_ndb.inc index 249f3362bce..d000a954733 100644 --- a/mysql-test/include/have_ndb.inc +++ b/mysql-test/include/have_ndb.inc @@ -3,32 +3,3 @@ disable_query_log; show variables like "have_ndbcluster"; enable_query_log; -# Setup connections to both MySQL Servers connected to the cluster -connect (server1,127.0.0.1,root,,test,$MASTER_MYPORT,); -connect (server2,127.0.0.1,root,,test,$MASTER_MYPORT1,); - -# Check that server1 has NDB support -connection server1; -disable_query_log; ---disable_warnings -drop table if exists t1, t2; ---enable_warnings -flush tables; -@r/have_ndb.require show variables like "have_ndbcluster"; -@r/server_id.require show variables like "server_id"; -enable_query_log; - -# Check that server2 has NDB support -connection server2; -disable_query_log; ---disable_warnings -drop table if exists t1, t2; ---enable_warnings -flush tables; -@r/have_ndb.require show variables like "have_ndbcluster"; -@r/server_id1.require show variables like "server_id"; -enable_query_log; - -# Set the default connection to 'server1' -connection server1; - diff --git a/mysql-test/t/ndb_cache_multi.test b/mysql-test/t/ndb_cache_multi.test index ac4a80cee30..7202b5f8558 100644 --- a/mysql-test/t/ndb_cache_multi.test +++ b/mysql-test/t/ndb_cache_multi.test @@ -1,5 +1,6 @@ -- source include/have_query_cache.inc -- source include/have_ndb.inc +-- source include/have_multi_ndb.inc --disable_warnings drop table if exists t1, t2; diff --git a/mysql-test/t/ndb_multi.test b/mysql-test/t/ndb_multi.test index 65b1c7b2db4..9286721b677 100644 --- a/mysql-test/t/ndb_multi.test +++ b/mysql-test/t/ndb_multi.test @@ -1,4 +1,6 @@ -- source include/have_ndb.inc +-- source include/have_multi_ndb.inc + --disable_warnings drop table if exists t1, t2, t3, t4; From cb7524e1036efb5bfb34dad05230b9698d25411b Mon Sep 17 00:00:00 2001 From: "msvensson@neptunus.homeip.net" <> Date: Fri, 14 Jan 2005 14:33:26 +0100 Subject: [PATCH 09/10] Merge problem FC3 fix --- BitKeeper/etc/logging_ok | 1 + sql/ha_ndbcluster.cc | 13 +++++++++---- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/BitKeeper/etc/logging_ok b/BitKeeper/etc/logging_ok index 9e25aca5fa2..b352bceba8a 100644 --- a/BitKeeper/etc/logging_ok +++ b/BitKeeper/etc/logging_ok @@ -141,6 +141,7 @@ mronstrom@build.mysql.com mronstrom@mysql.com mskold@mysql.com msvensson@build.mysql.com +msvensson@neptunus.homeip.net mwagner@cash.mwagner.org mwagner@evoq.mwagner.org mwagner@here.mwagner.org diff --git a/sql/ha_ndbcluster.cc b/sql/ha_ndbcluster.cc index 11dd20a46d6..0d83955a335 100644 --- a/sql/ha_ndbcluster.cc +++ b/sql/ha_ndbcluster.cc @@ -2905,9 +2905,13 @@ int ha_ndbcluster::reset() DBUG_RETURN(1); } +static const char *ha_ndb_bas_ext[]= { ha_ndb_ext, NullS }; -const char **ha_ndbcluster::bas_ext() const -{ static const char *ext[]= { ha_ndb_ext, NullS }; return ext; } +const char** +ha_ndbcluster::bas_ext() const +{ + return ha_ndb_bas_ext; +} /* @@ -4644,8 +4648,9 @@ ha_ndbcluster::cached_table_registration( } { Uint64 commit_count; - m_ndb->setDatabaseName(m_dbname); - if (ndb_get_table_statistics(m_ndb, m_tabname, 0, &commit_count)) + Ndb *ndb= get_ndb(); + ndb->setDatabaseName(m_dbname); + if (ndb_get_table_statistics(ndb, m_tabname, 0, &commit_count)) { *engine_data= 0; DBUG_RETURN(FALSE); From efd5ed2f9e7c671bebd05ae2d2a776d55f8f857c Mon Sep 17 00:00:00 2001 From: "msvensson@neptunus.homeip.net" <> Date: Tue, 1 Feb 2005 15:43:08 +0100 Subject: [PATCH 10/10] WL#2269 Enable query cache for NDB - Added a thread that fetches commit_count for open tables. This will mean that NDB will not have to be contacted for every use of a cached query. --- mysql-test/r/ndb_cache2.result | 193 +++++++++++++++++ mysql-test/r/ndb_cache_multi2.result | 74 +++++++ mysql-test/t/ndb_cache2.test | 126 +++++++++++ mysql-test/t/ndb_cache_multi2.test | 71 +++++++ sql/ha_ndbcluster.cc | 306 ++++++++++++++++++++------- sql/ha_ndbcluster.h | 2 + sql/mysqld.cc | 7 +- sql/set_var.cc | 3 + 8 files changed, 708 insertions(+), 74 deletions(-) create mode 100644 mysql-test/r/ndb_cache2.result create mode 100644 mysql-test/r/ndb_cache_multi2.result create mode 100644 mysql-test/t/ndb_cache2.test create mode 100644 mysql-test/t/ndb_cache_multi2.test diff --git a/mysql-test/r/ndb_cache2.result b/mysql-test/r/ndb_cache2.result new file mode 100644 index 00000000000..ce10e9dab00 --- /dev/null +++ b/mysql-test/r/ndb_cache2.result @@ -0,0 +1,193 @@ +drop table if exists t1; +set GLOBAL query_cache_type=on; +set GLOBAL query_cache_size=1355776; +set GLOBAL ndb_cache_check_time=5; +reset query cache; +flush status; +CREATE TABLE t1 ( pk int not null primary key, +a int, b int not null, c varchar(20)) ENGINE=ndbcluster; +insert into t1 value (1, 2, 3, 'First row'); +select * from t1; +pk a b c +1 2 3 First row +show status like "Qcache_queries_in_cache"; +Variable_name Value +Qcache_queries_in_cache 1 +show status like "Qcache_inserts"; +Variable_name Value +Qcache_inserts 1 +show status like "Qcache_hits"; +Variable_name Value +Qcache_hits 0 +select * from t1; +pk a b c +1 2 3 First row +show status like "Qcache_hits"; +Variable_name Value +Qcache_hits 1 +update t1 set a=3 where pk=1; +select * from t1; +pk a b c +1 3 3 First row +show status like "Qcache_inserts"; +Variable_name Value +Qcache_inserts 2 +show status like "Qcache_hits"; +Variable_name Value +Qcache_hits 1 +insert into t1 value (2, 7, 8, 'Second row'); +insert into t1 value (4, 5, 6, 'Fourth row'); +select * from t1; +pk a b c +2 7 8 Second row +4 5 6 Fourth row +1 3 3 First row +show status like "Qcache_inserts"; +Variable_name Value +Qcache_inserts 3 +show status like "Qcache_hits"; +Variable_name Value +Qcache_hits 1 +select * from t1; +pk a b c +2 7 8 Second row +4 5 6 Fourth row +1 3 3 First row +show status like "Qcache_hits"; +Variable_name Value +Qcache_hits 2 +select * from t1 where b=3; +pk a b c +1 3 3 First row +show status like "Qcache_queries_in_cache"; +Variable_name Value +Qcache_queries_in_cache 2 +show status like "Qcache_hits"; +Variable_name Value +Qcache_hits 2 +select * from t1 where b=3; +pk a b c +1 3 3 First row +show status like "Qcache_hits"; +Variable_name Value +Qcache_hits 3 +delete from t1 where c='Fourth row'; +show status like "Qcache_queries_in_cache"; +Variable_name Value +Qcache_queries_in_cache 0 +select * from t1 where b=3; +pk a b c +1 3 3 First row +show status like "Qcache_hits"; +Variable_name Value +Qcache_hits 3 +use test; +select * from t1; +pk a b c +2 7 8 Second row +1 3 3 First row +select * from t1 where b=3; +pk a b c +1 3 3 First row +show status like "Qcache_hits"; +Variable_name Value +Qcache_hits 4 +update t1 set a=4 where b=3; +use test; +show status like "Qcache_queries_in_cache"; +Variable_name Value +Qcache_queries_in_cache 0 +select * from t1; +pk a b c +2 7 8 Second row +1 4 3 First row +select * from t1; +pk a b c +2 7 8 Second row +1 4 3 First row +show status like "Qcache_inserts"; +Variable_name Value +Qcache_inserts 7 +show status like "Qcache_hits"; +Variable_name Value +Qcache_hits 5 +select * from t1; +pk a b c +2 7 8 Second row +1 4 3 First row +select * from t1; +pk a b c +2 7 8 Second row +1 4 3 First row +show status like "Qcache_queries_in_cache"; +Variable_name Value +Qcache_queries_in_cache 1 +show status like "Qcache_inserts"; +Variable_name Value +Qcache_inserts 7 +show status like "Qcache_hits"; +Variable_name Value +Qcache_hits 7 +begin; +update t1 set a=5 where pk=1; +show status like "Qcache_queries_in_cache"; +Variable_name Value +Qcache_queries_in_cache 0 +show status like "Qcache_inserts"; +Variable_name Value +Qcache_inserts 7 +show status like "Qcache_hits"; +Variable_name Value +Qcache_hits 7 +select * from t1; +pk a b c +2 7 8 Second row +1 4 3 First row +show status like "Qcache_queries_in_cache"; +Variable_name Value +Qcache_queries_in_cache 1 +show status like "Qcache_inserts"; +Variable_name Value +Qcache_inserts 8 +show status like "Qcache_hits"; +Variable_name Value +Qcache_hits 7 +commit; +show status like "Qcache_queries_in_cache"; +Variable_name Value +Qcache_queries_in_cache 1 +show status like "Qcache_inserts"; +Variable_name Value +Qcache_inserts 8 +show status like "Qcache_hits"; +Variable_name Value +Qcache_hits 7 +select * from t1; +pk a b c +2 7 8 Second row +1 5 3 First row +show status like "Qcache_inserts"; +Variable_name Value +Qcache_inserts 9 +show status like "Qcache_hits"; +Variable_name Value +Qcache_hits 7 +select * from t1; +pk a b c +2 7 8 Second row +1 5 3 First row +show status like "Qcache_queries_in_cache"; +Variable_name Value +Qcache_queries_in_cache 1 +show status like "Qcache_inserts"; +Variable_name Value +Qcache_inserts 9 +show status like "Qcache_hits"; +Variable_name Value +Qcache_hits 8 +drop table t1; +show status like "Qcache_queries_in_cache"; +Variable_name Value +Qcache_queries_in_cache 0 +SET GLOBAL query_cache_size=0; +SET GLOBAL ndb_cache_check_time=0; diff --git a/mysql-test/r/ndb_cache_multi2.result b/mysql-test/r/ndb_cache_multi2.result new file mode 100644 index 00000000000..6e435c071b5 --- /dev/null +++ b/mysql-test/r/ndb_cache_multi2.result @@ -0,0 +1,74 @@ +drop table if exists t1, t2; +set GLOBAL query_cache_type=on; +set GLOBAL query_cache_size=1355776; +set GLOBAL ndb_cache_check_time=1; +reset query cache; +flush status; +set GLOBAL query_cache_type=on; +set GLOBAL query_cache_size=1355776; +set GLOBAL ndb_cache_check_time=1; +reset query cache; +flush status; +create table t1 (a int) engine=ndbcluster; +create table t2 (a int) engine=ndbcluster; +insert into t1 value (2); +insert into t2 value (3); +select * from t1; +a +2 +select * from t2; +a +3 +show status like "Qcache_queries_in_cache"; +Variable_name Value +Qcache_queries_in_cache 2 +show status like "Qcache_inserts"; +Variable_name Value +Qcache_inserts 2 +show status like "Qcache_hits"; +Variable_name Value +Qcache_hits 0 +show status like "Qcache_queries_in_cache"; +Variable_name Value +Qcache_queries_in_cache 0 +show status like "Qcache_inserts"; +Variable_name Value +Qcache_inserts 0 +show status like "Qcache_hits"; +Variable_name Value +Qcache_hits 0 +select * from t1; +a +2 +show status like "Qcache_queries_in_cache"; +Variable_name Value +Qcache_queries_in_cache 1 +show status like "Qcache_inserts"; +Variable_name Value +Qcache_inserts 1 +show status like "Qcache_hits"; +Variable_name Value +Qcache_hits 0 +update t1 set a=3 where a=2; +show status like "Qcache_queries_in_cache"; +Variable_name Value +Qcache_queries_in_cache 2 +show status like "Qcache_inserts"; +Variable_name Value +Qcache_inserts 2 +show status like "Qcache_hits"; +Variable_name Value +Qcache_hits 0 +select * from t1; +a +3 +show status like "Qcache_queries_in_cache"; +Variable_name Value +Qcache_queries_in_cache 2 +show status like "Qcache_inserts"; +Variable_name Value +Qcache_inserts 3 +show status like "Qcache_hits"; +Variable_name Value +Qcache_hits 0 +drop table t1, t2; diff --git a/mysql-test/t/ndb_cache2.test b/mysql-test/t/ndb_cache2.test new file mode 100644 index 00000000000..5c1674a7021 --- /dev/null +++ b/mysql-test/t/ndb_cache2.test @@ -0,0 +1,126 @@ +-- source include/have_query_cache.inc +-- source include/have_ndb.inc + +--disable_warnings +drop table if exists t1; +--enable_warnings + + +# Turn on and reset query cache +set GLOBAL query_cache_type=on; +set GLOBAL query_cache_size=1355776; +# Turn on thread that will fetch commit count for open tables +set GLOBAL ndb_cache_check_time=5; +reset query cache; +flush status; + +# Wait for thread to wake up and start "working" +sleep 20; + +# Create test table in NDB +CREATE TABLE t1 ( pk int not null primary key, + a int, b int not null, c varchar(20)) ENGINE=ndbcluster; +insert into t1 value (1, 2, 3, 'First row'); + +# Perform one query which should be inerted in query cache +select * from t1; +show status like "Qcache_queries_in_cache"; +show status like "Qcache_inserts"; +show status like "Qcache_hits"; + +# Perform the same query and make sure the query cache is hit +select * from t1; +show status like "Qcache_hits"; + +# Update the table and make sure the correct data is returned +update t1 set a=3 where pk=1; +select * from t1; +show status like "Qcache_inserts"; +show status like "Qcache_hits"; + +# Insert a new record and make sure the correct data is returned +insert into t1 value (2, 7, 8, 'Second row'); +insert into t1 value (4, 5, 6, 'Fourth row'); +select * from t1; +show status like "Qcache_inserts"; +show status like "Qcache_hits"; +select * from t1; +show status like "Qcache_hits"; + +# Perform a "new" query and make sure the query cache is not hit +select * from t1 where b=3; +show status like "Qcache_queries_in_cache"; +show status like "Qcache_hits"; + +# Same query again... +select * from t1 where b=3; +show status like "Qcache_hits"; + +# Delete from the table +delete from t1 where c='Fourth row'; +show status like "Qcache_queries_in_cache"; +select * from t1 where b=3; +show status like "Qcache_hits"; + +# Start another connection and check that the query cache is hit +connect (con1,localhost,root,,); +connection con1; +use test; +select * from t1; +select * from t1 where b=3; +show status like "Qcache_hits"; + +# Update the table and switch to other connection +update t1 set a=4 where b=3; +connect (con2,localhost,root,,); +connection con2; +use test; +show status like "Qcache_queries_in_cache"; +select * from t1; +select * from t1; +show status like "Qcache_inserts"; +show status like "Qcache_hits"; +connection con1; +select * from t1; +select * from t1; +show status like "Qcache_queries_in_cache"; +show status like "Qcache_inserts"; +show status like "Qcache_hits"; + +# Use transactions and make sure the query cache is not updated until +# transaction is commited +begin; +update t1 set a=5 where pk=1; +show status like "Qcache_queries_in_cache"; +show status like "Qcache_inserts"; +show status like "Qcache_hits"; +connection con2; +select * from t1; +show status like "Qcache_queries_in_cache"; +show status like "Qcache_inserts"; +show status like "Qcache_hits"; +connection con1; +commit; +# Sleep to let the query cache thread update commit count +sleep 10; +show status like "Qcache_queries_in_cache"; +show status like "Qcache_inserts"; +show status like "Qcache_hits"; +connection con2; +select * from t1; +show status like "Qcache_inserts"; +show status like "Qcache_hits"; +connection con1; +select * from t1; +show status like "Qcache_queries_in_cache"; +show status like "Qcache_inserts"; +show status like "Qcache_hits"; + +drop table t1; + +show status like "Qcache_queries_in_cache"; + +SET GLOBAL query_cache_size=0; +SET GLOBAL ndb_cache_check_time=0; + + diff --git a/mysql-test/t/ndb_cache_multi2.test b/mysql-test/t/ndb_cache_multi2.test new file mode 100644 index 00000000000..a9d008dba7c --- /dev/null +++ b/mysql-test/t/ndb_cache_multi2.test @@ -0,0 +1,71 @@ +-- source include/have_query_cache.inc +-- source include/have_ndb.inc +-- source include/have_multi_ndb.inc + +--disable_warnings +drop table if exists t1, t2; +--enable_warnings + + +# Turn on and reset query cache on server1 +connection server1; +set GLOBAL query_cache_type=on; +set GLOBAL query_cache_size=1355776; +set GLOBAL ndb_cache_check_time=1; +reset query cache; +flush status; + +# Turn on and reset query cache on server2 +connection server2; +set GLOBAL query_cache_type=on; +set GLOBAL query_cache_size=1355776; +set GLOBAL ndb_cache_check_time=1; +reset query cache; +flush status; + +# Sleep so that the query cache check thread has time to start +sleep 15; + + +# Create test tables in NDB and load them into cache +# on server1 +connection server1; +create table t1 (a int) engine=ndbcluster; +create table t2 (a int) engine=ndbcluster; +insert into t1 value (2); +insert into t2 value (3); +select * from t1; +select * from t2; +show status like "Qcache_queries_in_cache"; +show status like "Qcache_inserts"; +show status like "Qcache_hits"; + + +# Connect server2, load table in to cache, then update the table +connection server2; +show status like "Qcache_queries_in_cache"; +show status like "Qcache_inserts"; +show status like "Qcache_hits"; +select * from t1; +show status like "Qcache_queries_in_cache"; +show status like "Qcache_inserts"; +show status like "Qcache_hits"; +update t1 set a=3 where a=2; + +# Sleep so that the query cache check thread has time to run +sleep 5; + +# Connect to server1 and check that cache is invalidated +# and correct data is returned +connection server1; +show status like "Qcache_queries_in_cache"; +show status like "Qcache_inserts"; +show status like "Qcache_hits"; +select * from t1; +show status like "Qcache_queries_in_cache"; +show status like "Qcache_inserts"; +show status like "Qcache_hits"; + +drop table t1, t2; + + diff --git a/sql/ha_ndbcluster.cc b/sql/ha_ndbcluster.cc index 0d83955a335..4f6e243db93 100644 --- a/sql/ha_ndbcluster.cc +++ b/sql/ha_ndbcluster.cc @@ -86,6 +86,12 @@ static int unpackfrm(const void **data, uint *len, static int ndb_get_table_statistics(Ndb*, const char *, Uint64* rows, Uint64* commits); +// Util thread variables +static pthread_t ndb_util_thread; +pthread_mutex_t LOCK_ndb_util_thread; +pthread_cond_t COND_ndb_util_thread; +extern "C" pthread_handler_decl(ndb_util_thread_func, arg); +ulong ndb_cache_check_time; /* Dummy buffer to read zero pack_length fields @@ -3865,6 +3871,7 @@ ha_ndbcluster::~ha_ndbcluster() } + /* Open a table for further use - fetch metadata for this table from NDB @@ -3963,16 +3970,14 @@ void ha_ndbcluster::release_thd_ndb(Thd_ndb* thd_ndb) Ndb* check_ndb_in_thd(THD* thd) { - DBUG_ENTER("check_ndb_in_thd"); - Thd_ndb *thd_ndb= (Thd_ndb*)thd->transaction.thd_ndb; - + Thd_ndb *thd_ndb= (Thd_ndb*)thd->transaction.thd_ndb; if (!thd_ndb) { if (!(thd_ndb= ha_ndbcluster::seize_thd_ndb())) - DBUG_RETURN(NULL); + return NULL; thd->transaction.thd_ndb= thd_ndb; } - DBUG_RETURN(thd_ndb->ndb); + return thd_ndb->ndb; } @@ -4310,13 +4315,21 @@ bool ndbcluster_init() (void) hash_init(&ndbcluster_open_tables,system_charset_info,32,0,0, (hash_get_key) ndbcluster_get_key,0,0); pthread_mutex_init(&ndbcluster_mutex,MY_MUTEX_INIT_FAST); + pthread_mutex_init(&LOCK_ndb_util_thread,MY_MUTEX_INIT_FAST); + pthread_cond_init(&COND_ndb_util_thread,NULL); + + // Create utility thread + pthread_t tmp; + if (pthread_create(&tmp,&connection_attrib,ndb_util_thread_func,0)) + { + DBUG_PRINT("error", ("Could not create ndb utility thread")); + goto ndbcluster_init_error; + } + ndbcluster_inited= 1; -#ifdef USE_DISCOVER_ON_STARTUP - if (ndb_discover_tables() != 0) - goto ndbcluster_init_error; -#endif DBUG_RETURN(FALSE); + ndbcluster_init_error: ndbcluster_end(); DBUG_RETURN(TRUE); @@ -4326,12 +4339,19 @@ bool ndbcluster_init() /* End use of the NDB Cluster table handler - free all global variables allocated by - ndcluster_init() + ndbcluster_init() */ bool ndbcluster_end() { DBUG_ENTER("ndbcluster_end"); + + // Kill ndb utility thread + (void) pthread_mutex_lock(&LOCK_ndb_util_thread); + DBUG_PRINT("exit",("killing ndb util thread: %lx",ndb_util_thread)); + (void) pthread_cond_signal(&COND_ndb_util_thread); + (void) pthread_mutex_unlock(&LOCK_ndb_util_thread); + if(g_ndb) delete g_ndb; g_ndb= NULL; @@ -4342,6 +4362,8 @@ bool ndbcluster_end() DBUG_RETURN(0); hash_free(&ndbcluster_open_tables); pthread_mutex_destroy(&ndbcluster_mutex); + pthread_mutex_destroy(&LOCK_ndb_util_thread); + pthread_cond_destroy(&COND_ndb_util_thread); ndbcluster_inited= 0; DBUG_RETURN(0); } @@ -4534,12 +4556,53 @@ const char* ha_ndbcluster::index_type(uint key_number) return "HASH"; } } + uint8 ha_ndbcluster::table_cache_type() { DBUG_ENTER("ha_ndbcluster::table_cache_type=HA_CACHE_TBL_ASKTRANSACT"); DBUG_RETURN(HA_CACHE_TBL_ASKTRANSACT); } + +uint ndb_get_commitcount(THD* thd, char* dbname, char* tabname, + Uint64* commit_count) +{ + DBUG_ENTER("ndb_get_commitcount"); + + if (ndb_cache_check_time > 0) + { + // Use cached commit_count from share + char name[FN_REFLEN]; + NDB_SHARE* share; + (void)strxnmov(name, FN_REFLEN, + "./",dbname,"/",tabname,NullS); + DBUG_PRINT("info", ("name: %s", name)); + pthread_mutex_lock(&ndbcluster_mutex); + if (!(share=(NDB_SHARE*) hash_search(&ndbcluster_open_tables, + (byte*) name, + strlen(name)))) + { + pthread_mutex_unlock(&ndbcluster_mutex); + DBUG_RETURN(1); + } + *commit_count= share->commit_count; + DBUG_PRINT("info", ("commit_count: %d", *commit_count)); + pthread_mutex_unlock(&ndbcluster_mutex); + DBUG_RETURN(0); + } + + // Get commit_count from NDB + Ndb *ndb; + if (!(ndb= check_ndb_in_thd(thd))) + DBUG_RETURN(1); + ndb->setDatabaseName(dbname); + + if (ndb_get_table_statistics(ndb, tabname, 0, commit_count)) + DBUG_RETURN(1); + DBUG_RETURN(0); +} + + static my_bool ndbcluster_cache_retrieval_allowed( @@ -4561,51 +4624,33 @@ ndbcluster_cache_retrieval_allowed( all cached queries with this table*/ { DBUG_ENTER("ndbcluster_cache_retrieval_allowed"); - char tabname[128]; - char *dbname= full_name; - my_bool is_autocommit; - { - int dbname_len= strlen(full_name); - int tabname_len= full_name_len-dbname_len-1; - memcpy(tabname, full_name+dbname_len+1, tabname_len); - tabname[tabname_len]= '\0'; - } - if (thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)) - is_autocommit = FALSE; - else - is_autocommit = TRUE; + + Uint64 commit_count; + bool is_autocommit= !(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)); + char* dbname= full_name; + char* tabname= dbname+strlen(dbname)+1; + DBUG_PRINT("enter",("dbname=%s, tabname=%s, autocommit=%d", - dbname,tabname,is_autocommit)); + dbname, tabname, is_autocommit)); + if (!is_autocommit) + DBUG_RETURN(FALSE); + + if (ndb_get_commitcount(thd, dbname, tabname, &commit_count)) { - DBUG_PRINT("info",("OPTION_NOT_AUTOCOMMIT=%d OPTION_BEGIN=%d", - thd->options & OPTION_NOT_AUTOCOMMIT, - thd->options & OPTION_BEGIN)); - // ToDo enable cache inside a transaction - // no need to invalidate though so leave *engine_data + *engine_data= *engine_data+1; // invalidate DBUG_RETURN(FALSE); } + DBUG_PRINT("info", ("*engine_data=%llu, commit_count=%llu", + *engine_data, commit_count)); + if (*engine_data != commit_count) { - Ndb *ndb; - Uint64 commit_count; - if (!(ndb= check_ndb_in_thd(thd))) - { - *engine_data= *engine_data+1; // invalidate - DBUG_RETURN(FALSE); - } - ndb->setDatabaseName(dbname); - if (ndb_get_table_statistics(ndb, tabname, 0, &commit_count)) - { - *engine_data= *engine_data+1; // invalidate - DBUG_RETURN(FALSE); - } - if (*engine_data != commit_count) - { - *engine_data= commit_count; // invalidate - DBUG_RETURN(FALSE); - } + *engine_data= commit_count; // invalidate + DBUG_PRINT("exit",("Do not use cache, commit_count has changed")); + DBUG_RETURN(FALSE); } - DBUG_PRINT("exit",("*engine_data=%d ok, use cache",*engine_data)); + + DBUG_PRINT("exit",("OK to use cache, *engine_data=%llu",*engine_data)); DBUG_RETURN(TRUE); } @@ -4630,35 +4675,24 @@ ha_ndbcluster::cached_table_registration( invalidate all cached queries with this table*/ { DBUG_ENTER("ha_ndbcluster::cached_table_registration"); - my_bool is_autocommit; - if (thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)) - is_autocommit = FALSE; - else - is_autocommit = TRUE; + + bool is_autocommit= !(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)); DBUG_PRINT("enter",("dbname=%s, tabname=%s, is_autocommit=%d", m_dbname,m_tabname,is_autocommit)); if (!is_autocommit) + DBUG_RETURN(FALSE); + + + Uint64 commit_count; + if (ndb_get_commitcount(thd, m_dbname, m_tabname, &commit_count)) { - DBUG_PRINT("info",("OPTION_NOT_AUTOCOMMIT=%d OPTION_BEGIN=%d", - thd->options & OPTION_NOT_AUTOCOMMIT, - thd->options & OPTION_BEGIN)); - // ToDo enable cache inside a transaction - // no need to invalidate though so leave *engine_data + *engine_data= 0; + DBUG_PRINT("error", ("Could not get commitcount")) DBUG_RETURN(FALSE); } - { - Uint64 commit_count; - Ndb *ndb= get_ndb(); - ndb->setDatabaseName(m_dbname); - if (ndb_get_table_statistics(ndb, m_tabname, 0, &commit_count)) - { - *engine_data= 0; - DBUG_RETURN(FALSE); - } - *engine_data= commit_count; - } + *engine_data= commit_count; *engine_callback= ndbcluster_cache_retrieval_allowed; - DBUG_PRINT("exit",("*engine_data=%d", *engine_data)); + DBUG_PRINT("exit",("*engine_data=%llu", *engine_data)); DBUG_RETURN(TRUE); } @@ -4700,8 +4734,14 @@ static NDB_SHARE* get_share(const char *table_name) } thr_lock_init(&share->lock); pthread_mutex_init(&share->mutex,MY_MUTEX_INIT_FAST); + share->commit_count= 0; } } + DBUG_PRINT("share", + ("table_name: %s, length: %d, use_count: %d, commit_count: %d", + share->table_name, share->table_name_length, share->use_count, + share->commit_count)); + share->use_count++; pthread_mutex_unlock(&ndbcluster_mutex); return share; @@ -4868,10 +4908,10 @@ ndb_get_table_statistics(Ndb* ndb, const char * table, ndb->closeTransaction(pTrans); if(row_count) - * row_count= sum_rows; + *row_count= sum_rows; if(commit_count) - * commit_count= sum_commits; - DBUG_PRINT("exit", ("records: %u commits: %u", sum_rows, sum_commits)); + *commit_count= sum_commits; + DBUG_PRINT("exit", ("records: %llu commits: %llu", sum_rows, sum_commits)); DBUG_RETURN(0); } while(0); @@ -4906,4 +4946,124 @@ int ha_ndbcluster::write_ndb_file() DBUG_RETURN(error); } + +// Utility thread main loop +extern "C" pthread_handler_decl(ndb_util_thread_func,arg __attribute__((unused))) +{ + THD *thd; // needs to be first for thread_stack + int error = 0; + struct timespec abstime; + + my_thread_init(); + DBUG_ENTER("ndb_util_thread"); + DBUG_PRINT("enter", ("ndb_cache_check_time: %d", ndb_cache_check_time)); + + thd= new THD; // note that contructor of THD uses DBUG_ ! + THD_CHECK_SENTRY(thd); + + pthread_detach_this_thread(); + ndb_util_thread = pthread_self(); + + thd->thread_stack = (char*)&thd; // remember where our stack is + if (thd->store_globals()) + { + thd->cleanup(); + delete thd; + DBUG_RETURN(NULL); + } + + List util_open_tables; + set_timespec(abstime, ndb_cache_check_time); + for (;;) + { + + pthread_mutex_lock(&LOCK_ndb_util_thread); + error= pthread_cond_timedwait(&COND_ndb_util_thread, + &LOCK_ndb_util_thread, + &abstime); + pthread_mutex_unlock(&LOCK_ndb_util_thread); + + DBUG_PRINT("ndb_util_thread", ("Started, ndb_cache_check_time: %d", + ndb_cache_check_time)); + + if (abort_loop) + break; // Shutting down server + + if (ndb_cache_check_time == 0) + { + set_timespec(abstime, 10); + continue; + } + + // Set new time to wake up + set_timespec(abstime, ndb_cache_check_time); + + // Lock mutex and fill list with pointers to all open tables + NDB_SHARE *share; + pthread_mutex_lock(&ndbcluster_mutex); + for (uint i= 0; i < ndbcluster_open_tables.records; i++) + { + share= (NDB_SHARE *)hash_element(&ndbcluster_open_tables, i); + share->use_count++; // Make sure the table can't be closed + + DBUG_PRINT("ndb_util_thread", + ("Found open table[%d]: %s, use_count: %d", + i, share->table_name, share->use_count)); + + // Store pointer to table + util_open_tables.push_back(share); + } + pthread_mutex_unlock(&ndbcluster_mutex); + + + // Iterate through the open files list + List_iterator_fast it(util_open_tables); + while (share=it++) + { + // Split tab- and dbname + char buf[FN_REFLEN]; + char *tabname, *db; + uint length= dirname_length(share->table_name); + tabname= share->table_name+length; + memcpy(buf, share->table_name, length-1); + buf[length-1]= 0; + db= buf+dirname_length(buf); + DBUG_PRINT("ndb_util_thread", + ("Fetching commit count for: %s, db: %s, tab: %s", + share->table_name, db, tabname)); + + // Contact NDB to get commit count for table + g_ndb->setDatabaseName(db); + Uint64 rows, commit_count; + if(ndb_get_table_statistics(g_ndb, tabname, + &rows, &commit_count) == 0){ + DBUG_PRINT("ndb_util_thread", + ("Table: %s, rows: %llu, commit_count: %llu", + share->table_name, rows, commit_count)); + share->commit_count= commit_count; + } + else + { + DBUG_PRINT("ndb_util_thread", + ("Error: Could not get commit count for table %s", + share->table_name)); + share->commit_count++; // Invalidate + } + // Decrease the use count and possibly free share + free_share(share); + } + + // Clear the list of open tables + util_open_tables.empty(); + + } + + thd->cleanup(); + delete thd; + DBUG_PRINT("exit", ("ndb_util_thread")); + my_thread_end(); + DBUG_RETURN(NULL); +} + + #endif /* HAVE_NDBCLUSTER_DB */ diff --git a/sql/ha_ndbcluster.h b/sql/ha_ndbcluster.h index b5cf727ead7..df88afa678a 100644 --- a/sql/ha_ndbcluster.h +++ b/sql/ha_ndbcluster.h @@ -38,6 +38,7 @@ class NdbBlob; // connectstring to cluster if given by mysqld extern const char *ndbcluster_connectstring; +extern ulong ndb_cache_check_time; typedef enum ndb_index_type { UNDEFINED_INDEX = 0, @@ -59,6 +60,7 @@ typedef struct st_ndbcluster_share { pthread_mutex_t mutex; char *table_name; uint table_name_length,use_count; + uint commit_count; } NDB_SHARE; /* diff --git a/sql/mysqld.cc b/sql/mysqld.cc index d1fef3519bf..671f38898c1 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -284,6 +284,7 @@ my_bool opt_console= 0, opt_bdb, opt_innodb, opt_isam, opt_ndbcluster; #ifdef HAVE_NDBCLUSTER_DB const char *opt_ndbcluster_connectstring= 0; my_bool opt_ndb_shm, opt_ndb_optimized_node_selection; +ulong opt_ndb_cache_check_time= 0; #endif my_bool opt_readonly, use_temp_pool, relay_log_purge; my_bool opt_sync_bdb_logs, opt_sync_frm; @@ -4016,7 +4017,7 @@ enum options_mysqld OPT_INNODB, OPT_ISAM, OPT_NDBCLUSTER, OPT_NDB_CONNECTSTRING, OPT_NDB_USE_EXACT_COUNT, OPT_NDB_FORCE_SEND, OPT_NDB_AUTOINCREMENT_PREFETCH_SZ, - OPT_NDB_SHM, OPT_NDB_OPTIMIZED_NODE_SELECTION, + OPT_NDB_SHM, OPT_NDB_OPTIMIZED_NODE_SELECTION, OPT_NDB_CACHE_CHECK_TIME, OPT_SKIP_SAFEMALLOC, OPT_TEMP_POOL, OPT_TX_ISOLATION, OPT_SKIP_STACK_TRACE, OPT_SKIP_SYMLINKS, @@ -4498,6 +4499,10 @@ Disable with --skip-ndbcluster (will save memory).", (gptr*) &opt_ndb_optimized_node_selection, (gptr*) &opt_ndb_optimized_node_selection, 0, GET_BOOL, OPT_ARG, 1, 0, 0, 0, 0, 0}, + { "ndb_cache_check_time", OPT_NDB_CACHE_CHECK_TIME, + "A dedicated thread is created to update cached commit count value at the given interval.", + (gptr*) &opt_ndb_cache_check_time, (gptr*) &opt_ndb_cache_check_time, 0, GET_ULONG, REQUIRED_ARG, + 0, 0, LONG_TIMEOUT, 0, 1, 0}, #endif {"new", 'n', "Use very new possible 'unsafe' functions.", (gptr*) &global_system_variables.new_mode, diff --git a/sql/set_var.cc b/sql/set_var.cc index 082c55db188..58c30c8e9bc 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -370,6 +370,7 @@ sys_var_thd_bool sys_ndb_use_exact_count("ndb_use_exact_count", &SV::ndb_use_exact_count); sys_var_thd_bool sys_ndb_use_transactions("ndb_use_transactions", &SV::ndb_use_transactions); +sys_var_long_ptr sys_ndb_cache_check_time("ndb_cache_check_time", &ndb_cache_check_time); #endif /* Time/date/datetime formats */ @@ -630,6 +631,7 @@ sys_var *sys_variables[]= &sys_ndb_force_send, &sys_ndb_use_exact_count, &sys_ndb_use_transactions, + &sys_ndb_cache_check_time, #endif &sys_unique_checks, &sys_warning_count @@ -797,6 +799,7 @@ struct show_var_st init_vars[]= { {sys_ndb_force_send.name, (char*) &sys_ndb_force_send, SHOW_SYS}, {sys_ndb_use_exact_count.name,(char*) &sys_ndb_use_exact_count, SHOW_SYS}, {sys_ndb_use_transactions.name,(char*) &sys_ndb_use_transactions, SHOW_SYS}, + {sys_ndb_cache_check_time.name,(char*) &sys_ndb_cache_check_time, SHOW_SYS}, #endif {sys_net_buffer_length.name,(char*) &sys_net_buffer_length, SHOW_SYS}, {sys_net_read_timeout.name, (char*) &sys_net_read_timeout, SHOW_SYS},