Commit 7a947614 authored by Sergey Vojtovich's avatar Sergey Vojtovich

Split tdc_remove_table()

TDC_RT_REMOVE_ALL -> tdc_remove_table(). Some occurrences replaced with
TDC_element::flush() (whenver TABLE_SHARE is available).

TDC_RT_REMOVE_NOT_OWN[_KEEP_SHARE] -> TDC_element::flush(). These modes
assume that current thread owns TABLE_SHARE reference, which means we can
avoid hash lookup and flush unused TABLE instances directly.

TDC_RT_REMOVE_UNUSED -> TDC_element::flush_unused(). Only [ab]used by
mysql_admin_table() currently. Should be removed eventually.

Part of MDEV-17882 - Cleanup refresh version
parent 06fae758
......@@ -430,8 +430,7 @@ rpl_slave_state::truncate_state_table(THD *thd)
MYSQL_OPEN_IGNORE_LOGGING_FORMAT)))
{
DBUG_ASSERT(!tlist.table->file->row_logging);
tdc_remove_table(thd, TDC_RT_REMOVE_NOT_OWN, "mysql",
rpl_gtid_slave_state_table_name.str);
tlist.table->s->tdc->flush(thd, true);
err= tlist.table->file->ha_truncate();
if (err)
......
......@@ -1156,8 +1156,7 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
}
else if (open_for_modify || fatal_error)
{
tdc_remove_table(thd, TDC_RT_REMOVE_UNUSED,
table->db.str, table->table_name.str);
table->table->s->tdc->flush_unused(true);
/*
May be something modified. Consequently, we have to
invalidate the query cache.
......
......@@ -442,8 +442,7 @@ bool close_cached_tables(THD *thd, TABLE_LIST *tables,
DBUG_RETURN(true);
for (TABLE_LIST *table= tables; table; table= table->next_local)
tdc_remove_table(thd, TDC_RT_REMOVE_ALL, table->db.str,
table->table_name.str);
tdc_remove_table(thd, table->db.str, table->table_name.str);
}
DBUG_RETURN(false);
}
......@@ -1310,8 +1309,7 @@ bool wait_while_table_is_used(THD *thd, TABLE *table,
thd->variables.lock_wait_timeout))
DBUG_RETURN(TRUE);
tdc_remove_table(thd, TDC_RT_REMOVE_NOT_OWN,
table->s->db.str, table->s->table_name.str);
table->s->tdc->flush(thd, true);
/* extra() call must come only after all instances above are closed */
if (function != HA_EXTRA_NOT_USED)
(void) table->file->extra(function);
......@@ -1350,9 +1348,8 @@ void drop_open_table(THD *thd, TABLE *table, const LEX_CSTRING *db_name,
handlerton *table_type= table->s->db_type();
table->file->extra(HA_EXTRA_PREPARE_FOR_DROP);
table->s->tdc->flush(thd, true);
close_thread_table(thd, &thd->open_tables);
/* Remove the table share from the table cache. */
tdc_remove_table(thd, TDC_RT_REMOVE_ALL, db_name->str, table_name->str);
/* Remove the table from the storage engine and rm the .frm. */
quick_rm_table(thd, table_type, db_name, table_name, 0);
}
......@@ -2947,8 +2944,7 @@ static bool auto_repair_table(THD *thd, TABLE_LIST *table_list)
tdc_release_share(share);
/* Remove the repaired share from the table cache. */
tdc_remove_table(thd, TDC_RT_REMOVE_ALL,
table_list->db.str, table_list->table_name.str);
tdc_remove_table(thd, table_list->db.str, table_list->table_name.str);
end_free:
my_free(entry);
return result;
......@@ -3119,7 +3115,7 @@ Open_table_context::recover_from_failed_open()
get_timeout(), 0)))
break;
tdc_remove_table(m_thd, TDC_RT_REMOVE_ALL, m_failed_table->db.str,
tdc_remove_table(m_thd, m_failed_table->db.str,
m_failed_table->table_name.str);
m_thd->get_stmt_da()->clear_warning_info(m_thd->query_id);
......@@ -3155,7 +3151,7 @@ Open_table_context::recover_from_failed_open()
get_timeout(), 0)))
break;
tdc_remove_table(m_thd, TDC_RT_REMOVE_ALL, m_failed_table->db.str,
tdc_remove_table(m_thd, m_failed_table->db.str,
m_failed_table->table_name.str);
result= auto_repair_table(m_thd, m_failed_table);
......
......@@ -825,8 +825,7 @@ bool Sql_cmd_alter_table_truncate_partition::execute(THD *thd)
if (thd->mdl_context.upgrade_shared_lock(ticket, MDL_EXCLUSIVE, timeout))
DBUG_RETURN(TRUE);
tdc_remove_table(thd, TDC_RT_REMOVE_NOT_OWN, first_table->db.str,
first_table->table_name.str);
first_table->table->s->tdc->flush(thd, true);
partition= (ha_partition*) first_table->table->file;
/* Invoke the handler method responsible for truncating the partition. */
......
......@@ -309,8 +309,7 @@ do_rename(THD *thd, TABLE_LIST *ren_table, const LEX_CSTRING *new_db,
Shared table. Just drop the old .frm as it's not correct anymore
Discovery will find the old table when it's accessed
*/
tdc_remove_table(thd, TDC_RT_REMOVE_ALL,
ren_table->db.str, ren_table->table_name.str);
tdc_remove_table(thd, ren_table->db.str, ren_table->table_name.str);
quick_rm_table(thd, 0, &ren_table->db, &old_alias, FRM_ONLY, 0);
DBUG_RETURN(0);
}
......@@ -329,8 +328,7 @@ do_rename(THD *thd, TABLE_LIST *ren_table, const LEX_CSTRING *new_db,
DBUG_RETURN(1);
#endif
tdc_remove_table(thd, TDC_RT_REMOVE_ALL,
ren_table->db.str, ren_table->table_name.str);
tdc_remove_table(thd, ren_table->db.str, ren_table->table_name.str);
if (hton != view_pseudo_hton)
{
......
......@@ -2485,8 +2485,7 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists,
table->table= 0;
}
else
tdc_remove_table(thd, TDC_RT_REMOVE_ALL, table->db.str,
table->table_name.str);
tdc_remove_table(thd, table->db.str, table->table_name.str);
/* Check that we have an exclusive lock on the table to be dropped. */
DBUG_ASSERT(thd->mdl_context.is_lock_owner(MDL_key::TABLE, table->db.str,
......@@ -7753,8 +7752,7 @@ static bool mysql_inplace_alter_table(THD *thd,
thd->variables.lock_wait_timeout))
goto cleanup;
tdc_remove_table(thd, TDC_RT_REMOVE_NOT_OWN_KEEP_SHARE,
table->s->db.str, table->s->table_name.str);
table->s->tdc->flush(thd, false);
}
/*
......
......@@ -375,8 +375,7 @@ bool Sql_cmd_truncate_table::lock_table(THD *thd, TABLE_LIST *table_ref,
else
{
/* Table is already locked exclusively. Remove cached instances. */
tdc_remove_table(thd, TDC_RT_REMOVE_ALL, table_ref->db.str,
table_ref->table_name.str);
tdc_remove_table(thd, table_ref->db.str, table_ref->table_name.str);
}
DBUG_RETURN(FALSE);
......
......@@ -655,8 +655,7 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views,
*/
if (!res)
tdc_remove_table(thd, TDC_RT_REMOVE_ALL, view->db.str,
view->table_name.str);
tdc_remove_table(thd, view->db.str, view->table_name.str);
if (!res && mysql_bin_log.is_open())
{
......@@ -1876,8 +1875,7 @@ bool mysql_drop_view(THD *thd, TABLE_LIST *views, enum_drop_mode drop_mode)
For a view, there is a TABLE_SHARE object.
Remove it from the table definition cache, in case the view was cached.
*/
tdc_remove_table(thd, TDC_RT_REMOVE_ALL, view->db.str,
view->table_name.str);
tdc_remove_table(thd, view->db.str, view->table_name.str);
query_cache_invalidate3(thd, view, 0);
sp_cache_invalidate();
}
......
......@@ -997,36 +997,9 @@ void tdc_release_share(TABLE_SHARE *share)
/**
Remove all or some (depending on parameter) instances of TABLE and
TABLE_SHARE from the table definition cache.
Removes all TABLE instances and corresponding TABLE_SHARE
@param thd Thread context
@param remove_type Type of removal:
TDC_RT_REMOVE_ALL - remove all TABLE instances and
TABLE_SHARE instance. There
should be no used TABLE objects
and caller should have exclusive
metadata lock on the table.
TDC_RT_REMOVE_NOT_OWN - remove all TABLE instances
except those that belong to
this thread. There should be
no TABLE objects used by other
threads and caller should have
exclusive metadata lock on the
table.
TDC_RT_REMOVE_UNUSED - remove all unused TABLE
instances (if there are no
used instances will also
remove TABLE_SHARE).
TDC_RT_REMOVE_NOT_OWN_KEEP_SHARE -
remove all TABLE instances
except those that belong to
this thread, but don't mark
TABLE_SHARE as old. There
should be no TABLE objects
used by other threads and
caller should have exclusive
metadata lock on the table.
@param db Name of database
@param table_name Name of table
......@@ -1034,27 +1007,21 @@ void tdc_release_share(TABLE_SHARE *share)
(other) thread (this should be achieved by using meta-data locks).
*/
bool tdc_remove_table(THD *thd, enum_tdc_remove_table_type remove_type,
const char *db, const char *table_name)
void tdc_remove_table(THD *thd, const char *db, const char *table_name)
{
Share_free_tables::List purge_tables;
TABLE *table;
TDC_element *element;
uint my_refs= 1;
bool res= false;
DBUG_ENTER("tdc_remove_table");
DBUG_PRINT("enter",("name: %s remove_type: %d", table_name, remove_type));
DBUG_PRINT("enter", ("name: %s", table_name));
DBUG_ASSERT(remove_type == TDC_RT_REMOVE_UNUSED ||
thd->mdl_context.is_lock_owner(MDL_key::TABLE, db, table_name,
DBUG_ASSERT(thd->mdl_context.is_lock_owner(MDL_key::TABLE, db, table_name,
MDL_EXCLUSIVE));
mysql_mutex_lock(&LOCK_unused_shares);
if (!(element= tdc_lock_share(thd, db, table_name)))
{
mysql_mutex_unlock(&LOCK_unused_shares);
DBUG_ASSERT(remove_type != TDC_RT_REMOVE_NOT_OWN_KEEP_SHARE);
DBUG_RETURN(false);
DBUG_VOID_RETURN;
}
DBUG_ASSERT(element != MY_ERRPTR); // What can we do about it?
......@@ -1070,80 +1037,24 @@ bool tdc_remove_table(THD *thd, enum_tdc_remove_table_type remove_type,
mysql_mutex_unlock(&LOCK_unused_shares);
tdc_delete_share_from_hash(element);
DBUG_RETURN(false);
DBUG_VOID_RETURN;
}
mysql_mutex_unlock(&LOCK_unused_shares);
element->ref_count++;
if (remove_type != TDC_RT_REMOVE_NOT_OWN_KEEP_SHARE)
element->flushed= true;
tc_remove_all_unused_tables(element, &purge_tables);
if (remove_type == TDC_RT_REMOVE_NOT_OWN ||
remove_type == TDC_RT_REMOVE_NOT_OWN_KEEP_SHARE)
{
All_share_tables_list::Iterator it(element->all_tables);
while ((table= it++))
{
if (table->in_use == thd)
my_refs++;
}
}
mysql_mutex_unlock(&element->LOCK_table_share);
while ((table= purge_tables.pop_front()))
while (auto table= purge_tables.pop_front())
intern_close_table(table);
if (remove_type != TDC_RT_REMOVE_UNUSED)
{
/*
Even though current thread holds exclusive metadata lock on this share
(asserted above), concurrent FLUSH TABLES threads may be in process of
closing unused table instances belonging to this share. E.g.:
thr1 (FLUSH TABLES): table= share->tdc.free_tables.pop_front();
thr1 (FLUSH TABLES): share->tdc.all_tables.remove(table);
thr2 (ALTER TABLE): tdc_remove_table();
thr1 (FLUSH TABLES): intern_close_table(table);
Current remove type assumes that all table instances (except for those
that are owned by current thread) must be closed before
thd_remove_table() returns. Wait for such tables now.
intern_close_table() decrements ref_count and signals COND_release. When
ref_count drops down to number of references owned by current thread
waiting is completed.
Unfortunately TABLE_SHARE::wait_for_old_version() cannot be used here
because it waits for all table instances, whereas we have to wait only
for those that are not owned by current thread.
*/
mysql_mutex_lock(&element->LOCK_table_share);
while (element->ref_count > my_refs)
mysql_cond_wait(&element->COND_release, &element->LOCK_table_share);
DBUG_ASSERT(element->all_tables.is_empty() ||
remove_type != TDC_RT_REMOVE_ALL);
#ifndef DBUG_OFF
if (remove_type == TDC_RT_REMOVE_NOT_OWN ||
remove_type == TDC_RT_REMOVE_NOT_OWN_KEEP_SHARE)
{
All_share_tables_list::Iterator it(element->all_tables);
while ((table= it++))
DBUG_ASSERT(table->in_use == thd);
}
#endif
mysql_mutex_unlock(&element->LOCK_table_share);
}
else
{
mysql_mutex_lock(&element->LOCK_table_share);
res= element->ref_count > 1;
mysql_mutex_unlock(&element->LOCK_table_share);
}
tdc_release_share(element->share);
DBUG_RETURN(res);
mysql_mutex_lock(&element->LOCK_table_share);
element->wait_for_refs(1);
DBUG_ASSERT(element->all_tables.is_empty());
element->ref_count--;
tdc_delete_share_from_hash(element);
DBUG_VOID_RETURN;
}
......@@ -1284,3 +1195,94 @@ int show_tc_active_instances(THD *thd, SHOW_VAR *var, char *buff,
tc_active_instances.load(std::memory_order_relaxed);
return 0;
}
/**
Waits until ref_count goes down to given number
@param my_refs Number of references owned by the caller
Caller must own at least one TABLE_SHARE reference.
Even though current thread holds exclusive metadata lock on this share,
concurrent FLUSH TABLES threads may be in process of closing unused table
instances belonging to this share. E.g.:
thr1 (FLUSH TABLES): table= share->tdc.free_tables.pop_front();
thr1 (FLUSH TABLES): share->tdc.all_tables.remove(table);
thr2 (ALTER TABLE): tdc_remove_table();
thr1 (FLUSH TABLES): intern_close_table(table);
Current remove type assumes that all table instances (except for those
that are owned by current thread) must be closed before
thd_remove_table() returns. Wait for such tables now.
intern_close_table() decrements ref_count and signals COND_release. When
ref_count drops down to number of references owned by current thread
waiting is completed.
Unfortunately TABLE_SHARE::wait_for_old_version() cannot be used here
because it waits for all table instances, whereas we have to wait only
for those that are not owned by current thread.
*/
void TDC_element::wait_for_refs(uint my_refs)
{
while (ref_count > my_refs)
mysql_cond_wait(&COND_release, &LOCK_table_share);
}
/**
Flushes unused TABLE instances
@param thd Thread context
@param mark_flushed Whether to destroy TABLE_SHARE when released
Caller is allowed to own used TABLE instances.
There must be no TABLE objects used by other threads and caller must own
exclusive metadata lock on the table.
*/
void TDC_element::flush(THD *thd, bool mark_flushed)
{
DBUG_ASSERT(thd->mdl_context.is_lock_owner(MDL_key::TABLE, share->db.str,
share->table_name.str,
MDL_EXCLUSIVE));
flush_unused(mark_flushed);
mysql_mutex_lock(&LOCK_table_share);
All_share_tables_list::Iterator it(all_tables);
uint my_refs= 0;
while (auto table= it++)
{
if (table->in_use == thd)
my_refs++;
}
wait_for_refs(my_refs);
#ifndef DBUG_OFF
it.rewind();
while (auto table= it++)
DBUG_ASSERT(table->in_use == thd);
#endif
mysql_mutex_unlock(&LOCK_table_share);
}
/**
Flushes unused TABLE instances
*/
void TDC_element::flush_unused(bool mark_flushed)
{
Share_free_tables::List purge_tables;
mysql_mutex_lock(&LOCK_table_share);
if (mark_flushed)
flushed= true;
tc_remove_all_unused_tables(this, &purge_tables);
mysql_mutex_unlock(&LOCK_table_share);
while (auto table= purge_tables.pop_front())
intern_close_table(table);
}
......@@ -54,17 +54,13 @@ struct TDC_element
/** Avoid false sharing between TDC_element and free_tables */
char pad[CPU_LEVEL1_DCACHE_LINESIZE];
Share_free_tables free_tables[1];
};
enum enum_tdc_remove_table_type
{
TDC_RT_REMOVE_ALL,
TDC_RT_REMOVE_NOT_OWN,
TDC_RT_REMOVE_UNUSED,
TDC_RT_REMOVE_NOT_OWN_KEEP_SHARE
inline void wait_for_refs(uint my_refs);
void flush(THD *thd, bool mark_flushed);
void flush_unused(bool mark_flushed);
};
extern ulong tdc_size;
extern ulong tc_size;
extern uint32 tc_instances;
......@@ -81,8 +77,7 @@ int tdc_share_is_cached(THD *thd, const char *db, const char *table_name);
extern TABLE_SHARE *tdc_acquire_share(THD *thd, TABLE_LIST *tl, uint flags,
TABLE **out_table= 0);
extern void tdc_release_share(TABLE_SHARE *share);
extern bool tdc_remove_table(THD *thd, enum_tdc_remove_table_type remove_type,
const char *db, const char *table_name);
void tdc_remove_table(THD *thd, const char *db, const char *table_name);
extern int tdc_wait_for_old_version(THD *thd, const char *db,
const char *table_name,
......
......@@ -24,7 +24,7 @@
#include "sql_priv.h"
#include "sql_plugin.h"
#include "ha_heap.h"
#include "sql_base.h" // enum_tdc_remove_table_type
#include "sql_base.h"
static handler *heap_create_handler(handlerton *hton,
TABLE_SHARE *table,
......
......@@ -67,7 +67,7 @@ static int MDL_and_TDC(THD *thd,
table);
return error;
}
tdc_remove_table(thd, TDC_RT_REMOVE_ALL, db, table);
tdc_remove_table(thd, db, table);
return error;
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment