MDEV-7324 - Lock-free hash for table definition cache

This commit is contained in:
Sergey Vojtovich 2014-12-28 19:42:17 +04:00
parent 8883c54ac0
commit 6dbc48ca79
22 changed files with 911 additions and 773 deletions

View file

@ -231,6 +231,9 @@ void lf_hash_init(LF_HASH *hash, uint element_size, uint flags,
void lf_hash_destroy(LF_HASH *hash);
int lf_hash_insert(LF_HASH *hash, LF_PINS *pins, const void *data);
void *lf_hash_search(LF_HASH *hash, LF_PINS *pins, const void *key, uint keylen);
void *lf_hash_search_using_hash_value(LF_HASH *hash, LF_PINS *pins,
my_hash_value_type hash_value,
const void *key, uint keylen);
int lf_hash_delete(LF_HASH *hash, LF_PINS *pins, const void *key, uint keylen);
int lf_hash_iterate(LF_HASH *hash, LF_PINS *pins,
my_hash_walk_action action, void *argument);

View file

@ -2119,9 +2119,9 @@ select Host, Db from mysql.host limit 0;
Host Db
show open tables from mysql;
Database Table In_use Name_locked
mysql user 0 0
mysql general_log 0 0
mysql host 0 0
mysql user 0 0
call proc_1();
show open tables from mysql;
Database Table In_use Name_locked
@ -2132,9 +2132,9 @@ select Host, Db from mysql.host limit 0;
Host Db
show open tables from mysql;
Database Table In_use Name_locked
mysql user 0 0
mysql general_log 0 0
mysql host 0 0
mysql user 0 0
call proc_1();
show open tables from mysql;
Database Table In_use Name_locked
@ -2145,9 +2145,9 @@ select Host, Db from mysql.host limit 0;
Host Db
show open tables from mysql;
Database Table In_use Name_locked
mysql user 0 0
mysql general_log 0 0
mysql host 0 0
mysql user 0 0
call proc_1();
show open tables from mysql;
Database Table In_use Name_locked
@ -2158,9 +2158,9 @@ select Host, Db from mysql.host limit 0;
Host Db
show open tables from mysql;
Database Table In_use Name_locked
mysql user 0 0
mysql general_log 0 0
mysql host 0 0
mysql user 0 0
flush tables;
create function func_1() returns int begin flush tables; return 1; end|
ERROR 0A000: FLUSH is not allowed in stored function or trigger
@ -2176,9 +2176,9 @@ select Host, Db from mysql.host limit 0;
Host Db
show open tables from mysql;
Database Table In_use Name_locked
mysql user 0 0
mysql general_log 0 0
mysql host 0 0
mysql user 0 0
prepare abc from "flush tables";
execute abc;
show open tables from mysql;
@ -2190,9 +2190,9 @@ select Host, Db from mysql.host limit 0;
Host Db
show open tables from mysql;
Database Table In_use Name_locked
mysql user 0 0
mysql general_log 0 0
mysql host 0 0
mysql user 0 0
execute abc;
show open tables from mysql;
Database Table In_use Name_locked
@ -2203,9 +2203,9 @@ select Host, Db from mysql.host limit 0;
Host Db
show open tables from mysql;
Database Table In_use Name_locked
mysql user 0 0
mysql general_log 0 0
mysql host 0 0
mysql user 0 0
execute abc;
show open tables from mysql;
Database Table In_use Name_locked
@ -2216,9 +2216,9 @@ select Host, Db from mysql.host limit 0;
Host Db
show open tables from mysql;
Database Table In_use Name_locked
mysql user 0 0
mysql general_log 0 0
mysql host 0 0
mysql user 0 0
flush tables;
deallocate prepare abc;
create procedure proc_1() flush logs;

View file

@ -259,8 +259,8 @@ create table t1(n int);
insert into t1 values (1);
show open tables;
Database Table In_use Name_locked
test t1 0 0
mysql general_log 0 0
test t1 0 0
drop table t1;
create table t1 (a int not null, b VARCHAR(10), INDEX (b) ) AVG_ROW_LENGTH=10 CHECKSUM=1 COMMENT="test" ENGINE=MYISAM MIN_ROWS=10 MAX_ROWS=100 PACK_KEYS=1 DELAY_KEY_WRITE=1 ROW_FORMAT=fixed;
show create table t1;

View file

@ -24,11 +24,11 @@ wait/synch/rwlock/sql/LOCK_grant YES YES
wait/synch/rwlock/sql/LOCK_system_variables_hash YES YES
wait/synch/rwlock/sql/LOCK_sys_init_connect YES YES
wait/synch/rwlock/sql/LOCK_sys_init_slave YES YES
wait/synch/rwlock/sql/LOCK_tdc YES YES
wait/synch/rwlock/sql/LOGGER::LOCK_logger YES YES
wait/synch/rwlock/sql/MDL_context::LOCK_waiting_for YES YES
wait/synch/rwlock/sql/MDL_lock::rwlock YES YES
wait/synch/rwlock/sql/Query_cache_query::lock YES YES
wait/synch/rwlock/sql/THR_LOCK_servers YES YES
select * from performance_schema.setup_instruments
where name like 'Wait/Synch/Cond/sql/%'
and name not in (

View file

@ -5,9 +5,9 @@ WHERE name LIKE 'wait/synch/mutex/%'
truncate table performance_schema.events_statements_summary_by_digest;
flush status;
select NAME from performance_schema.mutex_instances
where NAME = 'wait/synch/mutex/sql/TABLE_SHARE::tdc.LOCK_table_share' GROUP BY NAME;
where NAME = 'wait/synch/mutex/mysys/THR_LOCK::mutex' GROUP BY NAME;
NAME
wait/synch/mutex/sql/TABLE_SHARE::tdc.LOCK_table_share
wait/synch/mutex/mysys/THR_LOCK::mutex
select NAME from performance_schema.rwlock_instances
where NAME = 'wait/synch/rwlock/sql/LOCK_grant';
NAME
@ -24,7 +24,7 @@ id b
1 initial value
SET @before_count = (SELECT SUM(TIMER_WAIT)
FROM performance_schema.events_waits_history_long
WHERE (EVENT_NAME = 'wait/synch/mutex/sql/TABLE_SHARE::tdc.LOCK_table_share'));
WHERE (EVENT_NAME = 'wait/synch/mutex/mysys/THR_LOCK::mutex'));
SELECT * FROM t1;
id b
1 initial value
@ -37,12 +37,12 @@ id b
8 initial value
SET @after_count = (SELECT SUM(TIMER_WAIT)
FROM performance_schema.events_waits_history_long
WHERE (EVENT_NAME = 'wait/synch/mutex/sql/TABLE_SHARE::tdc.LOCK_table_share'));
WHERE (EVENT_NAME = 'wait/synch/mutex/mysys/THR_LOCK::mutex'));
SELECT IF((@after_count - @before_count) > 0, 'Success', 'Failure') test_fm1_timed;
test_fm1_timed
Success
UPDATE performance_schema.setup_instruments SET enabled = 'NO'
WHERE NAME = 'wait/synch/mutex/sql/TABLE_SHARE::tdc.LOCK_table_share';
WHERE NAME = 'wait/synch/mutex/mysys/THR_LOCK::mutex';
TRUNCATE TABLE performance_schema.events_waits_history_long;
TRUNCATE TABLE performance_schema.events_waits_history;
TRUNCATE TABLE performance_schema.events_waits_current;
@ -51,7 +51,7 @@ id b
1 initial value
SET @before_count = (SELECT SUM(TIMER_WAIT)
FROM performance_schema.events_waits_history_long
WHERE (EVENT_NAME = 'wait/synch/mutex/sql/TABLE_SHARE::tdc.LOCK_table_share'));
WHERE (EVENT_NAME = 'wait/synch/mutex/mysys/THR_LOCK::mutex'));
SELECT * FROM t1;
id b
1 initial value
@ -64,7 +64,7 @@ id b
8 initial value
SET @after_count = (SELECT SUM(TIMER_WAIT)
FROM performance_schema.events_waits_history_long
WHERE (EVENT_NAME = 'wait/synch/mutex/sql/TABLE_SHARE::tdc.LOCK_table_share'));
WHERE (EVENT_NAME = 'wait/synch/mutex/mysys/THR_LOCK::mutex'));
SELECT IF((COALESCE(@after_count, 0) - COALESCE(@before_count, 0)) = 0, 'Success', 'Failure') test_fm2_timed;
test_fm2_timed
Success

