summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuke Chen <luke.chen@mongodb.com>2020-08-07 14:35:38 +1000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2020-08-07 04:44:34 +0000
commit7a351100d3f8e52da17dd440101d99e86aedb772 (patch)
tree0c8e4294300919fd86c9365e5ea70d4283600f13
parente199a343576be60a13154cf771e7a4d623e1d6c0 (diff)
downloadmongo-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
-rw-r--r--src/third_party/wiredtiger/dist/api_data.py5
-rw-r--r--src/third_party/wiredtiger/import.data2
-rw-r--r--src/third_party/wiredtiger/src/block/block_write.c4
-rw-r--r--src/third_party/wiredtiger/src/btree/bt_vrfy.c2
-rw-r--r--src/third_party/wiredtiger/src/conn/conn_log.c8
-rw-r--r--src/third_party/wiredtiger/src/conn/conn_open.c4
-rw-r--r--src/third_party/wiredtiger/src/cursor/cur_backup.c6
-rw-r--r--src/third_party/wiredtiger/src/docs/backup.dox7
-rw-r--r--src/third_party/wiredtiger/src/include/connection.h3
-rw-r--r--src/third_party/wiredtiger/src/include/extern.h4
-rw-r--r--src/third_party/wiredtiger/src/include/wiredtiger.in5
-rw-r--r--src/third_party/wiredtiger/src/log/log.c10
-rw-r--r--src/third_party/wiredtiger/src/lsm/lsm_work_unit.c2
-rw-r--r--src/third_party/wiredtiger/src/meta/meta_ckpt.c36
-rw-r--r--src/third_party/wiredtiger/src/meta/meta_ext.c2
-rw-r--r--src/third_party/wiredtiger/src/schema/schema_util.c4
-rw-r--r--src/third_party/wiredtiger/src/txn/txn_ckpt.c61
-rw-r--r--src/third_party/wiredtiger/src/txn/txn_log.c5
-rw-r--r--src/third_party/wiredtiger/test/suite/test_backup01.py23
-rw-r--r--src/third_party/wiredtiger/test/suite/test_checkpoint05.py84
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()