Commit 77017191 authored by unknown's avatar unknown

WL#3071 - Maria checkpoint

- serializing calls to flush_pagecache_blocks_int() on the same file
to avoid known concurrency bugs
- having that, we can now enable the background thread, as the
flushes it does are now supposedly safe in concurrent situations.
- new type of flush FLUSH_KEEP_LAZY: when the background checkpoint
thread is flushing a packet of dirty pages between two checkpoints,
it uses this flush type, indeed if a file is already being flushed
by another thread it's smarter to move on to the next file than wait.
- maria_checkpoint_frequency renamed to maria_checkpoint_interval.


include/my_sys.h:
  new type of flushing for the page cache: FLUSH_KEEP_LAZY
mysql-test/r/maria.result:
  result update
mysys/mf_keycache.c:
  indentation. No FLUSH_KEEP_LAZY support in key cache.
storage/maria/ha_maria.cc:
  maria_checkpoint_frequency was somehow a hidden part of the
  Checkpoint API and that was not good. Now we have checkpoint_interval,
  local to ha_maria.cc, which serves as container for the user-visible
  maria_checkpoint_interval global variable; setting it calls
  update_checkpoint_interval which passes the new value to
  ma_checkpoint_init(). There is no hiding anymore.
  By default, enable background thread which does checkpoints
  every 30 seconds, and dirty page flush in between. That thread takes
  a checkpoint when it ends, so no need for maria_hton_panic to take one.
  The | is | and not ||, because maria_panic() must always be called.
  frequency->interval.
storage/maria/ma_checkpoint.c:
  Use FLUSH_KEEP_LAZY for background thread when it flushes packets of
  dirty pages between two checkpoints: it is smarter to move on to
  the next file than wait for it to have been completely flushed, which
  may take long.
  Comments about flush concurrency bugs moved from ma_pagecache.c.
  Removing out-of-date comment.
  frequency->interval.
  create_background_thread -> (interval>0).
  In ma_checkpoint_background(), some variables need to be preserved
  between iterations.
storage/maria/ma_checkpoint.h:
  new prototype
storage/maria/ma_pagecache.c:
  - concurrent calls of flush_pagecache_blocks_int() on the same file
  cause bugs (see @note in that function); we fix them by serializing
  in this situation. For that we use a global hash of (file, wqueue).
  When flush_pagecache_blocks_int() starts it looks into the hash,
  using the file as key. If not found, it inserts (file,wqueue) into the
  hash, flushes the file, and finally removes itself from the hash and
  wakes up any waiter in the queue. If found, it adds itself to the
  wqueue and waits.
  - As a by-product, we can remove changed_blocks_is_incomplete
  and replace it by scanning the hash, replace the sleep() by a queue wait.
  - new type of flush FLUSH_KEEP_LAZY: when flushing a file, if it's
  already being flushed by another thread (even partially), return
  immediately.
storage/maria/ma_pagecache.h:
  In pagecache, a hash of files currently being flushed (i.e. there
  is a call to flush_pagecache_blocks_int() for them).
storage/maria/ma_recovery.c:
  new prototype
storage/maria/ma_test1.c:
  new prototype
storage/maria/ma_test2.c:
  new prototype