View file

@ -19,7 +19,7 @@ flush status;
# Make sure objects are instrumented
select NAME from performance_schema.mutex_instances
where NAME = 'wait/synch/mutex/sql/TABLE_SHARE::tdc.LOCK_table_share' GROUP BY NAME;
where NAME = 'wait/synch/mutex/mysys/THR_LOCK::mutex' GROUP BY NAME;
select NAME from performance_schema.rwlock_instances
where NAME = 'wait/synch/rwlock/sql/LOCK_grant';
@ -49,18 +49,18 @@ SELECT * FROM t1 WHERE id = 1;
SET @before_count = (SELECT SUM(TIMER_WAIT)
FROM performance_schema.events_waits_history_long
WHERE (EVENT_NAME = 'wait/synch/mutex/sql/TABLE_SHARE::tdc.LOCK_table_share'));
WHERE (EVENT_NAME = 'wait/synch/mutex/mysys/THR_LOCK::mutex'));
SELECT * FROM t1;
SET @after_count = (SELECT SUM(TIMER_WAIT)
FROM performance_schema.events_waits_history_long
WHERE (EVENT_NAME = 'wait/synch/mutex/sql/TABLE_SHARE::tdc.LOCK_table_share'));
WHERE (EVENT_NAME = 'wait/synch/mutex/mysys/THR_LOCK::mutex'));
SELECT IF((@after_count - @before_count) > 0, 'Success', 'Failure') test_fm1_timed;
UPDATE performance_schema.setup_instruments SET enabled = 'NO'
WHERE NAME = 'wait/synch/mutex/sql/TABLE_SHARE::tdc.LOCK_table_share';
WHERE NAME = 'wait/synch/mutex/mysys/THR_LOCK::mutex';
TRUNCATE TABLE performance_schema.events_waits_history_long;
TRUNCATE TABLE performance_schema.events_waits_history;
@ -70,13 +70,13 @@ SELECT * FROM t1 WHERE id = 1;
SET @before_count = (SELECT SUM(TIMER_WAIT)
FROM performance_schema.events_waits_history_long
WHERE (EVENT_NAME = 'wait/synch/mutex/sql/TABLE_SHARE::tdc.LOCK_table_share'));
WHERE (EVENT_NAME = 'wait/synch/mutex/mysys/THR_LOCK::mutex'));
SELECT * FROM t1;
SET @after_count = (SELECT SUM(TIMER_WAIT)
FROM performance_schema.events_waits_history_long
WHERE (EVENT_NAME = 'wait/synch/mutex/sql/TABLE_SHARE::tdc.LOCK_table_share'));
WHERE (EVENT_NAME = 'wait/synch/mutex/mysys/THR_LOCK::mutex'));
SELECT IF((COALESCE(@after_count, 0) - COALESCE(@before_count, 0)) = 0, 'Success', 'Failure') test_fm2_timed;

View file

