List:Commits« Previous MessageNext Message »
From:Konstantin Osipov Date:June 8 2010 1:02pm
Subject:bzr commit into mysql-trunk-runtime branch (kostja:3047) WL#5419
View as plain text  
#At file:///opt/local/work/trunk-runtime/ based on revid:kostja@stripped

 3047 Konstantin Osipov	2010-06-08
      Implement WL#5419 "LOCK_open scalability: make tdc_refresh_version 
      an atomic counter".
      
      refresh_version is a global version of the table definition
      cache. At start of the server it is set to 1, and
      is incremented on each SIGHUP or FLUSH TABLES/FLUSH TABLES
      WITH READ LOCK statement. 
      Each TABLE_SHARE (the cacheable element of the TDC)
      has a local copy of the global refresh_version,
      assigned when the share is added to the table definition cache.
      A mismatch between TABLE_SHARE::version and refresh_version
      is an indication that the table share is obsolete,
      and needs to be flushed from the cache. Whichever
      frontend connection is the last to be working with the 
      "obsolete" share performs the removal from the cache.
      
      To ensure the synchronized access to the value of
      refresh_version, MySQL used LOCK_open mutex.
      With this patch, refresh_version becomes an atomic
      counter and can be accessed without a mutex.
     @ libmysqld/lib_sql.cc
        thd->version is set in THD constructor, remove
        code duplication.
     @ sql/event_scheduler.cc
        thd->version is set in THD constructor, remove
        code duplication.
     @ sql/ha_ndbcluster.cc
        thd->version is set in THD constructor, remove
        code duplication.
     @ sql/ha_ndbcluster_binlog.cc
        thd->version is set in THD constructor, remove
        code duplication.
        Use a method to access TABLE_SHARE::version.
     @ sql/lock.cc
        Use an atomic API to access the table definition cache
        version.
     @ sql/mysqld.cc
        Implement an atomic counter for tdc_refresh_version.
     @ sql/mysqld.h
        Implement an atomic counter for tdc_refresh_version.
     @ sql/slave.cc
        Remove an artifact of copy-paste programming.
     @ sql/sql_base.cc
        Since the table share is now an atomic counter, set
        and read outside LOCK_open, we may no longer rely on the
        frontend threads to close all old shares after 
        the flush thread incremented refresh_version.
        It may so happen that the frontend thread reads the
        version value before it is incremented, while 
        puts back the table to the table cache after it.
        Make it the responsibility of the flush thread to
        free all unused shares, even after incrementing 
        refresh_version.
        
        Improve style of get_table_share_with_create().
        
        Now that we no longer need to protect refresh_version
        with LOCK_open, we can split the large critical
        section in open_table() into pieces, and not call
        create_table_from_share() under LOCK_open.
        
        Move acquisition of LOCK_open inside check_if_table_exists().
        
        QQ: should we set share->version to 0 under LOCK_open?
     @ sql/sql_base.h
        Changed signature of close_thread_table().
     @ sql/sql_class.cc
        Use an atomic API to access refresh_version.
     @ sql/sql_class.h
        Make the type of thd->version match refresh_version and
        TABLE_SHARE::version.
     @ sql/sql_connect.cc
        Remove bad copy-paste.
     @ sql/sql_handler.cc
        Use a new API to close_thread_table().
     @ sql/sql_insert.cc
        Remove dead code.
     @ sql/sql_manager.cc
        Rename flush_tables() to tdc_flush_unused_tables().
     @ sql/sql_parse.cc
        Remove dead code.
        Access refresh_version through an API.
     @ sql/sql_plugin.cc
        Moved acquisition of LOCK_open inside check_if_table_exists().
     @ sql/sql_table.cc
        Style, use the new signature of close_thread_table().
     @ sql/sql_test.cc
        Use an API to access refresh_version.
     @ sql/table.cc
        Use an API to access refresh_version.
     @ sql/table.h
        Move needs_reopen() inside table.cc.

    modified:
      libmysqld/lib_sql.cc
      sql/event_scheduler.cc
      sql/ha_ndbcluster.cc
      sql/ha_ndbcluster_binlog.cc
      sql/lock.cc
      sql/mysqld.cc
      sql/mysqld.h
      sql/slave.cc
      sql/sql_base.cc
      sql/sql_base.h
      sql/sql_class.cc
      sql/sql_class.h
      sql/sql_connect.cc
      sql/sql_handler.cc
      sql/sql_insert.cc
      sql/sql_manager.cc
      sql/sql_parse.cc
      sql/sql_plugin.cc
      sql/sql_table.cc
      sql/sql_test.cc
      sql/table.cc
      sql/table.h