parent 9bcbf851
......@@ -283,7 +283,13 @@ enum flush_type
FLUSH_IGNORE_CHANGED, /* remove block from the cache */
/* as my_disable_flush_pagecache_blocks is always 0, it is
strictly equivalent to FLUSH_KEEP */
FLUSH_FORCE_WRITE
FLUSH_FORCE_WRITE,
/**
@brief like FLUSH_KEEP but return immediately if file is already being
flushed (even partially) by another thread; only for page cache,
forbidden for key cache.
*/
FLUSH_KEEP_LAZY
};
typedef struct st_record_cache /* Used when cacheing records */
......
......@@ -1976,7 +1976,7 @@ drop table t1;
show variables like 'maria%';
Variable_name Value
maria_block_size 8192
maria_checkpoint_frequency 0
maria_checkpoint_interval 30
maria_max_sort_file_size 9223372036853727232
maria_pagecache_age_threshold 300
maria_pagecache_buffer_size 8384512
......
......@@ -3557,10 +3557,11 @@ static int flush_key_blocks_int(KEY_CACHE *keycache,
file, keycache->blocks_used, keycache->blocks_changed));
#if !defined(DBUG_OFF) && defined(EXTRA_DEBUG)
DBUG_EXECUTE("check_keycache",
test_key_cache(keycache, "start of flush_key_blocks", 0););
DBUG_EXECUTE("check_keycache",
test_key_cache(keycache, "start of flush_key_blocks", 0););
#endif
DBUG_ASSERT(type != FLUSH_KEEP_LAZY);
cache= cache_buff;
if (keycache->disk_blocks > 0 &&
(!my_disable_flush_key_blocks || type != FLUSH_KEEP))
......
......@@ -81,9 +81,11 @@ TYPELIB maria_stats_method_typelib=
maria_stats_method_names, NULL
};
static void update_checkpoint_frequency(MYSQL_THD thd,
struct st_mysql_sys_var *var,
void *var_ptr, void *save);
/** @brief Interval between background checkpoints in seconds */
static ulong checkpoint_interval;
static void update_checkpoint_interval(MYSQL_THD thd,
struct st_mysql_sys_var *var,
void *var_ptr, void *save);
static MYSQL_SYSVAR_ULONG(block_size, maria_block_size,
PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY,
......@@ -91,12 +93,11 @@ static MYSQL_SYSVAR_ULONG(block_size, maria_block_size,
MARIA_KEY_BLOCK_LENGTH, MARIA_MIN_KEY_BLOCK_LENGTH,
MARIA_MAX_KEY_BLOCK_LENGTH, MARIA_MIN_KEY_BLOCK_LENGTH);
static MYSQL_SYSVAR_ULONG(checkpoint_frequency, maria_checkpoint_frequency,
static MYSQL_SYSVAR_ULONG(checkpoint_interval, checkpoint_interval,
PLUGIN_VAR_RQCMDARG,
"Frequency of automatic checkpoints, in seconds;"
" 0 means 'no checkpoints'.",
/* disabled for now */
NULL, update_checkpoint_frequency, 0, 0, UINT_MAX, 1);
"Interval between automatic checkpoints, in seconds;"
" 0 means 'no automatic checkpoints'.",
NULL, update_checkpoint_interval, 30, 0, UINT_MAX, 1);
static MYSQL_SYSVAR_ULONGLONG(max_sort_file_size,
maria_max_temp_length, PLUGIN_VAR_RQCMDARG,
......@@ -2376,8 +2377,9 @@ bool ha_maria::check_if_incompatible_data(HA_CREATE_INFO *info,
static int maria_hton_panic(handlerton *hton, ha_panic_function flag)
{
ma_checkpoint_execute(CHECKPOINT_FULL, FALSE); /* can't catch error */
return maria_panic(flag);
/* If no background checkpoints, we need to do one now */
return ((checkpoint_interval == 0) ?
ma_checkpoint_execute(CHECKPOINT_FULL, FALSE) : 0) | maria_panic(flag);
}
......@@ -2440,7 +2442,7 @@ static int ha_maria_init(void *p)
MYSQL_VERSION_ID, server_id, maria_log_pagecache,
TRANSLOG_DEFAULT_FLAGS) ||
maria_recover() ||
ma_checkpoint_init(TRUE);
ma_checkpoint_init(checkpoint_interval);
maria_multi_threaded= TRUE;
return res;
}
......@@ -2523,7 +2525,7 @@ my_bool ha_maria::register_query_cache_table(THD *thd, char *table_name,
static struct st_mysql_sys_var* system_variables[]= {
MYSQL_SYSVAR(block_size),
MYSQL_SYSVAR(checkpoint_frequency),
MYSQL_SYSVAR(checkpoint_interval),
MYSQL_SYSVAR(max_sort_file_size),
MYSQL_SYSVAR(pagecache_age_threshold),
MYSQL_SYSVAR(pagecache_buffer_size),
......@@ -2536,24 +2538,15 @@ static struct st_mysql_sys_var* system_variables[]= {
/**
@brief Updates the checkpoint frequency and restarts the background thread.
Background thread has a loop which correctness depends on a constant
checkpoint frequency. So when the user wants to modify it, we stop and
restart the thread.
@brief Updates the checkpoint interval and restarts the background thread.
*/
static void update_checkpoint_frequency(MYSQL_THD thd,
static void update_checkpoint_interval(MYSQL_THD thd,
struct st_mysql_sys_var *var,
void *var_ptr, void *save)
{
ulong new_value= (ulong)(*(long *)save), *dest= (ulong *)var_ptr;
if (new_value != *dest) /* it's actually a change */
{
ma_checkpoint_end();
*dest= new_value;
ma_checkpoint_init(TRUE);
}
ma_checkpoint_end();
ma_checkpoint_init(*(ulong *)var_ptr= (ulong)(*(long *)save));
}
static SHOW_VAR status_variables[]= {
......
This diff is collapsed.
......@@ -32,7 +32,7 @@ typedef enum enum_ma_checkpoint_level {
} CHECKPOINT_LEVEL;
C_MODE_START
int ma_checkpoint_init(my_bool create_background_thread);
int ma_checkpoint_init(ulong interval);
void ma_checkpoint_end(void);
int ma_checkpoint_execute(CHECKPOINT_LEVEL level, my_bool no_wait);
C_MODE_END
......
This diff is collapsed.
......@@ -22,6 +22,7 @@ C_MODE_START
#include "ma_loghandler_lsn.h"
#include <m_string.h>
#include <hash.h>
/* Type of the page */
enum pagecache_page_type
......@@ -159,6 +160,7 @@ typedef struct st_pagecache
my_bool resize_in_flush; /* true during flush of resize operation */
my_bool can_be_used; /* usage of cache for read/write is allowed */
my_bool in_init; /* Set to 1 in MySQL during init/resize */
HASH files_in_flush; /**< files in flush_pagecache_blocks_int() */
} PAGECACHE;
/** @brief Return values for PAGECACHE_FLUSH_FILTER */
......
......@@ -229,7 +229,7 @@ int maria_apply_log(LSN from_lsn, enum maria_apply_log_way apply,
if (!all_active_trans || !all_tables)
goto err;
if (take_checkpoints && ma_checkpoint_init(FALSE))
if (take_checkpoints && ma_checkpoint_init(0))
goto err;
recovery_message_printed= REC_MSG_NONE;
......
......@@ -83,7 +83,7 @@ int main(int argc,char *argv[])
translog_init(maria_data_root, TRANSLOG_FILE_SIZE,
0, 0, maria_log_pagecache,
TRANSLOG_DEFAULT_FLAGS) ||
(transactional && (trnman_init(0) || ma_checkpoint_init(FALSE))))
(transactional && (trnman_init(0) || ma_checkpoint_init(0))))
{
fprintf(stderr, "Error in initialization");
exit(1);
......
......@@ -98,7 +98,7 @@ int main(int argc, char *argv[])
translog_init(maria_data_root, TRANSLOG_FILE_SIZE,
0, 0, maria_log_pagecache,
TRANSLOG_DEFAULT_FLAGS) ||
(transactional && (trnman_init(0) || ma_checkpoint_init(FALSE))))
(transactional && (trnman_init(0) || ma_checkpoint_init(0))))
{
fprintf(stderr, "Error in initialization");
exit(1);
......
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