@ -2224,24 +2224,32 @@ deallocate prepare abc;
create procedure proc_1() flush tables;
flush tables;
--sorted_result
show open tables from mysql;
select Host, User from mysql.user limit 0;
select Host, Db from mysql.host limit 0;
--sorted_result
show open tables from mysql;
call proc_1();
--sorted_result
show open tables from mysql;
select Host, User from mysql.user limit 0;
select Host, Db from mysql.host limit 0;
--sorted_result
show open tables from mysql;
call proc_1();
--sorted_result
show open tables from mysql;
select Host, User from mysql.user limit 0;
select Host, Db from mysql.host limit 0;
--sorted_result
show open tables from mysql;
call proc_1();
--sorted_result
show open tables from mysql;
select Host, User from mysql.user limit 0;
select Host, Db from mysql.host limit 0;
--sorted_result
show open tables from mysql;
flush tables;
delimiter |;
@ -2261,24 +2269,31 @@ drop procedure proc_1;
flush tables;
select Host, User from mysql.user limit 0;
select Host, Db from mysql.host limit 0;
--sorted_result
show open tables from mysql;
--enable_ps_protocol
prepare abc from "flush tables";
execute abc;
--sorted_result
show open tables from mysql;
select Host, User from mysql.user limit 0;
select Host, Db from mysql.host limit 0;
--sorted_result
show open tables from mysql;
execute abc;
--sorted_result
show open tables from mysql;
select Host, User from mysql.user limit 0;
select Host, Db from mysql.host limit 0;
--sorted_result
show open tables from mysql;
execute abc;
--sorted_result
show open tables from mysql;
select Host, User from mysql.user limit 0;
select Host, Db from mysql.host limit 0;
--sorted_result
show open tables from mysql;
flush tables;
deallocate prepare abc;

View file

@ -135,9 +135,11 @@ show create table t1;
drop table t1;
flush tables;
--sorted_result
show open tables;
create table t1(n int);
insert into t1 values (1);
--sorted_result
show open tables;
drop table t1;
@ -617,6 +619,7 @@ show databases;
show tables;
show events;
show table status;
--sorted_result
show open tables;
show plugins;
show columns in t1;

View file

