summaryrefslogtreecommitdiff
path: root/src/third_party
diff options
context:
space:
mode:
authorLuke Chen <luke.chen@mongodb.com>2019-12-19 03:46:45 +0000
committerevergreen <evergreen@mongodb.com>2019-12-19 03:46:45 +0000
commit114ee790aaed1357f1d99dee1d610c26ae12858e (patch)
tree545a23afda994a5920a62bcf9ee4dfd7d52367a7 /src/third_party
parent6adf8ad187352b0b253d4fbb282c8af9d1e89f18 (diff)
downloadmongo-114ee790aaed1357f1d99dee1d610c26ae12858e.tar.gz
Import wiredtiger: 77f9aebf23bb7fcb3911345741f20d67128f8da6 from branch mongodb-4.4
ref: 81a6cd2196..77f9aebf23 for: 4.3.3 WT-4969 Remove lsm_merge configuration option WT-5006 Migrate Jenkins “wiredtiger-test-split-stress” job to Evergreen WT-5037 Move Application Developer Resources documentation in github to WT documentation WT-5077 Handle ENOENT without failure when copying the directory WT-5139 WiredTiger incremental backup API WT-5221 Bypass test_wt2853_perf in Evergreen make-check-msan-test WT-5257 Coverity analysis bug: 113971 Dereference after null check WT-5263 Prepared updates written to the lookaside file are not always read as needed WT-5309 Update format.sh script to add prefix command argument WT-5314 Avoid loading extensions that have blank name for Python tests WT-5319 Avoid clearing the saved last-key when no instantiated key
Diffstat (limited to 'src/third_party')
-rw-r--r--src/third_party/wiredtiger/dist/api_data.py36
-rw-r--r--src/third_party/wiredtiger/dist/filelist2
-rw-r--r--src/third_party/wiredtiger/dist/s_define.list2
-rw-r--r--src/third_party/wiredtiger/dist/s_string.ok1
-rw-r--r--src/third_party/wiredtiger/examples/c/Makefile.am1
-rw-r--r--src/third_party/wiredtiger/examples/c/ex_backup_block.c460
-rw-r--r--src/third_party/wiredtiger/import.data2
-rw-r--r--src/third_party/wiredtiger/src/config/config_def.c69
-rw-r--r--src/third_party/wiredtiger/src/config/config_upgrade.c31
-rw-r--r--src/third_party/wiredtiger/src/conn/conn_api.c21
-rw-r--r--src/third_party/wiredtiger/src/conn/conn_handle.c8
-rw-r--r--src/third_party/wiredtiger/src/cursor/cur_backup.c460
-rw-r--r--src/third_party/wiredtiger/src/cursor/cur_backup_incr.c233
-rw-r--r--src/third_party/wiredtiger/src/docs/devdoc-index.dox35
-rw-r--r--src/third_party/wiredtiger/src/docs/devdoc-optrack.dox351
-rw-r--r--src/third_party/wiredtiger/src/docs/devdoc-perf-flamegraphs.dox74
-rw-r--r--src/third_party/wiredtiger/src/docs/devdoc-perf.dox160
-rw-r--r--src/third_party/wiredtiger/src/docs/devdoc-schema.dox2
-rw-r--r--src/third_party/wiredtiger/src/docs/devdoc-statistics.dox165
-rw-r--r--src/third_party/wiredtiger/src/docs/spell.ok39
-rw-r--r--src/third_party/wiredtiger/src/docs/style/wiredtiger.css6
-rw-r--r--src/third_party/wiredtiger/src/include/btree.i7
-rw-r--r--src/third_party/wiredtiger/src/include/connection.h130
-rw-r--r--src/third_party/wiredtiger/src/include/cursor.h45
-rw-r--r--src/third_party/wiredtiger/src/include/extern.h6
-rw-r--r--src/third_party/wiredtiger/src/include/meta.h26
-rw-r--r--src/third_party/wiredtiger/src/include/wiredtiger.in69
-rw-r--r--src/third_party/wiredtiger/src/include/wt_internal.h2
-rw-r--r--src/third_party/wiredtiger/src/meta/meta_turtle.c18
-rw-r--r--src/third_party/wiredtiger/src/reconcile/rec_visibility.c13
-rw-r--r--src/third_party/wiredtiger/src/reconcile/rec_write.c1
-rw-r--r--src/third_party/wiredtiger/test/csuite/random_directio/util.c8
-rwxr-xr-xsrc/third_party/wiredtiger/test/evergreen.yml18
-rwxr-xr-xsrc/third_party/wiredtiger/test/format/format.sh24
-rw-r--r--src/third_party/wiredtiger/test/format/snap.c6
-rw-r--r--src/third_party/wiredtiger/test/suite/test_backup06.py16
-rw-r--r--src/third_party/wiredtiger/test/suite/test_backup10.py5
-rw-r--r--src/third_party/wiredtiger/test/suite/test_backup11.py246
-rw-r--r--src/third_party/wiredtiger/test/suite/test_backup12.py187
-rwxr-xr-xsrc/third_party/wiredtiger/test/suite/wttest.py2
-rw-r--r--src/third_party/wiredtiger/test/utility/test_util.h12
41 files changed, 2701 insertions, 298 deletions
diff --git a/src/third_party/wiredtiger/dist/api_data.py b/src/third_party/wiredtiger/dist/api_data.py
index 6d9d4f1db3d..6f967a92118 100644
--- a/src/third_party/wiredtiger/dist/api_data.py
+++ b/src/third_party/wiredtiger/dist/api_data.py
@@ -602,9 +602,6 @@ connection_runtime_config = [
merge LSM chunks where possible''',
type='boolean')
]),
- Config('lsm_merge', 'true', r'''
- merge LSM chunks where possible (deprecated)''',
- type='boolean', undoc=True),
Config('operation_timeout_ms', '0', r'''
when non-zero, a requested limit on the number of elapsed real time milliseconds
application threads will take to complete database operations. Time is measured from the
@@ -680,6 +677,7 @@ connection_runtime_config = [
list, such as <code>"verbose=[evictserver,read]"</code>''',
type='list', choices=[
'api',
+ 'backup',
'block',
'checkpoint',
'checkpoint_progress',
@@ -1208,6 +1206,38 @@ methods = {
characters are hexadecimal encoded. These formats are compatible
with the @ref util_dump and @ref util_load commands''',
choices=['hex', 'json', 'print']),
+ Config('incremental', '', r'''
+ configure the cursor for block incremental backup usage. These formats
+ are only compatible with the backup data source; see @ref backup''',
+ type='category', subconfig=[
+ Config('enabled', 'false', r'''
+ whether to configure this backup as the starting point for a subsequent
+ incremental backup''',
+ type='boolean'),
+ Config('file', '', r'''
+ the file name when opening a duplicate incremental backup cursor.
+ That duplicate cursor will return the block modifications relevant
+ to the given file name'''),
+ Config('force_stop', 'false', r'''
+ causes all block incremental backup information to be released. This is
+ on an open_cursor call and the resources will be released when this
+ cursor is closed. No other operations should be done on this open cursor''',
+ type='boolean'),
+ Config('granularity', '16MB', r'''
+ this setting manages the granularity of how WiredTiger maintains modification
+ maps internally. The larger the granularity, the smaller amount of information
+ WiredTiger need to maintain''',
+ min='1MB', max='2GB'),
+ Config('src_id', '', r'''
+ a string that identifies a previous checkpoint backup source as the source
+ of this incremental backup. This identifier must have already been created
+ by use of the 'this_id' configuration in an earlier backup. A source id is
+ required to begin an incremental backup'''),
+ Config('this_id', '', r'''
+ a string that identifies the current system state as a future backup source
+ for an incremental backup via 'src_id'. This identifier is required when opening
+ an incremental backup cursor and an error will be returned if one is not provided'''),
+ ]),
Config('next_random', 'false', r'''
configure the cursor to return a pseudo-random record from the
object when the WT_CURSOR::next method is called; valid only for
diff --git a/src/third_party/wiredtiger/dist/filelist b/src/third_party/wiredtiger/dist/filelist
index 9e7eb0b23ac..6cf9369fecd 100644
--- a/src/third_party/wiredtiger/dist/filelist
+++ b/src/third_party/wiredtiger/dist/filelist
@@ -64,7 +64,6 @@ src/config/config_check.c
src/config/config_collapse.c
src/config/config_def.c
src/config/config_ext.c
-src/config/config_upgrade.c
src/conn/api_calc_modify.c
src/conn/api_strerror.c
src/conn/api_version.c
@@ -81,6 +80,7 @@ src/conn/conn_reconfig.c
src/conn/conn_stat.c
src/conn/conn_sweep.c
src/cursor/cur_backup.c
+src/cursor/cur_backup_incr.c
src/cursor/cur_bulk.c
src/cursor/cur_config.c
src/cursor/cur_ds.c
diff --git a/src/third_party/wiredtiger/dist/s_define.list b/src/third_party/wiredtiger/dist/s_define.list
index 85a240550ea..3a5f70691a7 100644
--- a/src/third_party/wiredtiger/dist/s_define.list
+++ b/src/third_party/wiredtiger/dist/s_define.list
@@ -15,12 +15,14 @@ WT_ALIGN_CHECK
WT_ATOMIC_CAS
WT_ATOMIC_CAS_FUNC
WT_ATOMIC_FUNC
+WT_BACKUP_INVALID
WT_BLOCK_DESC_SIZE
WT_BLOCK_EXTLIST_VERSION_ORIG
WT_BLOCK_HEADER_SIZE
WT_CACHE_LINE_ALIGNMENT
WT_CACHE_LINE_PAD_BEGIN
WT_CACHE_LINE_PAD_END
+WT_CKPT_BLOCK_MODS
WT_CLOCKDIFF_NS
WT_CONN_CHECK_PANIC
WT_DEADLOCK
diff --git a/src/third_party/wiredtiger/dist/s_string.ok b/src/third_party/wiredtiger/dist/s_string.ok
index 66480a55eec..6cda20d9b3d 100644
--- a/src/third_party/wiredtiger/dist/s_string.ok
+++ b/src/third_party/wiredtiger/dist/s_string.ok
@@ -1128,6 +1128,7 @@ pvA
pwrite
py
qdown
+qqq
qrrSS
qsort
quartile
diff --git a/src/third_party/wiredtiger/examples/c/Makefile.am b/src/third_party/wiredtiger/examples/c/Makefile.am
index e441d45af46..5e9b91843d5 100644
--- a/src/third_party/wiredtiger/examples/c/Makefile.am
+++ b/src/third_party/wiredtiger/examples/c/Makefile.am
@@ -8,6 +8,7 @@ noinst_PROGRAMS = \
ex_all \
ex_async \
ex_backup \
+ ex_backup_block \
ex_call_center \
ex_config_parse \
ex_cursor \
diff --git a/src/third_party/wiredtiger/examples/c/ex_backup_block.c b/src/third_party/wiredtiger/examples/c/ex_backup_block.c
new file mode 100644
index 00000000000..fbae6e6da5d
--- /dev/null
+++ b/src/third_party/wiredtiger/examples/c/ex_backup_block.c
@@ -0,0 +1,460 @@
+/*-
+ * Public Domain 2014-2019 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.
+ *
+ * ex_backup_block.c
+ * demonstrates how to use block-based incremental backup.
+ */
+#include <test_util.h>
+
+static const char *const home = "WT_BLOCK";
+static const char *const home_full = "WT_BLOCK_LOG_FULL";
+static const char *const home_incr = "WT_BLOCK_LOG_INCR";
+
+static const char *const full_out = "./backup_block_full";
+static const char *const incr_out = "./backup_block_incr";
+
+static const char *const uri = "table:main";
+static const char *const uri2 = "table:extra";
+
+typedef struct __filelist {
+ const char *name;
+ bool exist;
+} FILELIST;
+
+static FILELIST *last_flist = NULL;
+static size_t filelist_count = 0;
+
+#define FLIST_INIT 16
+
+#define CONN_CONFIG "create,cache_size=100MB,log=(enabled=true,file_max=100K)"
+#define MAX_ITERATIONS 5
+#define MAX_KEYS 10000
+
+static int
+compare_backups(int i)
+{
+ int ret;
+ char buf[1024], msg[32];
+
+ /*
+ * We run 'wt dump' on both the full backup directory and the
+ * incremental backup directory for this iteration. Since running
+ * 'wt' runs recovery and makes both directories "live", we need
+ * a new directory for each iteration.
+ *
+ * If i == 0, we're comparing against the main, original directory
+ * with the final incremental directory.
+ */
+ if (i == 0)
+ (void)snprintf(buf, sizeof(buf), "../../wt -R -h %s dump main > %s.%d", home, full_out, i);
+ else
+ (void)snprintf(
+ buf, sizeof(buf), "../../wt -R -h %s.%d dump main > %s.%d", home_full, i, full_out, i);
+ error_check(system(buf));
+ /*
+ * Now run dump on the incremental directory.
+ */
+ (void)snprintf(
+ buf, sizeof(buf), "../../wt -R -h %s.%d dump main > %s.%d", home_incr, i, incr_out, i);
+ error_check(system(buf));
+
+ /*
+ * Compare the files.
+ */
+ (void)snprintf(buf, sizeof(buf), "cmp %s.%d %s.%d", full_out, i, incr_out, i);
+ ret = system(buf);
+ if (i == 0)
+ (void)snprintf(msg, sizeof(msg), "%s", "MAIN");
+ else
+ (void)snprintf(msg, sizeof(msg), "%d", i);
+ printf("Iteration %s: Tables %s.%d and %s.%d %s\n", msg, full_out, i, incr_out, i,
+ ret == 0 ? "identical" : "differ");
+ if (ret != 0)
+ exit(1);
+
+ /*
+ * If they compare successfully, clean up.
+ */
+ if (i != 0) {
+ (void)snprintf(buf, sizeof(buf), "rm -rf %s.%d %s.%d %s.%d %s.%d", home_full, i, home_incr,
+ i, full_out, i, incr_out, i);
+ error_check(system(buf));
+ }
+ return (ret);
+}
+
+/*
+ * Set up all the directories needed for the test. We have a full backup directory for each
+ * iteration and an incremental backup for each iteration. That way we can compare the full and
+ * incremental each time through.
+ */
+static void
+setup_directories(void)
+{
+ int i;
+ char buf[1024];
+
+ for (i = 0; i < MAX_ITERATIONS; i++) {
+ /*
+ * For incremental backups we need 0-N. The 0 incremental directory will compare with the
+ * original at the end.
+ */
+ (void)snprintf(buf, sizeof(buf), "rm -rf %s.%d && mkdir %s.%d", home_incr, i, home_incr, i);
+ error_check(system(buf));
+ if (i == 0)
+ continue;
+ /*
+ * For full backups we need 1-N.
+ */
+ (void)snprintf(buf, sizeof(buf), "rm -rf %s.%d && mkdir %s.%d", home_full, i, home_full, i);
+ error_check(system(buf));
+ }
+}
+
+static void
+add_work(WT_SESSION *session, int iter)
+{
+ WT_CURSOR *cursor, *cursor2;
+ int i;
+ char k[32], v[32];
+
+ error_check(session->open_cursor(session, uri, NULL, NULL, &cursor));
+ /*
+ * Only on even iterations add content to the extra table. This illustrates and shows that
+ * sometimes only some tables will be updated.
+ */
+ cursor2 = NULL;
+ if (iter % 2 == 0)
+ error_check(session->open_cursor(session, uri2, NULL, NULL, &cursor2));
+ /*
+ * Perform some operations with individual auto-commit transactions.
+ */
+ for (i = 0; i < MAX_KEYS; i++) {
+ (void)snprintf(k, sizeof(k), "key.%d.%d", iter, i);
+ (void)snprintf(v, sizeof(v), "value.%d.%d", iter, i);
+ cursor->set_key(cursor, k);
+ cursor->set_value(cursor, v);
+ error_check(cursor->insert(cursor));
+ if (cursor2 != NULL) {
+ cursor2->set_key(cursor2, k);
+ cursor2->set_value(cursor2, v);
+ error_check(cursor2->insert(cursor2));
+ }
+ }
+ error_check(cursor->close(cursor));
+ if (cursor2 != NULL)
+ error_check(cursor2->close(cursor2));
+}
+
+static int
+finalize_files(FILELIST *flistp, size_t count)
+{
+ size_t i;
+ char buf[512];
+
+ /*
+ * Process files that were removed. Any file that is not marked in the previous list as existing
+ * in this iteration should be removed. Free all previous filenames as we go along. Then free
+ * the overall list.
+ */
+ for (i = 0; i < filelist_count; ++i) {
+ if (last_flist[i].name == NULL)
+ break;
+ if (!last_flist[i].exist) {
+ (void)snprintf(buf, sizeof(buf), "rm WT_BLOCK_LOG_*/%s", last_flist[i].name);
+ error_check(system(buf));
+ }
+ free((void *)last_flist[i].name);
+ }
+ free(last_flist);
+
+ /* Set up the current list as the new previous list. */
+ last_flist = flistp;
+ filelist_count = count;
+ return (0);
+}
+
+/*
+ * Process a file name. Build up a list of current file names. But also process the file names from
+ * the previous iteration. Mark any name we see as existing so that the finalize function can remove
+ * any that don't exist. We walk the list each time. This is slow.
+ */
+static int
+process_file(FILELIST **flistp, size_t *countp, size_t *allocp, const char *filename)
+{
+ FILELIST *flist;
+ size_t alloc, i, new, orig;
+
+ /* Build up the current list, growing as needed. */
+ i = *countp;
+ alloc = *allocp;
+ flist = *flistp;
+ if (i == alloc) {
+ orig = alloc * sizeof(FILELIST);
+ new = orig * 2;
+ flist = realloc(flist, new);
+ testutil_assert(flist != NULL);
+ memset(flist + alloc, 0, new - orig);
+ *allocp = alloc * 2;
+ *flistp = flist;
+ }
+
+ flist[i].name = strdup(filename);
+ flist[i].exist = false;
+ ++(*countp);
+
+ /* Check against the previous list. */
+ for (i = 0; i < filelist_count; ++i) {
+ /* If name is NULL, we've reached the end of the list. */
+ if (last_flist[i].name == NULL)
+ break;
+ if (strcmp(filename, last_flist[i].name) == 0) {
+ last_flist[i].exist = true;
+ break;
+ }
+ }
+ return (0);
+}
+
+static void
+take_full_backup(WT_SESSION *session, int i)
+{
+ FILELIST *flist;
+ WT_CURSOR *cursor;
+ size_t alloc, count;
+ int j, ret;
+ char buf[1024], h[256];
+ const char *filename, *hdir;
+
+ /*
+ * First time through we take a full backup into the incremental directories. Otherwise only
+ * into the appropriate full directory.
+ */
+ if (i != 0) {
+ (void)snprintf(h, sizeof(h), "%s.%d", home_full, i);
+ hdir = h;
+ } else
+ hdir = home_incr;
+ if (i == 0) {
+ (void)snprintf(buf, sizeof(buf), "incremental=(enabled=true,this_id=ID%d)", i);
+ error_check(session->open_cursor(session, "backup:", NULL, buf, &cursor));
+ } else
+ error_check(session->open_cursor(session, "backup:", NULL, NULL, &cursor));
+
+ count = 0;
+ alloc = FLIST_INIT;
+ flist = calloc(alloc, sizeof(FILELIST));
+ testutil_assert(flist != NULL);
+ while ((ret = cursor->next(cursor)) == 0) {
+ error_check(cursor->get_key(cursor, &filename));
+ error_check(process_file(&flist, &count, &alloc, filename));
+ if (i == 0)
+ /*
+ * Take a full backup into each incremental directory.
+ */
+ for (j = 0; j < MAX_ITERATIONS; j++) {
+ (void)snprintf(h, sizeof(h), "%s.%d", home_incr, j);
+ (void)snprintf(buf, sizeof(buf), "cp %s/%s %s/%s", home, filename, h, filename);
+ printf("FULL: Copy: %s\n", buf);
+ error_check(system(buf));
+ }
+ else {
+ (void)snprintf(h, sizeof(h), "%s.%d", home_full, i);
+ (void)snprintf(buf, sizeof(buf), "cp %s/%s %s/%s", home, filename, hdir, filename);
+ printf("FULL %d: Copy: %s\n", i, buf);
+ error_check(system(buf));
+ }
+ }
+ scan_end_check(ret == WT_NOTFOUND);
+ error_check(cursor->close(cursor));
+ error_check(finalize_files(flist, count));
+}
+
+static void
+take_incr_backup(WT_SESSION *session, int i)
+{
+ FILELIST *flist;
+ WT_CURSOR *backup_cur, *incr_cur;
+ uint64_t offset, size, type;
+ size_t alloc, count;
+ int j, ret, rfd, wfd;
+ char buf[1024], h[256];
+ const char *filename;
+
+ /*! [incremental backup using block transfer]*/
+
+ /* Open the backup data source for incremental backup. */
+ (void)snprintf(buf, sizeof(buf), "incremental=(src_id=ID%d,this_id=ID%d)", i - 1, i);
+ error_check(session->open_cursor(session, "backup:", NULL, buf, &backup_cur));
+ rfd = wfd = -1;
+ count = 0;
+ alloc = FLIST_INIT;
+ flist = calloc(alloc, sizeof(FILELIST));
+ testutil_assert(flist != NULL);
+ /* For each file listed, open a duplicate backup cursor and copy the blocks. */
+ while ((ret = backup_cur->next(backup_cur)) == 0) {
+ error_check(backup_cur->get_key(backup_cur, &filename));
+ error_check(process_file(&flist, &count, &alloc, filename));
+ (void)snprintf(h, sizeof(h), "%s.0", home_incr);
+ (void)snprintf(buf, sizeof(buf), "cp %s/%s %s/%s", home, filename, h, filename);
+ printf("Copying backup: %s\n", buf);
+ error_check(system(buf));
+#if 0
+ (void)snprintf(buf, sizeof(buf), "%s/%s", home, filename);
+ printf("Open source %s for reading\n", buf);
+ error_check(rfd = open(buf, O_RDONLY, 0));
+ (void)snprintf(h, sizeof(h), "%s.%d", home_incr, i);
+ (void)snprintf(buf, sizeof(buf), "%s/%s", h, filename);
+ printf("Open dest %s for writing\n", buf);
+ error_check(wfd = open(buf, O_WRONLY, 0));
+#endif
+
+ (void)snprintf(buf, sizeof(buf), "incremental=(file=%s)", filename);
+ error_check(session->open_cursor(session, NULL, backup_cur, buf, &incr_cur));
+ printf("Taking incremental %d: File %s\n", i, filename);
+ while ((ret = incr_cur->next(incr_cur)) == 0) {
+ error_check(incr_cur->get_key(incr_cur, &offset, &size, &type));
+ printf("Incremental %s: KEY: Off %" PRIu64 " Size: %" PRIu64 " Type: %" PRIu64 "\n",
+ filename, offset, size, type);
+ scan_end_check(type == WT_BACKUP_FILE || type == WT_BACKUP_RANGE);
+ if (type == WT_BACKUP_RANGE) {
+ /*
+ * We should never get a range key after a whole file so the read file descriptor
+ * should be valid. If the read descriptor is valid, so it the write one.
+ */
+ scan_end_check(rfd != -1);
+ printf("Incremental %s: Range Offset: %" PRIu64 " Size: %" PRIu64 "\n", filename,
+ offset, size);
+ error_sys_check(lseek(rfd, (wt_off_t)offset, SEEK_SET));
+ error_sys_check(read(rfd, buf, (size_t)size));
+ error_sys_check(lseek(wfd, (wt_off_t)offset, SEEK_SET));
+ error_sys_check(write(wfd, buf, (size_t)size));
+ } else {
+/* Whole file, so close both files and just copy the whole thing. */
+#if 0
+ error_check(close(rfd));
+ error_check(close(wfd));
+#endif
+ rfd = wfd = -1;
+ (void)snprintf(buf, sizeof(buf), "cp %s/%s %s/%s", home, filename, h, filename);
+ printf("Incremental: Whole file copy: %s\n", buf);
+ error_check(system(buf));
+ }
+ }
+ scan_end_check(ret == WT_NOTFOUND);
+ error_check(incr_cur->close(incr_cur));
+
+ /* Close file descriptors if they're open. */
+ if (rfd != -1) {
+ error_check(close(rfd));
+ error_check(close(wfd));
+ }
+ /*
+ * For each file, we want to copy the file into each of the later incremental directories so
+ * that they start out at the same for the next incremental round. We then check each
+ * incremental directory along the way.
+ */
+ for (j = i; j < MAX_ITERATIONS; j++) {
+ (void)snprintf(h, sizeof(h), "%s.%d", home_incr, j);
+ (void)snprintf(buf, sizeof(buf), "cp %s/%s %s/%s", home, filename, h, filename);
+ error_check(system(buf));
+ }
+ }
+ scan_end_check(ret == WT_NOTFOUND);
+
+ error_check(backup_cur->close(backup_cur));
+ error_check(finalize_files(flist, count));
+ /*! [incremental backup using block transfer]*/
+}
+
+int
+main(int argc, char *argv[])
+{
+ WT_CONNECTION *wt_conn;
+ WT_CURSOR *backup_cur;
+ WT_SESSION *session;
+ int i;
+ char cmd_buf[256];
+
+ (void)argc; /* Unused variable */
+ (void)testutil_set_progname(argv);
+
+ (void)snprintf(cmd_buf, sizeof(cmd_buf), "rm -rf %s && mkdir %s", home, home);
+ error_check(system(cmd_buf));
+ error_check(wiredtiger_open(home, NULL, CONN_CONFIG, &wt_conn));
+
+ setup_directories();
+ error_check(wt_conn->open_session(wt_conn, NULL, NULL, &session));
+ error_check(session->create(session, uri, "key_format=S,value_format=S"));
+ error_check(session->create(session, uri2, "key_format=S,value_format=S"));
+ printf("Adding initial data\n");
+ add_work(session, 0);
+
+ printf("Taking initial backup\n");
+ take_full_backup(session, 0);
+
+ error_check(session->checkpoint(session, NULL));
+
+ for (i = 1; i < MAX_ITERATIONS; i++) {
+ printf("Iteration %d: adding data\n", i);
+ add_work(session, i);
+ error_check(session->checkpoint(session, NULL));
+ /*
+ * The full backup here is only needed for testing and comparison purposes. A normal
+ * incremental backup procedure would not include this.
+ */
+ printf("Iteration %d: taking full backup\n", i);
+ take_full_backup(session, i);
+ /*
+ * Taking the incremental backup also calls truncate to archive the log files, if the copies
+ * were successful. See that function for details on that call.
+ */
+ printf("Iteration %d: taking incremental backup\n", i);
+ take_incr_backup(session, i);
+
+ printf("Iteration %d: dumping and comparing data\n", i);
+ error_check(compare_backups(i));
+ }
+
+ /*
+ * After we're done, release resources. Test the force stop setting.
+ */
+ (void)snprintf(cmd_buf, sizeof(cmd_buf), "incremental=(force_stop=true)");
+ error_check(session->open_cursor(session, "backup:", NULL, cmd_buf, &backup_cur));
+ error_check(backup_cur->close(backup_cur));
+
+ /*
+ * Close the connection. We're done and want to run the final comparison between the incremental
+ * and original.
+ */
+ error_check(wt_conn->close(wt_conn, NULL));
+
+ printf("Final comparison: dumping and comparing data\n");
+ error_check(compare_backups(0));
+
+ return (EXIT_SUCCESS);
+}
diff --git a/src/third_party/wiredtiger/import.data b/src/third_party/wiredtiger/import.data
index 46aa3e9bfa1..ce4e98616c1 100644
--- a/src/third_party/wiredtiger/import.data
+++ b/src/third_party/wiredtiger/import.data
@@ -1,5 +1,5 @@
{
- "commit": "81a6cd219659835dd32ca563c24dadee54edef7d",
+ "commit": "77f9aebf23bb7fcb3911345741f20d67128f8da6",
"github": "wiredtiger/wiredtiger.git",
"vendor": "wiredtiger",
"branch": "mongodb-4.4"
diff --git a/src/third_party/wiredtiger/src/config/config_def.c b/src/third_party/wiredtiger/src/config/config_def.c
index 958c267a7ce..337dbb2e593 100644
--- a/src/third_party/wiredtiger/src/config/config_def.c
+++ b/src/third_party/wiredtiger/src/config/config_def.c
@@ -117,7 +117,6 @@ static const WT_CONFIG_CHECK confchk_WT_CONNECTION_reconfigure[] = {
{"io_capacity", "category", NULL, NULL, confchk_wiredtiger_open_io_capacity_subconfigs, 1},
{"log", "category", NULL, NULL, confchk_WT_CONNECTION_reconfigure_log_subconfigs, 4},
{"lsm_manager", "category", NULL, NULL, confchk_wiredtiger_open_lsm_manager_subconfigs, 2},
- {"lsm_merge", "boolean", NULL, NULL, NULL, 0},
{"operation_timeout_ms", "int", NULL, "min=1", NULL, 0},
{"operation_tracking", "category", NULL, NULL,
confchk_wiredtiger_open_operation_tracking_subconfigs, 2},
@@ -134,7 +133,7 @@ static const WT_CONFIG_CHECK confchk_WT_CONNECTION_reconfigure[] = {
"\"split_4\",\"split_5\",\"split_6\",\"split_7\",\"split_8\"]",
NULL, 0},
{"verbose", "list", NULL,
- "choices=[\"api\",\"block\",\"checkpoint\","
+ "choices=[\"api\",\"backup\",\"block\",\"checkpoint\","
"\"checkpoint_progress\",\"compact\",\"compact_progress\","
"\"error_returns\",\"evict\",\"evict_stuck\",\"evictserver\","
"\"fileops\",\"handleops\",\"log\",\"lookaside\","
@@ -294,11 +293,19 @@ static const WT_CONFIG_CHECK confchk_WT_SESSION_log_flush[] = {
{"sync", "string", NULL, "choices=[\"background\",\"off\",\"on\"]", NULL, 0},
{NULL, NULL, NULL, NULL, NULL, 0}};
+static const WT_CONFIG_CHECK confchk_WT_SESSION_open_cursor_incremental_subconfigs[] = {
+ {"enabled", "boolean", NULL, NULL, NULL, 0}, {"file", "string", NULL, NULL, NULL, 0},
+ {"force_stop", "boolean", NULL, NULL, NULL, 0},
+ {"granularity", "int", NULL, "min=1MB,max=2GB", NULL, 0},
+ {"src_id", "string", NULL, NULL, NULL, 0}, {"this_id", "string", NULL, NULL, NULL, 0},
+ {NULL, NULL, NULL, NULL, NULL, 0}};
+
static const WT_CONFIG_CHECK confchk_WT_SESSION_open_cursor[] = {
{"append", "boolean", NULL, NULL, NULL, 0}, {"bulk", "string", NULL, NULL, NULL, 0},
{"checkpoint", "string", NULL, NULL, NULL, 0},
{"checkpoint_wait", "boolean", NULL, NULL, NULL, 0},
{"dump", "string", NULL, "choices=[\"hex\",\"json\",\"print\"]", NULL, 0},
+ {"incremental", "category", NULL, NULL, confchk_WT_SESSION_open_cursor_incremental_subconfigs, 6},
{"next_random", "boolean", NULL, NULL, NULL, 0},
{"next_random_sample_size", "string", NULL, NULL, NULL, 0},
{"overwrite", "boolean", NULL, NULL, NULL, 0}, {"raw", "boolean", NULL, NULL, NULL, 0},
@@ -551,8 +558,7 @@ static const WT_CONFIG_CHECK confchk_wiredtiger_open[] = {
{"io_capacity", "category", NULL, NULL, confchk_wiredtiger_open_io_capacity_subconfigs, 1},
{"log", "category", NULL, NULL, confchk_wiredtiger_open_log_subconfigs, 9},
{"lsm_manager", "category", NULL, NULL, confchk_wiredtiger_open_lsm_manager_subconfigs, 2},
- {"lsm_merge", "boolean", NULL, NULL, NULL, 0}, {"mmap", "boolean", NULL, NULL, NULL, 0},
- {"multiprocess", "boolean", NULL, NULL, NULL, 0},
+ {"mmap", "boolean", NULL, NULL, NULL, 0}, {"multiprocess", "boolean", NULL, NULL, NULL, 0},
{"operation_timeout_ms", "int", NULL, "min=1", NULL, 0},
{"operation_tracking", "category", NULL, NULL,
confchk_wiredtiger_open_operation_tracking_subconfigs, 2},
@@ -576,7 +582,7 @@ static const WT_CONFIG_CHECK confchk_wiredtiger_open[] = {
{"use_environment", "boolean", NULL, NULL, NULL, 0},
{"use_environment_priv", "boolean", NULL, NULL, NULL, 0},
{"verbose", "list", NULL,
- "choices=[\"api\",\"block\",\"checkpoint\","
+ "choices=[\"api\",\"backup\",\"block\",\"checkpoint\","
"\"checkpoint_progress\",\"compact\",\"compact_progress\","
"\"error_returns\",\"evict\",\"evict_stuck\",\"evictserver\","
"\"fileops\",\"handleops\",\"log\",\"lookaside\","
@@ -619,8 +625,7 @@ static const WT_CONFIG_CHECK confchk_wiredtiger_open_all[] = {
{"io_capacity", "category", NULL, NULL, confchk_wiredtiger_open_io_capacity_subconfigs, 1},
{"log", "category", NULL, NULL, confchk_wiredtiger_open_log_subconfigs, 9},
{"lsm_manager", "category", NULL, NULL, confchk_wiredtiger_open_lsm_manager_subconfigs, 2},
- {"lsm_merge", "boolean", NULL, NULL, NULL, 0}, {"mmap", "boolean", NULL, NULL, NULL, 0},
- {"multiprocess", "boolean", NULL, NULL, NULL, 0},
+ {"mmap", "boolean", NULL, NULL, NULL, 0}, {"multiprocess", "boolean", NULL, NULL, NULL, 0},
{"operation_timeout_ms", "int", NULL, "min=1", NULL, 0},
{"operation_tracking", "category", NULL, NULL,
confchk_wiredtiger_open_operation_tracking_subconfigs, 2},
@@ -644,7 +649,7 @@ static const WT_CONFIG_CHECK confchk_wiredtiger_open_all[] = {
{"use_environment", "boolean", NULL, NULL, NULL, 0},
{"use_environment_priv", "boolean", NULL, NULL, NULL, 0},
{"verbose", "list", NULL,
- "choices=[\"api\",\"block\",\"checkpoint\","
+ "choices=[\"api\",\"backup\",\"block\",\"checkpoint\","
"\"checkpoint_progress\",\"compact\",\"compact_progress\","
"\"error_returns\",\"evict\",\"evict_stuck\",\"evictserver\","
"\"fileops\",\"handleops\",\"log\",\"lookaside\","
@@ -687,8 +692,7 @@ static const WT_CONFIG_CHECK confchk_wiredtiger_open_basecfg[] = {
{"io_capacity", "category", NULL, NULL, confchk_wiredtiger_open_io_capacity_subconfigs, 1},
{"log", "category", NULL, NULL, confchk_wiredtiger_open_log_subconfigs, 9},
{"lsm_manager", "category", NULL, NULL, confchk_wiredtiger_open_lsm_manager_subconfigs, 2},
- {"lsm_merge", "boolean", NULL, NULL, NULL, 0}, {"mmap", "boolean", NULL, NULL, NULL, 0},
- {"multiprocess", "boolean", NULL, NULL, NULL, 0},
+ {"mmap", "boolean", NULL, NULL, NULL, 0}, {"multiprocess", "boolean", NULL, NULL, NULL, 0},
{"operation_timeout_ms", "int", NULL, "min=1", NULL, 0},
{"operation_tracking", "category", NULL, NULL,
confchk_wiredtiger_open_operation_tracking_subconfigs, 2},
@@ -710,7 +714,7 @@ static const WT_CONFIG_CHECK confchk_wiredtiger_open_basecfg[] = {
{"transaction_sync", "category", NULL, NULL, confchk_wiredtiger_open_transaction_sync_subconfigs,
2},
{"verbose", "list", NULL,
- "choices=[\"api\",\"block\",\"checkpoint\","
+ "choices=[\"api\",\"backup\",\"block\",\"checkpoint\","
"\"checkpoint_progress\",\"compact\",\"compact_progress\","
"\"error_returns\",\"evict\",\"evict_stuck\",\"evictserver\","
"\"fileops\",\"handleops\",\"log\",\"lookaside\","
@@ -753,8 +757,7 @@ static const WT_CONFIG_CHECK confchk_wiredtiger_open_usercfg[] = {
{"io_capacity", "category", NULL, NULL, confchk_wiredtiger_open_io_capacity_subconfigs, 1},
{"log", "category", NULL, NULL, confchk_wiredtiger_open_log_subconfigs, 9},
{"lsm_manager", "category", NULL, NULL, confchk_wiredtiger_open_lsm_manager_subconfigs, 2},
- {"lsm_merge", "boolean", NULL, NULL, NULL, 0}, {"mmap", "boolean", NULL, NULL, NULL, 0},
- {"multiprocess", "boolean", NULL, NULL, NULL, 0},
+ {"mmap", "boolean", NULL, NULL, NULL, 0}, {"multiprocess", "boolean", NULL, NULL, NULL, 0},
{"operation_timeout_ms", "int", NULL, "min=1", NULL, 0},
{"operation_tracking", "category", NULL, NULL,
confchk_wiredtiger_open_operation_tracking_subconfigs, 2},
@@ -776,7 +779,7 @@ static const WT_CONFIG_CHECK confchk_wiredtiger_open_usercfg[] = {
{"transaction_sync", "category", NULL, NULL, confchk_wiredtiger_open_transaction_sync_subconfigs,
2},
{"verbose", "list", NULL,
- "choices=[\"api\",\"block\",\"checkpoint\","
+ "choices=[\"api\",\"backup\",\"block\",\"checkpoint\","
"\"checkpoint_progress\",\"compact\",\"compact_progress\","
"\"error_returns\",\"evict\",\"evict_stuck\",\"evictserver\","
"\"fileops\",\"handleops\",\"log\",\"lookaside\","
@@ -820,13 +823,13 @@ static const WT_CONFIG_ENTRY config_entries[] = {{"WT_CONNECTION.add_collator",
",file_manager=(close_handle_minimum=250,close_idle_time=30,"
"close_scan_interval=10),io_capacity=(total=0),log=(archive=true,"
"os_cache_dirty_pct=0,prealloc=true,zero_fill=false),"
- "lsm_manager=(merge=true,worker_thread_max=4),lsm_merge=true,"
+ "lsm_manager=(merge=true,worker_thread_max=4),"
"operation_timeout_ms=0,operation_tracking=(enabled=false,"
"path=\".\"),shared_cache=(chunk=10MB,name=,quota=0,reserve=0,"
"size=500MB),statistics=none,statistics_log=(json=false,"
"on_close=false,sources=,timestamp=\"%b %d %H:%M:%S\",wait=0),"
"timing_stress_for_test=,verbose=",
- confchk_WT_CONNECTION_reconfigure, 27},
+ confchk_WT_CONNECTION_reconfigure, 26},
{"WT_CONNECTION.rollback_to_stable", "", NULL, 0}, {"WT_CONNECTION.set_file_system", "", NULL, 0},
{"WT_CONNECTION.set_timestamp",
"commit_timestamp=,durable_timestamp=,force=false,"
@@ -887,10 +890,12 @@ static const WT_CONFIG_ENTRY config_entries[] = {{"WT_CONNECTION.add_collator",
{"WT_SESSION.log_printf", "", NULL, 0},
{"WT_SESSION.open_cursor",
"append=false,bulk=false,checkpoint=,checkpoint_wait=true,dump=,"
- "next_random=false,next_random_sample_size=0,overwrite=true,"
- "raw=false,read_once=false,readonly=false,skip_sort_check=false,"
- "statistics=,target=",
- confchk_WT_SESSION_open_cursor, 14},
+ "incremental=(enabled=false,file=,force_stop=false,"
+ "granularity=16MB,src_id=,this_id=),next_random=false,"
+ "next_random_sample_size=0,overwrite=true,raw=false,"
+ "read_once=false,readonly=false,skip_sort_check=false,statistics="
+ ",target=",
+ confchk_WT_SESSION_open_cursor, 15},
{"WT_SESSION.prepare_transaction", "prepare_timestamp=", confchk_WT_SESSION_prepare_transaction,
1},
{"WT_SESSION.query_timestamp", "get=read", confchk_WT_SESSION_query_timestamp, 1},
@@ -995,8 +1000,8 @@ static const WT_CONFIG_ENTRY config_entries[] = {{"WT_CONNECTION.add_collator",
"io_capacity=(total=0),log=(archive=true,compressor=,"
"enabled=false,file_max=100MB,os_cache_dirty_pct=0,path=\".\","
"prealloc=true,recover=on,zero_fill=false),"
- "lsm_manager=(merge=true,worker_thread_max=4),lsm_merge=true,"
- "mmap=true,multiprocess=false,operation_timeout_ms=0,"
+ "lsm_manager=(merge=true,worker_thread_max=4),mmap=true,"
+ "multiprocess=false,operation_timeout_ms=0,"
"operation_tracking=(enabled=false,path=\".\"),readonly=false,"
"salvage=false,session_max=100,session_scratch_max=2MB,"
"session_table_cache=true,shared_cache=(chunk=10MB,name=,quota=0,"
@@ -1005,7 +1010,7 @@ static const WT_CONFIG_ENTRY config_entries[] = {{"WT_CONNECTION.add_collator",
",wait=0),timing_stress_for_test=,transaction_sync=(enabled=false"
",method=fsync),use_environment=true,use_environment_priv=false,"
"verbose=,write_through=",
- confchk_wiredtiger_open, 51},
+ confchk_wiredtiger_open, 50},
{"wiredtiger_open_all",
"async=(enabled=false,ops_max=1024,threads=2),buffer_alignment=-1"
",builtin_extension_config=,cache_cursors=true,"
@@ -1025,8 +1030,8 @@ static const WT_CONFIG_ENTRY config_entries[] = {{"WT_CONNECTION.add_collator",
"io_capacity=(total=0),log=(archive=true,compressor=,"
"enabled=false,file_max=100MB,os_cache_dirty_pct=0,path=\".\","
"prealloc=true,recover=on,zero_fill=false),"
- "lsm_manager=(merge=true,worker_thread_max=4),lsm_merge=true,"
- "mmap=true,multiprocess=false,operation_timeout_ms=0,"
+ "lsm_manager=(merge=true,worker_thread_max=4),mmap=true,"
+ "multiprocess=false,operation_timeout_ms=0,"
"operation_tracking=(enabled=false,path=\".\"),readonly=false,"
"salvage=false,session_max=100,session_scratch_max=2MB,"
"session_table_cache=true,shared_cache=(chunk=10MB,name=,quota=0,"
@@ -1035,7 +1040,7 @@ static const WT_CONFIG_ENTRY config_entries[] = {{"WT_CONNECTION.add_collator",
",wait=0),timing_stress_for_test=,transaction_sync=(enabled=false"
",method=fsync),use_environment=true,use_environment_priv=false,"
"verbose=,version=(major=0,minor=0),write_through=",
- confchk_wiredtiger_open_all, 52},
+ confchk_wiredtiger_open_all, 51},
{"wiredtiger_open_basecfg",
"async=(enabled=false,ops_max=1024,threads=2),buffer_alignment=-1"
",builtin_extension_config=,cache_cursors=true,"
@@ -1053,8 +1058,8 @@ static const WT_CONFIG_ENTRY config_entries[] = {{"WT_CONNECTION.add_collator",
"io_capacity=(total=0),log=(archive=true,compressor=,"
"enabled=false,file_max=100MB,os_cache_dirty_pct=0,path=\".\","
"prealloc=true,recover=on,zero_fill=false),"
- "lsm_manager=(merge=true,worker_thread_max=4),lsm_merge=true,"
- "mmap=true,multiprocess=false,operation_timeout_ms=0,"
+ "lsm_manager=(merge=true,worker_thread_max=4),mmap=true,"
+ "multiprocess=false,operation_timeout_ms=0,"
"operation_tracking=(enabled=false,path=\".\"),readonly=false,"
"salvage=false,session_max=100,session_scratch_max=2MB,"
"session_table_cache=true,shared_cache=(chunk=10MB,name=,quota=0,"
@@ -1062,7 +1067,7 @@ static const WT_CONFIG_ENTRY config_entries[] = {{"WT_CONNECTION.add_collator",
",on_close=false,path=\".\",sources=,timestamp=\"%b %d %H:%M:%S\""
",wait=0),timing_stress_for_test=,transaction_sync=(enabled=false"
",method=fsync),verbose=,version=(major=0,minor=0),write_through=",
- confchk_wiredtiger_open_basecfg, 46},
+ confchk_wiredtiger_open_basecfg, 45},
{"wiredtiger_open_usercfg",
"async=(enabled=false,ops_max=1024,threads=2),buffer_alignment=-1"
",builtin_extension_config=,cache_cursors=true,"
@@ -1080,8 +1085,8 @@ static const WT_CONFIG_ENTRY config_entries[] = {{"WT_CONNECTION.add_collator",
"io_capacity=(total=0),log=(archive=true,compressor=,"
"enabled=false,file_max=100MB,os_cache_dirty_pct=0,path=\".\","
"prealloc=true,recover=on,zero_fill=false),"
- "lsm_manager=(merge=true,worker_thread_max=4),lsm_merge=true,"
- "mmap=true,multiprocess=false,operation_timeout_ms=0,"
+ "lsm_manager=(merge=true,worker_thread_max=4),mmap=true,"
+ "multiprocess=false,operation_timeout_ms=0,"
"operation_tracking=(enabled=false,path=\".\"),readonly=false,"
"salvage=false,session_max=100,session_scratch_max=2MB,"
"session_table_cache=true,shared_cache=(chunk=10MB,name=,quota=0,"
@@ -1089,7 +1094,7 @@ static const WT_CONFIG_ENTRY config_entries[] = {{"WT_CONNECTION.add_collator",
",on_close=false,path=\".\",sources=,timestamp=\"%b %d %H:%M:%S\""
",wait=0),timing_stress_for_test=,transaction_sync=(enabled=false"
",method=fsync),verbose=,write_through=",
- confchk_wiredtiger_open_usercfg, 45},
+ confchk_wiredtiger_open_usercfg, 44},
{NULL, NULL, NULL, 0}};
int
diff --git a/src/third_party/wiredtiger/src/config/config_upgrade.c b/src/third_party/wiredtiger/src/config/config_upgrade.c
deleted file mode 100644
index be67fa0c3f3..00000000000
--- a/src/third_party/wiredtiger/src/config/config_upgrade.c
+++ /dev/null
@@ -1,31 +0,0 @@
-/*-
- * Copyright (c) 2014-2019 MongoDB, Inc.
- * Copyright (c) 2008-2014 WiredTiger, Inc.
- * All rights reserved.
- *
- * See the file LICENSE for redistribution information.
- */
-
-#include "wt_internal.h"
-
-/*
- * __wt_config_upgrade --
- * Upgrade a configuration string by appended the replacement version.
- */
-int
-__wt_config_upgrade(WT_SESSION_IMPL *session, WT_ITEM *buf)
-{
- WT_CONFIG_ITEM v;
- const char *config;
-
- config = buf->data;
-
- /*
- * wiredtiger_open:
- * lsm_merge=boolean -> lsm_manager=(merge=boolean)
- */
- if (__wt_config_getones(session, config, "lsm_merge", &v) != WT_NOTFOUND)
- WT_RET(__wt_buf_catfmt(session, buf, ",lsm_manager=(merge=%s)", v.val ? "true" : "false"));
-
- return (0);
-}
diff --git a/src/third_party/wiredtiger/src/conn/conn_api.c b/src/third_party/wiredtiger/src/conn/conn_api.c
index 7e8a898193f..2b5f1fb7ae3 100644
--- a/src/third_party/wiredtiger/src/conn/conn_api.c
+++ b/src/third_party/wiredtiger/src/conn/conn_api.c
@@ -1428,9 +1428,6 @@ __conn_config_file(
/* Check any version. */
WT_ERR(__conn_config_check_version(session, cbuf->data));
- /* Upgrade the configuration string. */
- WT_ERR(__wt_config_upgrade(session, cbuf));
-
/* Check the configuration information. */
WT_ERR(__wt_config_check(session, is_user ? WT_CONFIG_REF(session, wiredtiger_open_usercfg) :
WT_CONFIG_REF(session, wiredtiger_open_basecfg),
@@ -1519,7 +1516,6 @@ __conn_config_env(WT_SESSION_IMPL *session, const char *cfg[], WT_ITEM *cbuf)
/* Upgrade the configuration string. */
WT_ERR(__wt_buf_setstr(session, cbuf, env_config));
- WT_ERR(__wt_config_upgrade(session, cbuf));
/* Check the configuration information. */
WT_ERR(__wt_config_check(session, WT_CONFIG_REF(session, wiredtiger_open), env_config, 0));
@@ -1825,14 +1821,15 @@ typedef struct {
int
__wt_verbose_config(WT_SESSION_IMPL *session, const char *cfg[])
{
- static const WT_NAME_FLAG verbtypes[] = {{"api", WT_VERB_API}, {"block", WT_VERB_BLOCK},
- {"checkpoint", WT_VERB_CHECKPOINT}, {"checkpoint_progress", WT_VERB_CHECKPOINT_PROGRESS},
- {"compact", WT_VERB_COMPACT}, {"compact_progress", WT_VERB_COMPACT_PROGRESS},
- {"error_returns", WT_VERB_ERROR_RETURNS}, {"evict", WT_VERB_EVICT},
- {"evict_stuck", WT_VERB_EVICT_STUCK}, {"evictserver", WT_VERB_EVICTSERVER},
- {"fileops", WT_VERB_FILEOPS}, {"handleops", WT_VERB_HANDLEOPS}, {"log", WT_VERB_LOG},
- {"lookaside", WT_VERB_LOOKASIDE}, {"lookaside_activity", WT_VERB_LOOKASIDE_ACTIVITY},
- {"lsm", WT_VERB_LSM}, {"lsm_manager", WT_VERB_LSM_MANAGER}, {"metadata", WT_VERB_METADATA},
+ static const WT_NAME_FLAG verbtypes[] = {{"api", WT_VERB_API}, {"backup", WT_VERB_BACKUP},
+ {"block", WT_VERB_BLOCK}, {"checkpoint", WT_VERB_CHECKPOINT},
+ {"checkpoint_progress", WT_VERB_CHECKPOINT_PROGRESS}, {"compact", WT_VERB_COMPACT},
+ {"compact_progress", WT_VERB_COMPACT_PROGRESS}, {"error_returns", WT_VERB_ERROR_RETURNS},
+ {"evict", WT_VERB_EVICT}, {"evict_stuck", WT_VERB_EVICT_STUCK},
+ {"evictserver", WT_VERB_EVICTSERVER}, {"fileops", WT_VERB_FILEOPS},
+ {"handleops", WT_VERB_HANDLEOPS}, {"log", WT_VERB_LOG}, {"lookaside", WT_VERB_LOOKASIDE},
+ {"lookaside_activity", WT_VERB_LOOKASIDE_ACTIVITY}, {"lsm", WT_VERB_LSM},
+ {"lsm_manager", WT_VERB_LSM_MANAGER}, {"metadata", WT_VERB_METADATA},
{"mutex", WT_VERB_MUTEX}, {"overflow", WT_VERB_OVERFLOW}, {"read", WT_VERB_READ},
{"rebalance", WT_VERB_REBALANCE}, {"reconcile", WT_VERB_RECONCILE},
{"recovery", WT_VERB_RECOVERY}, {"recovery_progress", WT_VERB_RECOVERY_PROGRESS},
diff --git a/src/third_party/wiredtiger/src/conn/conn_handle.c b/src/third_party/wiredtiger/src/conn/conn_handle.c
index e5c82d49a48..37136926060 100644
--- a/src/third_party/wiredtiger/src/conn/conn_handle.c
+++ b/src/third_party/wiredtiger/src/conn/conn_handle.c
@@ -94,6 +94,7 @@ void
__wt_connection_destroy(WT_CONNECTION_IMPL *conn)
{
WT_SESSION_IMPL *session;
+ u_int i;
/* Check there's something to destroy. */
if (conn == NULL)
@@ -131,6 +132,13 @@ __wt_connection_destroy(WT_CONNECTION_IMPL *conn)
__wt_cond_destroy(session, &conn->lsm_manager.work_cond);
/* Free allocated memory. */
+ /*
+ * XXX we need to persist this information when we are working on making incremental backups
+ * persistent across restarts.
+ */
+ for (i = 0; i < WT_BLKINCR_MAX; ++i)
+ __wt_free(session, conn->incr_backups[i].id_str);
+
__wt_free(session, conn->cfg);
__wt_free(session, conn->debug_ckpt);
__wt_free(session, conn->error_prefix);
diff --git a/src/third_party/wiredtiger/src/cursor/cur_backup.c b/src/third_party/wiredtiger/src/cursor/cur_backup.c
index 4869bcb3b71..cb7e11a64cc 100644
--- a/src/third_party/wiredtiger/src/cursor/cur_backup.c
+++ b/src/third_party/wiredtiger/src/cursor/cur_backup.c
@@ -11,9 +11,9 @@
static int __backup_all(WT_SESSION_IMPL *);
static int __backup_list_append(WT_SESSION_IMPL *, WT_CURSOR_BACKUP *, const char *);
static int __backup_list_uri_append(WT_SESSION_IMPL *, const char *, bool *);
-static int __backup_start(WT_SESSION_IMPL *, WT_CURSOR_BACKUP *, bool, const char *[]);
+static int __backup_start(
+ WT_SESSION_IMPL *, WT_CURSOR_BACKUP *, WT_CURSOR_BACKUP *, const char *[]);
static int __backup_stop(WT_SESSION_IMPL *, WT_CURSOR_BACKUP *);
-static int __backup_uri(WT_SESSION_IMPL *, const char *[], bool, bool *, bool *);
/*
* __curbackup_next --
@@ -28,6 +28,7 @@ __curbackup_next(WT_CURSOR *cursor)
cb = (WT_CURSOR_BACKUP *)cursor;
CURSOR_API_CALL(cursor, session, next, NULL);
+ WT_CURSOR_BACKUP_CHECK_STOP(cb);
if (cb->list == NULL || cb->list[cb->next] == NULL) {
F_CLR(cursor, WT_CURSTD_KEY_SET);
@@ -57,6 +58,7 @@ __curbackup_reset(WT_CURSOR *cursor)
cb = (WT_CURSOR_BACKUP *)cursor;
CURSOR_API_CALL_PREPARE_ALLOWED(cursor, session, reset, NULL);
+ WT_CURSOR_BACKUP_CHECK_STOP(cb);
cb->next = 0;
F_CLR(cursor, WT_CURSTD_KEY_SET | WT_CURSTD_VALUE_SET);
@@ -66,6 +68,35 @@ err:
}
/*
+ * __backup_incr_release --
+ * Free all resources relating to incremental backup.
+ */
+static int
+__backup_incr_release(WT_SESSION_IMPL *session, WT_CURSOR_BACKUP *cb, bool force)
+{
+ WT_BLKINCR *blk;
+ WT_CONNECTION_IMPL *conn;
+ u_int i;
+
+ WT_UNUSED(cb);
+ WT_UNUSED(force);
+ conn = S2C(session);
+ /*
+ * Clear flags. Remove file. Release any memory information.
+ */
+ F_CLR(conn, WT_CONN_INCR_BACKUP);
+ for (i = 0; i < WT_BLKINCR_MAX; ++i) {
+ blk = &conn->incr_backups[i];
+ F_CLR(blk, WT_BLKINCR_VALID);
+ }
+ /* __wt_block_backup_remove... */
+ conn->ckpt_incr_granularity = 0;
+ WT_RET(__wt_remove_if_exists(session, WT_BLKINCR_BACKUP, true));
+
+ return (0);
+}
+
+/*
* __backup_free --
* Free list resources for a backup cursor.
*/
@@ -79,6 +110,13 @@ __backup_free(WT_SESSION_IMPL *session, WT_CURSOR_BACKUP *cb)
__wt_free(session, cb->list[i]);
__wt_free(session, cb->list);
}
+ if (cb->incr_file != NULL)
+ __wt_free(session, cb->incr_file);
+ if (cb->incr_src != NULL)
+ __wt_free(session, cb->incr_src);
+ if (cb->incr_this != NULL)
+ __wt_free(session, cb->incr_this);
+ __wt_curbackup_free_incr(session, cb);
}
/*
@@ -96,6 +134,12 @@ __curbackup_close(WT_CURSOR *cursor)
CURSOR_API_CALL_PREPARE_ALLOWED(cursor, session, close, NULL);
err:
+ if (F_ISSET(cb, WT_CURBACKUP_FORCE_STOP)) {
+ __wt_verbose(
+ session, WT_VERB_BACKUP, "%s", "Releasing resources from forced stop incremental");
+ __backup_incr_release(session, cb, true);
+ }
+
/*
* When starting a hot backup, we serialize hot backup cursors and set the connection's
* hot-backup flag. Once that's done, we set the cursor's backup-locker flag, implying the
@@ -147,7 +191,7 @@ __wt_curbackup_open(WT_SESSION_IMPL *session, const char *uri, WT_CURSOR *other,
__wt_cursor_reopen_notsup, /* reopen */
__curbackup_close); /* close */
WT_CURSOR *cursor;
- WT_CURSOR_BACKUP *cb;
+ WT_CURSOR_BACKUP *cb, *othercb;
WT_DECL_RET;
WT_STATIC_ASSERT(offsetof(WT_CURSOR_BACKUP, iface) == 0);
@@ -157,19 +201,23 @@ __wt_curbackup_open(WT_SESSION_IMPL *session, const char *uri, WT_CURSOR *other,
*cursor = iface;
cursor->session = (WT_SESSION *)session;
cursor->key_format = "S"; /* Return the file names as the key. */
- cursor->value_format = ""; /* No value. */
+ cursor->value_format = ""; /* No value, for now. */
session->bkp_cursor = cb;
+ othercb = (WT_CURSOR_BACKUP *)other;
+ if (othercb != NULL)
+ WT_CURSOR_BACKUP_CHECK_STOP(othercb);
/*
* Start the backup and fill in the cursor's list. Acquire the schema lock, we need a consistent
* view when creating a copy.
*/
WT_WITH_CHECKPOINT_LOCK(
- session, WT_WITH_SCHEMA_LOCK(session, ret = __backup_start(session, cb, other != NULL, cfg)));
+ session, WT_WITH_SCHEMA_LOCK(session, ret = __backup_start(session, cb, othercb, cfg)));
WT_ERR(ret);
-
- WT_ERR(__wt_cursor_init(cursor, uri, NULL, cfg, cursorp));
+ WT_ERR(cb->incr_file == NULL ?
+ __wt_cursor_init(cursor, uri, NULL, cfg, cursorp) :
+ __wt_curbackup_open_incr(session, uri, other, cursor, cfg, cursorp));
if (0) {
err:
@@ -181,6 +229,105 @@ err:
}
/*
+ * __backup_get_ckpt --
+ * Get the most recent checkpoint information and store it in the structure.
+ *
+ * XXX - Currently set return to static void for the compiler, when this function has real content
+ * it should be static int.
+ */
+static void
+__backup_get_ckpt(WT_SESSION_IMPL *session, WT_BLKINCR *incr)
+{
+ WT_UNUSED(session);
+ WT_UNUSED(incr);
+ /*
+ * Look up the most recent checkpoint and store information about it in incr.
+ *
+ * XXX When this function has content, return a real value. return (0);
+ */
+ return;
+}
+
+/*
+ * __backup_add_id --
+ * Add the identifier for block based incremental backup.
+ */
+static int
+__backup_add_id(WT_SESSION_IMPL *session, WT_CONFIG_ITEM *cval)
+{
+ WT_BLKINCR *blk;
+ WT_CONNECTION_IMPL *conn;
+ WT_DECL_RET;
+ u_int i;
+
+ conn = S2C(session);
+ blk = NULL;
+ for (i = 0; i < WT_BLKINCR_MAX; ++i) {
+ blk = &conn->incr_backups[i];
+ /* If it isn't use, we can use it. */
+ if (!F_ISSET(blk, WT_BLKINCR_INUSE))
+ break;
+ }
+ /*
+ * We didn't find an entry. This should not happen.
+ */
+ if (i == WT_BLKINCR_MAX)
+ WT_PANIC_RET(session, WT_NOTFOUND, "Could not find an incremental backup slot to use");
+
+ /* Use the slot. */
+ if (blk->id_str != NULL)
+ __wt_verbose(
+ session, WT_VERB_BACKUP, "Freeing and reusing backup slot with old id %s", blk->id_str);
+ /* Free any string that was there. */
+ __wt_free(session, blk->id_str);
+ WT_ERR(__wt_strndup(session, cval->str, cval->len, &blk->id_str));
+ __wt_verbose(session, WT_VERB_BACKUP, "Using backup slot %u for id %s", i, blk->id_str);
+ /*
+ * XXX This function can error in the future.
+ *
+ * WT_ERR(__backup_get_ckpt(session, blk));
+ */
+ __backup_get_ckpt(session, blk);
+ F_SET(blk, WT_BLKINCR_VALID);
+ return (0);
+
+err:
+ if (blk != NULL)
+ __wt_free(session, blk->id_str);
+ return (ret);
+}
+
+/*
+ * __backup_find_id --
+ * Find the source identifier for block based incremental backup. Error if it is not a valid id.
+ */
+static int
+__backup_find_id(WT_SESSION_IMPL *session, WT_CONFIG_ITEM *cval, WT_BLKINCR **incrp)
+{
+ WT_BLKINCR *blk;
+ WT_CONNECTION_IMPL *conn;
+ u_int i;
+
+ conn = S2C(session);
+ for (i = 0; i < WT_BLKINCR_MAX; ++i) {
+ blk = &conn->incr_backups[i];
+ /* If it isn't valid, skip it. */
+ if (!F_ISSET(blk, WT_BLKINCR_VALID))
+ continue;
+ if (WT_STRING_MATCH(blk->id_str, cval->str, cval->len)) {
+ if (F_ISSET(blk, WT_BLKINCR_INUSE))
+ WT_RET_MSG(session, EINVAL, "Incremental backup structure already in use");
+ if (incrp != NULL)
+ *incrp = blk;
+ __wt_verbose(session, WT_VERB_BACKUP, "Found backup slot %u for id %s", i, blk->id_str);
+ return (0);
+ }
+ }
+ __wt_verbose(session, WT_VERB_BACKUP, "Did not find %.*s", (int)cval->len, cval->str);
+ return (WT_NOTFOUND);
+}
+
+/*
* __backup_log_append --
* Append log files needed for backup.
*/
@@ -208,21 +355,203 @@ err:
}
/*
+ * __backup_config --
+ * Backup configuration.
+ */
+static int
+__backup_config(WT_SESSION_IMPL *session, WT_CURSOR_BACKUP *cb, const char *cfg[],
+ WT_CURSOR_BACKUP *othercb, bool *foundp, bool *log_only, bool *incr_only)
+{
+ WT_CONFIG targetconf;
+ WT_CONFIG_ITEM cval, k, v;
+ WT_CONNECTION_IMPL *conn;
+ WT_DECL_ITEM(tmp);
+ WT_DECL_RET;
+ const char *uri;
+ bool incremental_config, is_dup, log_config, target_list;
+
+ *foundp = *incr_only = *log_only = false;
+
+ conn = S2C(session);
+ incremental_config = log_config = false;
+ is_dup = othercb != NULL;
+
+ /*
+ * Per-file offset incremental hot backup configurations take a starting checkpoint and optional
+ * maximum transfer size, and the subsequent duplicate cursors take a file object.
+ */
+ WT_RET_NOTFOUND_OK(__wt_config_gets(session, cfg, "incremental.force_stop", &cval));
+ if (cval.val) {
+ /*
+ * If we're force stopping incremental backup, set the flag. The resources involved in
+ * incremental backup will be released on cursor close and that is the only expected usage
+ * for this cursor.
+ */
+ if (is_dup)
+ WT_RET_MSG(session, EINVAL,
+ "Incremental force stop can only be specified on a primary backup cursor");
+ F_SET(cb, WT_CURBACKUP_FORCE_STOP);
+ return (0);
+ }
+ WT_RET_NOTFOUND_OK(__wt_config_gets(session, cfg, "incremental.enabled", &cval));
+ if (cval.val) {
+ if (!F_ISSET(conn, WT_CONN_INCR_BACKUP)) {
+ WT_RET(__wt_config_gets(session, cfg, "incremental.granularity", &cval));
+ /* XXX may not need cb->incr_granularity */
+ if (conn->ckpt_incr_granularity != 0)
+ WT_RET_MSG(session, EINVAL, "Cannot change the incremental backup granularity");
+ conn->ckpt_incr_granularity = cb->incr_granularity = (uint64_t)cval.val;
+ }
+ /* XXX Granularity can only be set once at the beginning */
+ F_SET(conn, WT_CONN_INCR_BACKUP);
+ incremental_config = true;
+ }
+
+ /*
+ * Specifying an incremental file means we're opening a duplicate backup cursor.
+ */
+ WT_RET(__wt_config_gets(session, cfg, "incremental.file", &cval));
+ if (cval.len != 0) {
+ if (!is_dup)
+ WT_RET_MSG(session, EINVAL,
+ "Incremental file name can only be specified on a duplicate backup cursor");
+ WT_RET(__wt_strndup(session, cval.str, cval.len, &cb->incr_file));
+ incremental_config = true;
+ }
+
+ /*
+ * See if we have a source identifier. We must process the source identifier before processing
+ * the 'this' identifier. That will mark which source is in use so that we can use any slot that
+ * is not in use as a new source starting point for this identifier.
+ */
+ WT_RET(__wt_config_gets(session, cfg, "incremental.src_id", &cval));
+ if (cval.len != 0) {
+ if (is_dup)
+ WT_RET_MSG(session, EINVAL,
+ "Incremental source identifier can only be specified on a primary backup cursor");
+ WT_RET(__backup_find_id(session, &cval, &cb->incr));
+ /* XXX might not need this incr_src field */
+ WT_RET(__wt_strndup(session, cval.str, cval.len, &cb->incr_src));
+ F_SET(cb->incr, WT_BLKINCR_INUSE);
+ incremental_config = true;
+ }
+ /*
+ * Use WT_ERR from here out because we need to clear the in use flag on error now.
+ */
+
+ /*
+ * Look for a new checkpoint name to retain and mark as a starting point.
+ */
+ WT_ERR(__wt_config_gets(session, cfg, "incremental.this_id", &cval));
+ if (cval.len != 0) {
+ if (is_dup)
+ WT_ERR_MSG(session, EINVAL,
+ "Incremental identifier can only be specified on a primary backup cursor");
+ ret = __backup_find_id(session, &cval, NULL);
+ if (ret != WT_NOTFOUND)
+ WT_ERR_MSG(session, EINVAL, "Incremental identifier already exists");
+
+ WT_ERR(__backup_add_id(session, &cval));
+ /* XXX might not need this incr_this field */
+ WT_ERR(__wt_strndup(session, cval.str, cval.len, &cb->incr_this));
+ incremental_config = true;
+ }
+
+ /*
+ * If we find a non-empty target configuration string, we have a job, otherwise it's not our
+ * problem.
+ */
+ WT_ERR(__wt_config_gets(session, cfg, "target", &cval));
+ __wt_config_subinit(session, &targetconf, &cval);
+ for (target_list = false; (ret = __wt_config_next(&targetconf, &k, &v)) == 0;
+ target_list = true) {
+ /* If it is our first time through, allocate. */
+ if (!target_list) {
+ *foundp = true;
+ WT_ERR(__wt_scr_alloc(session, 512, &tmp));
+ }
+
+ WT_ERR(__wt_buf_fmt(session, tmp, "%.*s", (int)k.len, k.str));
+ uri = tmp->data;
+ if (v.len != 0)
+ WT_ERR_MSG(session, EINVAL, "%s: invalid backup target: URIs may need quoting", uri);
+
+ /*
+ * Handle log targets. We do not need to go through the schema worker, just call the
+ * function to append them. Set log_only only if it is our only URI target.
+ */
+ if (WT_PREFIX_MATCH(uri, "log:")) {
+ log_config = true;
+ *log_only = !target_list;
+ WT_ERR(__backup_log_append(session, session->bkp_cursor, false));
+ } else if (is_dup)
+ WT_ERR_MSG(
+ session, EINVAL, "duplicate backup cursor cannot be used for non-log target");
+ else {
+ *log_only = false;
+
+ /*
+ * If backing up individual tables, we have to include indexes, which may involve
+ * opening those indexes. Acquire the table lock in write mode for that case.
+ */
+ WT_WITH_TABLE_WRITE_LOCK(session,
+ ret = __wt_schema_worker(session, uri, NULL, __backup_list_uri_append, cfg, 0));
+ WT_ERR(ret);
+ }
+ }
+ WT_ERR_NOTFOUND_OK(ret);
+
+ /*
+ * Compatibility checking.
+ *
+ * Log archive cannot mix with log-file based incremental backups (but if a duplicate cursor,
+ * archiving has been temporarily suspended).
+ *
+ * Duplicate backup cursors are only for log targets or block-based incremental backups. But log
+ * targets don't make sense with block-based incremental backup.
+ */
+ if (!is_dup && log_config && FLD_ISSET(conn->log_flags, WT_CONN_LOG_ARCHIVE))
+ WT_ERR_MSG(session, EINVAL,
+ "incremental log file backup not possible when automatic log archival configured");
+ if (is_dup && (!incremental_config && !log_config))
+ WT_ERR_MSG(session, EINVAL,
+ "duplicate backup cursor must be for block-based incremental or logging backup");
+ if (incremental_config && (log_config || target_list))
+ WT_ERR_MSG(
+ session, EINVAL, "block-based incremental backup incompatible with a list of targets");
+
+ if (incremental_config) {
+ if (is_dup && !F_ISSET(othercb, WT_CURBACKUP_INCR))
+ WT_ERR_MSG(session, EINVAL,
+ "Incremental duplicate cursor must have an incremental primary backup cursor");
+ F_SET(cb, WT_CURBACKUP_INCR);
+ }
+err:
+ if (ret != 0 && cb->incr != NULL)
+ F_CLR(cb->incr, WT_BLKINCR_INUSE);
+ __wt_scr_free(session, &tmp);
+ return (ret);
+}
+
+/*
* __backup_start --
* Start a backup.
*/
static int
-__backup_start(WT_SESSION_IMPL *session, WT_CURSOR_BACKUP *cb, bool is_dup, const char *cfg[])
+__backup_start(
+ WT_SESSION_IMPL *session, WT_CURSOR_BACKUP *cb, WT_CURSOR_BACKUP *othercb, const char *cfg[])
{
+ WT_CONFIG_ITEM cval;
WT_CONNECTION_IMPL *conn;
WT_DECL_RET;
WT_FSTREAM *srcfs;
const char *dest;
- bool exist, log_only, target_list;
+ bool exist, is_dup, is_incr, log_only, target_list;
conn = S2C(session);
srcfs = NULL;
dest = NULL;
+ is_dup = othercb != NULL;
cb->next = 0;
cb->list = NULL;
@@ -240,6 +569,21 @@ __backup_start(WT_SESSION_IMPL *session, WT_CURSOR_BACKUP *cb, bool is_dup, cons
if (F_ISSET(session, WT_SESSION_BACKUP_DUP) && is_dup)
WT_RET_MSG(session, EINVAL, "there is already a duplicate backup cursor open");
+ /*
+ * We want to check for forced stopping early before we do anything else. If it is set, we just
+ * set a flag and we're done. Actions will be performed on cursor close.
+ */
+ WT_RET_NOTFOUND_OK(__wt_config_gets(session, cfg, "incremental.force_stop", &cval));
+ if (cval.val) {
+ /*
+ * If we're force stopping incremental backup, set the flag. The resources involved in
+ * incremental backup will be released on cursor close and that is the only expected usage
+ * for this cursor.
+ */
+ F_SET(cb, WT_CURBACKUP_FORCE_STOP);
+ return (0);
+ }
+
if (!is_dup) {
/*
* The hot backup copy is done outside of WiredTiger, which means file blocks can't be freed
@@ -260,10 +604,10 @@ __backup_start(WT_SESSION_IMPL *session, WT_CURSOR_BACKUP *cb, bool is_dup, cons
/*
* Create a temporary backup file. This must be opened before generating the list of targets
- * in backup_uri. This file will later be renamed to the correct name depending on whether
- * or not we're doing an incremental backup. We need a temp file so that if we fail or crash
- * while filling it, the existence of a partial file doesn't confuse restarting in the
- * source database.
+ * in backup_config. This file will later be renamed to the correct name depending on
+ * whether or not we're doing an incremental backup. We need a temp file so that if we fail
+ * or crash while filling it, the existence of a partial file doesn't confuse restarting in
+ * the source database.
*/
WT_ERR(__wt_fopen(session, WT_BACKUP_TMP, WT_FS_OPEN_CREATE, WT_STREAM_WRITE, &cb->bfs));
}
@@ -273,14 +617,11 @@ __backup_start(WT_SESSION_IMPL *session, WT_CURSOR_BACKUP *cb, bool is_dup, cons
* database objects and log files to the list.
*/
target_list = false;
- WT_ERR(__backup_uri(session, cfg, is_dup, &target_list, &log_only));
+ WT_ERR(__backup_config(session, cb, cfg, othercb, &target_list, &log_only, &is_incr));
/*
- * For a duplicate cursor, all the work is done in backup_uri. The only usage accepted is
- * "target=("log:")" so error if not log only.
+ * For a duplicate cursor, all the work is done in backup_config.
*/
if (is_dup) {
- if (!log_only)
- WT_ERR_MSG(session, EINVAL, "duplicate backup cursor must be for logs only");
F_SET(cb, WT_CURBACKUP_DUP);
F_SET(session, WT_SESSION_BACKUP_DUP);
goto done;
@@ -317,12 +658,12 @@ __backup_start(WT_SESSION_IMPL *session, WT_CURSOR_BACKUP *cb, bool is_dup, cons
* We also open an incremental backup source file so that we can detect a crash with an
* incremental backup existing in the source directory versus an improper destination.
*/
- dest = WT_INCREMENTAL_BACKUP;
- WT_ERR(__wt_fopen(session, WT_INCREMENTAL_SRC, WT_FS_OPEN_CREATE, WT_STREAM_WRITE, &srcfs));
- WT_ERR(__backup_list_append(session, cb, WT_INCREMENTAL_BACKUP));
+ dest = WT_LOGINCR_BACKUP;
+ WT_ERR(__wt_fopen(session, WT_LOGINCR_SRC, WT_FS_OPEN_CREATE, WT_STREAM_WRITE, &srcfs));
+ WT_ERR(__backup_list_append(session, cb, dest));
} else {
dest = WT_METADATA_BACKUP;
- WT_ERR(__backup_list_append(session, cb, WT_METADATA_BACKUP));
+ WT_ERR(__backup_list_append(session, cb, dest));
WT_ERR(__wt_fs_exist(session, WT_BASECONFIG, &exist));
if (exist)
WT_ERR(__backup_list_append(session, cb, WT_BASECONFIG));
@@ -372,6 +713,8 @@ __backup_stop(WT_SESSION_IMPL *session, WT_CURSOR_BACKUP *cb)
/* If it's not a dup backup cursor, make sure one isn't open. */
WT_ASSERT(session, !F_ISSET(session, WT_SESSION_BACKUP_DUP));
WT_WITH_HOTBACKUP_WRITE_LOCK(session, conn->hot_backup_list = NULL);
+ if (cb->incr != NULL)
+ F_CLR(cb->incr, WT_BLKINCR_INUSE);
__backup_free(session, cb);
/* Remove any backup specific file. */
@@ -396,75 +739,6 @@ __backup_all(WT_SESSION_IMPL *session)
}
/*
- * __backup_uri --
- * Backup a list of objects.
- */
-static int
-__backup_uri(WT_SESSION_IMPL *session, const char *cfg[], bool is_dup, bool *foundp, bool *log_only)
-{
- WT_CONFIG targetconf;
- WT_CONFIG_ITEM cval, k, v;
- WT_DECL_ITEM(tmp);
- WT_DECL_RET;
- const char *uri;
- bool target_list;
-
- *foundp = *log_only = false;
-
- /*
- * If we find a non-empty target configuration string, we have a job, otherwise it's not our
- * problem.
- */
- WT_RET(__wt_config_gets(session, cfg, "target", &cval));
- __wt_config_subinit(session, &targetconf, &cval);
- for (target_list = false; (ret = __wt_config_next(&targetconf, &k, &v)) == 0;
- target_list = true) {
- /* If it is our first time through, allocate. */
- if (!target_list) {
- *foundp = true;
- WT_ERR(__wt_scr_alloc(session, 512, &tmp));
- }
-
- WT_ERR(__wt_buf_fmt(session, tmp, "%.*s", (int)k.len, k.str));
- uri = tmp->data;
- if (v.len != 0)
- WT_ERR_MSG(session, EINVAL, "%s: invalid backup target: URIs may need quoting", uri);
-
- /*
- * Handle log targets. We do not need to go through the schema worker, just call the
- * function to append them. Set log_only only if it is our only URI target.
- */
- if (WT_PREFIX_MATCH(uri, "log:")) {
- /*
- * Log archive cannot mix with incremental backup, don't let that happen. If we're a
- * duplicate cursor archiving is already temporarily suspended.
- */
- if (!is_dup && FLD_ISSET(S2C(session)->log_flags, WT_CONN_LOG_ARCHIVE))
- WT_ERR_MSG(session, EINVAL,
- "incremental backup not possible when "
- "automatic log archival configured");
- *log_only = !target_list;
- WT_ERR(__backup_log_append(session, session->bkp_cursor, false));
- } else {
- *log_only = false;
-
- /*
- * If backing up individual tables, we have to include indexes, which may involve
- * opening those indexes. Acquire the table lock in write mode for that case.
- */
- WT_WITH_TABLE_WRITE_LOCK(session,
- ret = __wt_schema_worker(session, uri, NULL, __backup_list_uri_append, cfg, 0));
- WT_ERR(ret);
- }
- }
- WT_ERR_NOTFOUND_OK(ret);
-
-err:
- __wt_scr_free(session, &tmp);
- return (ret);
-}
-
-/*
* __wt_backup_file_remove --
* Remove the incremental and meta-data backup files.
*/
@@ -479,8 +753,8 @@ __wt_backup_file_remove(WT_SESSION_IMPL *session)
* there's any chance of an incremental backup file existing.
*/
WT_TRET(__wt_remove_if_exists(session, WT_BACKUP_TMP, true));
- WT_TRET(__wt_remove_if_exists(session, WT_INCREMENTAL_BACKUP, true));
- WT_TRET(__wt_remove_if_exists(session, WT_INCREMENTAL_SRC, true));
+ WT_TRET(__wt_remove_if_exists(session, WT_LOGINCR_BACKUP, true));
+ WT_TRET(__wt_remove_if_exists(session, WT_LOGINCR_SRC, true));
WT_TRET(__wt_remove_if_exists(session, WT_METADATA_BACKUP, true));
return (ret);
}
diff --git a/src/third_party/wiredtiger/src/cursor/cur_backup_incr.c b/src/third_party/wiredtiger/src/cursor/cur_backup_incr.c
new file mode 100644
index 00000000000..c8c59d3f9db
--- /dev/null
+++ b/src/third_party/wiredtiger/src/cursor/cur_backup_incr.c
@@ -0,0 +1,233 @@
+/*-
+ * Copyright (c) 2014-2019 MongoDB, Inc.
+ * Copyright (c) 2008-2014 WiredTiger, Inc.
+ * All rights reserved.
+ *
+ * See the file LICENSE for redistribution information.
+ */
+
+#include "wt_internal.h"
+
+/*
+ * __alloc_merge --
+ * Merge two allocation lists.
+ */
+static void
+__alloc_merge(
+ uint64_t *a, uint64_t a_cnt, uint64_t *b, uint64_t b_cnt, uint64_t *res, uint64_t *res_cnt)
+{
+ uint64_t total;
+
+ for (total = 0; a_cnt > 0 || b_cnt > 0; ++total, res += 2) {
+ if (a_cnt > 0 && b_cnt > 0) {
+ if (a[0] <= b[0]) {
+ res[0] = a[0];
+ if (a[0] + a[1] < b[0])
+ res[1] = a[1];
+ else {
+ res[1] = (b[0] + b[1]) - a[0];
+ b += 2;
+ --b_cnt;
+ }
+ a += 2;
+ --a_cnt;
+ } else if (b[0] <= a[0]) {
+ res[0] = b[0];
+ if (b[0] + b[1] < a[0])
+ res[1] = b[1];
+ else {
+ res[1] = (a[0] + a[1]) - b[0];
+ a += 2;
+ --a_cnt;
+ }
+ b += 2;
+ --b_cnt;
+ }
+ } else if (a_cnt > 0) {
+ res[0] = a[0];
+ res[1] = a[1];
+ a += 2;
+ --a_cnt;
+ } else if (b_cnt > 0) {
+ res[0] = b[0];
+ res[1] = b[1];
+ b += 2;
+ --b_cnt;
+ }
+ }
+ *res_cnt = total;
+}
+
+/*
+ * __curbackup_incr_next --
+ * WT_CURSOR->next method for the btree cursor type when configured with incremental_backup.
+ */
+static int
+__curbackup_incr_next(WT_CURSOR *cursor)
+{
+ WT_BTREE *btree;
+ WT_CKPT *ckpt, *ckptbase;
+ WT_CURSOR_BACKUP *cb;
+ WT_DECL_RET;
+ WT_SESSION_IMPL *session;
+ wt_off_t size;
+ uint64_t *a, *b, *current, *next;
+ uint64_t entries, total;
+ uint32_t raw;
+ bool start, stop;
+
+ ckptbase = NULL;
+ a = b = NULL;
+
+ cb = (WT_CURSOR_BACKUP *)cursor;
+ btree = cb->incr_cursor == NULL ? NULL : ((WT_CURSOR_BTREE *)cb->incr_cursor)->btree;
+ raw = F_MASK(cursor, WT_CURSTD_RAW);
+ CURSOR_API_CALL(cursor, session, get_value, btree);
+ F_CLR(cursor, WT_CURSTD_RAW);
+
+ if (cb->incr_init) {
+ /* We have this object's incremental information, Check if we're done. */
+ if (cb->incr_list_offset >= cb->incr_list_count - WT_BACKUP_INCR_COMPONENTS)
+ return (WT_NOTFOUND);
+
+ /*
+ * If we returned all of the data, step to the next block, otherwise return the next chunk
+ * of the current block.
+ */
+ if (cb->incr_list[cb->incr_list_offset + 1] <= cb->incr_granularity)
+ cb->incr_list_offset += WT_BACKUP_INCR_COMPONENTS;
+ else {
+ cb->incr_list[cb->incr_list_offset] += cb->incr_granularity;
+ cb->incr_list[cb->incr_list_offset + 1] -= cb->incr_granularity;
+ cb->incr_list[cb->incr_list_offset + 2] = WT_BACKUP_RANGE;
+ }
+ } else if (btree == NULL) {
+ /* We don't have this object's incremental information, and it's a full file copy. */
+ WT_ERR(__wt_fs_size(session, cb->incr_file, &size));
+
+ cb->incr_list_count = WT_BACKUP_INCR_COMPONENTS;
+ cb->incr_init = true;
+ cb->incr_list_offset = 0;
+ __wt_cursor_set_key(cursor, 0, size, WT_BACKUP_FILE);
+ } else {
+ /*
+ * We don't have this object's incremental information, and it's not a full file copy. Get a
+ * list of the checkpoints available for the file and flag the starting/stopping ones. It
+ * shouldn't be possible to specify checkpoints that no longer exist, but check anyway.
+ */
+ ret = __wt_meta_ckptlist_get(session, cb->incr_file, false, &ckptbase);
+ WT_ERR(ret == WT_NOTFOUND ? ENOENT : ret);
+
+ /*
+ * Count up the maximum number of block entries we might have to merge, and allocate a pair
+ * of temporary arrays in which to do the merge.
+ */
+ entries = 0;
+ WT_CKPT_FOREACH (ckptbase, ckpt)
+ entries += ckpt->alloc_list_entries;
+ WT_ERR(__wt_calloc_def(session, entries * WT_BACKUP_INCR_COMPONENTS, &a));
+ WT_ERR(__wt_calloc_def(session, entries * WT_BACKUP_INCR_COMPONENTS, &b));
+
+ /* Merge the block lists into a final list of blocks to copy. */
+ start = stop = false;
+ total = 0;
+ current = NULL;
+ next = a;
+ WT_CKPT_FOREACH (ckptbase, ckpt) {
+ if (strcmp(ckpt->name, cb->incr_checkpoint_start) == 0) {
+ start = true;
+ WT_ERR_ASSERT(session, ckpt->alloc_list_entries == 0, __wt_panic(session),
+ "incremental backup start checkpoint has allocation list blocks");
+ continue;
+ }
+ if (start == true) {
+ if (strcmp(ckpt->name, cb->incr_checkpoint_stop) == 0)
+ stop = true;
+
+ __alloc_merge(
+ current, total, ckpt->alloc_list, ckpt->alloc_list_entries, next, &total);
+ current = next;
+ next = next == a ? b : a;
+ }
+
+ if (stop == true)
+ break;
+ }
+
+ if (!start)
+ WT_ERR_MSG(session, ENOENT, "incremental backup start checkpoint %s not found",
+ cb->incr_checkpoint_start);
+ if (!stop)
+ WT_ERR_MSG(session, ENOENT, "incremental backup stop checkpoint %s not found",
+ cb->incr_checkpoint_stop);
+
+ /* There may be nothing that needs copying. */
+ if (total == 0)
+ WT_ERR(WT_NOTFOUND);
+
+ if (next == a) {
+ cb->incr_list = b;
+ b = NULL;
+ } else {
+ cb->incr_list = a;
+ a = NULL;
+ }
+ cb->incr_list_count = total;
+ cb->incr_list_offset = 0;
+ WT_ERR(__wt_scr_alloc(session, 0, &cb->incr_block));
+ cb->incr_init = true;
+
+ F_SET(cursor, WT_CURSTD_KEY_EXT | WT_CURSTD_VALUE_EXT);
+ }
+
+err:
+ __wt_free(session, a);
+ __wt_free(session, b);
+ __wt_meta_ckptlist_free(session, &ckptbase);
+ F_SET(cursor, raw);
+ API_END_RET(session, ret);
+}
+
+/*
+ * __wt_curbackup_free_incr --
+ * Free the duplicate backup cursor for a file-based incremental backup.
+ */
+void
+__wt_curbackup_free_incr(WT_SESSION_IMPL *session, WT_CURSOR_BACKUP *cb)
+{
+ __wt_free(session, cb->incr_file);
+ if (cb->incr_cursor != NULL)
+ __wt_cursor_close(cb->incr_cursor);
+ __wt_free(session, cb->incr_checkpoint_start);
+ __wt_free(session, cb->incr_checkpoint_stop);
+ __wt_free(session, cb->incr_list);
+ __wt_scr_free(session, &cb->incr_block);
+}
+
+/*
+ * __wt_curbackup_open_incr --
+ * Initialize the duplicate backup cursor for a file-based incremental backup.
+ */
+int
+__wt_curbackup_open_incr(WT_SESSION_IMPL *session, const char *uri, WT_CURSOR *other,
+ WT_CURSOR *cursor, const char *cfg[], WT_CURSOR **cursorp)
+{
+ WT_CURSOR_BACKUP *cb, *other_cb;
+
+ cb = (WT_CURSOR_BACKUP *)cursor;
+ other_cb = (WT_CURSOR_BACKUP *)other;
+ WT_UNUSED(session);
+ cursor->key_format = WT_UNCHECKED_STRING(qqq);
+ cursor->value_format = "";
+
+ /*
+ * Inherit from the backup cursor but reset specific functions for incremental.
+ */
+ cursor->next = __curbackup_incr_next;
+ cursor->get_key = __wt_cursor_get_key;
+ cursor->get_value = __wt_cursor_get_value_notsup;
+ cb->incr_granularity = other_cb->incr_granularity;
+
+ /* XXX Return full file info for all files for now. */
+ return (__wt_cursor_init(cursor, uri, NULL, cfg, cursorp));
+}
diff --git a/src/third_party/wiredtiger/src/docs/devdoc-index.dox b/src/third_party/wiredtiger/src/docs/devdoc-index.dox
index 7ada556aa1a..d88826cb52d 100644
--- a/src/third_party/wiredtiger/src/docs/devdoc-index.dox
+++ b/src/third_party/wiredtiger/src/docs/devdoc-index.dox
@@ -1,13 +1,12 @@
/*! @page devdoc-index Developer Documentation
+@subpage devdoc-schema
+
Most applications begin to make use of WiredTiger by creating a table (or other
data object) to store their data in. Create is one of several schema operations
available in WiredTiger.
-For more information on how schema operations are implemented in WiredTiger,
-see:
-
-- @subpage devdoc-schema
+@subpage devdoc-dhandle-lifecycle
An internal structure called Data Handle (dhandle) is used to represent and
access a table in WiredTiger. A dhandle gets created when a table is accessed
@@ -15,8 +14,32 @@ for the first time. It is kept in a global list and is shared across the
sessions. When a dhandle is not needed anymore and has been idle for a while,
it is closed and destroyed, releasing all the resources associated with it.
-For more information on the lifecycle of a dhandle, see:
+@subpage devdoc-statistics
+
+WiredTiger can generate statistics that are useful for providing information
+necessary when performance tuning your WiredTiger application. Here we focus
+on analyzing and reviewing the data generated by the statistics logging
+functionality.
+
+@subpage devdoc-optrack
+
+The purpose of operation tracking is to visualize WiredTiger's execution so
+that correlations between performance anomalies are easily spotted. This
+operation tracking tutorial provides a general overview of operation tracking
+and describes ways to visualize the data in fine detail.
+
+@subpage devdoc-perf
+
+Linux `perf` is a tool that allows counting and sampling of various events in
+the hardware and in the kernel. Hardware events are available via performance
+monitoring units (PMU); they measure CPU cycles, cache misses, branches, etc.
+Kernel events include scheduling context switches, page faults and block I/O.
+Here we provide a quick cheat sheet of how to use `perf` with WiredTiger.
+
+@subpage devdoc-perf-flamegraphs
-- @subpage devdoc-dhandle-lifecycle
+Why is my CPU busy? FlameGraphs help visually summarize on-CPU call stacks and
+allow for the quick identification of hot code paths. Here we explain how to
+generate FlameGraphs from WiredTiger `perf` data.
*/
diff --git a/src/third_party/wiredtiger/src/docs/devdoc-optrack.dox b/src/third_party/wiredtiger/src/docs/devdoc-optrack.dox
new file mode 100644
index 00000000000..360b3b6de47
--- /dev/null
+++ b/src/third_party/wiredtiger/src/docs/devdoc-optrack.dox
@@ -0,0 +1,351 @@
+/*! @page devdoc-optrack Operation Tracking
+
+# Overview
+
+This tutorial will walk you through using operation tracking on one of `wtperf`
+workloads: from preparing the workload for most effective data collection, to
+gathering, visualizing and interpreting the execution logs.
+
+## Why use operation tracking?
+
+Analysis of WiredTiger JIRA tickets revealed that in diagnosing performance
+bugs engineers rely mostly on information that shows which functions were
+executed over time and across threads. They seek to find correlations between
+performance anomalies, such as long latencies or periods of low throughput, and
+events on these timelines. The purpose of operation tracking is to visualize
+the execution so that these correlations could be easily spotted.
+
+## How does operation tracking work?
+
+Operation tracking allows you to put a pair of macros `WT_TRACK_OP_INIT` and
+`WT_TRACK_OP_END` into any function in WiredTiger whose invocation time you
+want to be measured and recorded. As your program runs, the `WT_TRACK_OP_INIT`
+macro will produce the function entry timestamp and `WT_TRACK_OP_END` macro
+will produce the function exit timestamp. These timestamps, along with the
+function name, will be recorded in a set of log files. It is typical to put the
+`WT_TRACK_OP_INIT` in the beginning of the function and the `WT_TRACK_OP_END`
+macro at every exit point from the function, but you can put them anywhere
+within the function body. The only condition required in order to correctly
+visualize the logs is that each `WT_TRACK_OP_INIT` has a matching
+`WT_TRACK_OP_END`. It is totally okay to insert tracking macros into nested
+functions.
+
+## Will operation tracking introduce runtime overhead?
+
+The answer depends on your workload, and how many functions you track. For
+`wtperf` suite the largest overhead of 30% was observed on the CPU-bound
+workload `small-btree`. For I/O-bound workloads, such as
+`evict-btree-stress-multi`, the overhead is negligible, unless you decided to
+track short frequently executed functions.
+
+# Prerequisites
+
+## Prepare your workload
+
+Modify your benchmark so that it produces the desired behavior with as little
+running time as possible. The longer the benchmark runs the more data it will
+produce, and the more difficult it will be for you to go over all this data.
+
+In our running example with the `evict-btree-stress-multi.wtperf` workload, we
+separate the database creation phase from the work phase. We first create the
+database with operation tracking disabled. Then we enable operation tracking
+and run the work phase. So operation tracking logs are generated during the
+work phase only. We also reduce the work phase to 30 seconds, to make sure that
+the volume of information is manageable.
+
+## Install the python modules
+
+To generate data visualizations, you will need the python modules bokeh,
+matplotlib and pandas installed. You can install them via the following
+commands (if you don't use pip modify for your python module manager):
+
+ pip install bokeh matplotlib pandas
+
+## Enable operation tracking
+To enable operation tracking, you need to add the following option to your
+connection configuration string.
+
+ operation_tracking=(enabled=true,path=.)
+
+For example, if you are running `wtperf`, you would add it to the `conn_config`
+string:
+
+ ./wtperf -c conn_config="operation_tracking=(enabled=true,path=.)"
+
+The "path" argument determines where the log files, produced by operation
+tracking, will be stored. Now you can run your workload.
+
+# Working with log files
+
+## Becoming familiar with the operation tracking log files
+
+After the run is complete, the path directory will have a bunch of files of the
+following format:
+
+ optrack.00000<pid>.000000000<sid>
+
+and the file
+
+ optrack-map.00000<pid>
+
+where `pid` is the process ID and `sid` is the session ID.
+
+The first kind of files are the actual log files. The second is the map file.
+Log files are generated in the binary format, because this is a lot more
+efficient than writing strings, and the map file is needed to decode them.
+
+## Processing the log files
+
+Now the binary log files need to be converted into the text format. To do that
+use the script `wt_optrack_decode.py` in the WiredTiger tree. We will refer to
+the path to your WiredTiger tree as WT. Suppose that the process ID that
+generated the operation tracking files is 25660. Then you'd run the decode
+script like so:
+
+ % WT/tools/optrack/wt_optrack_decode.py -m optrack-map.0000025660 optrack.0000025660.00000000*
+
+As the script runs you will see lots of output on the screen reporting the
+progress through the parsing process. One kind of output you might see is
+something like this:
+
+ TSC_NSEC ratio parsed: 2.8000
+
+This refers to the clock cycles to nanoseconds ratio encoded into the log
+files. In the example above, we had 2.8 ticks per nanosecond, suggesting that
+the workload was run on a 2.8 GHz processor. This ratio is used later to
+convert the timestamps, which are recorded in clock cycles for efficiency, to
+nanoseconds.
+
+In the end, you should see lots of output files whose names look like the
+optrack log files, but with the suffixes `-internal.txt` and `-external.txt`.
+The "internal" files are the log files for WT internal sessions (such as
+eviction threads). The "external" files are for the sessions created by the
+client application.
+
+Now that the files have been decoded, it's time to visualize the data. This can
+be done using the script find-latency-spikes.py located in the `tools/optrack`
+directory of the WiredTiger tree. To process your text files from our example,
+you would run this script like so:
+
+ % WT/tools/optrack/find-latency-spikes.py optrack.0000025660.00000000*.txt
+
+As the script runs, you will probably see messages similar to this one:
+
+ Processing file optrack.0000025660.0000000026-external.txt
+ Your data may have errors. Check the file .optrack.0000025660.0000000026-external.txt.log for details.
+
+This means that as the script was processing the log file, it found some
+inconsistencies, for example there was a function entry timestamp, but no
+corresponding function exit, or vice versa. Sometimes these errors are benign.
+For example, the log file will never have an exit timestamp from a
+session-closing function, because operation tracking is torn down as the
+session closes. In other cases, an error may indicate that there is a mistake
+in how the operation-tracking macros were inserted. For example, the programmer
+may have forgotten to insert a macro where there is an early return from a
+function. Examining the log file mentioned in the error message will give you
+more details on the error.
+
+The data processing script will not fail or exit if it encounters these error
+messages, it will proceed despite them, attempting to visualize the portion of
+the log that did not contain the errors. The function timestamps implicated in
+the errors will simply be dropped.
+
+At the very end of the processing, you will see messages like this:
+
+ Normalizing data...
+ Generating timeline charts... 99% complete
+ Generating outlier histograms... 100% complete
+
+This is how you know the processing has completed. The script takes a while to
+run, but it is parallelizable, so that is something that can be done in the
+future to speed it up.
+
+# Looking at the data: Part 1
+
+In the directory where you ran the data processing script, you will have the
+file `WT-outliers.html` and the directory `BUCKET-FILES` to which that HTML
+file refers. If you would like to look at the visualization on another machine,
+transfer both the HTML file and the directory to that machine. Otherwise, just
+open `WT-outliers.html` locally in your browser.
+
+To get an idea of what kind of output you will see, take a look at [this
+example data](http://www.ece.ubc.ca/~sasha/TUTORIAL-DEMO/) generated by the
+workload described earlier in this tutorial. Please open the URL, because we
+will now walk over what it shows.
+
+The main page shows a number of outlier histograms. Each histogram corresponds
+to a function that was tracked by the operation tracking system in WT. The
+x-axis shows the execution timeline (in nanoseconds). The y-axis shows how many
+abnormally long executions of the function occurred during very period of the
+execution.
+
+![](http://www.ece.ubc.ca/~sasha/TUTORIAL-DEMO-CONFIG/IMG/outlier_histograms.png)
+
+You can click on outlier bars and look at the detailed visualization of the
+period during which the abnormally long function invocations occurred. But
+before we do that, let's talk about what is considered an "outlier" or an
+"abnormally long execution" and how to control these thresholds. As annotations
+on the histograms show, by default a function invocation is considered an
+outlier if its duration is longer than two standard deviations above average.
+(The average is computed for this function over the entire execution). If you
+would like a more precise control of outlier thresholds, you can do that in a
+configuration file that is read by the execution script.
+
+Before we talk about other visualization screens, let's actually generate a
+visualization with some more meaningful outlier thresholds than two standard
+deviations. This way, it will be easier to navigate the data, and we will be
+able to observe some very interesting behavior that gives us a clue as to why
+eviction is sometimes slow. (If you can't wait to find out, skip over the next
+step.)
+
+## Getting a more precise control of the outlier thresholds
+
+Let's learn how to configure custom outlier thresholds. The easiest way to
+begin is to grab a sample configuration file and edit it to your needs:
+
+ % cp WT/tools/optrack/sample.config .
+
+Here is how that file looks:
+
+ # Units in which timestamps are recorded
+ 1000000000
+ # Outlier threshold for individual functions
+ __curfile_reset 100 ms
+ __curfile_search 50 ms
+ __curfile_update 3 ms
+ __evict_lru_pages 75 ms
+ __evict_lru_walk 20 ms
+ __evict_page 75 ms
+ __evict_walk 20 ms
+ __wt_cache_eviction_worker 10 ms
+ __wt_cond_wait_signal 5 s
+ __wt_cursor_set_valuev 10 ms
+
+In that file all lines prefixed with a # are comments.
+
+The first non-comment line specifies the units in which your timestamps are
+recorded. The units are specified by providing a value indicating how many time
+units there are in a second. By default operation scripts generate timestamps
+in nanoseconds, hence you see a value of 1000000000 on the first line in our
+sample file. If your time units were, say, milliseconds, you'd change that
+value to 1000, and so on.
+
+The remaining non-comment lines in the file provide the outlier thresholds for
+any functions you care to have a custom threshold. The format is:
+
+ <function name> <value> <unit>
+
+For example, to specify a threshold of 15 milliseconds for the
+`__wt_cache_eviction_worker` function, you'd insert the following line:
+
+ __wt_cache_eviction_worker 15 ms
+
+Valid units are nanoseconds (ns), microseconds (us), milliseconds (ms) and
+seconds (s).
+
+If you don't specify a threshold for a function the default of two standard
+deviations will be used. Let's actually configure some more meaningful outlier
+thresholds in our `sample.config` file and re-run the visualization script. Here
+is the modified `sample.config`:
+
+ # Units in which timestamps are recorded
+ 1000000000
+ # Outlier threshold for individual functions
+ __curfile_search 30 ms
+ __evict_lru_pages 250 ms
+ __evict_lru_walk 1 ms
+ __evict_page 15 ms
+ __evict_walk 1 ms
+ __wt_cache_eviction_worker 15 ms
+ __wt_cond_wait_signal 10 s
+
+We rerun the data processing script with the configuration file argument, like
+so:
+
+ % WT/tools/optrack/find-latency-spikes.py -c sample.config optrack.0000025660.00000000*.txt
+
+The new charts can be accessed
+[here](http://www.ece.ubc.ca/~sasha/TUTORIAL-DEMO-CONFIG/).
+
+# Looking at the data: Part 2
+
+Let's examine the visualized data in more detail. Please click on [this
+URL](http://www.ece.ubc.ca/~sasha/TUTORIAL-DEMO-CONFIG/):
+
+Although I insert the screenshots here, you will have more fun if you access it
+directly.
+
+Now you can see the outlier charts complying with the configuration parameters
+we supplied. For example, on the chart for the `__wt_cache_eviction_worker`, we
+see only the intervals where that function took longer than 15 ms to complete.
+
+![](http://www.ece.ubc.ca/~sasha/TUTORIAL-DEMO-CONFIG/IMG/wt_cache_eviction_worker_outliers.png)
+
+Let's click on one of those intervals to examine what happened there. I am
+going to click on the tallest bar in the chart, which will take me to a
+detailed output of function call activity by thread for interval 137 at [this
+URL](http://www.ece.ubc.ca/~sasha/TUTORIAL-DEMO-CONFIG/BUCKET-FILES/bucket-137.html).
+
+There is a lot of graphics there, so the URL might take a few seconds to load.
+
+At the very top of the page you see a small navigation bar. The bar tells you
+where you are in the execution (the red marker). By clicking on other intervals
+within the bar you can navigate to other intervals. For example, if you wanted
+to look at the execution interval located after the current one, you will just
+click on the white bar following the current, red-highlighted, bar.
+
+![](http://www.ece.ubc.ca/~sasha/TUTORIAL-DEMO-CONFIG/IMG/interval_137_nav_bar.png)
+
+Next you see a legend that shows all functions that were called during this
+execution interval and their corresponding colours.
+
+![](http://www.ece.ubc.ca/~sasha/TUTORIAL-DEMO-CONFIG/IMG/interval_137_lgnd.png)
+
+Then you will see the most interesting information: function calls across
+threads that occurred during this period. Durations and hierarchy of function
+calls is preserved, meaning that longer functions will be shown with longer
+bars and if function A called function B, B will be shown on top of A.
+
+By hovering over the colored bars corresponding to functions, you will see a
+box appear telling you the name of the function, how long it took and its
+original timestamp in your operation tracking log file. This way, if you are
+suspicious about the correctness of your log or want to go back to it for some
+reason, you know what to look for.
+
+In our example visualization, if we scroll down to the function sequences for
+external threads, we will quickly see a few instances where
+__wt_cache_eviction_worker took longer than 15 ms, for example here:
+
+![](http://www.ece.ubc.ca/~sasha/TUTORIAL-DEMO-CONFIG/IMG/wt_cache_eviction_worker_over_15ms.png)
+
+As we can see, the threads are simply waiting on the condition variable inside
+the eviction worker. To try and understand why, we might want to scroll up and
+look at the activity of internal threads during this period. Interestingly
+enough, most of the internal threads are also waiting on the condition variable
+during this period.
+
+![](http://www.ece.ubc.ca/~sasha/TUTORIAL-DEMO-CONFIG/IMG/interval_137_threads_waiting.png)
+
+Looking at the internal thread with session id 1 shows something suspicious.
+
+![](http://www.ece.ubc.ca/~sasha/TUTORIAL-DEMO-CONFIG/IMG/session_1_dead_period.png)
+
+During this period where all threads are waiting, this thread shows no activity
+at all, whereas prior and after that "dead" period it was making regular calls
+to `__evict_walk`. Perhaps that thread was unscheduled or stopped calling
+__evict_walk for other reasons. Perhaps other threads were dependent on work
+performed within `__evict_walk` to make forward progress.
+
+To better understand why `__evict_walk` was interrupted for almost 10ms we
+might want to go back and insert operation tracking macros inside the functions
+called by `__evict_walk`, to see if the thread was doing some other work during
+this time or was simply unscheduled.
+
+# Summary
+
+Operation tracking is great for debugging performance problems where you are
+interested in understanding the activity of the workload over time and across
+threads. To effectively navigate large quantities of data it is best to
+reproduce your performance problem in a short run.
+
+*/
diff --git a/src/third_party/wiredtiger/src/docs/devdoc-perf-flamegraphs.dox b/src/third_party/wiredtiger/src/docs/devdoc-perf-flamegraphs.dox
new file mode 100644
index 00000000000..1e5bc1ad4fd
--- /dev/null
+++ b/src/third_party/wiredtiger/src/docs/devdoc-perf-flamegraphs.dox
@@ -0,0 +1,74 @@
+/*! @page devdoc-perf-flamegraphs CPU Flame Graphs
+
+# Introduction
+
+[FlameGraphs](http://www.brendangregg.com/FlameGraphs/cpuflamegraphs.html),
+invented by [Brendan Gregg](http://www.brendangregg.com), help visually
+summarize on-CPU call stacks.
+
+Below is an image of a FlameGraph generated from a 10-second run of the
+evict-btree-stress-multi wtperf job:
+
+![](https://github.com/wiredtiger/wiredtiger/wiki/images/FlameGraph-evict-stress-multi-truncated.png)
+
+To see it in action, download the non-redacted interactive version
+[here](https://github.com/wiredtiger/wiredtiger/wiki/images/FlameGraph-evict-stress-multi.svg)
+and open it in your web browser.
+
+Quoting from [Brendan Gregg's
+website](http://www.brendangregg.com/FlameGraphs/cpuflamegraphs.html), here is
+how to interpret the output of the FlameGraph:
+
+- Each box represents a function in the stack (a "stack frame").
+- The y-axis shows stack depth (number of frames on the stack). The top box
+ shows the function that was on-CPU. Everything beneath that is ancestry. The
+ function beneath a function is its parent, just like the stack traces shown
+ earlier. (Some flame graph implementations prefer to invert the order and use
+ an "icicle layout", so flames look upside down.)
+- The x-axis spans the sample population. It does not show the passing of time
+ from left to right, as most graphs do. The left to right ordering has no
+ meaning (it's sorted alphabetically to maximize frame merging).
+- The width of the box shows the total time it was on-CPU or part of an
+ ancestry that was on-CPU (based on sample count). Functions with wide boxes
+ may consume more CPU per execution than those with narrow boxes, or, they may
+ simply be called more often. The call count is not shown (or known via
+ sampling).
+
+__Note:__ If you generate a FlameGraph yourself and then open it in a browser,
+the image will be interactive, allowing you to hover over the functions to see
+their full names.
+
+# Generating FlameGraphs
+
+To generate FlameGraphs, you first need to run perf and generate a perf.data
+file using the `perf record -g` command. [Click here](@ref devdoc-perf) for
+instructions explaining how to do this with WiredTiger. Don't forget the `-g`
+option: you need it in order to record the call stacks!
+
+ # Clone the FlameGraph repository
+ git clone https://github.com/brendangregg/FlameGraph
+ cd FlameGraph
+
+ # Place perf.data into the current directory, then run these commands.
+ # Run them on the same machine where you ran `perf record` so perf can
+ # resolve the symbols.
+ perf script | ./stackcollapse-perf.pl > out.perf-folded
+ ./flamegraph.pl out.perf-folded > perf-kernel.svg
+
+Then open the resultant `svg` file in your web browser.
+
+Here is another FlameGraph for a full run of the `many-stable-stress.wtperf`
+job:
+
+![](https://github.com/wiredtiger/wiredtiger/wiki/images/FlameGraph-many-tables-stress-truncated.png)
+
+Download the non-redacted interactive version
+[here](https://github.com/wiredtiger/wiredtiger/wiki/images/FlameGraph-many-tables-stress.svg)
+and open it in your web browser.
+
+If you see 'unknown' call stacks in your FlameGraph, this means that perf could
+not resolve the symbols. To fix this, install the versions of the libraries or
+the OS kernel with symbols included, link against those libraries and re-gather
+the `perf` output.
+
+*/
diff --git a/src/third_party/wiredtiger/src/docs/devdoc-perf.dox b/src/third_party/wiredtiger/src/docs/devdoc-perf.dox
new file mode 100644
index 00000000000..4739fba897f
--- /dev/null
+++ b/src/third_party/wiredtiger/src/docs/devdoc-perf.dox
@@ -0,0 +1,160 @@
+/*! @page devdoc-perf Performance Monitoring
+
+# Overview
+
+Linux `perf` is a tool that allows counting and sampling of various events in
+the hardware and in the kernel. Hardware events are available via performance
+monitoring units (PMU); they measure CPU cycles, cache misses, branches, etc.
+Kernel events include scheduling context switches, page faults, block I/O, etc.
+
+`perf` is good for answering these and similar questions:
+
+- What is the breakdown of CPU time (or cache misses or branch mispredictions)
+ by function?
+- What was the total number of page faults generated by two different runs of
+ my program?
+- How many context switches occurred during the execution?
+- How many times did my program issued a `sched_yield` system call?
+- Which functions triggered most page faults?
+- How much block I/O did my program generate?
+
+Despite [claims that perf can also tell you where and why your program was
+blocking](https://perf.wiki.kernel.org/index.php/Tutorial#Profiling_sleep_times),
+I was not able to get it to do so after many attempts. For alternative ways to
+perform off-CPU analysis, read [this post by Brendan
+Gregg](http://www.brendangregg.com/offcpuanalysis.html) or use WiredTiger's
+@ref devdoc-optrack.
+
+What follows is a quick cheat sheet of how to use `perf` with WiredTiger. Most
+of the information in this cheat sheet comes from [this excellent tutorial by
+Brendan Gregg](http://www.brendangregg.com/perf.html). If you need more
+information, please refer to that source.
+
+# Running perf with WiredTiger
+
+Build WiredTiger as usual. You do not need to add any special compilation
+flags.
+
+To run `perf` with WiredTiger, you simply insert the name of the `perf` command
+before the WiredTiger executable. For example, if you want to record default
+`perf` statistics for a `wtperf` job, you'd run:
+
+ perf stat wtperf <wtperf options>
+
+If you want to profile CPU time of `wtperf`, you'd run:
+
+ perf record wtperf <wtperf options>
+
+Any environment variables you care about will go before the `perf` command.
+
+If you wanted to run `perf` on an already running WiredTiger process, you
+supply a `-p <PID>` option to either of these commands:
+
+ perf record -p <PID>
+
+or
+
+ perf stat -p <PID>
+
+# Counting events
+
+The command `perf stat` will report the total count of events for the entire
+execution.
+
+ perf stat wtperf <wtperf options>
+
+By default it will report things like clock cycles, context switches, CPU
+migrations, page faults, cycles, instructions, branches and branch misses.
+
+Here is a sample output:
+
+ 55275.953046 task-clock (msec) # 1.466 CPUs utilized
+ 7,349,023 context-switches # 0.133 M/sec
+ 38,219 cpu-migrations # 0.691 K/sec
+ 160,562 page-faults # 0.003 M/sec
+ 173,665,597,703 cycles # 3.142 GHz
+ 90,146,578,819 instructions # 0.52 insn per cycle
+ 22,199,890,600 branches # 401.619 M/sec
+ 387,770,656 branch-misses # 1.75% of all branches
+
+ 37.710693533 seconds time elapsed
+
+Alternatively, you can decide what events you want to include. For example, if
+you wanted to count last-level cache (LLC) misses, one way to do this is like
+so:
+
+ perf stat -e LLC-load-misses wtperf <wtperf options>
+
+Or if you wanted all default events and all cache events, run:
+
+ perf stat -d wtperf <wtperf options>
+
+To learn about all events you can count or sample with perf, use the command:
+
+ perf list
+
+To count all system calls by type, run:
+
+ perf stat -e 'syscalls:sys_enter_*'
+
+# Sampling events (profiling)
+
+Unlike `perf stat`, which counts all events, `perf record` samples events. The
+simplest way to invoke it is like so:
+
+ perf record <command>
+
+This will sample on-CPU functions.
+
+# Examining the output
+
+By default, the output will be placed into a file named `perf.data`. To examine
+it, run:
+
+ perf report
+
+`perf report` will show, for each sampled event, the call stacks that were
+recorded at the time of sampling. The call stacks will be sorted from the most
+frequently occurring to the least frequently occurring.
+
+At the time of this writing, the default sampling frequency is 4000; that is, a
+sample is taken after every 4000 events. This number may change, so to be sure
+check the setting in the header in the `perf` output, like so:
+
+ perf report --header
+
+If you want to change the output file, use the `-o` option:
+
+ perf record -o <new_output_file> <command>
+
+And run the reporting with the `-i` option:
+
+ perf report -i <new_output_file>
+
+Here is a way to look at the output with all the call stacks expanded:
+
+ perf report -n --stdio
+
+# More examples
+
+Sample on-cpu functions with call stacks:
+
+ perf record -g <command>
+
+Sample as above, but using a specific sampling frequency (99 hertz):
+
+ perf record -F 99 -g <command>
+
+Sample last-level cache load misses with call stacks:
+
+ perf record -e LLC-load-misses -g <command>
+
+Sample cache misses for a running process:
+
+ perf record -e LLC-load-misses -g -p <PID>
+
+Sample page faults:
+
+ perf record -e page-faults -g
+
+*/
diff --git a/src/third_party/wiredtiger/src/docs/devdoc-schema.dox b/src/third_party/wiredtiger/src/docs/devdoc-schema.dox
index 509fbf16d67..dd59aa2535b 100644
--- a/src/third_party/wiredtiger/src/docs/devdoc-schema.dox
+++ b/src/third_party/wiredtiger/src/docs/devdoc-schema.dox
@@ -1,4 +1,4 @@
-/*! @page devdoc-schema Schema operations
+/*! @page devdoc-schema Schema Operations
A schema defines the format of the application data in WiredTiger. WiredTiger
supports various types of schemas (See @ref schema for more
diff --git a/src/third_party/wiredtiger/src/docs/devdoc-statistics.dox b/src/third_party/wiredtiger/src/docs/devdoc-statistics.dox
new file mode 100644
index 00000000000..d8befa61136
--- /dev/null
+++ b/src/third_party/wiredtiger/src/docs/devdoc-statistics.dox
@@ -0,0 +1,165 @@
+/*! @page devdoc-statistics Statistics Logging
+
+# Introduction
+
+The following content describes a subset of the statistics features available
+in WiredTiger. For more complete information see the [statistics]
+(http://source.wiredtiger.com/2.0.1/statistics.html) page in the WiredTiger
+documentation. This content focuses on analyzing and reviewing the data
+generated by the statistics logging functionality.
+
+# Enabling statistics
+
+By default statistics are disabled in WiredTiger - this is because tracking
+statistics has a performance overhead. WiredTiger has two modes for tracking
+statistics `fast` and `all`. Fast statistics are those that don't incur a
+significant performance overhead. The effect on performance of enabling
+statistics varies depending on the database and access characteristics. To
+enable statistics you should include either `statistics=[fast,clear]` or
+`statistics=[all,clear]` in the configuration string to `wiredtiger_open`.
+
+Alternatively you can set the `WIREDTIGER_CONFIG` environment variable to
+include the relevant configuration string. Note that the environment variable
+is checked at `wiredtiger_open` time - changing the environment variable after
+WiredTiger is already running will not alter the behavior of WiredTiger.
+
+# Accessing statistics
+
+WiredTiger provides two methods for accessing statistics: using a `statistics:`
+uri in the WiredTiger `WT_CURSOR` API and using the built in statistics
+logging functionality. The remainder of this document assumes the use of the
+statistics logging functionality as the tools provided parse the output that
+it generates.
+
+Enabling statistics logging causes WiredTiger to generate files containing
+statistics information in the database directory. To enable statistics logging
+include statistics_log=(wait=30) in the configuration string to `wiredtiger_open`.
+
+The wait option configures how often the statistics are written to the log file
+in seconds. Gathering and writing statistics has overhead - so this period
+should not be too small. The default location for statistics log files is in
+the WiredTiger database directory. The files are named `WiredTigerStat.XX`,
+where `XX` corresponds to the current hour. A new file is created for each
+hour. If your application runs for more than 24 hours and the file names wrap,
+the statistics logging output is appended to the already existing files.
+Statistics log file names are configurable. The output in the statistics log
+files is textual and human readable.
+
+# Visualizing statistics
+
+WiredTiger provides several tools that generate visual representations of
+statistics based on statistics log files.
+
+## Data Driven Documents (D3) graph
+
+[D3](http://d3js.org/) is a standard for generating interactive graphs in a
+HTML page. There is a python program in the WiredTiger source tree at
+`tools/wtstats.py` that can be used to generate a D3 html page based on
+WiredTiger statistics log(s). In order to use the Python script you need to
+have Python installed and a recent version of the Python `nvd3` library.
+
+To install `nvd3`:
+
+ $ git clone https://github.com/areski/python-nvd3.git
+ $ cd python-nvd3/
+ $ sudo python setup.py install
+
+It is not sufficient to run `pip install python-nvd3` - the version provided
+by the library is too old.
+
+Once you have the prerequisites installed you can generate a graph that
+contains your statistics by running:
+
+ $ cd ~/work/wiredtiger
+ $ python ./tools/wtstats.py <path/to/db/directory/WiredTigerStat.*>
+ $ ls wtstats.html
+ wtstats.html
+
+There are several optional arguments. Run with `-h` or `--help` to see them.
+For example, `-o file` or `--output file` redirects where the HTML output goes.
+Also `--focus` generates a chart that contains a focus slider to allow you to
+zoom in on particular time periods.
+
+You can open the generated HTML document in your browser, and see the generated
+statistics. The keys in the graph are clickable - double clicking on one of the
+keys will show only that statistic. Single clicking will enable/disable a
+particular statistic.
+
+## Gnuplot graphs
+
+WiredTiger provides a python application that plots relevant statistics on
+individual graphs using gnuplot. The following commands will generate graphs of
+this type:
+
+ $ cd ~/work/wiredtiger
+ $ python tools/statlog.py <path/to/db/directory/WiredTigerStat.*>
+ $ ls reports
+ raw report.010.png report.020.png report.030.png
+ report.001.png report.011.png report.021.png report.031.png
+ report.002.png report.012.png report.022.png report.032.png
+ report.003.png report.013.png report.023.png report.033.png
+ report.004.png report.014.png report.024.png report.034.png
+ report.005.png report.015.png report.025.png report.035.png
+ report.006.png report.016.png report.026.png report.036.png
+ report.007.png report.017.png report.027.png report.037.png
+ report.008.png report.018.png report.028.png report.038.png
+ report.009.png report.019.png report.029.png report.039.png
+
+Each of the images contains a graph for a single statistic over time.
+
+# A complete example
+
+The following steps take you through the process of generating a D3 graph of
+WiredTiger statistics based on the statistics generated from running an
+application included in the WiredTiger tree called `wtperf`. These instructions
+assume you have all the necessary tools to build WiredTiger, Python and
+python-nvd3 installed.
+
+ $ echo "First get and build a working WiredTiger install"
+ $ cd work
+ $ wget http://source.wiredtiger.com/releases/wiredtiger-2.0.1.tar.bz2
+ $ tar jxf wiredtiger-2.0.1.tar.bz2 && cd wiredtiger-2.0.1
+ $ ./configure && make -j 12
+ $ echo "Run an application that will generate statistics"
+ $ mkdir WT_TEST && ./bench/wtperf/wtperf \
+ -O bench/wtperf/runners/update-lsm.wtperf \
+ -o "conn_config=\"cache_size=1G,statistics=[fast,clear],statistics_log=(wait=30)\""
+ $ echo "Now generate a graph from the statistics"
+ $ python ./tools/wtstats.py WT_TEST/WiredTigerStat.*
+
+The above steps will have generated a file called `wtstats.html` open this file
+in a web browser and review the statistics. The page you see should look
+something like:
+
+![First page](https://raw.github.com/wiki/wiredtiger/wiredtiger/attachments/wtstats_all_lines.png)
+
+If you double click on the "btree cursor insert calls per second" key, it will
+show just that statistic in the window. Depending on the test you ran (I ran a
+modified version of the `update-large-lsm.wtperf` configuration), the graph may
+look something like:
+
+![Cursor insert graph](https://raw.github.com/wiki/wiredtiger/wiredtiger/attachments/wtstats_cursor_inserts.png)
+
+This is an interesting graph. We can do some analysis on the meaning of the
+results:
+
+- From 0 to about 180 seconds there are about 130k inserts a second. This
+probably corresponds to the time during which the cache isn't full, so we get a
+lot of work done.
+
+- Between 240 and 510 seconds we see approximately 110k inserts per second -
+this is probably the rate we will sustain once the cache is full.
+
+- At 510 seconds we see a huge spike to 210k inserts a second - this
+corresponds to the time in the wtperf application where we are about to switch
+from populating a database, to running the workload phase. Since this is an LSM
+tree, the inserts here are the result of merge operations.
+
+- The remainder of the time we sustain approximately 80k inserts a second - this
+is probably the steady state during the workload phase of the benchmark.
+
+From this particular statistic we can gather that it's unlikely the cursor
+insert rate that is a bottle neck in this particular test - since we could
+achieve an insert rate over double the steady state insert rate when required.
+
+*/
diff --git a/src/third_party/wiredtiger/src/docs/spell.ok b/src/third_party/wiredtiger/src/docs/spell.ok
index 0677b92028d..1f93969ca69 100644
--- a/src/third_party/wiredtiger/src/docs/spell.ok
+++ b/src/third_party/wiredtiger/src/docs/spell.ok
@@ -7,6 +7,7 @@ Atomicity
BLOBs
CFLAGS
CPPFLAGS
+CPUs
CRC
Cheng
Christoph
@@ -21,6 +22,7 @@ Datastore
DbCursor
DbEnv
DbMultiple
+Durations
EAGAIN
EB
EBUSY
@@ -32,6 +34,8 @@ EmpId
Encryptors
Facebook
Facebook's
+FlameGraph
+FlameGraphs
FlexeLint
FreeBSD
FreeBSD's
@@ -45,11 +49,13 @@ HyperDex
HyperLevelDB
IEC
JDK
+JIRA
JavaScript
KMS
LD
LDFLAGS
LIBS
+LLC
LLVM
LOGREC
LRU
@@ -67,9 +73,11 @@ Mewhort
MongoDB
Multithreaded
NOTFOUND
+NSEC
NUMA
NoSQL
OPTYPE
+PMU
PPC
PRELOAD
README
@@ -84,6 +92,7 @@ Rrx
SCons
Seward's
SiH
+TSC
TXT
UNC
URIs
@@ -125,6 +134,7 @@ benchmarking
bigram
bindir
bitstring
+bokeh
bool
boolean
booleans
@@ -159,9 +169,12 @@ conn
const
control's
copydoc
+cp
cpp
+cpu
crashless
crc
+curfile
cursortype
customerABC
cv
@@ -190,8 +203,8 @@ destructors
dev
devdoc
dhandle
-dhandles
dhandle's
+dhandles
disjunction
disjunctions
distclean
@@ -239,6 +252,8 @@ filesystem
filesystems
fillfactor
firstname
+flamegraph
+flamegraphs
fnv
forw
fput
@@ -266,20 +281,22 @@ htmlinclude
huffman
hugepage
icount
+ie
iflag
indices
init
+insn
intl
inuse
io
ip
-ie
je
jemalloc
jitter
jni
jrx
json
+jxf
kb
keyexist
keyfmt
@@ -289,6 +306,7 @@ keyvalue
kvs
lang
lastname
+latencies
le
len
li
@@ -305,6 +323,7 @@ logc
lookup
lookups
lrtf
+lru
lsm
lsn
lt
@@ -315,6 +334,7 @@ malloc
malloc'd
marshalled
marshalling
+matplotlib
maxintlitem
maxintlpage
maxleafitem
@@ -327,12 +347,14 @@ memp
metadata
metatracking
minkey
+mispredictions
mixin
mixins
mkdir
mmap
mpool
mpoolfile
+msec
msg
msgs
multi
@@ -363,6 +385,7 @@ nosync
notgranted
notyet
nowait
+ns
nul
num
numa
@@ -377,6 +400,7 @@ os
outlier
outliers
ovfl
+parallelizable
pareto
pcoll
pdf
@@ -435,14 +459,17 @@ sHq
sanitization
scalable
scanf
+sched
schemas
scons
+screenshots
secretkey
selectable
seqname
seqno
serializable
sess
+sid
skiplist
spinlock
spinlocks
@@ -463,8 +490,11 @@ subdatabases
subdirectory
subpage
substring
+sudo
superset
+svg
sys
+syscalls
sz
tRuE
tablename
@@ -474,6 +504,8 @@ td
th
thang
tid
+timeline
+timelines
timestamp
timestamps
todo
@@ -488,6 +520,7 @@ tt
txn
txnid
txns
+txt
typedef
uint
ul
@@ -507,11 +540,13 @@ utf
util
valign
valuefmt
+valuev
vec
versa
vm
vpmsum
warmup
+wget
whitespace
wiredtiger
workQ
diff --git a/src/third_party/wiredtiger/src/docs/style/wiredtiger.css b/src/third_party/wiredtiger/src/docs/style/wiredtiger.css
index b16b21f2029..984e525f42f 100644
--- a/src/third_party/wiredtiger/src/docs/style/wiredtiger.css
+++ b/src/third_party/wiredtiger/src/docs/style/wiredtiger.css
@@ -20,6 +20,12 @@ a {
color: #3D578C;
}
+img {
+ display: block;
+ max-width: 100%;
+ height: auto;
+}
+
.tablist {
width: 800px;
}
diff --git a/src/third_party/wiredtiger/src/include/btree.i b/src/third_party/wiredtiger/src/include/btree.i
index 18199997a19..5099bc4dce9 100644
--- a/src/third_party/wiredtiger/src/include/btree.i
+++ b/src/third_party/wiredtiger/src/include/btree.i
@@ -838,10 +838,6 @@ __wt_row_leaf_key_info(
*ikeyp = NULL;
if (cellp != NULL)
*cellp = WT_PAGE_REF_OFFSET(page, WT_CELL_DECODE_OFFSET(v));
- if (datap != NULL) {
- *(void **)datap = NULL;
- *sizep = 0;
- }
return (false);
case WT_K_FLAG:
/* Encoded key: no instantiated key, no cell. */
@@ -999,6 +995,9 @@ __wt_row_leaf_value_cell(WT_SESSION_IMPL *session, WT_PAGE *page, WT_ROW *rip,
size_t size;
void *copy, *key;
+ size = 0; /* -Werror=maybe-uninitialized */
+ key = NULL; /* -Werror=maybe-uninitialized */
+
/* If we already have an unpacked key cell, use it. */
if (kpack != NULL)
vcell = (WT_CELL *)((uint8_t *)kpack->cell + __wt_cell_total_len(kpack));
diff --git a/src/third_party/wiredtiger/src/include/connection.h b/src/third_party/wiredtiger/src/include/connection.h
index db4b2e9b41e..ab810ee86fb 100644
--- a/src/third_party/wiredtiger/src/include/connection.h
+++ b/src/third_party/wiredtiger/src/include/connection.h
@@ -298,6 +298,10 @@ struct __wt_connection_impl {
uint64_t ckpt_write_bytes;
uint64_t ckpt_write_pages;
+ /* Checkpoint and incremental backup data */
+ uint64_t ckpt_incr_granularity;
+ WT_BLKINCR incr_backups[WT_BLKINCR_MAX];
+
/* Connection's maximum and base write generations. */
uint64_t max_write_gen;
uint64_t base_write_gen;
@@ -441,40 +445,41 @@ struct __wt_connection_impl {
/* AUTOMATIC FLAG VALUE GENERATION START */
#define WT_VERB_API 0x000000001u
-#define WT_VERB_BLOCK 0x000000002u
-#define WT_VERB_CHECKPOINT 0x000000004u
-#define WT_VERB_CHECKPOINT_PROGRESS 0x000000008u
-#define WT_VERB_COMPACT 0x000000010u
-#define WT_VERB_COMPACT_PROGRESS 0x000000020u
-#define WT_VERB_ERROR_RETURNS 0x000000040u
-#define WT_VERB_EVICT 0x000000080u
-#define WT_VERB_EVICTSERVER 0x000000100u
-#define WT_VERB_EVICT_STUCK 0x000000200u
-#define WT_VERB_FILEOPS 0x000000400u
-#define WT_VERB_HANDLEOPS 0x000000800u
-#define WT_VERB_LOG 0x000001000u
-#define WT_VERB_LOOKASIDE 0x000002000u
-#define WT_VERB_LOOKASIDE_ACTIVITY 0x000004000u
-#define WT_VERB_LSM 0x000008000u
-#define WT_VERB_LSM_MANAGER 0x000010000u
-#define WT_VERB_METADATA 0x000020000u
-#define WT_VERB_MUTEX 0x000040000u
-#define WT_VERB_OVERFLOW 0x000080000u
-#define WT_VERB_READ 0x000100000u
-#define WT_VERB_REBALANCE 0x000200000u
-#define WT_VERB_RECONCILE 0x000400000u
-#define WT_VERB_RECOVERY 0x000800000u
-#define WT_VERB_RECOVERY_PROGRESS 0x001000000u
-#define WT_VERB_SALVAGE 0x002000000u
-#define WT_VERB_SHARED_CACHE 0x004000000u
-#define WT_VERB_SPLIT 0x008000000u
-#define WT_VERB_TEMPORARY 0x010000000u
-#define WT_VERB_THREAD_GROUP 0x020000000u
-#define WT_VERB_TIMESTAMP 0x040000000u
-#define WT_VERB_TRANSACTION 0x080000000u
-#define WT_VERB_VERIFY 0x100000000u
-#define WT_VERB_VERSION 0x200000000u
-#define WT_VERB_WRITE 0x400000000u
+#define WT_VERB_BACKUP 0x000000002u
+#define WT_VERB_BLOCK 0x000000004u
+#define WT_VERB_CHECKPOINT 0x000000008u
+#define WT_VERB_CHECKPOINT_PROGRESS 0x000000010u
+#define WT_VERB_COMPACT 0x000000020u
+#define WT_VERB_COMPACT_PROGRESS 0x000000040u
+#define WT_VERB_ERROR_RETURNS 0x000000080u
+#define WT_VERB_EVICT 0x000000100u
+#define WT_VERB_EVICTSERVER 0x000000200u
+#define WT_VERB_EVICT_STUCK 0x000000400u
+#define WT_VERB_FILEOPS 0x000000800u
+#define WT_VERB_HANDLEOPS 0x000001000u
+#define WT_VERB_LOG 0x000002000u
+#define WT_VERB_LOOKASIDE 0x000004000u
+#define WT_VERB_LOOKASIDE_ACTIVITY 0x000008000u
+#define WT_VERB_LSM 0x000010000u
+#define WT_VERB_LSM_MANAGER 0x000020000u
+#define WT_VERB_METADATA 0x000040000u
+#define WT_VERB_MUTEX 0x000080000u
+#define WT_VERB_OVERFLOW 0x000100000u
+#define WT_VERB_READ 0x000200000u
+#define WT_VERB_REBALANCE 0x000400000u
+#define WT_VERB_RECONCILE 0x000800000u
+#define WT_VERB_RECOVERY 0x001000000u
+#define WT_VERB_RECOVERY_PROGRESS 0x002000000u
+#define WT_VERB_SALVAGE 0x004000000u
+#define WT_VERB_SHARED_CACHE 0x008000000u
+#define WT_VERB_SPLIT 0x010000000u
+#define WT_VERB_TEMPORARY 0x020000000u
+#define WT_VERB_THREAD_GROUP 0x040000000u
+#define WT_VERB_TIMESTAMP 0x080000000u
+#define WT_VERB_TRANSACTION 0x100000000u
+#define WT_VERB_VERIFY 0x200000000u
+#define WT_VERB_VERSION 0x400000000u
+#define WT_VERB_WRITE 0x800000000u
/* AUTOMATIC FLAG VALUE GENERATION STOP */
uint64_t verbose;
@@ -506,34 +511,35 @@ struct __wt_connection_impl {
WT_FILE_SYSTEM *file_system;
/* AUTOMATIC FLAG VALUE GENERATION START */
-#define WT_CONN_CACHE_CURSORS 0x0000001u
-#define WT_CONN_CACHE_POOL 0x0000002u
-#define WT_CONN_CKPT_SYNC 0x0000004u
-#define WT_CONN_CLOSING 0x0000008u
-#define WT_CONN_CLOSING_NO_MORE_OPENS 0x0000010u
-#define WT_CONN_CLOSING_TIMESTAMP 0x0000020u
-#define WT_CONN_COMPATIBILITY 0x0000040u
-#define WT_CONN_DATA_CORRUPTION 0x0000080u
-#define WT_CONN_EVICTION_NO_LOOKASIDE 0x0000100u
-#define WT_CONN_EVICTION_RUN 0x0000200u
-#define WT_CONN_IN_MEMORY 0x0000400u
-#define WT_CONN_LEAK_MEMORY 0x0000800u
-#define WT_CONN_LOOKASIDE_OPEN 0x0001000u
-#define WT_CONN_LSM_MERGE 0x0002000u
-#define WT_CONN_OPTRACK 0x0004000u
-#define WT_CONN_PANIC 0x0008000u
-#define WT_CONN_READONLY 0x0010000u
-#define WT_CONN_RECONFIGURING 0x0020000u
-#define WT_CONN_RECOVERING 0x0040000u
-#define WT_CONN_SALVAGE 0x0080000u
-#define WT_CONN_SERVER_ASYNC 0x0100000u
-#define WT_CONN_SERVER_CAPACITY 0x0200000u
-#define WT_CONN_SERVER_CHECKPOINT 0x0400000u
-#define WT_CONN_SERVER_LOG 0x0800000u
-#define WT_CONN_SERVER_LSM 0x1000000u
-#define WT_CONN_SERVER_STATISTICS 0x2000000u
-#define WT_CONN_SERVER_SWEEP 0x4000000u
-#define WT_CONN_WAS_BACKUP 0x8000000u
+#define WT_CONN_CACHE_CURSORS 0x00000001u
+#define WT_CONN_CACHE_POOL 0x00000002u
+#define WT_CONN_CKPT_SYNC 0x00000004u
+#define WT_CONN_CLOSING 0x00000008u
+#define WT_CONN_CLOSING_NO_MORE_OPENS 0x00000010u
+#define WT_CONN_CLOSING_TIMESTAMP 0x00000020u
+#define WT_CONN_COMPATIBILITY 0x00000040u
+#define WT_CONN_DATA_CORRUPTION 0x00000080u
+#define WT_CONN_EVICTION_NO_LOOKASIDE 0x00000100u
+#define WT_CONN_EVICTION_RUN 0x00000200u
+#define WT_CONN_INCR_BACKUP 0x00000400u
+#define WT_CONN_IN_MEMORY 0x00000800u
+#define WT_CONN_LEAK_MEMORY 0x00001000u
+#define WT_CONN_LOOKASIDE_OPEN 0x00002000u
+#define WT_CONN_LSM_MERGE 0x00004000u
+#define WT_CONN_OPTRACK 0x00008000u
+#define WT_CONN_PANIC 0x00010000u
+#define WT_CONN_READONLY 0x00020000u
+#define WT_CONN_RECONFIGURING 0x00040000u
+#define WT_CONN_RECOVERING 0x00080000u
+#define WT_CONN_SALVAGE 0x00100000u
+#define WT_CONN_SERVER_ASYNC 0x00200000u
+#define WT_CONN_SERVER_CAPACITY 0x00400000u
+#define WT_CONN_SERVER_CHECKPOINT 0x00800000u
+#define WT_CONN_SERVER_LOG 0x01000000u
+#define WT_CONN_SERVER_LSM 0x02000000u
+#define WT_CONN_SERVER_STATISTICS 0x04000000u
+#define WT_CONN_SERVER_SWEEP 0x08000000u
+#define WT_CONN_WAS_BACKUP 0x10000000u
/* AUTOMATIC FLAG VALUE GENERATION STOP */
uint32_t flags;
};
diff --git a/src/third_party/wiredtiger/src/include/cursor.h b/src/third_party/wiredtiger/src/include/cursor.h
index b3d32ad8417..39740cc2be4 100644
--- a/src/third_party/wiredtiger/src/include/cursor.h
+++ b/src/third_party/wiredtiger/src/include/cursor.h
@@ -32,6 +32,21 @@
0 /* uint32_t flags */ \
}
+/*
+ * Block based incremental backup structure. These live in the connection.
+ */
+#define WT_BLKINCR_MAX 2
+struct __wt_blkincr {
+ const char *id_str; /* User's name for this backup. */
+ const char *ckpt_name; /* Requires WT-5115. All checkpoints must be this name */
+ void *data;
+/* AUTOMATIC FLAG VALUE GENERATION START */
+#define WT_BLKINCR_INUSE 0x1u /* This entry is active */
+#define WT_BLKINCR_VALID 0x2u /* This entry is valid */
+ /* AUTOMATIC FLAG VALUE GENERATION STOP */
+ uint64_t flags;
+};
+
struct __wt_cursor_backup {
WT_CURSOR iface;
@@ -43,12 +58,36 @@ struct __wt_cursor_backup {
size_t list_allocated;
size_t list_next;
+ /* File offset-based incremental backup. */
+ WT_BLKINCR *incr; /* Incremental backup in use */
+ char *incr_file; /* File name */
+ char *incr_src; /* Source identifier */
+ char *incr_this; /* New base identifier */
+ uint64_t incr_granularity; /* Maximum transfer size */
+
+ WT_CURSOR *incr_cursor; /* File cursor */
+ /* Start/stop checkpoints */
+ char *incr_checkpoint_start;
+ char *incr_checkpoint_stop;
+
+#define WT_BACKUP_INCR_COMPONENTS 3
+ bool incr_init; /* Cursor traversal initialized */
+ uint64_t *incr_list; /* List of file offset/size/type triples */
+ uint64_t incr_list_count; /* Count of file offset/size/type triples */
+ uint64_t incr_list_offset; /* Current offset */
+ uint64_t incr_size; /* Maximum transfer size */
+ WT_ITEM *incr_block; /* Current block of data */
+
/* AUTOMATIC FLAG VALUE GENERATION START */
-#define WT_CURBACKUP_DUP 0x1u /* Duplicated backup cursor */
-#define WT_CURBACKUP_LOCKER 0x2u /* Hot-backup started */
- /* AUTOMATIC FLAG VALUE GENERATION STOP */
+#define WT_CURBACKUP_DUP 0x1u /* Duplicated backup cursor */
+#define WT_CURBACKUP_FORCE_STOP 0x2u /* Force stop incremental backup */
+#define WT_CURBACKUP_INCR 0x4u /* Incremental backup cursor */
+#define WT_CURBACKUP_LOCKER 0x8u /* Hot-backup started */
+ /* AUTOMATIC FLAG VALUE GENERATION STOP */
uint8_t flags;
};
+#define WT_CURSOR_BACKUP_CHECK_STOP(cursor) \
+ WT_RET(F_ISSET(((WT_CURSOR_BACKUP *)(cursor)), WT_CURBACKUP_FORCE_STOP) ? EINVAL : 0);
#define WT_CURSOR_BACKUP_ID(cursor) (((WT_CURSOR_BACKUP *)(cursor))->maxid)
struct __wt_cursor_btree {
diff --git a/src/third_party/wiredtiger/src/include/extern.h b/src/third_party/wiredtiger/src/include/extern.h
index 3eb7583ecc6..5e530b8aaa1 100644
--- a/src/third_party/wiredtiger/src/include/extern.h
+++ b/src/third_party/wiredtiger/src/include/extern.h
@@ -408,8 +408,6 @@ extern int __wt_config_subgetraw(WT_SESSION_IMPL *session, WT_CONFIG_ITEM *cfg,
WT_CONFIG_ITEM *value) WT_GCC_FUNC_DECL_ATTRIBUTE((warn_unused_result));
extern int __wt_config_subgets(WT_SESSION_IMPL *session, WT_CONFIG_ITEM *cfg, const char *key,
WT_CONFIG_ITEM *value) WT_GCC_FUNC_DECL_ATTRIBUTE((warn_unused_result));
-extern int __wt_config_upgrade(WT_SESSION_IMPL *session, WT_ITEM *buf)
- WT_GCC_FUNC_DECL_ATTRIBUTE((warn_unused_result));
extern int __wt_configure_method(WT_SESSION_IMPL *session, const char *method, const char *uri,
const char *config, const char *type, const char *check)
WT_GCC_FUNC_DECL_ATTRIBUTE((warn_unused_result));
@@ -471,6 +469,9 @@ extern int __wt_copy_and_sync(WT_SESSION *wt_session, const char *from, const ch
extern int __wt_count_birthmarks(WT_UPDATE *upd) WT_GCC_FUNC_DECL_ATTRIBUTE((warn_unused_result));
extern int __wt_curbackup_open(WT_SESSION_IMPL *session, const char *uri, WT_CURSOR *other,
const char *cfg[], WT_CURSOR **cursorp) WT_GCC_FUNC_DECL_ATTRIBUTE((warn_unused_result));
+extern int __wt_curbackup_open_incr(WT_SESSION_IMPL *session, const char *uri, WT_CURSOR *other,
+ WT_CURSOR *cursor, const char *cfg[], WT_CURSOR **cursorp)
+ WT_GCC_FUNC_DECL_ATTRIBUTE((warn_unused_result));
extern int __wt_curbulk_init(WT_SESSION_IMPL *session, WT_CURSOR_BULK *cbulk, bool bitmap,
bool skip_sort_check) WT_GCC_FUNC_DECL_ATTRIBUTE((warn_unused_result));
extern int __wt_curconfig_open(WT_SESSION_IMPL *session, const char *uri, const char *cfg[],
@@ -1594,6 +1595,7 @@ extern void __wt_conn_config_discard(WT_SESSION_IMPL *session);
extern void __wt_conn_foc_discard(WT_SESSION_IMPL *session);
extern void __wt_conn_stat_init(WT_SESSION_IMPL *session);
extern void __wt_connection_destroy(WT_CONNECTION_IMPL *conn);
+extern void __wt_curbackup_free_incr(WT_SESSION_IMPL *session, WT_CURSOR_BACKUP *cb);
extern void __wt_cursor_close(WT_CURSOR *cursor);
extern void __wt_cursor_key_order_reset(WT_CURSOR_BTREE *cbt);
extern void __wt_cursor_reopen(WT_CURSOR *cursor, WT_DATA_HANDLE *dhandle);
diff --git a/src/third_party/wiredtiger/src/include/meta.h b/src/third_party/wiredtiger/src/include/meta.h
index 574c9400f8f..9845dbd7f7d 100644
--- a/src/third_party/wiredtiger/src/include/meta.h
+++ b/src/third_party/wiredtiger/src/include/meta.h
@@ -14,10 +14,14 @@
#define WT_USERCONFIG "WiredTiger.config" /* User configuration */
-#define WT_BACKUP_TMP "WiredTiger.backup.tmp" /* Backup tmp file */
-#define WT_METADATA_BACKUP "WiredTiger.backup" /* Hot backup file */
-#define WT_INCREMENTAL_BACKUP "WiredTiger.ibackup" /* Incremental backup */
-#define WT_INCREMENTAL_SRC "WiredTiger.isrc" /* Incremental source */
+/*
+ * Backup related WiredTiger files.
+ */
+#define WT_BACKUP_TMP "WiredTiger.backup.tmp" /* Backup tmp file */
+#define WT_BLKINCR_BACKUP "WiredTiger.backup.block" /* Block incremental durable file */
+#define WT_METADATA_BACKUP "WiredTiger.backup" /* Hot backup file */
+#define WT_LOGINCR_BACKUP "WiredTiger.ibackup" /* Log incremental backup */
+#define WT_LOGINCR_SRC "WiredTiger.isrc" /* Log incremental source */
#define WT_METADATA_TURTLE "WiredTiger.turtle" /* Metadata metadata */
#define WT_METADATA_TURTLE_SET "WiredTiger.turtle.set" /* Turtle temp file */
@@ -91,16 +95,20 @@ struct __wt_ckpt {
wt_timestamp_t newest_stop_ts;
uint64_t newest_stop_txn;
+ uint64_t *alloc_list; /* Checkpoint allocation list */
+ uint64_t alloc_list_entries;
+
WT_ITEM addr; /* Checkpoint cookie string */
WT_ITEM raw; /* Checkpoint cookie raw */
void *bpriv; /* Block manager private */
/* AUTOMATIC FLAG VALUE GENERATION START */
-#define WT_CKPT_ADD 0x1u /* Checkpoint to be added */
-#define WT_CKPT_DELETE 0x2u /* Checkpoint to be deleted */
-#define WT_CKPT_FAKE 0x4u /* Checkpoint is a fake */
-#define WT_CKPT_UPDATE 0x8u /* Checkpoint requires update */
- /* AUTOMATIC FLAG VALUE GENERATION STOP */
+#define WT_CKPT_ADD 0x01u /* Checkpoint to be added */
+#define WT_CKPT_BLOCK_MODS 0x02u /* Return list of modified blocks */
+#define WT_CKPT_DELETE 0x04u /* Checkpoint to be deleted */
+#define WT_CKPT_FAKE 0x08u /* Checkpoint is a fake */
+#define WT_CKPT_UPDATE 0x10u /* Checkpoint requires update */
+ /* AUTOMATIC FLAG VALUE GENERATION STOP */
uint32_t flags;
};
diff --git a/src/third_party/wiredtiger/src/include/wiredtiger.in b/src/third_party/wiredtiger/src/include/wiredtiger.in
index aec3014e788..e8a872178dd 100644
--- a/src/third_party/wiredtiger/src/include/wiredtiger.in
+++ b/src/third_party/wiredtiger/src/include/wiredtiger.in
@@ -1064,6 +1064,33 @@ struct __wt_session {
* non-printing characters are hexadecimal encoded. These formats are compatible with the
* @ref util_dump and @ref util_load commands., a string\, chosen from the following
* options: \c "hex"\, \c "json"\, \c "print"; default empty.}
+ * @config{incremental = (, configure the cursor for block incremental backup usage. These
+ * formats are only compatible with the backup data source; see @ref backup., a set of
+ * related configuration options defined below.}
+ * @config{&nbsp;&nbsp;&nbsp;&nbsp;enabled,
+ * whether to configure this backup as the starting point for a subsequent incremental
+ * backup., a boolean flag; default \c false.}
+ * @config{&nbsp;&nbsp;&nbsp;&nbsp;file, the
+ * file name when opening a duplicate incremental backup cursor. That duplicate cursor will
+ * return the block modifications relevant to the given file name., a string; default
+ * empty.}
+ * @config{&nbsp;&nbsp;&nbsp;&nbsp;force_stop, causes all block incremental backup
+ * information to be released. This is on an open_cursor call and the resources will be
+ * released when this cursor is closed. No other operations should be done on this open
+ * cursor., a boolean flag; default \c false.}
+ * @config{&nbsp;&nbsp;&nbsp;&nbsp;granularity,
+ * this setting manages the granularity of how WiredTiger maintains modification maps
+ * internally. The larger the granularity\, the smaller amount of information WiredTiger
+ * need to maintain., an integer between 1MB and 2GB; default \c 16MB.}
+ * @config{&nbsp;&nbsp;&nbsp;&nbsp;src_id, a string that identifies a previous checkpoint
+ * backup source as the source of this incremental backup. This identifier must have
+ * already been created by use of the 'this_id' configuration in an earlier backup. A
+ * source id is required to begin an incremental backup., a string; default empty.}
+ * @config{&nbsp;&nbsp;&nbsp;&nbsp;this_id, a string that identifies the current system
+ * state as a future backup source for an incremental backup via 'src_id'. This identifier
+ * is required when opening an incremental backup cursor and an error will be returned if
+ * one is not provided., a string; default empty.}
+ * @config{ ),,}
* @config{next_random, configure the cursor to return a pseudo-random record from the
* object when the WT_CURSOR::next method is called; valid only for row-store cursors. See
* @ref cursor_random for details., a boolean flag; default \c false.}
@@ -2335,14 +2362,14 @@ struct __wt_connection {
* ),,}
* @config{verbose, enable messages for various events. Options are given as a list\, such
* as <code>"verbose=[evictserver\,read]"</code>., a list\, with values chosen from the
- * following options: \c "api"\, \c "block"\, \c "checkpoint"\, \c "checkpoint_progress"\,
- * \c "compact"\, \c "compact_progress"\, \c "error_returns"\, \c "evict"\, \c
- * "evict_stuck"\, \c "evictserver"\, \c "fileops"\, \c "handleops"\, \c "log"\, \c
- * "lookaside"\, \c "lookaside_activity"\, \c "lsm"\, \c "lsm_manager"\, \c "metadata"\, \c
- * "mutex"\, \c "overflow"\, \c "read"\, \c "rebalance"\, \c "reconcile"\, \c "recovery"\,
- * \c "recovery_progress"\, \c "salvage"\, \c "shared_cache"\, \c "split"\, \c "temporary"\,
- * \c "thread_group"\, \c "timestamp"\, \c "transaction"\, \c "verify"\, \c "version"\, \c
- * "write"; default empty.}
+ * following options: \c "api"\, \c "backup"\, \c "block"\, \c "checkpoint"\, \c
+ * "checkpoint_progress"\, \c "compact"\, \c "compact_progress"\, \c "error_returns"\, \c
+ * "evict"\, \c "evict_stuck"\, \c "evictserver"\, \c "fileops"\, \c "handleops"\, \c
+ * "log"\, \c "lookaside"\, \c "lookaside_activity"\, \c "lsm"\, \c "lsm_manager"\, \c
+ * "metadata"\, \c "mutex"\, \c "overflow"\, \c "read"\, \c "rebalance"\, \c "reconcile"\,
+ * \c "recovery"\, \c "recovery_progress"\, \c "salvage"\, \c "shared_cache"\, \c "split"\,
+ * \c "temporary"\, \c "thread_group"\, \c "timestamp"\, \c "transaction"\, \c "verify"\, \c
+ * "version"\, \c "write"; default empty.}
* @configend
* @errors
*/
@@ -3017,13 +3044,13 @@ struct __wt_connection {
* information., a boolean flag; default \c false.}
* @config{verbose, enable messages for various events. Options are given as a list\, such as
* <code>"verbose=[evictserver\,read]"</code>., a list\, with values chosen from the following
- * options: \c "api"\, \c "block"\, \c "checkpoint"\, \c "checkpoint_progress"\, \c "compact"\, \c
- * "compact_progress"\, \c "error_returns"\, \c "evict"\, \c "evict_stuck"\, \c "evictserver"\, \c
- * "fileops"\, \c "handleops"\, \c "log"\, \c "lookaside"\, \c "lookaside_activity"\, \c "lsm"\, \c
- * "lsm_manager"\, \c "metadata"\, \c "mutex"\, \c "overflow"\, \c "read"\, \c "rebalance"\, \c
- * "reconcile"\, \c "recovery"\, \c "recovery_progress"\, \c "salvage"\, \c "shared_cache"\, \c
- * "split"\, \c "temporary"\, \c "thread_group"\, \c "timestamp"\, \c "transaction"\, \c "verify"\,
- * \c "version"\, \c "write"; default empty.}
+ * options: \c "api"\, \c "backup"\, \c "block"\, \c "checkpoint"\, \c "checkpoint_progress"\, \c
+ * "compact"\, \c "compact_progress"\, \c "error_returns"\, \c "evict"\, \c "evict_stuck"\, \c
+ * "evictserver"\, \c "fileops"\, \c "handleops"\, \c "log"\, \c "lookaside"\, \c
+ * "lookaside_activity"\, \c "lsm"\, \c "lsm_manager"\, \c "metadata"\, \c "mutex"\, \c "overflow"\,
+ * \c "read"\, \c "rebalance"\, \c "reconcile"\, \c "recovery"\, \c "recovery_progress"\, \c
+ * "salvage"\, \c "shared_cache"\, \c "split"\, \c "temporary"\, \c "thread_group"\, \c
+ * "timestamp"\, \c "transaction"\, \c "verify"\, \c "version"\, \c "write"; default empty.}
* @config{write_through, Use \c FILE_FLAG_WRITE_THROUGH on Windows to write to files. Ignored on
* non-Windows systems. Options are given as a list\, such as <code>"write_through=[data]"</code>.
* Configuring \c write_through requires care\, see @ref tuning_system_buffer_cache_direct_io for
@@ -4787,6 +4814,18 @@ extern int wiredtiger_extension_terminate(WT_CONNECTION *connection);
* @addtogroup wt
* @{
*/
+/*!
+ * @name Incremental backup types
+ * @anchor backup_types
+ * @{
+ */
+/*! invalid type */
+#define WT_BACKUP_INVALID 0
+/*! whole file */
+#define WT_BACKUP_FILE 1
+/*! file range */
+#define WT_BACKUP_RANGE 2
+/*! @} */
/*!
* @name Log record and operation types
diff --git a/src/third_party/wiredtiger/src/include/wt_internal.h b/src/third_party/wiredtiger/src/include/wt_internal.h
index 2b281443f21..1cd8753a0ac 100644
--- a/src/third_party/wiredtiger/src/include/wt_internal.h
+++ b/src/third_party/wiredtiger/src/include/wt_internal.h
@@ -77,6 +77,8 @@ struct __wt_async_op_impl;
typedef struct __wt_async_op_impl WT_ASYNC_OP_IMPL;
struct __wt_async_worker_state;
typedef struct __wt_async_worker_state WT_ASYNC_WORKER_STATE;
+struct __wt_blkincr;
+typedef struct __wt_blkincr WT_BLKINCR;
struct __wt_block;
typedef struct __wt_block WT_BLOCK;
struct __wt_block_ckpt;
diff --git a/src/third_party/wiredtiger/src/meta/meta_turtle.c b/src/third_party/wiredtiger/src/meta/meta_turtle.c
index db790537c79..044094133ce 100644
--- a/src/third_party/wiredtiger/src/meta/meta_turtle.c
+++ b/src/third_party/wiredtiger/src/meta/meta_turtle.c
@@ -185,7 +185,7 @@ __wt_turtle_init(WT_SESSION_IMPL *session)
{
WT_DECL_RET;
char *metaconf, *unused_value;
- bool exist_backup, exist_incr, exist_isrc, exist_turtle;
+ bool exist_backup, exist_bincr, exist_incr, exist_isrc, exist_turtle;
bool load, loadTurtle;
load = loadTurtle = false;
@@ -208,10 +208,22 @@ __wt_turtle_init(WT_SESSION_IMPL *session)
* turtle file and an incremental backup file, that is an error. Otherwise, if there's already a
* turtle file, we're done.
*/
- WT_RET(__wt_fs_exist(session, WT_INCREMENTAL_BACKUP, &exist_incr));
- WT_RET(__wt_fs_exist(session, WT_INCREMENTAL_SRC, &exist_isrc));
+ WT_RET(__wt_fs_exist(session, WT_LOGINCR_BACKUP, &exist_incr));
+ WT_RET(__wt_fs_exist(session, WT_LOGINCR_SRC, &exist_isrc));
WT_RET(__wt_fs_exist(session, WT_METADATA_BACKUP, &exist_backup));
WT_RET(__wt_fs_exist(session, WT_METADATA_TURTLE, &exist_turtle));
+ /*
+ * Block incremental is different. If it exists, then we have block incremental information we
+ * need to keep. Mark the connection as having block-based incremental backup turned on. XXX -
+ * Need to call something to read it in and set this up. Maybe here, maybe not.
+ */
+ WT_RET(__wt_fs_exist(session, WT_BLKINCR_BACKUP, &exist_bincr));
+ if (exist_bincr) {
+ F_SET(S2C(session), WT_CONN_INCR_BACKUP);
+ /* Load content into some structure. Not sure this is the right place. It may be too early.
+ */
+ }
+
if (exist_turtle) {
/*
* Failure to read means a bad turtle file. Remove it and create a new turtle file.
diff --git a/src/third_party/wiredtiger/src/reconcile/rec_visibility.c b/src/third_party/wiredtiger/src/reconcile/rec_visibility.c
index 2150bf63559..451d4f2faae 100644
--- a/src/third_party/wiredtiger/src/reconcile/rec_visibility.c
+++ b/src/third_party/wiredtiger/src/reconcile/rec_visibility.c
@@ -189,6 +189,13 @@ __wt_rec_upd_select(WT_SESSION_IMPL *session, WT_RECONCILE *r, WT_INSERT *ins, v
list_prepared = true;
if (upd->start_ts > max_ts)
max_ts = upd->start_ts;
+
+ /*
+ * Track the oldest update not on the page, used to decide whether reads can use the
+ * page image, hence using the start rather than the durable timestamp.
+ */
+ if (upd->start_ts < r->min_skipped_ts)
+ r->min_skipped_ts = upd->start_ts;
continue;
}
}
@@ -231,10 +238,8 @@ __wt_rec_upd_select(WT_SESSION_IMPL *session, WT_RECONCILE *r, WT_INSERT *ins, v
skipped_birthmark = true;
/*
- * Track the oldest update not on the page.
- *
- * This is used to decide whether reads can use the page image, hence using the start
- * rather than the durable timestamp.
+ * Track the oldest update not on the page, used to decide whether reads can use the
+ * page image, hence using the start rather than the durable timestamp.
*/
if (upd_select->upd == NULL && upd->start_ts < r->min_skipped_ts)
r->min_skipped_ts = upd->start_ts;
diff --git a/src/third_party/wiredtiger/src/reconcile/rec_write.c b/src/third_party/wiredtiger/src/reconcile/rec_write.c
index 3b3d13770d2..e52b86da055 100644
--- a/src/third_party/wiredtiger/src/reconcile/rec_write.c
+++ b/src/third_party/wiredtiger/src/reconcile/rec_write.c
@@ -1568,6 +1568,7 @@ __rec_split_write_supd(
key->data = WT_INSERT_KEY(supd->ins);
key->size = WT_INSERT_KEY_SIZE(supd->ins);
}
+ WT_ASSERT(session, next != NULL);
WT_ERR(__wt_compare(session, btree->collator, key, &next->key, &cmp));
if (cmp >= 0)
break;
diff --git a/src/third_party/wiredtiger/test/csuite/random_directio/util.c b/src/third_party/wiredtiger/test/csuite/random_directio/util.c
index 40de5d49f36..a9ad564c160 100644
--- a/src/third_party/wiredtiger/test/csuite/random_directio/util.c
+++ b/src/third_party/wiredtiger/test/csuite/random_directio/util.c
@@ -95,6 +95,14 @@ copy_directory(const char *fromdir, const char *todir, bool directio)
testutil_check(__wt_snprintf(fromfile, sizeof(fromfile), "%s/%s", fromdir, dp->d_name));
testutil_check(__wt_snprintf(tofile, sizeof(tofile), "%s/%s", todir, dp->d_name));
rfd = open(fromfile, O_RDONLY | openflags, 0);
+ /*
+ * The child process may have been stopped during a drop and WiredTiger drop will do an
+ * unlink call followed by syncing the directory. It is possible for the signal to have been
+ * delivered in between those calls so the file may no longer exist but reading the
+ * directory will still return its entry. Handle that case and skip the file if it happens.
+ */
+ if (rfd < 0 && errno == ENOENT)
+ continue;
testutil_assertfmt(rfd >= 0, "Open of source %s failed with %d\n", fromfile, errno);
wfd = open(tofile, O_WRONLY | O_CREAT, 0666);
testutil_assertfmt(wfd >= 0, "Open of dest %s failed with %d\n", tofile, errno);
diff --git a/src/third_party/wiredtiger/test/evergreen.yml b/src/third_party/wiredtiger/test/evergreen.yml
index e28772c915b..971d6b7bcea 100755
--- a/src/third_party/wiredtiger/test/evergreen.yml
+++ b/src/third_party/wiredtiger/test/evergreen.yml
@@ -283,7 +283,7 @@ tasks:
posix_configure_flags: --enable-silent-rules --enable-strict --enable-diagnostic --disable-static
- func: "make check all"
vars:
- test_env_vars: MSAN_OPTIONS=abort_on_error=1:disable_coredump=0 MSAN_SYMBOLIZER_PATH=/opt/mongodbtoolchain/v3/bin/llvm-symbolizer
+ test_env_vars: MSAN_OPTIONS=abort_on_error=1:disable_coredump=0 MSAN_SYMBOLIZER_PATH=/opt/mongodbtoolchain/v3/bin/llvm-symbolizer TESTUTIL_SLOW_MACHINE=1
- name: make-check-asan-test
depends_on:
@@ -1784,6 +1784,20 @@ tasks:
./time_shift_test.sh /usr/local/lib/faketime/libfaketimeMT.so.1 0-1 2>&1
+ - name: split-stress-test
+ commands:
+ - func: "get project"
+ - func: "compile wiredtiger"
+ vars:
+ configure_env_vars: CXX=/opt/mongodbtoolchain/v3/bin/g++ PATH=/opt/mongodbtoolchain/v3/bin:$PATH CFLAGS="-ggdb -fPIC"
+ - command: shell.exec
+ params:
+ working_dir: "wiredtiger/bench/workgen/runner"
+ script: |
+ set -o errexit
+ set -o verbose
+ for i in {1..10}; do ${python_binary|python} split_stress.py; done
+
buildvariants:
- name: ubuntu1804
display_name: Ubuntu 18.04
@@ -1813,6 +1827,7 @@ buildvariants:
- name: wtperf-test
- name: ftruncate-test
- name: long-test
+ - name: split-stress-test
- name: ubuntu1804-python3
display_name: Ubuntu 18.04 (Python3)
@@ -1965,3 +1980,4 @@ buildvariants:
tasks:
- name: compile
- name: unit-test
+ - name: split-stress-test
diff --git a/src/third_party/wiredtiger/test/format/format.sh b/src/third_party/wiredtiger/test/format/format.sh
index 9bdc55a4d69..3a140bcfc05 100755
--- a/src/third_party/wiredtiger/test/format/format.sh
+++ b/src/third_party/wiredtiger/test/format/format.sh
@@ -18,9 +18,10 @@ trap 'onintr' 2
usage() {
echo "usage: $0 [-aFSv] [-c config] "
- echo " [-h home] [-j parallel-jobs] [-n total-jobs] [-t minutes] [format-configuration]"
+ echo " [-b format-binary] [-h home] [-j parallel-jobs] [-n total-jobs] [-t minutes] [format-configuration]"
echo
echo " -a abort/recovery testing (defaults to off)"
+ echo " -b binary format binary (defaults to "./t")"
echo " -c config format configuration file (defaults to CONFIG.stress)"
echo " -F quit on first failure (defaults to off)"
echo " -h home run directory (defaults to .)"
@@ -74,12 +75,16 @@ parallel_jobs=8
smoke_test=0
total_jobs=0
verbose=0
+format_binary="./t"
while :; do
case "$1" in
-a)
abort_test=1
shift ;;
+ -b)
+ format_binary="$2"
+ shift ; shift ;;
-c)
config="$2"
shift ; shift ;;
@@ -155,16 +160,17 @@ cd $(dirname $0) || exit 1
# local.
[[ $config_found -eq 0 ]] && [[ -f "$config" ]] && config="$PWD/$config"
-# Find the format binary. Builds are normally in the WiredTiger source tree, in which case it's
-# in the same directory as format.sh, else it's in the build_posix tree. If the build is in the
-# build_posix tree, move there, we have to run in the directory where the format binary lives
-# because the format binary "knows" the wt utility is two directory levels above it.
-format_binary="./t"
-[[ -x $format_binary ]] || {
+# Find the last part of format_binary, which is format binary file. Builds are normally in the
+# WiredTiger source tree, in which case it's in the same directory as format.sh, else it's in
+# the build_posix tree. If the build is in the build_posix tree, move there, we have to run in
+# the directory where the format binary lives because the format binary "knows" the wt utility
+# is two directory levels above it.
+
+[[ -x ${format_binary##* } ]] || {
build_posix_directory="../../build_posix/test/format"
[[ ! -d $build_posix_directory ]] || cd $build_posix_directory || exit 1
- [[ -x $format_binary ]] || {
- echo "$name: format program \"$format_binary\" not found"
+ [[ -x ${format_binary##* } ]] || {
+ echo "$name: format program \"${format_binary##* }\" not found"
exit 1
}
}
diff --git a/src/third_party/wiredtiger/test/format/snap.c b/src/third_party/wiredtiger/test/format/snap.c
index 15df14b71dc..b304faa7376 100644
--- a/src/third_party/wiredtiger/test/format/snap.c
+++ b/src/third_party/wiredtiger/test/format/snap.c
@@ -397,6 +397,12 @@ snap_repeat_txn(WT_CURSOR *cursor, TINFO *tinfo)
if (current->opid != tinfo->opid)
break;
+ /*
+ * The transaction is not yet resolved, so the rules are as if the transaction has
+ * committed. Note we are NOT checking if reads are repeatable based on the chosen
+ * timestamp. This is because we expect snapshot isolation to work even in the presence of
+ * other threads of control committing in our past, until the transaction resolves.
+ */
if (snap_repeat_ok_commit(tinfo, current))
WT_RET(snap_verify(cursor, tinfo, current));
}
diff --git a/src/third_party/wiredtiger/test/suite/test_backup06.py b/src/third_party/wiredtiger/test/suite/test_backup06.py
index d4efba4c6f0..d9db39d241d 100644
--- a/src/third_party/wiredtiger/test/suite/test_backup06.py
+++ b/src/third_party/wiredtiger/test/suite/test_backup06.py
@@ -34,6 +34,12 @@ from suite_subprocess import suite_subprocess
import wiredtiger, wttest
from wiredtiger import stat
from wtdataset import SimpleDataSet, ComplexDataSet, ComplexLSMDataSet
+try:
+ # Windows does not getrlimit/setrlimit so we must catch the resource
+ # module load.
+ import resource
+except:
+ None
# test_backup06.py
# Test that opening a backup cursor does not open file handles.
@@ -80,11 +86,21 @@ class test_backup06(wttest.WiredTigerTestCase, suite_subprocess):
# Test that the open handle count does not change.
def test_cursor_open_handles(self):
+ if os.name == "nt":
+ self.skipTest('Unix specific test skipped on Windows')
+
+ limits = resource.getrlimit(resource.RLIMIT_NOFILE)
+ if limits[0] < 1024:
+ new = (1024, limits[1])
+ resource.setrlimit(resource.RLIMIT_NOFILE, new)
self.populate_many()
# Close and reopen the connection so the populate dhandles are
# not in the list.
self.reopen_conn()
+ new = (limits[0], limits[1])
+ resource.setrlimit(resource.RLIMIT_NOFILE, new)
+
# Confirm that opening a backup cursor does not open
# file handles.
stat_cursor = self.session.open_cursor('statistics:', None, None)
diff --git a/src/third_party/wiredtiger/test/suite/test_backup10.py b/src/third_party/wiredtiger/test/suite/test_backup10.py
index 3682db7cf01..b8806c439dc 100644
--- a/src/third_party/wiredtiger/test/suite/test_backup10.py
+++ b/src/third_party/wiredtiger/test/suite/test_backup10.py
@@ -38,7 +38,6 @@ from wtscenario import make_scenarios
class test_backup10(wttest.WiredTigerTestCase, suite_subprocess):
dir='backup.dir' # Backup directory name
logmax="100K"
- newuri="table:newtable"
uri="table:test"
nops=100
@@ -95,7 +94,6 @@ class test_backup10(wttest.WiredTigerTestCase, suite_subprocess):
if ret != 0:
break
newfile = bkup_c.get_key()
- self.assertNotEqual(newfile, self.newuri)
sz = os.path.getsize(newfile)
self.pr('Copy from: ' + newfile + ' (' + str(sz) + ') to ' + self.dir)
shutil.copy(newfile, self.dir)
@@ -130,6 +128,7 @@ class test_backup10(wttest.WiredTigerTestCase, suite_subprocess):
diff = dup_set.difference(orig_set)
self.assertEqual(len(diff), 1)
self.assertTrue(log3 in dup_set)
+ self.assertFalse(log3 in orig_set)
# Test a few error cases now.
# - We cannot make multiple duplcate backup cursors.
@@ -148,7 +147,7 @@ class test_backup10(wttest.WiredTigerTestCase, suite_subprocess):
dupc.close()
# Test we must use the log target.
- msg = "/must be for logs/"
+ msg = "/must be for /"
self.assertRaisesWithMessage(wiredtiger.WiredTigerError,
lambda:self.assertEquals(self.session.open_cursor(None,
bkup_c, None), 0), msg)
diff --git a/src/third_party/wiredtiger/test/suite/test_backup11.py b/src/third_party/wiredtiger/test/suite/test_backup11.py
new file mode 100644
index 00000000000..f1622e9d2bc
--- /dev/null
+++ b/src/third_party/wiredtiger/test/suite/test_backup11.py
@@ -0,0 +1,246 @@
+#!/usr/bin/env python
+#
+# Public Domain 2014-2019 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.
+
+import wiredtiger, wttest
+import os, shutil
+from helper import compare_files
+from suite_subprocess import suite_subprocess
+from wtdataset import simple_key
+from wtscenario import make_scenarios
+
+# test_backup11.py
+# Test cursor backup with a duplicate backup cursor.
+class test_backup11(wttest.WiredTigerTestCase, suite_subprocess):
+ dir='backup.dir' # Backup directory name
+ logmax="100K"
+ uri="table:test"
+ nops=100
+
+ pfx = 'test_backup'
+
+ # ('archiving', dict(archive='true')),
+ # ('not-archiving', dict(archive='false')),
+ scenarios = make_scenarios([
+ ('archiving', dict(archive='true')),
+ ])
+
+ # Create a large cache, otherwise this test runs quite slowly.
+ def conn_config(self):
+ return 'cache_size=1G,log=(archive=%s,' % self.archive + \
+ 'enabled,file_max=%s)' % self.logmax
+
+ def add_data(self):
+ log2 = "WiredTigerLog.0000000002"
+ log3 = "WiredTigerLog.0000000003"
+
+ self.session.create(self.uri, "key_format=S,value_format=S")
+
+ # Insert small amounts of data at a time stopping after we
+ # cross into log file 2.
+ loop = 0
+ c = self.session.open_cursor(self.uri)
+ while not os.path.exists(log2):
+ for i in range(0, self.nops):
+ num = i + (loop * self.nops)
+ key = 'key' + str(num)
+ val = 'value' + str(num)
+ c[key] = val
+ loop += 1
+ self.session.checkpoint()
+ c.close()
+ return loop
+
+ def test_backup11(self):
+
+ loop = self.add_data()
+
+ # Open up the backup cursor. This causes a new log file to be created.
+ # That log file is not part of the list returned. This is a full backup
+ # primary cursor with incremental configured.
+ os.mkdir(self.dir)
+ config = 'incremental=(enabled,this_id="ID1")'
+ bkup_c = self.session.open_cursor('backup:', None, config)
+
+ # Add some data that will appear in log file 3.
+ c = self.session.open_cursor(self.uri)
+ for i in range(0, self.nops):
+ num = i + (loop * self.nops)
+ key = 'key' + str(num)
+ val = 'value' + str(num)
+ c[key] = val
+ loop += 1
+ c.close()
+ self.session.log_flush('sync=on')
+ self.session.checkpoint()
+
+ # Now copy the files returned by the backup cursor.
+ orig_logs = []
+ while True:
+ ret = bkup_c.next()
+ if ret != 0:
+ break
+ newfile = bkup_c.get_key()
+ sz = os.path.getsize(newfile)
+ self.pr('Copy from: ' + newfile + ' (' + str(sz) + ') to ' + self.dir)
+ shutil.copy(newfile, self.dir)
+ if "WiredTigerLog" in newfile:
+ orig_logs.append(newfile)
+ self.assertEqual(ret, wiredtiger.WT_NOTFOUND)
+
+ # Now open a duplicate backup cursor.
+ # We *can* use a log target duplicate on an incremental primary backup so that
+ # a backup process can get all the log files that occur while that primary cursor
+ # is open.
+ config = 'target=("log:")'
+ dupc = self.session.open_cursor(None, bkup_c, config)
+ dup_logs = []
+ while True:
+ ret = dupc.next()
+ if ret != 0:
+ break
+ newfile = dupc.get_key()
+ self.assertTrue("WiredTigerLog" in newfile)
+ sz = os.path.getsize(newfile)
+ if (newfile not in orig_logs):
+ self.pr('DUP: Copy from: ' + newfile + ' (' + str(sz) + ') to ' + self.dir)
+ shutil.copy(newfile, self.dir)
+ # Record all log files returned for later verification.
+ dup_logs.append(newfile)
+ self.assertEqual(ret, wiredtiger.WT_NOTFOUND)
+ dupc.close()
+ bkup_c.close()
+
+ # Add more data
+ c = self.session.open_cursor(self.uri)
+ for i in range(0, self.nops):
+ num = i + (loop * self.nops)
+ key = 'key' + str(num)
+ val = 'value' + str(num)
+ c[key] = val
+ loop += 1
+ c.close()
+ self.session.log_flush('sync=on')
+ self.session.checkpoint()
+
+ # Test a few error cases now.
+ # - Incremental filename must be on duplicate, not primary.
+ # - An incremental duplicate must have an incremental primary.
+ # - We cannot make multiple incremental duplcate backup cursors.
+ # - We cannot duplicate the duplicate backup cursor.
+ # - We cannot mix block incremental with a log target on the same duplicate.
+ # - Incremental ids must be on primary, not duplicate.
+ # - Force stop must be on primary, not duplicate.
+
+ # - Incremental filename must be on duplicate, not primary.
+ # Test this first because we currently do not have a primary open.
+ config = 'incremental=(file=test.wt)'
+ msg = "/file name can only be specified on a duplicate/"
+ self.assertRaisesWithMessage(wiredtiger.WiredTigerError,
+ lambda:self.assertEquals(self.session.open_cursor('backup:',
+ None, config), 0), msg)
+
+ # Open a non-incremental full backup cursor.
+ # - An incremental duplicate must have an incremental primary.
+ bkup_c = self.session.open_cursor('backup:', None, None)
+ msg = "/must have an incremental primary/"
+ self.assertRaisesWithMessage(wiredtiger.WiredTigerError,
+ lambda:self.assertEquals(self.session.open_cursor(None,
+ bkup_c, config), 0), msg)
+ bkup_c.close()
+
+ config = 'incremental=(src_id="ID1",this_id="ID2")'
+ bkup_c = self.session.open_cursor('backup:', None, config)
+
+ self.pr("Opened backup for error testing")
+
+ # Now test all the error cases with an incremental primary open.
+ # - We cannot make multiple incremental duplcate backup cursors.
+ # - We cannot duplicate the duplicate backup cursor.
+ config = 'incremental=(file=test.wt)'
+ dupc = self.session.open_cursor(None, bkup_c, config)
+ msg = "/already a duplicate backup cursor open/"
+ self.pr("Test multiple dups")
+ self.pr("=========")
+ # Test multiple duplicate backup cursors.
+ self.assertRaisesWithMessage(wiredtiger.WiredTigerError,
+ lambda:self.assertEquals(self.session.open_cursor(None,
+ bkup_c, config), 0), msg)
+ # Test duplicate of duplicate backup cursor.
+ self.assertRaisesWithMessage(wiredtiger.WiredTigerError,
+ lambda:self.assertEquals(self.session.open_cursor(None,
+ dupc, config), 0), msg)
+ dupc.close()
+
+ # - A duplicate cursor must specify incremental or log target.
+ self.pr("Test dup and file target")
+ self.pr("=========")
+ msg = "/cannot be used for/"
+ config = 'target=("file:test.wt")'
+ self.assertRaisesWithMessage(wiredtiger.WiredTigerError,
+ lambda:self.assertEquals(self.session.open_cursor(None,
+ bkup_c, config), 0), msg)
+
+ # - We cannot mix block incremental with a log target on the same duplicate.
+ self.pr("Test mixed targets")
+ self.pr("=========")
+ config = 'incremental=(file=test.wt),target=("log:")'
+ msg = "/incremental backup incompatible/"
+ self.assertRaisesWithMessage(wiredtiger.WiredTigerError,
+ lambda:self.assertEquals(self.session.open_cursor(None,
+ bkup_c, config), 0), msg)
+
+ # - Incremental ids must be on primary, not duplicate.
+ self.pr("Test ids on dups")
+ self.pr("=========")
+ config = 'incremental=(src_id="ID1")'
+ msg = "/specified on a primary/"
+ self.assertRaisesWithMessage(wiredtiger.WiredTigerError,
+ lambda:self.assertEquals(self.session.open_cursor(None,
+ bkup_c, config), 0), msg)
+ config = 'incremental=(this_id="ID1")'
+ self.assertRaisesWithMessage(wiredtiger.WiredTigerError,
+ lambda:self.assertEquals(self.session.open_cursor(None,
+ bkup_c, config), 0), msg)
+
+ # - Force stop must be on primary, not duplicate.
+ #self.pr("Test force stop")
+ #self.pr("=========")
+ #config = 'incremental=(force_stop=true)'
+ #print "config is " + config
+ #self.assertRaisesWithMessage(wiredtiger.WiredTigerError,
+ # lambda:self.assertEquals(self.session.open_cursor(None,
+ # bkup_c, config), 0), msg)
+
+ bkup_c.close()
+
+ # After the full backup, open and recover the backup database.
+ #backup_conn = self.wiredtiger_open(self.dir)
+ #backup_conn.close()
+
+if __name__ == '__main__':
+ wttest.run()
diff --git a/src/third_party/wiredtiger/test/suite/test_backup12.py b/src/third_party/wiredtiger/test/suite/test_backup12.py
new file mode 100644
index 00000000000..6726164d038
--- /dev/null
+++ b/src/third_party/wiredtiger/test/suite/test_backup12.py
@@ -0,0 +1,187 @@
+#!/usr/bin/env python
+#
+# Public Domain 2014-2019 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.
+
+import wiredtiger, wttest
+import os, shutil
+from helper import compare_files
+from suite_subprocess import suite_subprocess
+from wtdataset import simple_key
+from wtscenario import make_scenarios
+
+# test_backup12.py
+# Test cursor backup with a block-based incremental cursor.
+class test_backup12(wttest.WiredTigerTestCase, suite_subprocess):
+ dir='backup.dir' # Backup directory name
+ logmax="100K"
+ uri="table:test"
+ nops=100
+
+ pfx = 'test_backup'
+
+ # ('archiving', dict(archive='true')),
+ # ('not-archiving', dict(archive='false')),
+ scenarios = make_scenarios([
+ ('archiving', dict(archive='true')),
+ ])
+
+ # Create a large cache, otherwise this test runs quite slowly.
+ def conn_config(self):
+ return 'cache_size=1G,log=(archive=%s,' % self.archive + \
+ 'enabled,file_max=%s)' % self.logmax
+
+ def add_data(self):
+ log2 = "WiredTigerLog.0000000002"
+ log3 = "WiredTigerLog.0000000003"
+
+ self.session.create(self.uri, "key_format=S,value_format=S")
+
+ # Insert small amounts of data at a time stopping after we
+ # cross into log file 2.
+ loop = 0
+ c = self.session.open_cursor(self.uri)
+ while not os.path.exists(log2):
+ for i in range(0, self.nops):
+ num = i + (loop * self.nops)
+ key = 'key' + str(num)
+ val = 'value' + str(num)
+ c[key] = val
+ loop += 1
+ self.session.checkpoint()
+ c.close()
+ return loop
+
+ def test_backup12(self):
+
+ loop = self.add_data()
+
+ # Open up the backup cursor. This causes a new log file to be created.
+ # That log file is not part of the list returned. This is a full backup
+ # primary cursor with incremental configured.
+ os.mkdir(self.dir)
+ config = 'incremental=(enabled,this_id="ID1")'
+ bkup_c = self.session.open_cursor('backup:', None, config)
+
+ # Add some data that will appear in log file 3.
+ c = self.session.open_cursor(self.uri)
+ for i in range(0, self.nops):
+ num = i + (loop * self.nops)
+ key = 'key' + str(num)
+ val = 'value' + str(num)
+ c[key] = val
+ loop += 1
+ c.close()
+ self.session.log_flush('sync=on')
+ self.session.checkpoint()
+
+ # Now copy the files returned by the backup cursor.
+ orig_logs = []
+ while True:
+ ret = bkup_c.next()
+ if ret != 0:
+ break
+ newfile = bkup_c.get_key()
+ sz = os.path.getsize(newfile)
+ self.pr('Copy from: ' + newfile + ' (' + str(sz) + ') to ' + self.dir)
+ shutil.copy(newfile, self.dir)
+ if "WiredTigerLog" in newfile:
+ orig_logs.append(newfile)
+ self.assertEqual(ret, wiredtiger.WT_NOTFOUND)
+
+ # Now open a duplicate backup cursor.
+ # We *can* use a log target duplicate on an incremental primary backup so that
+ # a backup process can get all the log files that occur while that primary cursor
+ # is open.
+ config = 'target=("log:")'
+ dupc = self.session.open_cursor(None, bkup_c, config)
+ dup_logs = []
+ while True:
+ ret = dupc.next()
+ if ret != 0:
+ break
+ newfile = dupc.get_key()
+ self.assertTrue("WiredTigerLog" in newfile)
+ sz = os.path.getsize(newfile)
+ if (newfile not in orig_logs):
+ self.pr('DUP: Copy from: ' + newfile + ' (' + str(sz) + ') to ' + self.dir)
+ shutil.copy(newfile, self.dir)
+ # Record all log files returned for later verification.
+ dup_logs.append(newfile)
+ self.assertEqual(ret, wiredtiger.WT_NOTFOUND)
+ dupc.close()
+ bkup_c.close()
+
+ # Add more data.
+ c = self.session.open_cursor(self.uri)
+ for i in range(0, self.nops):
+ num = i + (loop * self.nops)
+ key = 'key' + str(num)
+ val = 'value' + str(num)
+ c[key] = val
+ loop += 1
+ c.close()
+ self.session.log_flush('sync=on')
+ self.session.checkpoint()
+
+ # Now do an incremental backup.
+ config = 'incremental=(src_id="ID1",this_id="ID2")'
+ bkup_c = self.session.open_cursor('backup:', None, config)
+ self.pr('Open backup cursor ID1')
+ while True:
+ ret = bkup_c.next()
+ if ret != 0:
+ break
+ newfile = bkup_c.get_key()
+ config = 'incremental=(file=' + newfile + ')'
+ self.pr('Open incremental cursor with ' + config)
+ dup_cnt = 0
+ dupc = self.session.open_cursor(None, bkup_c, config)
+ while True:
+ ret = dupc.next()
+ if ret != 0:
+ break
+ incrlist = dupc.get_keys()
+ offset = incrlist[0]
+ size = incrlist[1]
+ curtype = incrlist[2]
+ self.assertEqual(offset, 0)
+ # For now assert WT_BACKUP_FILE (which is 1).
+ self.assertEqual(curtype, 1)
+ dup_cnt += 1
+ dupc.close()
+ self.assertEqual(dup_cnt, 1)
+ self.pr('Copy from: ' + newfile + ' (' + str(sz) + ') to ' + self.dir)
+ shutil.copy(newfile, self.dir)
+ self.assertEqual(ret, wiredtiger.WT_NOTFOUND)
+ bkup_c.close()
+
+ # After the full backup, open and recover the backup database.
+ backup_conn = self.wiredtiger_open(self.dir)
+ backup_conn.close()
+
+if __name__ == '__main__':
+ wttest.run()
diff --git a/src/third_party/wiredtiger/test/suite/wttest.py b/src/third_party/wiredtiger/test/suite/wttest.py
index 4f97ac2aff7..668b53a7334 100755
--- a/src/third_party/wiredtiger/test/suite/wttest.py
+++ b/src/third_party/wiredtiger/test/suite/wttest.py
@@ -160,7 +160,7 @@ class TestSuiteConnection(object):
class ExtensionList(list):
skipIfMissing = False
def extension(self, dirname, name, extarg=None):
- if name != None and name != 'none':
+ if name and name != 'none':
ext = '' if extarg == None else '=' + extarg
self.append(dirname + '/' + name + ext)
diff --git a/src/third_party/wiredtiger/test/utility/test_util.h b/src/third_party/wiredtiger/test/utility/test_util.h
index 3442e8edcec..734026683c9 100644
--- a/src/third_party/wiredtiger/test/utility/test_util.h
+++ b/src/third_party/wiredtiger/test/utility/test_util.h
@@ -148,6 +148,18 @@ typedef struct {
} while (0)
/*
+ * error_sys_check --
+ * Complain and quit if a function call fails. A special name because it appears in the
+ * documentation. Allow any non-negative values.
+ */
+#define error_sys_check(call) \
+ do { \
+ int __r; \
+ if ((__r = (int)(call)) < 0 && __r != ENOTSUP) \
+ testutil_die(__r, "%s/%d: %s", __func__, __LINE__, #call); \
+ } while (0)
+
+/*
* error_check --
* Complain and quit if a function call fails. A special name because it appears in the
* documentation. Ignore ENOTSUP to allow library calls which might not be included in any