diff options
author | Luke Chen <luke.chen@mongodb.com> | 2020-08-07 14:35:38 +1000 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2020-08-07 04:44:34 +0000 |
commit | 7a351100d3f8e52da17dd440101d99e86aedb772 (patch) | |
tree | 0c8e4294300919fd86c9365e5ea70d4283600f13 | |
parent | e199a343576be60a13154cf771e7a4d623e1d6c0 (diff) | |
download | mongo-7a351100d3f8e52da17dd440101d99e86aedb772.tar.gz |
Import wiredtiger: 6563ee4aa0e45cd72cc68563dacb8cb5fc15c630 from branch mongodb-4.0
ref: cb9f737fc6..6563ee4aa0
for: 4.0.20
WT-5242 Minimize checkpoints pinned during backup
WT-6118 Fix missing checkpoint in backup
WT-6141 Disable checkpoint deletion during backup
20 files changed, 203 insertions, 74 deletions
diff --git a/src/third_party/wiredtiger/dist/api_data.py b/src/third_party/wiredtiger/dist/api_data.py index e94b6ec01c1..b221f610184 100644 --- a/src/third_party/wiredtiger/dist/api_data.py +++ b/src/third_party/wiredtiger/dist/api_data.py @@ -1380,8 +1380,9 @@ methods = { including the named checkpoint, or \c "to=<checkpoint>" to drop all checkpoints before and including the named checkpoint. Checkpoints cannot be - dropped while a hot backup is in progress or if open in - a cursor''', type='list'), + dropped if open in a cursor. While a hot backup is in + progress, checkpoints created prior to the start of the + backup cannot be dropped''', type='list'), Config('force', 'false', r''' if false (the default), checkpoints may be skipped if the underlying object has not been modified, if true, this option forces the checkpoint''', diff --git a/src/third_party/wiredtiger/import.data b/src/third_party/wiredtiger/import.data index fede97bc1cf..99d06b6e23d 100644 --- a/src/third_party/wiredtiger/import.data +++ b/src/third_party/wiredtiger/import.data @@ -2,5 +2,5 @@ "vendor": "wiredtiger", "github": "wiredtiger/wiredtiger.git", "branch": "mongodb-4.0", - "commit": "cb9f737fc6c1d78a4ad50df50cdfa4047e04e6c4" + "commit": "6563ee4aa0e45cd72cc68563dacb8cb5fc15c630" } diff --git a/src/third_party/wiredtiger/src/block/block_write.c b/src/third_party/wiredtiger/src/block/block_write.c index 918ec455ca0..65179670b66 100644 --- a/src/third_party/wiredtiger/src/block/block_write.c +++ b/src/third_party/wiredtiger/src/block/block_write.c @@ -41,9 +41,9 @@ __wt_block_truncate(WT_SESSION_IMPL *session, WT_BLOCK *block, wt_off_t len) * backups, stops all block truncation unnecessarily). We may want a * more targeted solution at some point. */ - if (!conn->hot_backup) { + if (conn->hot_backup_start == 0) { __wt_readlock(session, &conn->hot_backup_lock); - if (!conn->hot_backup) + if (conn->hot_backup_start == 0) ret = __wt_ftruncate(session, block->fh, len); __wt_readunlock(session, &conn->hot_backup_lock); } diff --git a/src/third_party/wiredtiger/src/btree/bt_vrfy.c b/src/third_party/wiredtiger/src/btree/bt_vrfy.c index 6fa2dbda197..fb716e03ffe 100644 --- a/src/third_party/wiredtiger/src/btree/bt_vrfy.c +++ b/src/third_party/wiredtiger/src/btree/bt_vrfy.c @@ -171,7 +171,7 @@ __wt_verify(WT_SESSION_IMPL *session, const char *cfg[]) goto done; /* Get a list of the checkpoints for this file. */ - WT_ERR(__wt_meta_ckptlist_get(session, btree->dhandle->name, &ckptbase)); + WT_ERR(__wt_meta_ckptlist_get(session, btree->dhandle->name, false, &ckptbase)); /* Inform the underlying block manager we're verifying. */ WT_ERR(bm->verify_start(bm, session, ckptbase, cfg)); diff --git a/src/third_party/wiredtiger/src/conn/conn_log.c b/src/third_party/wiredtiger/src/conn/conn_log.c index dda113028ec..3266aa2ad1e 100644 --- a/src/third_party/wiredtiger/src/conn/conn_log.c +++ b/src/third_party/wiredtiger/src/conn/conn_log.c @@ -377,7 +377,7 @@ __log_archive_once(WT_SESSION_IMPL *session, uint32_t backup_file) */ __wt_readlock(session, &conn->hot_backup_lock); locked = true; - if (!conn->hot_backup || backup_file != 0) { + if (conn->hot_backup_start == 0 || backup_file != 0) { for (i = 0; i < logcount; i++) { WT_ERR(__wt_log_extract_lognum(session, logfiles[i], &lognum)); if (lognum < min_lognum) @@ -560,9 +560,9 @@ __log_file_server(void *arg) * file system may not support truncate: both are OK, it's just more work during * cursor traversal. */ - if (!conn->hot_backup) { + if (conn->hot_backup_start == 0) { __wt_readlock(session, &conn->hot_backup_lock); - if (!conn->hot_backup && conn->log_cursors == 0) + if (conn->hot_backup_start == 0 && conn->log_cursors == 0) WT_ERR_ERROR_OK( __wt_ftruncate(session, close_fh, close_end_lsn.l.offset), ENOTSUP); __wt_readunlock(session, &conn->hot_backup_lock); @@ -894,7 +894,7 @@ __log_server(void *arg) * have agreed not to rename or remove any files in the database directory. */ __wt_readlock(session, &conn->hot_backup_lock); - if (!conn->hot_backup) + if (conn->hot_backup_start == 0) ret = __log_prealloc_once(session); __wt_readunlock(session, &conn->hot_backup_lock); WT_ERR(ret); diff --git a/src/third_party/wiredtiger/src/conn/conn_open.c b/src/third_party/wiredtiger/src/conn/conn_open.c index 9656a3d0167..a2c39fbeed5 100644 --- a/src/third_party/wiredtiger/src/conn/conn_open.c +++ b/src/third_party/wiredtiger/src/conn/conn_open.c @@ -16,6 +16,7 @@ int __wt_connection_open(WT_CONNECTION_IMPL *conn, const char *cfg[]) { WT_SESSION_IMPL *session; + time_t most_recent; /* Default session. */ session = conn->default_session; @@ -39,6 +40,9 @@ __wt_connection_open(WT_CONNECTION_IMPL *conn, const char *cfg[]) */ conn->default_session = session; + __wt_seconds(session, &most_recent); + conn->ckpt_most_recent = (uint64_t)most_recent; + /* * Publish: there must be a barrier to ensure the connection structure fields are set before * other threads read from the pointer. diff --git a/src/third_party/wiredtiger/src/cursor/cur_backup.c b/src/third_party/wiredtiger/src/cursor/cur_backup.c index 179bf9bd195..9d44897d602 100644 --- a/src/third_party/wiredtiger/src/cursor/cur_backup.c +++ b/src/third_party/wiredtiger/src/cursor/cur_backup.c @@ -234,7 +234,7 @@ __backup_start(WT_SESSION_IMPL *session, WT_CURSOR_BACKUP *cb, bool is_dup, cons * Single thread hot backups: we're holding the schema lock, so we know we'll serialize with * other attempts to start a hot backup. */ - if (conn->hot_backup && !is_dup) + if (conn->hot_backup_start != 0 && !is_dup) WT_RET_MSG(session, EINVAL, "there is already a backup cursor open"); if (F_ISSET(session, WT_SESSION_BACKUP_DUP) && is_dup) @@ -258,7 +258,7 @@ __backup_start(WT_SESSION_IMPL *session, WT_CURSOR_BACKUP *cb, bool is_dup, cons * complete and valid. */ __wt_writelock(session, &conn->hot_backup_lock); - conn->hot_backup = true; + conn->hot_backup_start = conn->ckpt_most_recent; conn->hot_backup_list = NULL; __wt_writeunlock(session, &conn->hot_backup_lock); @@ -392,7 +392,7 @@ __backup_stop(WT_SESSION_IMPL *session, WT_CURSOR_BACKUP *cb) /* Checkpoint deletion and next hot backup can proceed. */ __wt_writelock(session, &conn->hot_backup_lock); - conn->hot_backup = false; + conn->hot_backup_start = 0; __wt_writeunlock(session, &conn->hot_backup_lock); F_CLR(session, WT_SESSION_BACKUP_CURSOR); diff --git a/src/third_party/wiredtiger/src/docs/backup.dox b/src/third_party/wiredtiger/src/docs/backup.dox index b59d099175f..04127b6fd07 100644 --- a/src/third_party/wiredtiger/src/docs/backup.dox +++ b/src/third_party/wiredtiger/src/docs/backup.dox @@ -56,10 +56,9 @@ aggregate the file names from the cursor and then list the file names as arguments to a file archiver such as the system tar utility. During the period the backup cursor is open, database checkpoints can -be created, but no checkpoints can be deleted. This may result in -significant file growth. Additionally while the backup cursor is open -automatic log file archiving, even if enabled, will not reclaim any -log files. +be created, but checkpoints created prior to the backup cursor cannot +be deleted. Additionally while the backup cursor is open automatic log +file archiving, even if enabled, will not reclaim any log files. Additionally, if a crash occurs during the period the backup cursor is open and logging is disabled (in other words, when depending on diff --git a/src/third_party/wiredtiger/src/include/connection.h b/src/third_party/wiredtiger/src/include/connection.h index f739a6e61e5..2f1175c8540 100644 --- a/src/third_party/wiredtiger/src/include/connection.h +++ b/src/third_party/wiredtiger/src/include/connection.h @@ -253,13 +253,14 @@ struct __wt_connection_impl { WT_TXN_GLOBAL txn_global; /* Global transaction state */ WT_RWLOCK hot_backup_lock; /* Hot backup serialization */ - bool hot_backup; /* Hot backup in progress */ + uint64_t hot_backup_start; /* Clock value of most recent checkpoint needed by hot backup */ char **hot_backup_list; /* Hot backup file list */ WT_SESSION_IMPL *ckpt_session; /* Checkpoint thread session */ wt_thread_t ckpt_tid; /* Checkpoint thread */ bool ckpt_tid_set; /* Checkpoint thread set */ WT_CONDVAR *ckpt_cond; /* Checkpoint wait mutex */ + uint64_t ckpt_most_recent; /* Clock value of most recent checkpoint */ #define WT_CKPT_LOGSIZE(conn) ((conn)->ckpt_logsize != 0) wt_off_t ckpt_logsize; /* Checkpoint log size period */ bool ckpt_signalled; /* Checkpoint signalled */ diff --git a/src/third_party/wiredtiger/src/include/extern.h b/src/third_party/wiredtiger/src/include/extern.h index 6c0e23aa823..a9b6533bb0f 100644 --- a/src/third_party/wiredtiger/src/include/extern.h +++ b/src/third_party/wiredtiger/src/include/extern.h @@ -997,8 +997,8 @@ extern int __wt_meta_checkpoint_last_name(WT_SESSION_IMPL *session, const char * const char **namep) WT_GCC_FUNC_DECL_ATTRIBUTE((warn_unused_result)); extern int __wt_meta_checkpoint_clear(WT_SESSION_IMPL *session, const char *fname) WT_GCC_FUNC_DECL_ATTRIBUTE((warn_unused_result)); -extern int __wt_meta_ckptlist_get(WT_SESSION_IMPL *session, const char *fname, WT_CKPT **ckptbasep) - WT_GCC_FUNC_DECL_ATTRIBUTE((warn_unused_result)); +extern int __wt_meta_ckptlist_get(WT_SESSION_IMPL *session, const char *fname, bool update, + WT_CKPT **ckptbasep) WT_GCC_FUNC_DECL_ATTRIBUTE((warn_unused_result)); extern int __wt_meta_ckptlist_set(WT_SESSION_IMPL *session, const char *fname, WT_CKPT *ckptbase, WT_LSN *ckptlsn) WT_GCC_FUNC_DECL_ATTRIBUTE((warn_unused_result)); extern void __wt_meta_ckptlist_free(WT_SESSION_IMPL *session, WT_CKPT **ckptbasep); diff --git a/src/third_party/wiredtiger/src/include/wiredtiger.in b/src/third_party/wiredtiger/src/include/wiredtiger.in index b3fa068047e..307c694c191 100644 --- a/src/third_party/wiredtiger/src/include/wiredtiger.in +++ b/src/third_party/wiredtiger/src/include/wiredtiger.in @@ -1853,8 +1853,9 @@ struct __wt_session { * one of the following keys: \c "from=all" to drop all checkpoints\, \c "from=<checkpoint>" * to drop all checkpoints after and including the named checkpoint\, or \c * "to=<checkpoint>" to drop all checkpoints before and including the named checkpoint. - * Checkpoints cannot be dropped while a hot backup is in progress or if open in a cursor., - * a list of strings; default empty.} + * Checkpoints cannot be dropped if open in a cursor. While a hot backup is in progress\, + * checkpoints created prior to the start of the backup cannot be dropped., a list of + * strings; default empty.} * @config{force, if false (the default)\, checkpoints may be skipped if the underlying * object has not been modified\, if true\, this option forces the checkpoint., a boolean * flag; default \c false.} diff --git a/src/third_party/wiredtiger/src/log/log.c b/src/third_party/wiredtiger/src/log/log.c index b0b6e5da1ae..b74ba6817f8 100644 --- a/src/third_party/wiredtiger/src/log/log.c +++ b/src/third_party/wiredtiger/src/log/log.c @@ -1220,9 +1220,9 @@ __log_newfile(WT_SESSION_IMPL *session, bool conn_open, bool *created) * can copy the files in any way they choose, and a log file rename might confuse things. */ create_log = true; - if (conn->log_prealloc > 0 && !conn->hot_backup) { + if (conn->log_prealloc > 0 && conn->hot_backup_start == 0) { __wt_readlock(session, &conn->hot_backup_lock); - if (conn->hot_backup) + if (conn->hot_backup_start != 0) __wt_readunlock(session, &conn->hot_backup_lock); else { ret = __log_alloc_prealloc(session, log->fileid); @@ -1253,7 +1253,7 @@ __log_newfile(WT_SESSION_IMPL *session, bool conn_open, bool *created) * not using pre-allocated log files during backup * (see comment above). */ - if (!conn->hot_backup) + if (conn->hot_backup_start == 0) log->prep_missed++; WT_RET(__wt_log_allocfile(session, log->fileid, WT_LOG_FILENAME)); } @@ -1441,9 +1441,9 @@ __log_truncate_file(WT_SESSION_IMPL *session, WT_FH *log_fh, wt_off_t offset) conn = S2C(session); log = conn->log; - if (!F_ISSET(log, WT_LOG_TRUNCATE_NOTSUP) && !conn->hot_backup) { + if (!F_ISSET(log, WT_LOG_TRUNCATE_NOTSUP) && conn->hot_backup_start == 0) { __wt_readlock(session, &conn->hot_backup_lock); - if (conn->hot_backup) + if (conn->hot_backup_start != 0) __wt_readunlock(session, &conn->hot_backup_lock); else { ret = __wt_ftruncate(session, log_fh, offset); diff --git a/src/third_party/wiredtiger/src/lsm/lsm_work_unit.c b/src/third_party/wiredtiger/src/lsm/lsm_work_unit.c index 0bc4727047d..f009f2fd7f0 100644 --- a/src/third_party/wiredtiger/src/lsm/lsm_work_unit.c +++ b/src/third_party/wiredtiger/src/lsm/lsm_work_unit.c @@ -698,7 +698,7 @@ __wt_lsm_free_chunks(WT_SESSION_IMPL *session, WT_LSM_TREE *lsm_tree) * prevents us from removing a file that hot backup already * knows about. */ - if (S2C(session)->hot_backup) + if (S2C(session)->hot_backup_start != 0) break; /* diff --git a/src/third_party/wiredtiger/src/meta/meta_ckpt.c b/src/third_party/wiredtiger/src/meta/meta_ckpt.c index 2494acdc236..8e2d3dc0325 100644 --- a/src/third_party/wiredtiger/src/meta/meta_ckpt.c +++ b/src/third_party/wiredtiger/src/meta/meta_ckpt.c @@ -256,19 +256,24 @@ __ckpt_compare_order(const void *a, const void *b) * Load all available checkpoint information for a file. */ int -__wt_meta_ckptlist_get(WT_SESSION_IMPL *session, const char *fname, WT_CKPT **ckptbasep) +__wt_meta_ckptlist_get( + WT_SESSION_IMPL *session, const char *fname, bool update, WT_CKPT **ckptbasep) { WT_CKPT *ckpt, *ckptbase; WT_CONFIG ckptconf; WT_CONFIG_ITEM k, v; + WT_CONNECTION_IMPL *conn; WT_DECL_ITEM(buf); WT_DECL_RET; size_t allocated, slot; + time_t secs; + uint64_t most_recent; char *config; *ckptbasep = NULL; ckptbase = NULL; + conn = S2C(session); allocated = slot = 0; config = NULL; @@ -302,6 +307,27 @@ __wt_meta_ckptlist_get(WT_SESSION_IMPL *session, const char *fname, WT_CKPT **ck /* Sort in creation-order. */ __wt_qsort(ckptbase, slot, sizeof(WT_CKPT), __ckpt_compare_order); + if (update) { + /* + * We're updating the time value here instead of in the "set" helper because this needs to + * happen first in order to figure out what checkpoints we can safely remove. + */ + ckpt = &ckptbase[slot]; + __wt_seconds(session, &secs); + ckpt->sec = (uint64_t)secs; + /* + * Update time value for most recent checkpoint, not letting it move backwards. It is + * possible to race here, so use atomic CAS. This code relies on the fact that anyone we + * race with will only increase (never decrease) the most recent checkpoint time value. + */ + for (;;) { + WT_ORDERED_READ(most_recent, conn->ckpt_most_recent); + if (ckpt->sec <= most_recent || + __wt_atomic_cas64(&conn->ckpt_most_recent, most_recent, ckpt->sec)) + break; + } + } + /* Return the array to our caller. */ *ckptbasep = ckptbase; @@ -380,7 +406,6 @@ __wt_meta_ckptlist_set( WT_CKPT *ckpt; WT_DECL_ITEM(buf); WT_DECL_RET; - time_t secs; int64_t maxorder; const char *sep; bool has_lsn; @@ -419,13 +444,6 @@ __wt_meta_ckptlist_set( /* Set the order and timestamp. */ if (F_ISSET(ckpt, WT_CKPT_ADD)) ckpt->order = ++maxorder; - - /* - * XXX Assumes a time_t fits into a uintmax_t, which isn't guaranteed, a time_t has to - * be an arithmetic type, but not an integral type. - */ - __wt_seconds(session, &secs); - ckpt->sec = (uintmax_t)secs; } if (strcmp(ckpt->name, WT_CHECKPOINT) == 0) WT_ERR(__wt_buf_catfmt(session, buf, diff --git a/src/third_party/wiredtiger/src/meta/meta_ext.c b/src/third_party/wiredtiger/src/meta/meta_ext.c index e44b49454e2..efda2f723d3 100644 --- a/src/third_party/wiredtiger/src/meta/meta_ext.c +++ b/src/third_party/wiredtiger/src/meta/meta_ext.c @@ -88,7 +88,7 @@ int __wt_metadata_get_ckptlist(WT_SESSION *session, const char *name, WT_CKPT **ckptbasep) WT_GCC_FUNC_ATTRIBUTE((visibility("default"))) { - return (__wt_meta_ckptlist_get((WT_SESSION_IMPL *)session, name, ckptbasep)); + return (__wt_meta_ckptlist_get((WT_SESSION_IMPL *)session, name, false, ckptbasep)); } /* diff --git a/src/third_party/wiredtiger/src/schema/schema_util.c b/src/third_party/wiredtiger/src/schema/schema_util.c index 75a0e730201..4dc2ba480e4 100644 --- a/src/third_party/wiredtiger/src/schema/schema_util.c +++ b/src/third_party/wiredtiger/src/schema/schema_util.c @@ -23,14 +23,14 @@ __wt_schema_backup_check(WT_SESSION_IMPL *session, const char *name) char **backup_list; conn = S2C(session); - if (!conn->hot_backup) + if (conn->hot_backup_start == 0) return (0); __wt_readlock(session, &conn->hot_backup_lock); /* * There is a window at the end of a backup where the list has been cleared from the connection * but the flag is still set. It is safe to drop at that point. */ - if (!conn->hot_backup || (backup_list = conn->hot_backup_list) == NULL) { + if (conn->hot_backup_start == 0 || (backup_list = conn->hot_backup_list) == NULL) { __wt_readunlock(session, &conn->hot_backup_lock); return (0); } diff --git a/src/third_party/wiredtiger/src/txn/txn_ckpt.c b/src/third_party/wiredtiger/src/txn/txn_ckpt.c index 442c7eeee29..6d57c01a751 100644 --- a/src/third_party/wiredtiger/src/txn/txn_ckpt.c +++ b/src/third_party/wiredtiger/src/txn/txn_ckpt.c @@ -1127,7 +1127,6 @@ static void __drop(WT_CKPT *ckptbase, const char *name, size_t len) { WT_CKPT *ckpt; - u_int max_ckpt_drop; /* * If we're dropping internal checkpoints, match to the '.' separating the checkpoint name from @@ -1136,20 +1135,9 @@ __drop(WT_CKPT *ckptbase, const char *name, size_t len) * it's one we want to drop. */ if (strncmp(WT_CHECKPOINT, name, len) == 0) { - /* - * Currently, hot backup cursors block checkpoint drop, which means releasing a hot backup - * cursor can result in immediately attempting to drop lots of checkpoints, which involves a - * fair amount of work while holding locks. Limit the number of standard checkpoints dropped - * per checkpoint. - */ - max_ckpt_drop = 0; WT_CKPT_FOREACH (ckptbase, ckpt) - if (WT_PREFIX_MATCH(ckpt->name, WT_CHECKPOINT)) { + if (WT_PREFIX_MATCH(ckpt->name, WT_CHECKPOINT)) F_SET(ckpt, WT_CKPT_DELETE); -#define WT_MAX_CHECKPOINT_DROP 4 - if (++max_ckpt_drop >= WT_MAX_CHECKPOINT_DROP) - break; - } } else WT_CKPT_FOREACH (ckptbase, ckpt) if (WT_STRING_MATCH(ckpt->name, name, len)) @@ -1234,9 +1222,10 @@ __checkpoint_lock_dirty_tree( WT_CONNECTION_IMPL *conn; WT_DATA_HANDLE *dhandle; WT_DECL_RET; + u_int max_ckpt_drop; char *name_alloc; const char *name; - bool hot_backup_locked; + bool hot_backup_locked, is_wt_ckpt; btree = S2BT(session); conn = S2C(session); @@ -1264,7 +1253,7 @@ __checkpoint_lock_dirty_tree( WT_ASSERT(session, !need_tracking || WT_IS_METADATA(dhandle) || WT_META_TRACKING(session)); /* Get the list of checkpoints for this file. */ - WT_RET(__wt_meta_ckptlist_get(session, dhandle->name, &ckptbase)); + WT_RET(__wt_meta_ckptlist_get(session, dhandle->name, true, &ckptbase)); /* This may be a named checkpoint, check the configuration. */ cval.len = 0; @@ -1320,28 +1309,41 @@ __checkpoint_lock_dirty_tree( __wt_free(session, name_alloc); F_SET(ckpt, WT_CKPT_ADD); - /* - * We can't delete checkpoints if a backup cursor is open. WiredTiger checkpoints are uniquely - * named and it's OK to have multiple of them in the system: clear the delete flag for them, and - * otherwise fail. Hold the lock until we're done (blocking hot backups from starting), we don't - * want to race with a future hot backup. - */ __wt_readlock(session, &conn->hot_backup_lock); hot_backup_locked = true; - if (conn->hot_backup) - WT_CKPT_FOREACH (ckptbase, ckpt) { - if (!F_ISSET(ckpt, WT_CKPT_DELETE)) - continue; - if (WT_PREFIX_MATCH(ckpt->name, WT_CHECKPOINT)) { + + /* Check that it is OK to remove all the checkpoints marked for deletion. */ + max_ckpt_drop = 0; + WT_CKPT_FOREACH (ckptbase, ckpt) { + if (!F_ISSET(ckpt, WT_CKPT_DELETE)) + continue; + is_wt_ckpt = WT_PREFIX_MATCH(ckpt->name, WT_CHECKPOINT); + + /* + * If there is a hot backup, don't delete any WiredTiger checkpoint that could possibly have + * been created before the backup started. Fail if trying to delete any other named + * checkpoint. + */ + if (conn->hot_backup_start != 0 && ckpt->sec <= conn->hot_backup_start) { + if (is_wt_ckpt) { F_CLR(ckpt, WT_CKPT_DELETE); continue; } WT_ERR_MSG(session, EBUSY, "checkpoint %s blocked by hot backup: it would " - "delete an existing checkpoint, and checkpoints " - "cannot be deleted during a hot backup", + "delete an existing named checkpoint, and such " + "checkpoints cannot be deleted during a hot backup", ckpt->name); } + /* + * Dropping checkpoints involves a fair amount of work while holding locks. Limit the number + * of WiredTiger checkpoints dropped per checkpoint. + */ + if (is_wt_ckpt) +#define WT_MAX_CHECKPOINT_DROP 4 + if (++max_ckpt_drop >= WT_MAX_CHECKPOINT_DROP) + F_CLR(ckpt, WT_CKPT_DELETE); + } /* * Mark old checkpoints that are being deleted and figure out which trees we can skip in this @@ -1364,7 +1366,8 @@ __checkpoint_lock_dirty_tree( WT_CKPT_FOREACH (ckptbase, ckpt) { if (!F_ISSET(ckpt, WT_CKPT_DELETE)) continue; - + WT_ASSERT(session, !WT_PREFIX_MATCH(ckpt->name, WT_CHECKPOINT) || + conn->hot_backup_start == 0 || ckpt->sec > conn->hot_backup_start); /* * We can't delete checkpoints referenced by a cursor. WiredTiger checkpoints are * uniquely named and it's OK to have multiple in the system: clear the delete flag for diff --git a/src/third_party/wiredtiger/src/txn/txn_log.c b/src/third_party/wiredtiger/src/txn/txn_log.c index c06b8fcb1a4..c107faeb86a 100644 --- a/src/third_party/wiredtiger/src/txn/txn_log.c +++ b/src/third_party/wiredtiger/src/txn/txn_log.c @@ -537,8 +537,9 @@ __wt_txn_checkpoint_log(WT_SESSION_IMPL *session, bool full, uint32_t flags, WT_ * connection close, only during a full checkpoint. A clean close may not update any * metadata LSN and we do not want to archive in that case. */ - if (!conn->hot_backup && (!FLD_ISSET(conn->log_flags, WT_CONN_LOG_RECOVER_DIRTY) || - FLD_ISSET(conn->log_flags, WT_CONN_LOG_FORCE_DOWNGRADE)) && + if (conn->hot_backup_start == 0 && + (!FLD_ISSET(conn->log_flags, WT_CONN_LOG_RECOVER_DIRTY) || + FLD_ISSET(conn->log_flags, WT_CONN_LOG_FORCE_DOWNGRADE)) && txn->full_ckpt) __wt_log_ckpt(session, ckpt_lsn); diff --git a/src/third_party/wiredtiger/test/suite/test_backup01.py b/src/third_party/wiredtiger/test/suite/test_backup01.py index b002a9c4601..88dcc479924 100644 --- a/src/third_party/wiredtiger/test/suite/test_backup01.py +++ b/src/third_party/wiredtiger/test/suite/test_backup01.py @@ -30,6 +30,7 @@ import glob import os import shutil import string +import time from suite_subprocess import suite_subprocess import wiredtiger, wttest from wtdataset import SimpleDataSet, ComplexDataSet, ComplexLSMDataSet @@ -163,8 +164,7 @@ class test_backup(wttest.WiredTigerTestCase, suite_subprocess): self.assertEqual(ret, wiredtiger.WT_NOTFOUND) self.assertEqual(i, total) - # Test that named checkpoints can't be deleted while backup cursors are - # open, but that normal checkpoints continue to work. + # Test interaction between checkpoints and a backup cursor. def test_checkpoint_delete(self): # You cannot name checkpoints including LSM tables, skip those. self.populate(1) @@ -177,7 +177,8 @@ class test_backup(wttest.WiredTigerTestCase, suite_subprocess): self.objs[0][0], None, "checkpoint=one")) # Confirm opening a backup cursor causes checkpoint to fail if dropping - # a named checkpoint, but does not stop a default checkpoint. + # a named checkpoint created before the backup cursor, but does not stop a + # default checkpoint. cursor = self.session.open_cursor('backup:', None, None) self.session.checkpoint() msg = '/checkpoints cannot be deleted during a hot backup/' @@ -187,7 +188,23 @@ class test_backup(wttest.WiredTigerTestCase, suite_subprocess): self.assertRaisesWithMessage(wiredtiger.WiredTigerError, lambda: self.session.checkpoint("name=three,drop=(two)"), msg) self.session.checkpoint() + + # Need to pause a couple seconds; checkpoints that are assigned the same timestamp as + # the backup will be pinned, even if they occur after the backup starts. + time.sleep(2) + self.session.checkpoint("name=four") + self.session.checkpoint("drop=(four)") + self.assertRaises(wiredtiger.WiredTigerError, + lambda: self.session.open_cursor( + self.objs[0][0], None, "checkpoint=four")) + + # Confirm that after closing the backup cursor the original named checkpoint can + # be deleted. cursor.close() + self.session.checkpoint("drop=(two)") + self.assertRaises(wiredtiger.WiredTigerError, + lambda: self.session.open_cursor( + self.objs[0][0], None, "checkpoint=two")) if __name__ == '__main__': wttest.run() diff --git a/src/third_party/wiredtiger/test/suite/test_checkpoint05.py b/src/third_party/wiredtiger/test/suite/test_checkpoint05.py new file mode 100644 index 00000000000..58af3003a60 --- /dev/null +++ b/src/third_party/wiredtiger/test/suite/test_checkpoint05.py @@ -0,0 +1,84 @@ +#!/usr/bin/env python +# +# Public Domain 2014-2020 MongoDB, Inc. +# Public Domain 2008-2014 WiredTiger, Inc. +# +# This is free and unencumbered software released into the public domain. +# +# Anyone is free to copy, modify, publish, use, compile, sell, or +# distribute this software, either in source code form or as a compiled +# binary, for any purpose, commercial or non-commercial, and by any +# means. +# +# In jurisdictions that recognize copyright laws, the author or authors +# of this software dedicate any and all copyright interest in the +# software to the public domain. We make this dedication for the benefit +# of the public at large and to the detriment of our heirs and +# successors. We intend this dedication to be an overt act of +# relinquishment in perpetuity of all present and future rights to this +# software under copyright law. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +# IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +# OTHER DEALINGS IN THE SOFTWARE. +# +# test_checkpoint05.py +# Verify that we don't accumulate a lot of checkpoints while a backup +# cursor is open. WiredTiger checkpoints created after the backup cursor +# should get deleted as usual. + +import time +import wiredtiger, wttest + +class test_checkpoint05(wttest.WiredTigerTestCase): + conn_config = 'create,cache_size=100MB,log=(archive=false,enabled=true,file_max=100K)' + + def count_checkpoints(self): + metadata_cursor = self.session.open_cursor('metadata:', None, None) + + nckpt = 0 + while metadata_cursor.next() == 0: + key = metadata_cursor.get_key() + value = metadata_cursor[key] + nckpt = nckpt + value.count("WiredTigerCheckpoint") + metadata_cursor.close() + return nckpt + + def test_checkpoints_during_backup(self): + self.uri = 'table:ckpt05' + self.session.create(self.uri, 'key_format=i,value_format=i') + + # Setup: Insert some data and checkpoint it + cursor = self.session.open_cursor(self.uri, None) + for i in range(16): + cursor[i] = i + self.session.checkpoint(None) + + # Create backup and check how many checkpoints we have. + backup_cursor = self.session.open_cursor('backup:', None, None) + initial_count = self.count_checkpoints() + + # Checkpoints created immediately after a backup cursor may get pinned. + # Pause to avoid this. + time.sleep(2) + + # Take a bunch of checkpoints. + for i in range (50): + self.session.checkpoint('force=true') + cursor.close() + + # There may be a few more checkpoints than when we opened the + # backup cursor, but not too many more. The factor of three + # is generous. But if WT isn't deleting checkpoints there would + # be about 30x more checkpoints here. + final_count = self.count_checkpoints() + self.assertTrue (final_count < initial_count * 3) + + self.session.close() + +if __name__ == '__main__': + wttest.run() |