@ -122,7 +122,7 @@ retry:
{
if (unlikely(callback))
{
if (callback(cursor->curr + 1, (void*)key))
if (cur_hashnr & 1 && callback(cursor->curr + 1, (void*)key))
return 1;
}
else if (cur_hashnr >= hashnr)
@ -467,12 +467,13 @@ int lf_hash_delete(LF_HASH *hash, LF_PINS *pins, const void *key, uint keylen)
NOTE
see lsearch() for pin usage notes
*/
void *lf_hash_search(LF_HASH *hash, LF_PINS *pins, const void *key, uint keylen)
void *lf_hash_search_using_hash_value(LF_HASH *hash, LF_PINS *pins,
my_hash_value_type hashnr,
const void *key, uint keylen)
{
LF_SLIST * volatile *el, *found;
uint bucket, hashnr= calc_hash(hash, (uchar *)key, keylen);
uint bucket= hashnr % hash->size;
bucket= hashnr % hash->size;
lf_rwlock_by_pins(pins);
el= _lf_dynarray_lvalue(&hash->array, bucket);
if (unlikely(!el))
@ -521,6 +522,13 @@ int lf_hash_iterate(LF_HASH *hash, LF_PINS *pins,
return res;
}
void *lf_hash_search(LF_HASH *hash, LF_PINS *pins, const void *key, uint keylen)
{
return lf_hash_search_using_hash_value(hash, pins,
calc_hash(hash, (uchar*) key, keylen),
key, keylen);
}
static const uchar *dummy_key= (uchar*)"";
/*

View file

@ -5001,12 +5001,12 @@ bool ha_table_exists(THD *thd, const char *db, const char *table_name,
else if (engines_with_discover)
hton= &dummy;
TABLE_SHARE *share= tdc_lock_share(db, table_name);
if (share)
TDC_element *element= tdc_lock_share(thd, db, table_name);
if (element && element != MY_ERRPTR)
{
if (hton)
*hton= share->db_type();
tdc_unlock_share(share);
*hton= element->share->db_type();
tdc_unlock_share(element);
DBUG_RETURN(TRUE);
}

View file

@ -4789,7 +4789,8 @@ static int init_server_components()
all things are initialized so that unireg_abort() doesn't fail
*/
mdl_init();
if (tdc_init() | hostname_cache_init())
tdc_init();
if (hostname_cache_init())
unireg_abort(1);
query_cache_set_min_res_unit(query_cache_min_res_unit);

View file

@ -270,58 +270,75 @@ uint get_table_def_key(const TABLE_LIST *table_list, const char **key)
# Pointer to list of names of open tables.
*/
struct list_open_tables_arg
{
THD *thd;
const char *db;
const char *wild;
TABLE_LIST table_list;
OPEN_TABLE_LIST **start_list, *open_list;
};
static my_bool list_open_tables_callback(TDC_element *element,
list_open_tables_arg *arg)
{
char *db= (char*) element->m_key;
char *table_name= (char*) element->m_key + strlen((char*) element->m_key) + 1;
if (arg->db && my_strcasecmp(system_charset_info, arg->db, db))
return FALSE;
if (arg->wild && wild_compare(table_name, arg->wild, 0))
return FALSE;
/* Check if user has SELECT privilege for any column in the table */
arg->table_list.db= db;
arg->table_list.table_name= table_name;
arg->table_list.grant.privilege= 0;
if (check_table_access(arg->thd, SELECT_ACL, &arg->table_list, TRUE, 1, TRUE))
return FALSE;
if (!(*arg->start_list= (OPEN_TABLE_LIST *) arg->thd->alloc(
sizeof(**arg->start_list) + element->m_key_length)))
return TRUE;
strmov((*arg->start_list)->table=
strmov(((*arg->start_list)->db= (char*) ((*arg->start_list) + 1)),
db) + 1, table_name);
(*arg->start_list)->in_use= 0;
mysql_mutex_lock(&element->LOCK_table_share);
TDC_element::All_share_tables_list::Iterator it(element->all_tables);
TABLE *table;
while ((table= it++))
if (table->in_use)
++(*arg->start_list)->in_use;
mysql_mutex_unlock(&element->LOCK_table_share);
(*arg->start_list)->locked= 0; /* Obsolete. */
arg->start_list= &(*arg->start_list)->next;
*arg->start_list= 0;
return FALSE;
}
OPEN_TABLE_LIST *list_open_tables(THD *thd, const char *db, const char *wild)
{
OPEN_TABLE_LIST **start_list, *open_list;
TABLE_LIST table_list;
TABLE_SHARE *share;
TDC_iterator tdc_it;
list_open_tables_arg argument;
DBUG_ENTER("list_open_tables");
bzero((char*) &table_list,sizeof(table_list));
start_list= &open_list;
open_list=0;
argument.thd= thd;
argument.db= db;
argument.wild= wild;
bzero((char*) &argument.table_list, sizeof(argument.table_list));
argument.start_list= &argument.open_list;
argument.open_list= 0;
tdc_it.init();
while ((share= tdc_it.next()))
{
if (db && my_strcasecmp(system_charset_info, db, share->db.str))
continue;
if (wild && wild_compare(share->table_name.str, wild, 0))
continue;
if (tdc_iterate(thd, (my_hash_walk_action) list_open_tables_callback,
&argument, true))
DBUG_RETURN(0);
/* Check if user has SELECT privilege for any column in the table */
table_list.db= share->db.str;
table_list.table_name= share->table_name.str;
table_list.grant.privilege=0;
if (check_table_access(thd,SELECT_ACL,&table_list, TRUE, 1, TRUE))
continue;
if (!(*start_list = (OPEN_TABLE_LIST *)
sql_alloc(sizeof(**start_list)+share->table_cache_key.length)))
{
open_list=0; // Out of memory
break;
}
strmov((*start_list)->table=
strmov(((*start_list)->db= (char*) ((*start_list)+1)),
share->db.str)+1,
share->table_name.str);
(*start_list)->in_use= 0;
mysql_mutex_lock(&share->tdc.LOCK_table_share);
TABLE_SHARE::All_share_tables_list::Iterator it(share->tdc.all_tables);
TABLE *table;
while ((table= it++))
if (table->in_use)
++(*start_list)->in_use;
mysql_mutex_unlock(&share->tdc.LOCK_table_share);
(*start_list)->locked= 0; /* Obsolete. */
start_list= &(*start_list)->next;
*start_list=0;
}
tdc_it.deinit();
DBUG_RETURN(open_list);
DBUG_RETURN(argument.open_list);
}
/*****************************************************************************
@ -371,12 +388,12 @@ void free_io_cache(TABLE *table)
@pre Caller should have TABLE_SHARE::tdc.LOCK_table_share mutex.
*/
void kill_delayed_threads_for_table(TABLE_SHARE *share)
void kill_delayed_threads_for_table(TDC_element *element)
{
TABLE_SHARE::All_share_tables_list::Iterator it(share->tdc.all_tables);
TDC_element::All_share_tables_list::Iterator it(element->all_tables);
TABLE *tab;
mysql_mutex_assert_owner(&share->tdc.LOCK_table_share);
mysql_mutex_assert_owner(&element->LOCK_table_share);
if (!delayed_insert_threads)
return;
@ -385,7 +402,7 @@ void kill_delayed_threads_for_table(TABLE_SHARE *share)
{
THD *in_use= tab->in_use;
DBUG_ASSERT(in_use && tab->s->tdc.flushed);
DBUG_ASSERT(in_use && tab->s->tdc->flushed);
if ((in_use->system_thread & SYSTEM_THREAD_DELAYED_INSERT) &&
! in_use->killed)
{
@ -422,6 +439,30 @@ void kill_delayed_threads_for_table(TABLE_SHARE *share)
lock taken by thread trying to obtain global read lock.
*/
struct close_cached_tables_arg
{
ulong refresh_version;
TDC_element *element;
};
static my_bool close_cached_tables_callback(TDC_element *element,
close_cached_tables_arg *arg)
{
mysql_mutex_lock(&element->LOCK_table_share);
if (element->share && element->flushed &&
element->version < arg->refresh_version)
{
/* wait_for_old_version() will unlock mutex and free share */
arg->element= element;
return TRUE;
}
mysql_mutex_unlock(&element->LOCK_table_share);
return FALSE;
}
bool close_cached_tables(THD *thd, TABLE_LIST *tables,
bool wait_for_refresh, ulong timeout)
{
@ -517,38 +558,21 @@ bool close_cached_tables(THD *thd, TABLE_LIST *tables,
if (!tables)
{
bool found= true;
int r= 0;
close_cached_tables_arg argument;
argument.refresh_version= refresh_version;
set_timespec(abstime, timeout);
while (found && !thd->killed)
{
TABLE_SHARE *share;
TDC_iterator tdc_it;
found= false;
tdc_it.init();
while ((share= tdc_it.next()))
{
mysql_mutex_lock(&share->tdc.LOCK_table_share);
if (share->tdc.flushed && share->tdc.version < refresh_version)
{
/* wait_for_old_version() will unlock mutex and free share */
found= true;
break;
}
mysql_mutex_unlock(&share->tdc.LOCK_table_share);
}
tdc_it.deinit();
while (!thd->killed &&
(r= tdc_iterate(thd,
(my_hash_walk_action) close_cached_tables_callback,
&argument)) == 1 &&
!argument.element->share->wait_for_old_version(thd, &abstime,
MDL_wait_for_subgraph::DEADLOCK_WEIGHT_DDL))
/* no-op */;
if (found)
{
if (share->wait_for_old_version(thd, &abstime,
MDL_wait_for_subgraph::DEADLOCK_WEIGHT_DDL))
{
result= TRUE;
break;
}
}
}
if (r)
result= TRUE;
}
else
{
@ -592,53 +616,72 @@ err_with_reopen:
if specified string is NULL, then any table with a connection string.
*/
struct close_cached_connection_tables_arg
{
THD *thd;
LEX_STRING *connection;
TABLE_LIST *tables;
};
static my_bool close_cached_connection_tables_callback(
TDC_element *element, close_cached_connection_tables_arg *arg)
{
TABLE_LIST *tmp;
mysql_mutex_lock(&element->LOCK_table_share);
/* Ignore if table is not open or does not have a connect_string */
if (!element->share || !element->share->connect_string.length ||
!element->ref_count)
{
mysql_mutex_unlock(&element->LOCK_table_share);
return FALSE;
}
/* Compare the connection string */
if (arg->connection &&
(arg->connection->length > element->share->connect_string.length ||
(arg->connection->length < element->share->connect_string.length &&
(element->share->connect_string.str[arg->connection->length] != '/' &&
element->share->connect_string.str[arg->connection->length] != '\\')) ||
strncasecmp(arg->connection->str, element->share->connect_string.str,
arg->connection->length)))
return FALSE;
/* close_cached_tables() only uses these elements */
if (!(tmp= (TABLE_LIST*) alloc_root(arg->thd->mem_root, sizeof(TABLE_LIST))) ||
!(tmp->db= strdup_root(arg->thd->mem_root, element->share->db.str)) ||
!(tmp->table_name= strdup_root(arg->thd->mem_root,
element->share->table_name.str)))
return TRUE;
tmp->next_local= arg->tables;
arg->tables= tmp;
mysql_mutex_unlock(&element->LOCK_table_share);
return FALSE;
}
bool close_cached_connection_tables(THD *thd, LEX_STRING *connection)
{
TABLE_LIST tmp, *tables= NULL;
bool result= FALSE;
TABLE_SHARE *share;
TDC_iterator tdc_it;
close_cached_connection_tables_arg argument;
DBUG_ENTER("close_cached_connections");
DBUG_ASSERT(thd);
bzero(&tmp, sizeof(TABLE_LIST));
argument.thd= thd;
argument.connection= connection;
argument.tables= NULL;
tdc_it.init();
while ((share= tdc_it.next()))
{
mysql_mutex_lock(&share->tdc.LOCK_table_share);
/* Ignore if table is not open or does not have a connect_string */
if (!share->connect_string.length || !share->tdc.ref_count)
{
mysql_mutex_unlock(&share->tdc.LOCK_table_share);
continue;
}
mysql_mutex_unlock(&share->tdc.LOCK_table_share);
if (tdc_iterate(thd,
(my_hash_walk_action) close_cached_connection_tables_callback,
&argument))
DBUG_RETURN(true);
/* Compare the connection string */
if (connection &&
(connection->length > share->connect_string.length ||
(connection->length < share->connect_string.length &&
(share->connect_string.str[connection->length] != '/' &&
share->connect_string.str[connection->length] != '\\')) ||
strncasecmp(connection->str, share->connect_string.str,
connection->length)))
continue;
/* close_cached_tables() only uses these elements */
tmp.db= share->db.str;
tmp.table_name= share->table_name.str;
tmp.next_local= tables;
tables= (TABLE_LIST *) memdup_root(thd->mem_root, (char*)&tmp,
sizeof(TABLE_LIST));
}
tdc_it.deinit();
if (tables)
result= close_cached_tables(thd, tables, FALSE, LONG_TIMEOUT);
DBUG_RETURN(result);
DBUG_RETURN(argument.tables ?
close_cached_tables(thd, argument.tables, FALSE, LONG_TIMEOUT) :
false);
}
@ -1775,7 +1818,7 @@ bool wait_while_table_is_used(THD *thd, TABLE *table,
DBUG_ENTER("wait_while_table_is_used");
DBUG_PRINT("enter", ("table: '%s' share: 0x%lx db_stat: %u version: %lu",
table->s->table_name.str, (ulong) table->s,
table->db_stat, table->s->tdc.version));
table->db_stat, table->s->tdc->version));
if (thd->mdl_context.upgrade_shared_lock(
table->mdl_ticket, MDL_EXCLUSIVE,
@ -2388,10 +2431,10 @@ retry_share:
if (!(flags & MYSQL_OPEN_IGNORE_FLUSH))
{
if (share->tdc.flushed)
if (share->tdc->flushed)
{
DBUG_PRINT("info", ("Found old share version: %lu current: %lu",
share->tdc.version, tdc_refresh_version()));
share->tdc->version, tdc_refresh_version()));
/*
We already have an MDL lock. But we have encountered an old
version of table in the table definition cache which is possible
@ -2422,7 +2465,7 @@ retry_share:
goto retry_share;
}
if (thd->open_tables && thd->open_tables->s->tdc.flushed)
if (thd->open_tables && thd->open_tables->s->tdc->flushed)
{
/*
If the version changes while we're opening the tables,

View file

@ -271,7 +271,7 @@ bool lock_tables(THD *thd, TABLE_LIST *tables, uint counter, uint flags);
int decide_logging_format(THD *thd, TABLE_LIST *tables);
void free_io_cache(TABLE *entry);
void intern_close_table(TABLE *entry);
void kill_delayed_threads_for_table(TABLE_SHARE *share);
void kill_delayed_threads_for_table(TDC_element *element);
void close_thread_table(THD *thd, TABLE **table_ptr);
bool close_temporary_tables(THD *thd);
TABLE_LIST *unique_table(THD *thd, TABLE_LIST *table, TABLE_LIST *table_list,

View file

@ -912,7 +912,8 @@ THD::THD(bool is_wsrep_applier)
#endif /* defined(ENABLED_DEBUG_SYNC) */
wait_for_commit_ptr(0),
main_da(0, false, false),
m_stmt_da(&main_da)
m_stmt_da(&main_da),
tdc_hash_pins(0)
#ifdef WITH_WSREP
,
wsrep_applier(is_wsrep_applier),
@ -1701,6 +1702,8 @@ THD::~THD()
free_root(&main_mem_root, MYF(0));
main_da.free_memory();
if (tdc_hash_pins)
lf_hash_put_pins(tdc_hash_pins);
if (status_var.memory_used != 0)
{
DBUG_PRINT("error", ("memory_used: %lld", status_var.memory_used));

View file

@ -3769,6 +3769,8 @@ public:
(rgi_slave && rgi_have_temporary_tables()));
}
LF_PINS *tdc_hash_pins;
inline ulong wsrep_binlog_format() const
{
return WSREP_FORMAT(variables.binlog_format);

View file

@ -1134,7 +1134,7 @@ void mysql_ha_flush(THD *thd)
((hash_tables->table->mdl_ticket &&
hash_tables->table->mdl_ticket->has_pending_conflicting_lock()) ||
(!hash_tables->table->s->tmp_table &&
hash_tables->table->s->tdc.flushed)))
hash_tables->table->s->tdc->flushed)))
mysql_ha_close_table(hash_tables);
}

View file

@ -3048,7 +3048,7 @@ bool Delayed_insert::handle_inserts(void)
THD_STAGE_INFO(&thd, stage_insert);
max_rows= delayed_insert_limit;
if (thd.killed || table->s->tdc.flushed)
if (thd.killed || table->s->tdc->flushed)
{
thd.killed= KILL_SYSTEM_THREAD;
max_rows= ULONG_MAX; // Do as much as possible

View file

@ -76,35 +76,37 @@ print_where(COND *cond,const char *info, enum_query_type query_type)
/* This is for debugging purposes */
static my_bool print_cached_tables_callback(TDC_element *element,
void *arg __attribute__((unused)))
{
TABLE *entry;
mysql_mutex_lock(&element->LOCK_table_share);
TDC_element::All_share_tables_list::Iterator it(element->all_tables);
while ((entry= it++))
{
THD *in_use= entry->in_use;
printf("%-14.14s %-32s%6ld%8ld%6d %s\n",
entry->s->db.str, entry->s->table_name.str, element->version,
in_use ? in_use->thread_id : 0,
entry->db_stat ? 1 : 0,
in_use ? lock_descriptions[(int)entry->reginfo.lock_type] :
"Not in use");
}
mysql_mutex_unlock(&element->LOCK_table_share);
return FALSE;
}
static void print_cached_tables(void)
{
TABLE_SHARE *share;
TABLE *entry;
TDC_iterator tdc_it;
compile_time_assert(TL_WRITE_ONLY+1 == array_elements(lock_descriptions));
/* purecov: begin tested */
puts("DB Table Version Thread Open Lock");
tdc_it.init();
while ((share= tdc_it.next()))
{
mysql_mutex_lock(&share->tdc.LOCK_table_share);
TABLE_SHARE::All_share_tables_list::Iterator it(share->tdc.all_tables);
while ((entry= it++))
{
THD *in_use= entry->in_use;
printf("%-14.14s %-32s%6ld%8ld%6d %s\n",
entry->s->db.str, entry->s->table_name.str, entry->s->tdc.version,
in_use ? in_use->thread_id : 0,
entry->db_stat ? 1 : 0,
in_use ? lock_descriptions[(int)entry->reginfo.lock_type] :
"Not in use");
}
mysql_mutex_unlock(&share->tdc.LOCK_table_share);
}
tdc_it.deinit();
tdc_iterate(0, (my_hash_walk_action) print_cached_tables_callback, NULL, true);
printf("\nCurrent refresh version: %ld\n", tdc_refresh_version());
fflush(stdout);
/* purecov: end */

View file

@ -325,7 +325,7 @@ TABLE_SHARE *alloc_table_share(const char *db, const char *table_name,
&share->LOCK_share, MY_MUTEX_INIT_SLOW);
mysql_mutex_init(key_TABLE_SHARE_LOCK_ha_data,
&share->LOCK_ha_data, MY_MUTEX_INIT_FAST);
tdc_init_share(share);
tdc_assign_new_table_id(share);
}
DBUG_RETURN(share);
}
@ -422,7 +422,6 @@ void TABLE_SHARE::destroy()
{
mysql_mutex_destroy(&LOCK_share);
mysql_mutex_destroy(&LOCK_ha_data);
tdc_deinit_share(this);
}
my_hash_free(&name_hash);
@ -3866,11 +3865,11 @@ bool TABLE_SHARE::visit_subgraph(Wait_for_flush *wait_for_flush,
because we won't try to acquire tdc.LOCK_table_share while
holding a write-lock on MDL_lock::m_rwlock.
*/
mysql_mutex_lock(&tdc.LOCK_table_share);
tdc.all_tables_refs++;
mysql_mutex_unlock(&tdc.LOCK_table_share);
mysql_mutex_lock(&tdc->LOCK_table_share);
tdc->all_tables_refs++;
mysql_mutex_unlock(&tdc->LOCK_table_share);
All_share_tables_list::Iterator tables_it(tdc.all_tables);
TDC_element::All_share_tables_list::Iterator tables_it(tdc->all_tables);
/*
In case of multiple searches running in parallel, avoid going
@ -3888,7 +3887,7 @@ bool TABLE_SHARE::visit_subgraph(Wait_for_flush *wait_for_flush,
while ((table= tables_it++))
{
DBUG_ASSERT(table->in_use && tdc.flushed);
DBUG_ASSERT(table->in_use && tdc->flushed);
if (gvisitor->inspect_edge(&table->in_use->mdl_context))
{
goto end_leave_node;
@ -3898,7 +3897,7 @@ bool TABLE_SHARE::visit_subgraph(Wait_for_flush *wait_for_flush,
tables_it.rewind();
while ((table= tables_it++))
{
DBUG_ASSERT(table->in_use && tdc.flushed);
DBUG_ASSERT(table->in_use && tdc->flushed);
if (table->in_use->mdl_context.visit_subgraph(gvisitor))
{
goto end_leave_node;
@ -3911,10 +3910,10 @@ end_leave_node:
gvisitor->leave_node(src_ctx);
end:
mysql_mutex_lock(&tdc.LOCK_table_share);
if (!--tdc.all_tables_refs)
mysql_cond_broadcast(&tdc.COND_release);
mysql_mutex_unlock(&tdc.LOCK_table_share);
mysql_mutex_lock(&tdc->LOCK_table_share);
if (!--tdc->all_tables_refs)
mysql_cond_broadcast(&tdc->COND_release);
mysql_mutex_unlock(&tdc->LOCK_table_share);
return result;
}
@ -3949,14 +3948,14 @@ bool TABLE_SHARE::wait_for_old_version(THD *thd, struct timespec *abstime,
Wait_for_flush ticket(mdl_context, this, deadlock_weight);
MDL_wait::enum_wait_status wait_status;
mysql_mutex_assert_owner(&tdc.LOCK_table_share);
DBUG_ASSERT(tdc.flushed);
mysql_mutex_assert_owner(&tdc->LOCK_table_share);
DBUG_ASSERT(tdc->flushed);
tdc.m_flush_tickets.push_front(&ticket);
tdc->m_flush_tickets.push_front(&ticket);
mdl_context->m_wait.reset_status();
mysql_mutex_unlock(&tdc.LOCK_table_share);
mysql_mutex_unlock(&tdc->LOCK_table_share);
mdl_context->will_wait_for(&ticket);
@ -3967,21 +3966,10 @@ bool TABLE_SHARE::wait_for_old_version(THD *thd, struct timespec *abstime,
mdl_context->done_waiting_for();
mysql_mutex_lock(&tdc.LOCK_table_share);
tdc.m_flush_tickets.remove(&ticket);
if (tdc.m_flush_tickets.is_empty() && tdc.ref_count == 0)
{
/*
If our thread was the last one using the share,
we must destroy it here.
*/
mysql_mutex_unlock(&tdc.LOCK_table_share);
destroy();
}
else
mysql_mutex_unlock(&tdc.LOCK_table_share);
mysql_mutex_lock(&tdc->LOCK_table_share);
tdc->m_flush_tickets.remove(&ticket);
mysql_cond_broadcast(&tdc->COND_release);
mysql_mutex_unlock(&tdc->LOCK_table_share);
/*
@ -4027,7 +4015,7 @@ bool TABLE_SHARE::wait_for_old_version(THD *thd, struct timespec *abstime,
void TABLE::init(THD *thd, TABLE_LIST *tl)
{
DBUG_ASSERT(s->tdc.ref_count > 0 || s->tmp_table != NO_TMP_TABLE);
DBUG_ASSERT(s->tmp_table != NO_TMP_TABLE || s->tdc->ref_count > 0);
if (thd->lex->need_correct_ident())
alias_name_used= my_strcasecmp(table_alias_charset,

View file

@ -47,6 +47,7 @@ class ACL_internal_schema_access;
class ACL_internal_table_access;
class Field;
class Table_statistics;
class TDC_element;
/*
Used to identify NESTED_JOIN structures within a join (applicable only to
@ -611,32 +612,7 @@ struct TABLE_SHARE
mysql_mutex_t LOCK_ha_data; /* To protect access to ha_data */
mysql_mutex_t LOCK_share; /* To protect TABLE_SHARE */
typedef I_P_List <TABLE, TABLE_share> TABLE_list;
typedef I_P_List <TABLE, All_share_tables> All_share_tables_list;
struct
{
/**
Protects ref_count, m_flush_tickets, all_tables, free_tables, flushed,
all_tables_refs.
*/
mysql_mutex_t LOCK_table_share;
mysql_cond_t COND_release;
TABLE_SHARE *next, **prev; /* Link to unused shares */
uint ref_count; /* How many TABLE objects uses this */
uint all_tables_refs; /* Number of refs to all_tables */
/**
List of tickets representing threads waiting for the share to be flushed.
*/
Wait_for_flush_list m_flush_tickets;
/*
Doubly-linked (back-linked) lists of used and unused TABLE objects
for this share.
*/
All_share_tables_list all_tables;
TABLE_list free_tables;
ulong version;
bool flushed;
} tdc;
TDC_element *tdc;
LEX_CUSTRING tabledef_version;

File diff suppressed because it is too large Load diff

View file

@ -16,6 +16,165 @@
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
extern PSI_mutex_key key_TABLE_SHARE_LOCK_table_share;
extern PSI_cond_key key_TABLE_SHARE_COND_release;
class TDC_element
{
public:
uchar m_key[NAME_LEN + 1 + NAME_LEN + 1];
uint m_key_length;
ulong version;
bool flushed;
TABLE_SHARE *share;
typedef I_P_List <TABLE, TABLE_share> TABLE_list;
typedef I_P_List <TABLE, All_share_tables> All_share_tables_list;
/**
Protects ref_count, m_flush_tickets, all_tables, free_tables, flushed,
all_tables_refs.
*/
mysql_mutex_t LOCK_table_share;
mysql_cond_t COND_release;
TDC_element *next, **prev; /* Link to unused shares */
uint ref_count; /* How many TABLE objects uses this */
uint all_tables_refs; /* Number of refs to all_tables */
/**
List of tickets representing threads waiting for the share to be flushed.
*/
Wait_for_flush_list m_flush_tickets;
/*
Doubly-linked (back-linked) lists of used and unused TABLE objects
for this share.
*/
All_share_tables_list all_tables;
TABLE_list free_tables;
TDC_element() {}
TDC_element(const char *key, uint key_length) : m_key_length(key_length)
{
memcpy(m_key, key, key_length);
}
void assert_clean_share()
{
DBUG_ASSERT(share == 0);
DBUG_ASSERT(ref_count == 0);
DBUG_ASSERT(m_flush_tickets.is_empty());
DBUG_ASSERT(all_tables.is_empty());
DBUG_ASSERT(free_tables.is_empty());
DBUG_ASSERT(all_tables_refs == 0);
DBUG_ASSERT(next == 0);
DBUG_ASSERT(prev == 0);
}
/**
Acquire TABLE object from table cache.
@pre share must be protected against removal.
Acquired object cannot be evicted or acquired again.
@return TABLE object, or NULL if no unused objects.
*/
TABLE *acquire_table(THD *thd)
{
TABLE *table;
mysql_mutex_lock(&LOCK_table_share);
table= free_tables.pop_front();
if (table)
{
DBUG_ASSERT(!table->in_use);
table->in_use= thd;
/* The ex-unused table must be fully functional. */
DBUG_ASSERT(table->db_stat && table->file);
/* The children must be detached from the table. */
DBUG_ASSERT(!table->file->extra(HA_EXTRA_IS_ATTACHED_CHILDREN));
}
mysql_mutex_unlock(&LOCK_table_share);
return table;
}
/**
Get last element of free_tables.
*/
TABLE *free_tables_back()
{
TABLE_list::Iterator it(share->tdc->free_tables);
TABLE *entry, *last= 0;
while ((entry= it++))
last= entry;
return last;
}
/**
Wait for MDL deadlock detector to complete traversing tdc.all_tables.
Must be called before updating TABLE_SHARE::tdc.all_tables.
*/
void wait_for_mdl_deadlock_detector()
{
while (all_tables_refs)
mysql_cond_wait(&COND_release, &LOCK_table_share);
}
/**
Prepeare table share for use with table definition cache.
*/
static void lf_alloc_constructor(uchar *arg)
{
TDC_element *element= (TDC_element*) (arg + LF_HASH_OVERHEAD);
DBUG_ENTER("lf_alloc_constructor");
mysql_mutex_init(key_TABLE_SHARE_LOCK_table_share,
&element->LOCK_table_share, MY_MUTEX_INIT_FAST);
mysql_cond_init(key_TABLE_SHARE_COND_release, &element->COND_release, 0);
element->m_flush_tickets.empty();
element->all_tables.empty();
element->free_tables.empty();
element->all_tables_refs= 0;
element->share= 0;
element->ref_count= 0;
element->next= 0;
element->prev= 0;
DBUG_VOID_RETURN;
}
/**
Release table definition cache specific resources of table share.
*/
static void lf_alloc_destructor(uchar *arg)
{
TDC_element *element= (TDC_element*) (arg + LF_HASH_OVERHEAD);
DBUG_ENTER("lf_alloc_destructor");
element->assert_clean_share();
mysql_cond_destroy(&element->COND_release);
mysql_mutex_destroy(&element->LOCK_table_share);
DBUG_VOID_RETURN;
}
static uchar *key(const TDC_element *element, size_t *length,
my_bool not_used __attribute__((unused)))
{
*length= element->m_key_length;
return (uchar*) element->m_key;
}
};
enum enum_tdc_remove_table_type
{
TDC_RT_REMOVE_ALL,
@ -27,15 +186,14 @@ enum enum_tdc_remove_table_type
extern ulong tdc_size;
extern ulong tc_size;
extern int tdc_init(void);
extern void tdc_init(void);
extern void tdc_start_shutdown(void);
extern void tdc_deinit(void);
extern ulong tdc_records(void);
extern void tdc_purge(bool all);
extern void tdc_init_share(TABLE_SHARE *share);
extern void tdc_deinit_share(TABLE_SHARE *share);
extern TABLE_SHARE *tdc_lock_share(const char *db, const char *table_name);
extern void tdc_unlock_share(TABLE_SHARE *share);
extern TDC_element *tdc_lock_share(THD *thd, const char *db,
const char *table_name);
extern void tdc_unlock_share(TDC_element *element);
extern TABLE_SHARE *tdc_acquire_share(THD *thd, const char *db,
const char *table_name,
const char *key, uint key_length,
@ -52,6 +210,8 @@ extern int tdc_wait_for_old_version(THD *thd, const char *db,
extern ulong tdc_refresh_version(void);
extern ulong tdc_increment_refresh_version(void);
extern void tdc_assign_new_table_id(TABLE_SHARE *share);
extern int tdc_iterate(THD *thd, my_hash_walk_action action, void *argument,
bool no_dups= false);
extern uint tc_records(void);
extern void tc_purge(bool mark_flushed= false);
@ -125,13 +285,3 @@ static inline TABLE_SHARE *tdc_acquire_share_shortlived(THD *thd, TABLE_LIST *tl
return tdc_acquire_share(thd, tl->db, tl->table_name, key, key_length,
tl->mdl_request.key.tc_hash_value(), flags, 0);
}
class TDC_iterator
{
ulong idx;
public:
void init(void);
void deinit(void);
TABLE_SHARE *next(void);
};