Re: [Maria-developers] keycache flush problem
Hi! For those that don't know what this is about: - This is a fix for the case where you do a DROP TABLE of a MyISAM table with key delayed MyISAM writes all the changed key pages for the file to disk before closing and then deleting the table. This patch is a first attempt to fix that we don't write the key pages in case of drop.
"Oleksandr" == Oleksandr Byelkin <Oleksandr> writes:
Oleksandr> Hi! Oleksandr> I made different patch from you suggested maybe I am wrong but IMHO it Oleksandr> is better because: Oleksandr> 1) use already existing keycache calls Oleksandr> 2) do not require additional finding table MI_INFO by name and locking Oleksandr> around it Oleksandr> The main idea was that if we are going to drop table it can be passed Oleksandr> via existing table descriptors to the place where we call flush and it Oleksandr> does not matter if other threads trying to do something with the table Oleksandr> we will drop it in any case. Oleksandr> === modified file 'sql/handler.h' Oleksandr> --- sql/handler.h 2009-06-29 21:03:30 +0000 Oleksandr> +++ sql/handler.h 2009-08-09 19:52:01 +0000 Oleksandr> @@ -1342,6 +1342,7 @@ public: Oleksandr> virtual void column_bitmaps_signal(); Oleksandr> uint get_index(void) const { return active_index; } Oleksandr> virtual int close(void)=0; Oleksandr> + virtual void prepare_for_delete() {} Oleksandr> /** Oleksandr> @retval 0 Bulk update used by handler Why using prepare_for_delete(), instead of adding a more general call ? Oleksandr> === modified file 'sql/lock.cc' Oleksandr> --- sql/lock.cc 2009-04-25 10:05:32 +0000 Oleksandr> +++ sql/lock.cc 2009-08-09 22:08:41 +0000 Oleksandr> @@ -1049,10 +1049,12 @@ int lock_table_name(THD *thd, TABLE_LIST Oleksandr> DBUG_RETURN(-1); Oleksandr> table_list->table=table; Oleksandr> + table->s->deleting= table_list->deleting; Oleksandr> /* Return 1 if table is in use */ Oleksandr> DBUG_RETURN(test(remove_table_from_cache(thd, db, table_list->table_name, Oleksandr> - check_in_use ? RTFC_NO_FLAG : RTFC_WAIT_OTHER_THREAD_FLAG))); Oleksandr> + check_in_use ? RTFC_NO_FLAG : RTFC_WAIT_OTHER_THREAD_FLAG, Oleksandr> + table_list->deleting))); Oleksandr> } Oleksandr> === modified file 'sql/mysql_priv.h' Oleksandr> --- sql/mysql_priv.h 2009-04-25 10:05:32 +0000 Oleksandr> +++ sql/mysql_priv.h 2009-08-09 21:51:48 +0000 Oleksandr> @@ -1609,7 +1609,7 @@ uint prep_alter_part_table(THD *thd, TAB Oleksandr> #define RTFC_WAIT_OTHER_THREAD_FLAG 0x0002 Oleksandr> #define RTFC_CHECK_KILLED_FLAG 0x0004 Oleksandr> bool remove_table_from_cache(THD *thd, const char *db, const char *table, Oleksandr> - uint flags); Oleksandr> + uint flags, my_bool deleting); Oleksandr> #define NORMAL_PART_NAME 0 Oleksandr> #define TEMP_PART_NAME 1 Oleksandr> === modified file 'sql/sql_base.cc' Oleksandr> --- sql/sql_base.cc 2009-05-19 09:28:05 +0000 Oleksandr> +++ sql/sql_base.cc 2009-08-09 21:54:54 +0000 Oleksandr> @@ -927,7 +927,7 @@ bool close_cached_tables(THD *thd, TABLE Oleksandr> for (TABLE_LIST *table= tables; table; table= table->next_local) Oleksandr> { Oleksandr> if (remove_table_from_cache(thd, table->db, table->table_name, Oleksandr> - RTFC_OWNED_BY_THD_FLAG)) Oleksandr> + RTFC_OWNED_BY_THD_FLAG, table->deleting)) Oleksandr> found=1; Oleksandr> } Oleksandr> if (!found) Oleksandr> @@ -8395,7 +8395,7 @@ void flush_tables() Oleksandr> */ Oleksandr> bool remove_table_from_cache(THD *thd, const char *db, const char *table_name, Oleksandr> - uint flags) Oleksandr> + uint flags, my_bool deleting) Oleksandr> { Oleksandr> char key[MAX_DBKEY_LENGTH]; Oleksandr> uint key_length; Oleksandr> @@ -8482,7 +8482,10 @@ bool remove_table_from_cache(THD *thd, c Oleksandr> } Oleksandr> } Oleksandr> while (unused_tables && !unused_tables->s->version) Oleksandr> + { Oleksandr> + unused_tables->s->deleting= deleting; Oleksandr> VOID(hash_delete(&open_cache,(uchar*) unused_tables)); Oleksandr> + } Oleksandr> DBUG_PRINT("info", ("Removing table from table_def_cache")); Oleksandr> /* Remove table from table definition cache if it's not in use */ Oleksandr> @@ -8676,7 +8679,8 @@ int abort_and_upgrade_lock(ALTER_PARTITI Oleksandr> /* If MERGE child, forward lock handling to parent. */ Oleksandr> mysql_lock_abort(lpt->thd, lpt->table->parent ? lpt->table->parent : Oleksandr> lpt->table, TRUE); Oleksandr> - VOID(remove_table_from_cache(lpt->thd, lpt->db, lpt->table_name, flags)); Oleksandr> + VOID(remove_table_from_cache(lpt->thd, lpt->db, lpt->table_name, flags, Oleksandr> + FALSE)); Oleksandr> VOID(pthread_mutex_unlock(&LOCK_open)); Oleksandr> DBUG_RETURN(0); Oleksandr> } Oleksandr> @@ -8701,7 +8705,7 @@ void close_open_tables_and_downgrade(ALT Oleksandr> { Oleksandr> VOID(pthread_mutex_lock(&LOCK_open)); Oleksandr> remove_table_from_cache(lpt->thd, lpt->db, lpt->table_name, Oleksandr> - RTFC_WAIT_OTHER_THREAD_FLAG); Oleksandr> + RTFC_WAIT_OTHER_THREAD_FLAG, FALSE); Oleksandr> VOID(pthread_mutex_unlock(&LOCK_open)); Oleksandr> /* If MERGE child, forward lock handling to parent. */ Oleksandr> mysql_lock_downgrade_write(lpt->thd, lpt->table->parent ? lpt->table->parent : Oleksandr> === modified file 'sql/sql_table.cc' Oleksandr> --- sql/sql_table.cc 2009-06-18 12:39:21 +0000 Oleksandr> +++ sql/sql_table.cc 2009-08-09 21:48:04 +0000 Oleksandr> @@ -1599,6 +1599,8 @@ int mysql_rm_table_part2(THD *thd, TABLE Oleksandr> if ((share= get_cached_table_share(table->db, table->table_name))) Oleksandr> table->db_type= share->db_type(); Oleksandr> + table->deleting= TRUE; Oleksandr> + Oleksandr> /* Disable drop of enabled log tables */ Oleksandr> if (share && (share->table_category == TABLE_CATEGORY_PERFORMANCE) && Oleksandr> check_if_log_table(table->db_length, table->db, Oleksandr> @@ -1676,7 +1678,7 @@ int mysql_rm_table_part2(THD *thd, TABLE Oleksandr> abort_locked_tables(thd, db, table->table_name); Oleksandr> remove_table_from_cache(thd, db, table->table_name, Oleksandr> RTFC_WAIT_OTHER_THREAD_FLAG | Oleksandr> - RTFC_CHECK_KILLED_FLAG); Oleksandr> + RTFC_CHECK_KILLED_FLAG, TRUE); Oleksandr> /* Oleksandr> If the table was used in lock tables, remember it so that Oleksandr> unlock_table_names can free it Oleksandr> @@ -3862,7 +3864,7 @@ void wait_while_table_is_used(THD *thd,T Oleksandr> /* Wait until all there are no other threads that has this table open */ Oleksandr> remove_table_from_cache(thd, table->s->db.str, Oleksandr> table->s->table_name.str, Oleksandr> - RTFC_WAIT_OTHER_THREAD_FLAG); Oleksandr> + RTFC_WAIT_OTHER_THREAD_FLAG, FALSE); Oleksandr> /* extra() call must come only after all instances above are closed */ Oleksandr> VOID(table->file->extra(function)); Oleksandr> DBUG_VOID_RETURN; Oleksandr> @@ -4366,7 +4368,7 @@ static bool mysql_admin_table(THD* thd, Oleksandr> remove_table_from_cache(thd, table->table->s->db.str, Oleksandr> table->table->s->table_name.str, Oleksandr> RTFC_WAIT_OTHER_THREAD_FLAG | Oleksandr> - RTFC_CHECK_KILLED_FLAG); Oleksandr> + RTFC_CHECK_KILLED_FLAG, FALSE); Oleksandr> thd->exit_cond(old_message); Oleksandr> DBUG_EXECUTE_IF("wait_in_mysql_admin_table", wait_for_kill_signal(thd);); Oleksandr> if (thd->killed) Oleksandr> @@ -4624,7 +4626,8 @@ send_result_message: Oleksandr> { Oleksandr> pthread_mutex_lock(&LOCK_open); Oleksandr> remove_table_from_cache(thd, table->table->s->db.str, Oleksandr> - table->table->s->table_name.str, RTFC_NO_FLAG); Oleksandr> + table->table->s->table_name.str, Oleksandr> + RTFC_NO_FLAG, FALSE); Oleksandr> pthread_mutex_unlock(&LOCK_open); Oleksandr> } Oleksandr> /* May be something modified consequently we have to invalidate cache */ Oleksandr> === modified file 'sql/table.cc' Oleksandr> --- sql/table.cc 2009-06-29 21:03:30 +0000 Oleksandr> +++ sql/table.cc 2009-08-09 20:46:07 +0000 Oleksandr> @@ -1960,7 +1960,12 @@ int closefrm(register TABLE *table, bool Oleksandr> DBUG_PRINT("enter", ("table: 0x%lx", (long) table)); Oleksandr> if (table->db_stat) Oleksandr> - error=table->file->close(); Oleksandr> + { Oleksandr> + if (table->s->deleting) Oleksandr> + table->file->prepare_for_delete(); Oleksandr> + error= table->file->close(); Oleksandr> + } Oleksandr> + As we have a handler here, we not instead do ? table->file->extra(HA_EXTRA_PREPARE_FOR_DROP); There is no reason to add an extra prepare_for_delete() here. Oleksandr> --- storage/myisam/ha_myisam.cc 2009-06-29 21:03:30 +0000 Oleksandr> +++ storage/myisam/ha_myisam.cc 2009-08-09 20:42:15 +0000 Oleksandr> @@ -26,7 +26,9 @@ Oleksandr> #include <myisampack.h> Oleksandr> #include "ha_myisam.h" Oleksandr> #include <stdarg.h> Oleksandr> +C_MODE_START Oleksandr> #include "myisamdef.h" Oleksandr> +C_MODE_END Oleksandr> #include "rt_index.h" With my suggested change, no reason to do any changes in ha_myisam.cc or ha_myisam.h <cut> Oleksandr> +++ storage/myisam/mi_close.c 2009-08-09 22:01:32 +0000 Oleksandr> @@ -65,8 +65,9 @@ int mi_close(register MI_INFO *info) Oleksandr> { Oleksandr> if (share->kfile >= 0 && Oleksandr> flush_key_blocks(share->key_cache, share->kfile, Oleksandr> - share->temporary ? FLUSH_IGNORE_CHANGED : Oleksandr> - FLUSH_RELEASE)) Oleksandr> + (share->temporary || share->deleting) ? Oleksandr> + FLUSH_IGNORE_CHANGED : Oleksandr> + FLUSH_RELEASE)) Oleksandr> error=my_errno; Oleksandr> if (share->kfile >= 0) Oleksandr> { No reason for the above change. 1) In my suggestion, no reason to do this. 2) If we implement it your way, we could reuse 'share->temporary' for this cse. Oleksandr> === modified file 'storage/myisam/mi_locking.c' Oleksandr> --- storage/myisam/mi_locking.c 2009-04-01 09:34:52 +0000 Oleksandr> +++ storage/myisam/mi_locking.c 2009-08-09 20:42:00 +0000 Oleksandr> @@ -68,7 +68,10 @@ int mi_lock_database(MI_INFO *info, int Oleksandr> --share->tot_locks; Oleksandr> if (info->lock_type == F_WRLCK && !share->w_locks && Oleksandr> !share->delay_key_write && flush_key_blocks(share->key_cache, Oleksandr> - share->kfile,FLUSH_KEEP)) Oleksandr> + share->kfile, Oleksandr> + (share->deleting ? Oleksandr> + FLUSH_IGNORE_CHANGED : Oleksandr> + FLUSH_KEEP))) No reason to do the above. Reasons: - In case of delay_key_write, they above code will not be executed. - If delay_key_write is not set, things was flushed at previous statement. Did I miss some case? Oleksandr> --- storage/myisam/myisamdef.h 2009-04-25 09:04:38 +0000 Oleksandr> +++ storage/myisam/myisamdef.h 2009-08-09 20:41:25 +0000 Oleksandr> @@ -218,6 +218,7 @@ typedef struct st_mi_isam_share Oleksandr> my_bool changed, /* If changed since lock */ Oleksandr> global_changed, /* If changed since open */ Oleksandr> not_flushed, temporary, delay_key_write, concurrent_insert; Oleksandr> + my_bool deleting; /* we are going to delete this table */ Not needed. --------------- Other fixes: Please fix mi_extra.c as we dicussed (move the #ifdef so that things are flushed) do also the folloing fix to ma_extra.c: if (share->kfile.file >= 0) _ma_decrement_open_count(info); -> if (share->kfile.file >= 0 && do_flush) _ma_decrement_open_count(info); The idea is that we should not decrement the open_count in case of drop. This will ensure that if we die between flushing the key cache and close, the index will be rechecked. ------------- Regards, Monty
participants (1)
-
Michael Widenius