=== modified file 'libmysqld/lib_sql.cc'
--- a/libmysqld/lib_sql.cc	2010-06-02 12:23:50 +0000
+++ b/libmysqld/lib_sql.cc	2010-06-08 13:02:08 +0000
@@ -634,7 +634,6 @@ void *create_embedded_thd(int client_fla
     thd->variables.option_bits |= OPTION_BIG_SELECTS;
   thd->proc_info=0;				// Remove 'login'
   thd->command=COM_SLEEP;
-  thd->version=refresh_version;
   thd->set_time();
   thd->init_for_queries();
   thd->client_capabilities= client_flag;

=== modified file 'sql/event_scheduler.cc'
--- a/sql/event_scheduler.cc	2010-04-20 08:51:50 +0000
+++ b/sql/event_scheduler.cc	2010-06-08 13:02:08 +0000
@@ -203,7 +203,6 @@ pre_init_event_thread(THD* thd)
   */
 
   thd->proc_info= "Initialized";
-  thd->version= refresh_version;
   thd->set_time();
 
   /* Do not use user-supplied timeout value for system threads. */

=== modified file 'sql/ha_ndbcluster.cc'
--- a/sql/ha_ndbcluster.cc	2010-06-06 11:19:29 +0000
+++ b/sql/ha_ndbcluster.cc	2010-06-08 13:02:08 +0000
@@ -9510,7 +9510,6 @@ pthread_handler_t ndb_util_thread_func(v
   if (thd->store_globals())
     goto ndb_util_thread_fail;
   thd->init_for_queries();
-  thd->version=refresh_version;
   thd->main_security_ctx.host_or_ip= "";
   thd->client_capabilities = 0;
   my_net_init(&thd->net, 0);

=== modified file 'sql/ha_ndbcluster_binlog.cc'
--- a/sql/ha_ndbcluster_binlog.cc	2010-05-17 12:10:26 +0000
+++ b/sql/ha_ndbcluster_binlog.cc	2010-06-08 13:02:08 +0000
@@ -3678,7 +3678,6 @@ pthread_handler_t ndb_binlog_thread_func
   thd->init_for_queries();
   thd->command= COM_DAEMON;
   thd->system_thread= SYSTEM_THREAD_NDBCLUSTER_BINLOG;
-  thd->version= refresh_version;
   thd->main_security_ctx.host_or_ip= "";
   thd->client_capabilities= 0;
   my_net_init(&thd->net, 0);
@@ -3965,9 +3964,9 @@ restart:
          !ndb_binlog_running))
       break; /* Shutting down server */
 
