1
0
mirror of https://github.com/MariaDB/server.git synced 2025-07-30 16:24:05 +03:00

WL#3984 (Revise locking of mysql.general_log and mysql.slow_log)

Bug#25422 (Hang with log tables)
Bug 17876 (Truncating mysql.slow_log in a SP after using cursor locks the
          thread)
Bug 23044 (Warnings on flush of a log table)
Bug 29129 (Resetting general_log while the GLOBAL READ LOCK is set causes
           a deadlock)

Prior to this fix, the server would hang when performing concurrent
ALTER TABLE or TRUNCATE TABLE statements against the LOG TABLES,
which are mysql.general_log and mysql.slow_log.

The root cause traces to the following code:
in sql_base.cc, open_table()
  if (table->in_use != thd)
  {
    /* wait_for_condition will unlock LOCK_open for us */
    wait_for_condition(thd, &LOCK_open, &COND_refresh);
  }
The problem with this code is that the current implementation of the
LOGGER creates 'fake' THD objects, like
- Log_to_csv_event_handler::general_log_thd
- Log_to_csv_event_handler::slow_log_thd
which are not associated to a real thread running in the server,
so that waiting for these non-existing threads to release table locks
cause the dead lock.

In general, the design of Log_to_csv_event_handler does not fit into the
general architecture of the server, so that the concept of general_log_thd
and slow_log_thd has to be abandoned:
- this implementation does not work with table locking
- it will not work with commands like SHOW PROCESSLIST
- having the log tables always opened does not integrate well with DDL
operations / FLUSH TABLES / SET GLOBAL READ_ONLY

With this patch, the fundamental design of the LOGGER has been changed to:
- always open and close a log table when writing a log
- remove totally the usage of fake THD objects
- clarify how locking of log tables is implemented in general.

See WL#3984 for details related to the new locking design.

Additional changes (misc bugs exposed and fixed):

1)

mysqldump which would ignore some tables in dump_all_tables_in_db(),
 but forget to ignore the same in dump_all_views_in_db().

2)

mysqldump would also issue an empty "LOCK TABLE" command when all the tables
to lock are to be ignored (numrows == 0), instead of not issuing the query.

3)

Internal errors handlers could intercept errors but not warnings
(see sql_error.cc).

4)

Implementing a nested call to open tables, for the performance schema tables,
exposed an existing bug in remove_table_from_cache(), which would perform:
  in_use->some_tables_deleted=1;
against another thread, without any consideration about thread locking.
This call inside remove_table_from_cache() was not required anyway,
since calling mysql_lock_abort() takes care of aborting -- cleanly -- threads
that might hold a lock on a table.
This line (in_use->some_tables_deleted=1) has been removed.
This commit is contained in:
malff/marcsql@weblab.(none)
2007-07-27 00:31:06 -06:00
parent cbd6e56ffa
commit c7bbd8917c
39 changed files with 1490 additions and 993 deletions

View File

@ -21,6 +21,18 @@
#include <m_ctype.h>
#include "md5.h"
/* INFORMATION_SCHEMA name */
LEX_STRING INFORMATION_SCHEMA_NAME= {C_STRING_WITH_LEN("information_schema")};
/* MYSQL_SCHEMA name */
LEX_STRING MYSQL_SCHEMA_NAME= {C_STRING_WITH_LEN("mysql")};
/* GENERAL_LOG name */
LEX_STRING GENERAL_LOG_NAME= {C_STRING_WITH_LEN("general_log")};
/* SLOW_LOG name */
LEX_STRING SLOW_LOG_NAME= {C_STRING_WITH_LEN("slow_log")};
/* Functions defined in this file */
void open_table_error(TABLE_SHARE *share, int error, int db_errno,
@ -31,6 +43,7 @@ static void fix_type_pointers(const char ***array, TYPELIB *point_to_type,
uint types, char **names);
static uint find_field(Field **fields, uchar *record, uint start, uint length);
inline bool is_system_table_name(const char *name, uint length);
/**************************************************************************
Object_creation_ctx implementation.
@ -192,6 +205,49 @@ char *fn_rext(char *name)
return name + strlen(name);
}
TABLE_CATEGORY get_table_category(const LEX_STRING *db, const LEX_STRING *name)
{
DBUG_ASSERT(db != NULL);
DBUG_ASSERT(name != NULL);
if ((db->length == INFORMATION_SCHEMA_NAME.length) &&
(my_strcasecmp(system_charset_info,
INFORMATION_SCHEMA_NAME.str,
db->str) == 0))
{
return TABLE_CATEGORY_INFORMATION;
}
if ((db->length == MYSQL_SCHEMA_NAME.length) &&
(my_strcasecmp(system_charset_info,
MYSQL_SCHEMA_NAME.str,
db->str) == 0))
{
if (is_system_table_name(name->str, name->length))
{
return TABLE_CATEGORY_SYSTEM;
}
if ((name->length == GENERAL_LOG_NAME.length) &&
(my_strcasecmp(system_charset_info,
GENERAL_LOG_NAME.str,
name->str) == 0))
{
return TABLE_CATEGORY_PERFORMANCE;
}
if ((name->length == SLOW_LOG_NAME.length) &&
(my_strcasecmp(system_charset_info,
SLOW_LOG_NAME.str,
name->str) == 0))
{
return TABLE_CATEGORY_PERFORMANCE;
}
}
return TABLE_CATEGORY_USER;
}
/*
Allocate a setup TABLE_SHARE structure
@ -297,7 +353,8 @@ void init_tmp_table_share(TABLE_SHARE *share, const char *key,
bzero((char*) share, sizeof(*share));
init_sql_alloc(&share->mem_root, TABLE_ALLOC_BLOCK_SIZE, 0);
share->tmp_table= INTERNAL_TMP_TABLE;
share->table_category= TABLE_CATEGORY_TEMPORARY;
share->tmp_table= INTERNAL_TMP_TABLE;
share->db.str= (char*) key;
share->db.length= strlen(key);
share->table_cache_key.str= (char*) key;
@ -544,28 +601,11 @@ int open_table_def(THD *thd, TABLE_SHARE *share, uint db_flags)
*root_ptr= &share->mem_root;
error= open_binary_frm(thd, share, head, file);
*root_ptr= old_root;
if (share->db.length == 5 && !(lower_case_table_names ?
my_strcasecmp(system_charset_info, share->db.str, "mysql") :
strcmp(share->db.str, "mysql")))
{
/*
We can't mark all tables in 'mysql' database as system since we don't
allow to lock such tables for writing with any other tables (even with
other system tables) and some privilege tables need this.
*/
share->system_table= is_system_table_name(share->table_name.str,
share->table_name.length);
if (!share->system_table)
{
share->log_table= check_if_log_table(share->db.length, share->db.str,
share->table_name.length,
share->table_name.str, 0);
}
}
error_given= 1;
}
share->table_category= get_table_category(& share->db, & share->table_name);
if (!error)
thd->status_var.opened_shares++;