-    if (ndb_binlog_index && ndb_binlog_index->s->version < refresh_version)
+    if (ndb_binlog_index && ndb_binlog_index->s->needs_reopen())
     {
-      if (ndb_binlog_index->s->version < refresh_version)
+      if (ndb_binlog_index->s->needs_reopen())
       {
         close_thread_tables(thd);
         ndb_binlog_index= 0;

=== modified file 'sql/lock.cc'
--- a/sql/lock.cc	2010-06-06 11:19:29 +0000
+++ b/sql/lock.cc	2010-06-08 13:02:08 +0000
@@ -1298,7 +1298,7 @@ wait_if_global_read_lock(THD *thd, bool 
     old_message=thd->enter_cond(&COND_global_read_lock, &LOCK_global_read_lock,
 				"Waiting for release of readlock");
     while (must_wait && ! thd->killed &&
-	   (!abort_on_refresh || thd->version == refresh_version))
+	   (!abort_on_refresh || thd->version == get_tdc_refresh_version()))
     {
       DBUG_PRINT("signal", ("Waiting for COND_global_read_lock"));
       mysql_cond_wait(&COND_global_read_lock, &LOCK_global_read_lock);

=== modified file 'sql/mysqld.cc'
--- a/sql/mysqld.cc	2010-05-31 15:29:54 +0000
+++ b/sql/mysqld.cc	2010-06-08 13:02:08 +0000
@@ -509,10 +509,11 @@ ulong thread_cache_size=0, thread_pool_s
 ulong binlog_cache_size=0;
 ulonglong  max_binlog_cache_size=0;
 ulong query_cache_size=0;
-ulong refresh_version;  /* Increments on each reload */
+int32 tdc_refresh_version;  /* Increments on each FLUSH TABLES. */
 query_id_t global_query_id;
 my_atomic_rwlock_t global_query_id_lock;
 my_atomic_rwlock_t thread_running_lock;
+my_atomic_rwlock_t tdc_refresh_version_lock;
 ulong aborted_threads, aborted_connects;
 ulong delayed_insert_timeout, delayed_insert_limit, delayed_queue_size;
 ulong delayed_insert_threads, delayed_insert_writes, delayed_rows_in_use;
@@ -1555,6 +1556,7 @@ void clean_up(bool print_message)
   logger.cleanup_end();
   my_atomic_rwlock_destroy(&global_query_id_lock);
   my_atomic_rwlock_destroy(&thread_running_lock);
+  my_atomic_rwlock_destroy(&tdc_refresh_version_lock);
   mysql_mutex_lock(&LOCK_thread_count);
   DBUG_PRINT("quit", ("got thread count lock"));
   ready_to_exit=1;
@@ -6416,6 +6418,16 @@ static int show_open_tables(THD *thd, SH
   return 0;
 }
 
+
+static int show_tdc_refresh_version(THD *thd, SHOW_VAR *var, char *buff)
+{
+  var->type= SHOW_LONG;
+  var->value= buff;
+  *((long *)buff)= (long)get_tdc_refresh_version();
+  return 0;
+}
+
+
 static int show_prepared_stmt_count(THD *thd, SHOW_VAR *var, char *buff)
 {
   var->type= SHOW_LONG;
@@ -6713,7 +6725,7 @@ SHOW_VAR status_vars[]= {
   {"Delayed_errors",           (char*) &delayed_insert_errors,  SHOW_LONG},
   {"Delayed_insert_threads",   (char*) &delayed_insert_threads, SHOW_LONG_NOFLUSH},
   {"Delayed_writes",           (char*) &delayed_insert_writes,  SHOW_LONG},
-  {"Flush_commands",           (char*) &refresh_version,        SHOW_LONG_NOFLUSH},
+  {"Flush_commands",           (char*) &show_tdc_refresh_version, SHOW_FUNC},
   {"Handler_commit",           (char*) offsetof(STATUS_VAR, ha_commit_count), SHOW_LONG_STATUS},
   {"Handler_delete",           (char*) offsetof(STATUS_VAR, ha_delete_count), SHOW_LONG_STATUS},
   {"Handler_discover",         (char*) offsetof(STATUS_VAR, ha_discover_count), SHOW_LONG_STATUS},
@@ -7014,10 +7026,11 @@ static int mysql_init_variables(void)
   lc_messages_dir_ptr= lc_messages_dir;
   protocol_version= PROTOCOL_VERSION;
   what_to_log= ~ (1L << (uint) COM_TIME);
-  refresh_version= 1L;	/* Increments on each reload */
+  tdc_refresh_version= 1;   /* Increments on each FLUSH TABLES or SIGHUP. */
   global_query_id= thread_id= 1L;
   my_atomic_rwlock_init(&global_query_id_lock);
   my_atomic_rwlock_init(&thread_running_lock);
+  my_atomic_rwlock_init(&tdc_refresh_version_lock);
   strmov(server_version, MYSQL_SERVER_VERSION);
   threads.empty();
   thread_cache.empty();

=== modified file 'sql/mysqld.h'
--- a/sql/mysqld.h	2010-05-28 05:47:58 +0000
+++ b/sql/mysqld.h	2010-06-08 13:02:08 +0000
@@ -342,7 +342,10 @@ extern mysql_cond_t COND_thread_count;
 extern mysql_cond_t COND_refresh, COND_manager;
 extern mysql_cond_t COND_global_read_lock;
 extern int32 thread_running;
+extern int32 tdc_refresh_version;
 extern my_atomic_rwlock_t thread_running_lock;
+extern my_atomic_rwlock_t tdc_refresh_version_lock;
+
 
 extern char *opt_ssl_ca, *opt_ssl_capath, *opt_ssl_cert, *opt_ssl_cipher,
             *opt_ssl_key;
@@ -497,6 +500,24 @@ get_thread_running()
   return num_thread_running;
 }
 
+inline void inc_tdc_refresh_version()
+{
+  my_atomic_rwlock_wrlock(&tdc_refresh_version_lock);
+  my_atomic_add32(&tdc_refresh_version, 1);
+  my_atomic_rwlock_wrunlock(&tdc_refresh_version_lock);
+}
+
+
+inline uint32 get_tdc_refresh_version()
+{
+  uint32 ver;
+  my_atomic_rwlock_wrlock(&tdc_refresh_version_lock);
+  ver= (uint32) my_atomic_load32(&tdc_refresh_version);
+  my_atomic_rwlock_wrunlock(&tdc_refresh_version_lock);
+  return ver;
+}
+
+
 #if defined(MYSQL_DYNAMIC_PLUGIN) && defined(_WIN32)
 extern "C" THD *_current_thd_noinline();
 #define _current_thd() _current_thd_noinline()

=== modified file 'sql/slave.cc'
--- a/sql/slave.cc	2010-05-20 12:35:28 +0000
+++ b/sql/slave.cc	2010-06-08 13:02:08 +0000
@@ -2055,7 +2055,6 @@ static int init_slave_thread(THD* thd, S
     thd_proc_info(thd, "Waiting for the next event in relay log");
   else
     thd_proc_info(thd, "Waiting for master update");
-  thd->version=refresh_version;
   thd->set_time();
   /* Do not use user-supplied timeout value for system threads. */
   thd->variables.lock_wait_timeout= LONG_TIMEOUT;

=== modified file 'sql/sql_base.cc'
--- a/sql/sql_base.cc	2010-06-08 08:08:46 +0000
+++ b/sql/sql_base.cc	2010-06-08 13:02:08 +0000
@@ -559,7 +559,9 @@ found:
     DBUG_RETURN(0);
   }
 
-  if (!share->ref_count++ && share->prev)
+  ++share->ref_count;
+
+  if (share->ref_count == 1 && share->prev)
   {
     /*
       Share was not used before and it was in the old_unused_share list
@@ -690,15 +692,14 @@ void release_table_share(TABLE_SHARE *sh
   DBUG_PRINT("enter",
              ("share: 0x%lx  table: %s.%s  ref_count: %u  version: %lu",
               (ulong) share, share->db.str, share->table_name.str,
-              share->ref_count, share->version));
+              share->ref_count, (ulong) share->version));
 
   mysql_mutex_assert_owner(&LOCK_open);
 
   DBUG_ASSERT(share->ref_count);
   if (!--share->ref_count)
   {
-    if (share->version != refresh_version ||
-        table_def_shutdown_in_progress)
+    if (share->needs_reopen() || table_def_shutdown_in_progress)
       my_hash_delete(&table_def_cache, (uchar*) share);
     else
     {
@@ -833,7 +834,7 @@ OPEN_TABLE_LIST *list_open_tables(THD *t
     I_P_List_iterator<TABLE, TABLE_share> it(share->used_tables);
     while (it++)
       ++(*start_list)->in_use;
-    (*start_list)->locked= (share->version == 0) ? 1 : 0;
+    (*start_list)->locked= 0;                   /* Obsolete. */
     start_list= &(*start_list)->next;
     *start_list=0;
   }
@@ -968,9 +969,9 @@ bool close_cached_tables(THD *thd, TABLE
     mysql_mutex_lock(&LOCK_open);
   if (!tables)
   {
-    refresh_version++;				// Force close of open tables
+    inc_tdc_refresh_version();                  // Force close of open tables
     DBUG_PRINT("tcache", ("incremented global refresh_version to: %lu",
-                          refresh_version));
+                          (ulong) get_tdc_refresh_version()));
     kill_delayed_threads();
     /*
       Get rid of all unused TABLE and TABLE_SHARE instances. By doing
@@ -1064,8 +1065,21 @@ bool close_cached_tables(THD *thd, TABLE
       {
         TABLE_SHARE *share=(TABLE_SHARE*) my_hash_element(&table_def_cache,
                                                           idx);
-        if (share->version != refresh_version)
+        if (share->version != get_tdc_refresh_version())
         {
+          /*
+            Since close_thread_table() checks for refresh_version
+            outside LOCK_open a race is possible when it doesn't
+            remove the TABLE and/or TABLE_SHARE even though 
+            a FLUSH got started in parallel.
+          */
+          if (share->ref_count)
+          {
+            I_P_List_iterator<TABLE, TABLE_share> it(share->free_tables);
+            TABLE *table;
+            while ((table= it++))
+              free_cache_entry(table);
+          }
           found= TRUE;
           break;
         }
@@ -1076,7 +1090,7 @@ bool close_cached_tables(THD *thd, TABLE
       for (TABLE_LIST *table= tables; table; table= table->next_local)
       {
         TABLE_SHARE *share= get_cached_table_share(table->db, table->table_name);
-        if (share && share->version != refresh_version)
+        if (share && share->version != get_tdc_refresh_version())
         {
 	  found= TRUE;
           break;
@@ -1291,13 +1305,13 @@ static void mark_used_tables_as_free_for
 static void close_open_tables(THD *thd)
 {
   bool found_old_table= 0;
-
-  mysql_mutex_assert_not_owner(&LOCK_open);
+  uint32 tdc_rv= get_tdc_refresh_version();
 
   DBUG_PRINT("info", ("thd->open_tables: 0x%lx", (long) thd->open_tables));
 
   while (thd->open_tables)
-    found_old_table|= close_thread_table(thd, &thd->open_tables);
+    found_old_table|= close_thread_table(thd, &thd->open_tables,
+                                         tdc_rv);
 
   if (found_old_table)
   {
@@ -1331,6 +1345,7 @@ close_all_tables_for_name(THD *thd, TABL
 {
   char key[MAX_DBKEY_LENGTH];
   uint key_length= share->table_cache_key.length;
+  uint32 tdc_rv= get_tdc_refresh_version();
 
   memcpy(key, share->table_cache_key.str, key_length);
 
@@ -1357,7 +1372,7 @@ close_all_tables_for_name(THD *thd, TABL
       /* Inform handler that table will be dropped after close */
       if (table->db_stat) /* Not true for partitioned tables. */
         table->file->extra(HA_EXTRA_PREPARE_FOR_DROP);
-      close_thread_table(thd, prev);
+      close_thread_table(thd, prev, tdc_rv);
     }
     else
     {
@@ -1558,9 +1573,9 @@ void close_thread_tables(THD *thd)
 
 /* move one table to free list */
 
-bool close_thread_table(THD *thd, TABLE **table_ptr)
+bool close_thread_table(THD *thd, TABLE **table_ptr, uint32 tdc_rv)
 {
-  bool found_old_table= 0;
+  bool found_old_table= FALSE;
   TABLE *table= *table_ptr;
   DBUG_ENTER("close_thread_table");
   DBUG_ASSERT(table->key_read == 0);
@@ -1570,17 +1585,20 @@ bool close_thread_table(THD *thd, TABLE 
   table->mdl_ticket= NULL;
 
   mysql_mutex_lock(&thd->LOCK_thd_data);
-  *table_ptr=table->next;
+  *table_ptr= table->next;
   mysql_mutex_unlock(&thd->LOCK_thd_data);
-
-  mysql_mutex_lock(&LOCK_open);
-
-  if (table->s->needs_reopen() ||
-      thd->version != refresh_version || table->needs_reopen() ||
-      table_def_shutdown_in_progress)
+  /*
+    We don't have to check for table_def_shutdown_in_progress
+    under mutex because there is a guarantee of at least one
+    memory barrier between since after setting.
+  */
+  if (table->s->version != tdc_rv || thd->version != tdc_rv ||
+      table->needs_reopen() || table_def_shutdown_in_progress)
   {
+    mysql_mutex_lock(&LOCK_open);
     free_cache_entry(table);
-    found_old_table= 1;
+    mysql_mutex_unlock(&LOCK_open);
+    found_old_table= TRUE;
   }
   else
   {
@@ -1592,6 +1610,7 @@ bool close_thread_table(THD *thd, TABLE 
     free_field_buffers_larger_than(table,MAX_TDC_BLOB_SIZE);
 
     table->file->ha_reset();
+    mysql_mutex_lock(&LOCK_open);
     table_def_unuse_table(table);
     /*
       We free the least used table, not the subject table,
@@ -1599,8 +1618,8 @@ bool close_thread_table(THD *thd, TABLE 
     */
     if (table_cache_count > table_cache_size)
       free_cache_entry(unused_tables);
+    mysql_mutex_unlock(&LOCK_open);
   }
-  mysql_mutex_unlock(&LOCK_open);
   DBUG_RETURN(found_old_table);
 }
 
@@ -2175,7 +2194,7 @@ bool wait_while_table_is_used(THD *thd, 
   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->version));
+                       table->db_stat, (ulong) table->s->version));
 
   if (thd->mdl_context.upgrade_shared_lock_to_exclusive(
              table->mdl_ticket, thd->variables.lock_wait_timeout))
@@ -2225,7 +2244,7 @@ void drop_open_table(THD *thd, TABLE *ta
     table->s->version= 0;
 
     table->file->extra(HA_EXTRA_PREPARE_FOR_DROP);
-    close_thread_table(thd, &thd->open_tables);
+    close_thread_table(thd, &thd->open_tables, get_tdc_refresh_version());
     quick_rm_table(table_type, db_name, table_name, 0);
   }
   DBUG_VOID_RETURN;
@@ -2301,21 +2320,23 @@ void wait_for_condition(THD *thd, mysql_
 bool check_if_table_exists(THD *thd, TABLE_LIST *table, bool *exists)
 {
   char path[FN_REFLEN + 1];
-  int rc;
+  int rc= 0;
   DBUG_ENTER("check_if_table_exists");
 
-  mysql_mutex_assert_owner(&LOCK_open);
+  mysql_mutex_assert_not_owner(&LOCK_open);
 
   *exists= TRUE;
 
+  mysql_mutex_lock(&LOCK_open);
+
   if (get_cached_table_share(table->db, table->table_name))
-    DBUG_RETURN(FALSE);
+    goto end;
 
   build_table_filename(path, sizeof(path) - 1, table->db, table->table_name,
                        reg_ext, 0);
 
   if (!access(path, F_OK))
-    DBUG_RETURN(FALSE);
+    goto end;
 
   /* .FRM file doesn't exist. Check if some engine can provide it. */
 
@@ -2325,19 +2346,17 @@ bool check_if_table_exists(THD *thd, TAB
   {
     /* Table does not exists in engines as well. */
     *exists= FALSE;
-    DBUG_RETURN(FALSE);
-  }
-  else if (!rc)
-  {
-    /* Table exists in some engine and .FRM for it was created. */
-    DBUG_RETURN(FALSE);
+    rc= 0;
   }
-  else /* (rc > 0) */
+  else if (rc)
   {
     my_printf_error(ER_UNKNOWN_ERROR, "Failed to open '%-.64s', error while "
                     "unpacking from engine", MYF(0), table->table_name);
-    DBUG_RETURN(TRUE);
   }
+
+end:
+  mysql_mutex_unlock(&LOCK_open);
+  DBUG_RETURN(test(rc));
 }
 
 
@@ -2632,7 +2651,7 @@ bool open_table(THD *thd, TABLE_LIST *ta
     if (thd->global_read_lock.wait_if_global_read_lock(thd, 1, 1))
       DBUG_RETURN(TRUE);
 
-    if (thd->version != refresh_version)
+    if (thd->version != get_tdc_refresh_version())
     {
       (void) ot_ctx->request_backoff_action(Open_table_context::OT_WAIT_TDC,
                                             NULL);
@@ -2829,7 +2848,6 @@ bool open_table(THD *thd, TABLE_LIST *ta
   }
 
   hash_value= my_calc_hash(&table_def_cache, (uchar*) key, key_length);
-  mysql_mutex_lock(&LOCK_open);
 
   /*
     If it's the first table from a list of tables used in a query,
@@ -2840,37 +2858,24 @@ bool open_table(THD *thd, TABLE_LIST *ta
     Note: refresh_version is currently changed only during FLUSH TABLES.
   */
   if (!thd->open_tables)
-    thd->version=refresh_version;
-  else if ((thd->version != refresh_version) &&
-           ! (flags & MYSQL_OPEN_IGNORE_FLUSH))
-  {
-    /* Someone did a refresh while thread was opening tables */
-    mysql_mutex_unlock(&LOCK_open);
-    (void) ot_ctx->request_backoff_action(Open_table_context::OT_WAIT_TDC,
-                                          NULL);
-    DBUG_RETURN(TRUE);
-  }
+    thd->version= get_tdc_refresh_version();
 
   if (table_list->open_strategy == TABLE_LIST::OPEN_IF_EXISTS)
   {
     bool exists;
 
     if (check_if_table_exists(thd, table_list, &exists))
-      goto err_unlock2;
+      DBUG_RETURN(TRUE);
 
     if (!exists)
-    {
-      mysql_mutex_unlock(&LOCK_open);
       DBUG_RETURN(FALSE);
-    }
+
     /* Table exists. Let us try to open it. */
   }
   else if (table_list->open_strategy == TABLE_LIST::OPEN_STUB)
-  {
-    mysql_mutex_unlock(&LOCK_open);
     DBUG_RETURN(FALSE);
-  }
 
+  mysql_mutex_lock(&LOCK_open);
 #ifdef DISABLED_UNTIL_GRL_IS_MADE_PART_OF_MDL
   if (!(share= (TABLE_SHARE *) mdl_ticket->get_cached_object()))
 #endif
@@ -2892,7 +2897,7 @@ bool open_table(THD *thd, TABLE_LIST *ta
         my_error(ER_WRONG_MRG_TABLE, MYF(0));
         goto err_unlock;
       }
-      
+
       /*
         This table is a view. Validate its metadata version: in particular,
         that it was a view when the statement was prepared.
@@ -2961,7 +2966,7 @@ bool open_table(THD *thd, TABLE_LIST *ta
   }
 #endif
 
-  if (share->version != refresh_version)
+  if (share->version != thd->version)
   {
     if (!(flags & MYSQL_OPEN_IGNORE_FLUSH))
     {
@@ -2998,9 +3003,11 @@ bool open_table(THD *thd, TABLE_LIST *ta
     while (table_cache_count > table_cache_size && unused_tables)
       free_cache_entry(unused_tables);
 
+    mysql_mutex_unlock(&LOCK_open);
+
     /* make a new table */
     if (!(table=(TABLE*) my_malloc(sizeof(*table),MYF(MY_WME))))
-      goto err_unlock;
+      goto err_lock;
 
     error= open_table_from_share(thd, share, alias,
                                  (uint) (HA_OPEN_KEYFILE |
@@ -3015,29 +3022,31 @@ bool open_table(THD *thd, TABLE_LIST *ta
     {
       my_free(table, MYF(0));
 
-      if (error == 7)
+      if (error == 7 || share->crashed)
       {
+        mysql_mutex_lock(&LOCK_open);
         share->version= 0;
+        mysql_mutex_unlock(&LOCK_open);
+      }
+
+      if (error == 7)
         (void) ot_ctx->request_backoff_action(Open_table_context::OT_DISCOVER,
                                               table_list);
-      }
-      else if (share->crashed)
-      {
-        share->version= 0;
+     else if (share->crashed)
         (void) ot_ctx->request_backoff_action(Open_table_context::OT_REPAIR,
                                               table_list);
-      }
 
-      goto err_unlock;
+      goto err_lock;
     }
 
     if (open_table_entry_fini(thd, share, table))
     {
       closefrm(table, 0);
       my_free((uchar*)table, MYF(0));
-      goto err_unlock;
+      goto err_lock;
     }
 
+    mysql_mutex_lock(&LOCK_open);
     /* Add table to the share's used tables list. */
     table_def_add_used_table(thd, table);
   }
@@ -3099,6 +3108,8 @@ bool open_table(THD *thd, TABLE_LIST *ta
     table->file->extra(HA_EXTRA_DETACH_CHILDREN);
   DBUG_RETURN(FALSE);
 
+err_lock:
+  mysql_mutex_lock(&LOCK_open);
 err_unlock:
   release_table_share(share);
 err_unlock2:
@@ -3386,6 +3397,7 @@ void Locked_tables_list::unlink_from_lis
 void Locked_tables_list::
 unlink_all_closed_tables(THD *thd, MYSQL_LOCK *lock, size_t reopen_count)
 {
+  uint32 tdc_rv= get_tdc_refresh_version();
   /* If we managed to take a lock, unlock tables and free the lock. */
   if (lock)
     mysql_unlock_tables(thd, lock);
@@ -3409,7 +3421,7 @@ unlink_all_closed_tables(THD *thd, MYSQL
 
       thd->open_tables->pos_in_locked_tables->table= NULL;
 
-      close_thread_table(thd, &thd->open_tables);
+      close_thread_table(thd, &thd->open_tables, tdc_rv);
     }
     broadcast_refresh();
   }
@@ -8595,7 +8607,7 @@ my_bool mysql_rm_tmp_tables(void)
     all not used tables.
 */
 
-void flush_tables()
+void tdc_flush_unused_tables()
 {
   mysql_mutex_lock(&LOCK_open);
   while (unused_tables)
@@ -8807,7 +8819,7 @@ tdc_wait_for_old_versions(THD *thd, MDL_
 
       if ((share= get_cached_table_share(mdl_request->key.db_name(),
                                          mdl_request->key.name())) &&
-          share->version != refresh_version)
+          share->version != get_tdc_refresh_version())
         break;
     }
     if (!mdl_request)

=== modified file 'sql/sql_base.h'
--- a/sql/sql_base.h	2010-06-07 07:06:55 +0000
+++ b/sql/sql_base.h	2010-06-08 13:02:08 +0000
@@ -221,7 +221,7 @@ int abort_and_upgrade_lock_and_close_tab
 int decide_logging_format(THD *thd, TABLE_LIST *tables);
 void free_io_cache(TABLE *entry);
 void intern_close_table(TABLE *entry);
-bool close_thread_table(THD *thd, TABLE **table_ptr);
+bool close_thread_table(THD *thd, TABLE **table_ptr, uint32 tdc_rv);
 void close_temporary_tables(THD *thd);
 TABLE_LIST *unique_table(THD *thd, TABLE_LIST *table, TABLE_LIST *table_list,
                          bool check_alias);
@@ -233,7 +233,6 @@ bool rename_temporary_table(THD* thd, TA
 			    const char *table_name);
 void mysql_wait_completed_table(ALTER_PARTITION_PARAM_TYPE *lpt, TABLE *my_table);
 void remove_db_from_cache(const char *db);
-void flush_tables();
 bool is_equal(const LEX_STRING *a, const LEX_STRING *b);
 
 /* Functions to work with system tables. */
@@ -263,6 +262,7 @@ void tdc_remove_table(THD *thd, enum_tdc
 bool tdc_open_view(THD *thd, TABLE_LIST *table_list, const char *alias,
                    char *cache_key, uint cache_key_length,
                    MEM_ROOT *mem_root, uint flags);
+void tdc_flush_unused_tables();
 TABLE *find_table_for_mdl_upgrade(TABLE *list, const char *db,
                                   const char *table_name,
                                   bool no_error);

=== modified file 'sql/sql_class.cc'
--- a/sql/sql_class.cc	2010-05-31 15:29:54 +0000
+++ b/sql/sql_class.cc	2010-06-08 13:02:08 +0000
@@ -592,7 +592,7 @@ THD::THD()
   *scramble= '\0';
 
   /* Call to init() below requires fully initialized Open_tables_state. */
-  init_open_tables_state(this, refresh_version);
+  init_open_tables_state(this, get_tdc_refresh_version());
 
   init();
 #if defined(ENABLED_PROFILING)

=== modified file 'sql/sql_class.h'
--- a/sql/sql_class.h	2010-06-08 08:08:46 +0000
+++ b/sql/sql_class.h	2010-06-08 13:02:08 +0000
@@ -1006,7 +1006,7 @@ public:
     of the main statement is called.
   */
   enum enum_locked_tables_mode locked_tables_mode;
-  ulong	version;
+  uint32 version;
   uint current_tablenr;
 
   enum enum_flags {
@@ -1028,7 +1028,7 @@ public:
   /**
      Prepare Open_tables_state instance for operations dealing with tables.
   */
-  void init_open_tables_state(THD *thd, ulong version_arg)
+  void init_open_tables_state(THD *thd, uint32 version_arg)
   {
     reset_open_tables_state(thd);
     version= version_arg;

=== modified file 'sql/sql_connect.cc'
--- a/sql/sql_connect.cc	2010-05-07 16:17:55 +0000
+++ b/sql/sql_connect.cc	2010-06-08 13:02:08 +0000
@@ -1083,7 +1083,6 @@ static void prepare_new_connection_state
     embedded server library.
     TODO: refactor this to avoid code duplication there
   */
-  thd->version= refresh_version;
   thd->proc_info= 0;
   thd->command= COM_SLEEP;
   thd->set_time();

=== modified file 'sql/sql_handler.cc'
--- a/sql/sql_handler.cc	2010-06-08 08:08:46 +0000
+++ b/sql/sql_handler.cc	2010-06-08 13:02:08 +0000
@@ -131,7 +131,7 @@ static void mysql_ha_close_table(THD *th
     /* Non temporary table. */
     tables->table->file->ha_index_or_rnd_end();
     tables->table->open_by_handler= 0;
-    if (close_thread_table(thd, &tables->table))
+    if (close_thread_table(thd, &tables->table, get_tdc_refresh_version()))
     {
       /* Tell threads waiting for refresh that something has happened */
       broadcast_refresh();

=== modified file 'sql/sql_insert.cc'
--- a/sql/sql_insert.cc	2010-06-06 11:19:29 +0000
+++ b/sql/sql_insert.cc	2010-06-08 13:02:08 +0000
@@ -1814,7 +1814,6 @@ public:
     thd.security_ctx->user=thd.security_ctx->priv_user=(char*) delayed_user;
     thd.security_ctx->host=(char*) my_localhost;
     thd.current_tablenr=0;
-    thd.version=refresh_version;
     thd.command=COM_DELAYED_INSERT;
     thd.lex->current_select= 0; 		// for my_message_sql
     thd.lex->sql_command= SQLCOM_INSERT;        // For innodb::store_lock()

=== modified file 'sql/sql_manager.cc'
--- a/sql/sql_manager.cc	2010-03-31 14:05:33 +0000
+++ b/sql/sql_manager.cc	2010-06-08 13:02:08 +0000
@@ -108,7 +108,7 @@ pthread_handler_t handle_manager(void *a
 
     if (error == ETIMEDOUT || error == ETIME)
     {
-      flush_tables();
+      tdc_flush_unused_tables();
       error = 0;
       reset_flush_time = TRUE;
     }

=== modified file 'sql/sql_parse.cc'
--- a/sql/sql_parse.cc	2010-06-06 11:19:29 +0000
+++ b/sql/sql_parse.cc	2010-06-08 13:02:08 +0000
@@ -497,7 +497,6 @@ static void handle_bootstrap_impl(THD *t
 #endif /* EMBEDDED_LIBRARY */
 
   thd_proc_info(thd, 0);
-  thd->version=refresh_version;
   thd->security_ctx->priv_user=
     thd->security_ctx->user= (char*) my_strdup("boot", MYF(MY_WME));
   thd->security_ctx->priv_host[0]=0;
@@ -1385,7 +1384,7 @@ bool dispatch_command(enum enum_server_c
                         (int) thread_count, (ulong) thd->query_id,
                         current_global_status_var.long_query_count,
                         current_global_status_var.opened_tables,
-                        refresh_version,
+                        (ulong) get_tdc_refresh_version(),
                         cached_open_tables(),
                         (uint) (queries_per_second1000 / 1000),
                         (uint) (queries_per_second1000 % 1000));

=== modified file 'sql/sql_plugin.cc'
--- a/sql/sql_plugin.cc	2010-04-29 20:33:06 +0000
+++ b/sql/sql_plugin.cc	2010-06-08 13:02:08 +0000
@@ -1475,10 +1475,8 @@ static void plugin_load(MEM_ROOT *tmp_ro
     When building an embedded library, if the mysql.plugin table
     does not exist, we silently ignore the missing table
   */
-  mysql_mutex_lock(&LOCK_open);
   if (check_if_table_exists(new_thd, &tables, &table_exists))
     table_exists= FALSE;
-  mysql_mutex_unlock(&LOCK_open);
   if (!table_exists)
     goto end;
 #endif /* EMBEDDED_LIBRARY */

=== modified file 'sql/sql_table.cc'
--- a/sql/sql_table.cc	2010-06-08 08:08:46 +0000
+++ b/sql/sql_table.cc	2010-06-08 13:02:08 +0000
@@ -5149,7 +5149,7 @@ send_result_message:
     if (table->table)
     {
       if (fatal_error)
-        table->table->s->version=0;               // Force close of table
+        table->table->s->version= 0;               // Force close of table
       else if (open_for_modify)
       {
         if (table->table->s->tmp_table)
@@ -5434,7 +5434,7 @@ bool mysql_create_like_table(THD* thd, T
             (MYSQL_OPEN_GET_NEW_TABLE). Now we can close the table without
             risking to close some locked table.
           */
-          close_thread_table(thd, &thd->open_tables);
+          close_thread_table(thd, &thd->open_tables, get_tdc_refresh_version());
         }
       }
       else                                      // Case 1
@@ -7488,7 +7488,7 @@ bool mysql_alter_table(THD *thd,char *ne
                                                create_info);
 
     DBUG_ASSERT(thd->open_tables == t_table);
-    close_thread_table(thd, &thd->open_tables);
+    close_thread_table(thd, &thd->open_tables, get_tdc_refresh_version());
     table_list->table= 0;
 
     if (error)

=== modified file 'sql/sql_test.cc'
--- a/sql/sql_test.cc	2010-06-06 11:19:29 +0000
+++ b/sql/sql_test.cc	2010-06-08 13:02:08 +0000
@@ -95,7 +95,7 @@ static void print_cached_tables(void)
     while ((entry= it++))
     {
       printf("%-14.14s %-32s%6ld%8ld%6d  %s\n",
-             entry->s->db.str, entry->s->table_name.str, entry->s->version,
+             entry->s->db.str, entry->s->table_name.str, (ulong) entry->s->version,
              entry->in_use->thread_id, entry->db_stat ? 1 : 0,
              lock_descriptions[(int)entry->reginfo.lock_type]);
     }
@@ -104,7 +104,7 @@ static void print_cached_tables(void)
     {
       unused++;
       printf("%-14.14s %-32s%6ld%8ld%6d  %s\n",
-             entry->s->db.str, entry->s->table_name.str, entry->s->version,
+             entry->s->db.str, entry->s->table_name.str, (ulong) entry->s->version,
              0L, entry->db_stat ? 1 : 0, "Not in use");
     }
   }
@@ -127,7 +127,7 @@ static void print_cached_tables(void)
   if (count != unused)
     printf("Unused_links (%d) doesn't match table_def_cache: %d\n", count,
            unused);
-  printf("\nCurrent refresh version: %ld\n",refresh_version);
+  printf("\nCurrent refresh version: %lu\n", (ulong) get_tdc_refresh_version());
   if (my_hash_check(&table_def_cache))
     printf("Error: Table definition hash table is corrupted\n");
   fflush(stdout);

=== modified file 'sql/table.cc'
--- a/sql/table.cc	2010-06-06 11:19:29 +0000
+++ b/sql/table.cc	2010-06-08 13:02:08 +0000
@@ -308,7 +308,7 @@ TABLE_SHARE *alloc_table_share(TABLE_LIS
     share->normalized_path.str=    share->path.str;
     share->normalized_path.length= path_length;
 
-    share->version=       refresh_version;
+    share->version=       get_tdc_refresh_version();
 
     /*
       Since alloc_table_share() can be called without any locking (for
@@ -2998,6 +2998,17 @@ Table_check_intact::check(TABLE *table, 
 }
 
 
+/* 
+  If we need to purge this table share from the table definition
+  cache due to a rolling FLUSH TABLES.
+*/
+
+bool TABLE_SHARE::needs_reopen() const
+{
+  return version != get_tdc_refresh_version();
+}
+
+
 /*
   Create Item_field for each column in the table.
 

=== modified file 'sql/table.h'
--- a/sql/table.h	2010-06-06 11:19:29 +0000
+++ b/sql/table.h	2010-06-08 13:02:08 +0000
@@ -464,8 +464,6 @@ TABLE_CATEGORY get_table_category(const 
 
 struct TABLE_share;
 
-extern ulong refresh_version;
-
 typedef struct st_table_field_type
 {
   LEX_STRING name;
@@ -574,7 +572,8 @@ struct TABLE_SHARE
   ha_rows min_rows, max_rows;		/* create information */
   ulong   avg_row_length;		/* create information */
   ulong   raid_chunksize;
-  ulong   version, mysql_version;
+  /* Server version in which this .frm was created. */
+  ulong   mysql_version;
   ulong   timestamp_offset;		/* Set to offset+1 of record */
   ulong   reclength;			/* Recordlength */
 
@@ -589,6 +588,8 @@ struct TABLE_SHARE
   enum enum_ha_unused unused1;
   enum enum_ha_unused unused2;
 
+  /* tdc_refresh_version when this TABLE_SHARE was added to TDC. */
+  uint32 version;
   uint ref_count;                       /* How many TABLE objects uses this */
   uint open_count;			/* Number of tables in open list */
   uint blob_ptr_size;			/* 4 or 8 */
@@ -615,6 +616,7 @@ struct TABLE_SHARE
   uint next_number_keypart;             /* autoinc keypart number in a key */
   uint error, open_errno, errarg;       /* error from open_table_def() */
   uint column_bitmap_size;
+  /* Version of the .FRM file format. */
   uchar frm_version;
   bool null_field_first;
   bool system;                          /* Set if system table (one record) */
@@ -740,10 +742,7 @@ struct TABLE_SHARE
   /*
     Must all TABLEs be reopened?
   */
-  inline bool needs_reopen()
-  {
-    return version != refresh_version;
-  }
+  bool needs_reopen() const;
   /**
     Convert unrelated members of TABLE_SHARE to one enum
     representing its type.


Attachment: [text/bzr-bundle] bzr/kostja@sun.com-20100608130208-9b1wwn2mwb1ebpyy.bundle
Thread
bzr commit into mysql-trunk-runtime branch (kostja:3047) WL#5419Konstantin Osipov8 Jun