diff options
author | Luke Chen <luke.chen@mongodb.com> | 2021-10-29 15:52:50 +1100 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2021-10-29 06:03:28 +0000 |
commit | e161271e7f0011cd8fe63a4d3c6cc1e741b6f94f (patch) | |
tree | 02a97173f992208906fd2650247107b0abda15a0 | |
parent | 71d531626767adf5939424a2328e41b73d0b389e (diff) | |
download | mongo-e161271e7f0011cd8fe63a4d3c6cc1e741b6f94f.tar.gz |
Import wiredtiger: 0d0df1c83cd1300cc486ef5bc184b9411981bf82 from branch mongodb-master
ref: e32b9e4077..0d0df1c83c
for: 5.2.0
WT-3445 Add multiple tables to format tester.
36 files changed, 3002 insertions, 2225 deletions
diff --git a/src/third_party/wiredtiger/dist/api_data.py b/src/third_party/wiredtiger/dist/api_data.py index b7913c04a0a..ffe26ac16bd 100644 --- a/src/third_party/wiredtiger/dist/api_data.py +++ b/src/third_party/wiredtiger/dist/api_data.py @@ -510,47 +510,49 @@ table_meta = format_meta + table_only_config # Connection runtime config, shared by conn.reconfigure and wiredtiger_open connection_runtime_config = [ - Config('block_cache', '', r'''block cache configuration options''', - type='category', subconfig=[ - Config('cache_on_checkpoint', 'true', r''' - Cache blocks that are written by a checkpoint''', - type='boolean'), - Config('cache_on_writes', 'true', r''' - cache newly generated blocks''', - type='boolean'), - Config('enabled', 'false', r''' - enable block cache''', - type='boolean'), - Config('blkcache_eviction_aggression', '1800', r''' - how many seconds an unused block remains in the cache before it is evicted''', - min='1', max='7200'), - Config('full_target', '95', r''' - a fraction of cache that must be full before eviction - will remove unused blocks''', - min='30', max='100'), - Config('size', '0', r''' - maximum memory to allocate for the block cache''', - min='0', max='6155GB'), - Config('hashsize', '0', r''' - number of buckets in the hashtable that keeps track of blocks.''', - min='512', max='256K'), - Config('max_percent_overhead', '10', r''' - maximum tolerated overhead expressed as the number of - blocks added and removed as percent of blocks looked up; - cache population and eviction will be suppressed if the overhead - exceeds the supplied threshold''', - min='1', max='500'), - Config('nvram_path', '', r''' - the absolute path to the file system mounted on the NVRAM device''',), - Config('percent_file_in_dram', '50', r''' - bypass cache if that percent of file fits in DRAM''', - min='0', max='100'), - Config('system_ram', '0', r''' - amount of DRAM expected to be available on the system''', - min='0', max='1024GB'), - Config('type', '', r''' - cache location: DRAM or NVRAM'''), - ]), + Config('block_cache', '', r''' + block cache configuration options''', + type='category', subconfig=[ + Config('cache_on_checkpoint', 'true', r''' + cache blocks written by a checkpoint''', + type='boolean'), + Config('cache_on_writes', 'true', r''' + cache blocks as they are written (other than checkpoint blocks)''', + type='boolean'), + Config('enabled', 'false', r''' + enable block cache''', + type='boolean'), + Config('blkcache_eviction_aggression', '1800', r''' + seconds an unused block remains in the cache before it is evicted''', + min='1', max='7200'), + Config('full_target', '95', r''' + the fraction of the block cache that must be full before eviction + will remove unused blocks''', + min='30', max='100'), + Config('size', '0', r''' + maximum memory to allocate for the block cache''', + min='0', max='10TB'), + Config('hashsize', '0', r''' + number of buckets in the hashtable that keeps track of blocks''', + min='512', max='256K'), + Config('max_percent_overhead', '10', r''' + maximum tolerated overhead expressed as the number of blocks added + and removed as percent of blocks looked up; cache population + and eviction will be suppressed if the overhead exceeds the + supplied threshold''', + min='1', max='500'), + Config('nvram_path', '', r''' + the absolute path to the file system mounted on the NVRAM device'''), + Config('percent_file_in_dram', '50', r''' + bypass cache for a file if the set percentage of the file fits in system DRAM + (as specified by block_cache.system_ram)''', + min='0', max='100'), + Config('system_ram', '0', r''' + the bytes of system DRAM available for caching filesystem blocks''', + min='0', max='1024GB'), + Config('type', '', r''' + cache location: DRAM or NVRAM'''), + ]), Config('cache_size', '100MB', r''' maximum heap memory to allocate for the cache. A database should configure either \c cache_size or \c shared_cache but not both''', diff --git a/src/third_party/wiredtiger/dist/s_clang-format.list b/src/third_party/wiredtiger/dist/s_clang-format.list index 51c807aa2a4..f50d772a061 100644 --- a/src/third_party/wiredtiger/dist/s_clang-format.list +++ b/src/third_party/wiredtiger/dist/s_clang-format.list @@ -17,3 +17,4 @@ src/os_posix/os_getopt.c src/support/hash_city.c src/support/hash_fnv.c src/support/stat.c +test/format/config_def.c diff --git a/src/third_party/wiredtiger/dist/s_longlines b/src/third_party/wiredtiger/dist/s_longlines index 16fa1252131..9d38a481fbc 100755 --- a/src/third_party/wiredtiger/dist/s_longlines +++ b/src/third_party/wiredtiger/dist/s_longlines @@ -12,6 +12,7 @@ l=`(cd .. && -e '/checksum\/zseries/d' \ -e '/config\/config_def\.c/d' \ -e '/dist\/stat_data\.py/d' \ + -e '/format\/config_def\.c/d' \ -e '/include\/extern\.h/d' \ -e '/include\/extern_posix\.h/d' \ -e '/include\/extern_win\.h/d' \ diff --git a/src/third_party/wiredtiger/dist/s_string.ok b/src/third_party/wiredtiger/dist/s_string.ok index 9bcfd210a09..a2f43166fc2 100644 --- a/src/third_party/wiredtiger/dist/s_string.ok +++ b/src/third_party/wiredtiger/dist/s_string.ok @@ -144,6 +144,7 @@ FALLOC FALLTHROUGH FH FIXME +FLCS FLD FLSv FLv @@ -446,6 +447,7 @@ Unordered Uryyb VALGRIND VARCHAR +VLCS VLDB VMSG VPM diff --git a/src/third_party/wiredtiger/import.data b/src/third_party/wiredtiger/import.data index 028444491b7..3129c26a068 100644 --- a/src/third_party/wiredtiger/import.data +++ b/src/third_party/wiredtiger/import.data @@ -2,5 +2,5 @@ "vendor": "wiredtiger", "github": "wiredtiger/wiredtiger.git", "branch": "mongodb-master", - "commit": "e32b9e4077922c0e20f97b90577a419079d16dcb" + "commit": "0d0df1c83cd1300cc486ef5bc184b9411981bf82" } diff --git a/src/third_party/wiredtiger/src/config/config_def.c b/src/third_party/wiredtiger/src/config/config_def.c index 25aceda326b..fbe1981fade 100644 --- a/src/third_party/wiredtiger/src/config/config_def.c +++ b/src/third_party/wiredtiger/src/config/config_def.c @@ -42,7 +42,7 @@ static const WT_CONFIG_CHECK confchk_wiredtiger_open_block_cache_subconfigs[] = {"max_percent_overhead", "int", NULL, "min=1,max=500", NULL, 0}, {"nvram_path", "string", NULL, NULL, NULL, 0}, {"percent_file_in_dram", "int", NULL, "min=0,max=100", NULL, 0}, - {"size", "int", NULL, "min=0,max=6155GB", NULL, 0}, + {"size", "int", NULL, "min=0,max=10TB", NULL, 0}, {"system_ram", "int", NULL, "min=0,max=1024GB", NULL, 0}, {"type", "string", NULL, NULL, NULL, 0}, {NULL, NULL, NULL, NULL, NULL, 0}}; diff --git a/src/third_party/wiredtiger/src/include/wiredtiger.in b/src/third_party/wiredtiger/src/include/wiredtiger.in index 1cea9a9aef3..2bbebb4c5ce 100644 --- a/src/third_party/wiredtiger/src/include/wiredtiger.in +++ b/src/third_party/wiredtiger/src/include/wiredtiger.in @@ -2015,32 +2015,35 @@ struct __wt_connection { * @config{block_cache = (, block cache configuration options., a set of related * configuration options defined below.} * @config{ - * blkcache_eviction_aggression, how many seconds an unused block remains in the cache - * before it is evicted., an integer between 1 and 7200; default \c 1800.} - * @config{ cache_on_checkpoint, Cache blocks that are written by a + * blkcache_eviction_aggression, seconds an unused block remains in the cache before it is + * evicted., an integer between 1 and 7200; default \c 1800.} + * @config{ cache_on_checkpoint, cache blocks written by a * checkpoint., a boolean flag; default \c true.} * @config{ - * cache_on_writes, cache newly generated blocks., a boolean flag; default \c true.} - * @config{ enabled, enable block cache., a boolean flag; default \c - * false.} - * @config{ full_target, a fraction of cache that must be - * full before eviction will remove unused blocks., an integer between 30 and 100; default - * \c 95.} - * @config{ hashsize, number of buckets in the hashtable that - * keeps track of blocks., an integer between 512 and 256K; default \c 0.} - * @config{ max_percent_overhead, maximum tolerated overhead - * expressed as the number of blocks added and removed as percent of blocks looked up; cache - * population and eviction will be suppressed if the overhead exceeds the supplied - * threshold., an integer between 1 and 500; default \c 10.} - * @config{ nvram_path, the absolute path to the file system mounted - * on the NVRAM device., a string; default empty.} + * cache_on_writes, cache blocks as they are written (other than checkpoint blocks)., a + * boolean flag; default \c true.} + * @config{ enabled, enable block + * cache., a boolean flag; default \c false.} + * @config{ full_target, + * the fraction of the block cache that must be full before eviction will remove unused + * blocks., an integer between 30 and 100; default \c 95.} + * @config{ + * hashsize, number of buckets in the hashtable that keeps track of blocks., an integer + * between 512 and 256K; default \c 0.} * @config{ - * percent_file_in_dram, bypass cache if that percent of file fits in DRAM., an integer - * between 0 and 100; default \c 50.} - * @config{ size, maximum memory - * to allocate for the block cache., an integer between 0 and 6155GB; default \c 0.} - * @config{ system_ram, amount of DRAM expected to be available on - * the system., an integer between 0 and 1024GB; default \c 0.} + * max_percent_overhead, maximum tolerated overhead expressed as the number of blocks added + * and removed as percent of blocks looked up; cache population and eviction will be + * suppressed if the overhead exceeds the supplied threshold., an integer between 1 and 500; + * default \c 10.} + * @config{ nvram_path, the absolute path to the file + * system mounted on the NVRAM device., a string; default empty.} + * @config{ percent_file_in_dram, bypass cache for a file if the set + * percentage of the file fits in system DRAM (as specified by block_cache.system_ram)., an + * integer between 0 and 100; default \c 50.} + * @config{ size, maximum + * memory to allocate for the block cache., an integer between 0 and 10TB; default \c 0.} + * @config{ system_ram, the bytes of system DRAM available for + * caching filesystem blocks., an integer between 0 and 1024GB; default \c 0.} * @config{ type, cache location: DRAM or NVRAM., a string; default * empty.} * @config{ ),,} @@ -2703,16 +2706,18 @@ struct __wt_connection { * @configstart{wiredtiger_open, see dist/api_data.py} * @config{block_cache = (, block cache configuration options., a set of related configuration * options defined below.} - * @config{ blkcache_eviction_aggression, how many - * seconds an unused block remains in the cache before it is evicted., an integer between 1 and - * 7200; default \c 1800.} - * @config{ cache_on_checkpoint, Cache blocks that - * are written by a checkpoint., a boolean flag; default \c true.} - * @config{ - * cache_on_writes, cache newly generated blocks., a boolean flag; default \c true.} - * @config{ enabled, enable block cache., a boolean flag; default \c false.} - * @config{ full_target, a fraction of cache that must be full before - * eviction will remove unused blocks., an integer between 30 and 100; default \c 95.} + * @config{ blkcache_eviction_aggression, seconds an + * unused block remains in the cache before it is evicted., an integer between 1 and 7200; default + * \c 1800.} + * @config{ cache_on_checkpoint, cache blocks written by a + * checkpoint., a boolean flag; default \c true.} + * @config{ cache_on_writes, + * cache blocks as they are written (other than checkpoint blocks)., a boolean flag; default \c + * true.} + * @config{ enabled, enable block cache., a boolean flag; default \c + * false.} + * @config{ full_target, the fraction of the block cache that must be + * full before eviction will remove unused blocks., an integer between 30 and 100; default \c 95.} * @config{ hashsize, number of buckets in the hashtable that keeps track of * blocks., an integer between 512 and 256K; default \c 0.} * @config{ @@ -2722,12 +2727,15 @@ struct __wt_connection { * @config{ nvram_path, the absolute path to the file system mounted on the * NVRAM device., a string; default empty.} * @config{ percent_file_in_dram, - * bypass cache if that percent of file fits in DRAM., an integer between 0 and 100; default \c 50.} + * bypass cache for a file if the set percentage of the file fits in system DRAM (as specified by + * block_cache.system_ram)., an integer between 0 and 100; default \c 50.} * @config{ size, maximum memory to allocate for the block cache., an integer - * between 0 and 6155GB; default \c 0.} - * @config{ system_ram, amount of DRAM - * expected to be available on the system., an integer between 0 and 1024GB; default \c 0.} - * @config{ type, cache location: DRAM or NVRAM., a string; default empty.} + * between 0 and 10TB; default \c 0.} + * @config{ system_ram, the bytes of + * system DRAM available for caching filesystem blocks., an integer between 0 and 1024GB; default \c + * 0.} + * @config{ type, cache location: DRAM or NVRAM., a string; default + * empty.} * @config{ ),,} * @config{buffer_alignment, in-memory alignment (in bytes) for buffers used for I/O. The default * value of -1 indicates a platform-specific alignment value should be used (4KB on Linux systems diff --git a/src/third_party/wiredtiger/src/txn/txn_rollback_to_stable.c b/src/third_party/wiredtiger/src/txn/txn_rollback_to_stable.c index 6b85d469c96..6639f019709 100644 --- a/src/third_party/wiredtiger/src/txn/txn_rollback_to_stable.c +++ b/src/third_party/wiredtiger/src/txn/txn_rollback_to_stable.c @@ -206,7 +206,7 @@ __rollback_has_stable_update(WT_UPDATE *upd) { while (upd != NULL && (upd->type == WT_UPDATE_INVALID || upd->txnid == WT_TXN_ABORTED)) upd = upd->next; - return upd != NULL; + return (upd != NULL); } /* @@ -1428,7 +1428,7 @@ __rollback_to_stable_btree_apply( wt_timestamp_t max_durable_ts, newest_start_durable_ts, newest_stop_durable_ts; size_t addr_size; uint64_t rollback_txnid, write_gen; - uint32_t btree_id, handle_open_flags; + uint32_t btree_id; char ts_string[2][WT_TS_INT_STRING_SIZE]; bool dhandle_allocated, durable_ts_found, has_txn_updates_gt_than_ckpt_snap, perform_rts; bool prepared_updates; @@ -1529,16 +1529,7 @@ __rollback_to_stable_btree_apply( if (perform_rts || max_durable_ts > rollback_timestamp || prepared_updates || !durable_ts_found || has_txn_updates_gt_than_ckpt_snap) { - /* - * MongoDB does not close all open handles before calling rollback-to-stable; otherwise, - * don't permit that behavior, the application is likely making a mistake. - */ -#ifdef WT_STANDALONE_BUILD - handle_open_flags = WT_DHANDLE_DISCARD | WT_DHANDLE_EXCLUSIVE; -#else - handle_open_flags = 0; -#endif - ret = __wt_session_get_dhandle(session, uri, NULL, NULL, handle_open_flags); + ret = __wt_session_get_dhandle(session, uri, NULL, NULL, 0); if (ret != 0) WT_ERR_MSG(session, ret, "%s: unable to open handle%s", uri, ret == EBUSY ? ", error indicates handle is unavailable due to concurrent use" : ""); diff --git a/src/third_party/wiredtiger/test/evergreen.yml b/src/third_party/wiredtiger/test/evergreen.yml index a6e6b543c99..bbf198aa017 100755 --- a/src/third_party/wiredtiger/test/evergreen.yml +++ b/src/third_party/wiredtiger/test/evergreen.yml @@ -384,7 +384,7 @@ functions: set -o errexit set -o verbose for i in $(seq ${times|1}); do - ./t -1 -c ${config|../../../test/format/CONFIG.stress} ${extra_args|} || ( [ -f RUNDIR/CONFIG ] && cat RUNDIR/CONFIG ) 2>&1 + ./t -c ${config|../../../test/format/CONFIG.stress} ${extra_args|} || ( [ -f RUNDIR/CONFIG ] && cat RUNDIR/CONFIG ) 2>&1 done "format test script": command: shell.exec diff --git a/src/third_party/wiredtiger/test/format/CONFIG.stress b/src/third_party/wiredtiger/test/format/CONFIG.stress index a57a4f2df51..5ea078899f3 100644 --- a/src/third_party/wiredtiger/test/format/CONFIG.stress +++ b/src/third_party/wiredtiger/test/format/CONFIG.stress @@ -2,7 +2,7 @@ btree.huffman_value=0 cache.minimum=20 runs.rows=1000000:5000000 +runs.tables=3:10 runs.threads=4:32 runs.timer=6:30 runs.type=row,var -runs=100 diff --git a/src/third_party/wiredtiger/test/format/Makefile.am b/src/third_party/wiredtiger/test/format/Makefile.am index 771e41fd662..8897feee112 100644 --- a/src/third_party/wiredtiger/test/format/Makefile.am +++ b/src/third_party/wiredtiger/test/format/Makefile.am @@ -4,8 +4,8 @@ AM_CPPFLAGS +=-I$(top_srcdir)/test/utility noinst_PROGRAMS = t t_SOURCES =\ - alter.c backup.c bulk.c checkpoint.c compact.c config.c config_compat.c hs.c import.c kv.c ops.c \ - random.c salvage.c snap.c t.c trace.c util.c wts.c + alter.c backup.c bulk.c checkpoint.c compact.c config.c config_compat.c config_def.c hs.c \ + import.c kv.c ops.c random.c salvage.c snap.c t.c trace.c util.c wts.c t_LDADD = $(top_builddir)/test/utility/libtest_util.la t_LDADD +=$(top_builddir)/libwiredtiger.la diff --git a/src/third_party/wiredtiger/test/format/alter.c b/src/third_party/wiredtiger/test/format/alter.c index 7c3dc4f02d9..2a87f0654ec 100644 --- a/src/third_party/wiredtiger/test/format/alter.c +++ b/src/third_party/wiredtiger/test/format/alter.c @@ -35,6 +35,7 @@ WT_THREAD_RET alter(void *arg) { + TABLE *table; WT_CONNECTION *conn; WT_DECL_RET; WT_SESSION *session; @@ -60,10 +61,10 @@ alter(void *arg) testutil_check(__wt_snprintf( buf, sizeof(buf), "access_pattern_hint=%s", access_value ? "random" : "none")); access_value = !access_value; - /* - * Alter can return EBUSY if concurrent with other operations. - */ - while ((ret = session->alter(session, g.uri, buf)) != 0 && ret != EBUSY) + + /* Alter can return EBUSY if concurrent with other operations. */ + table = table_select(NULL); + while ((ret = session->alter(session, table->uri, buf)) != 0 && ret != EBUSY) testutil_die(ret, "session.alter"); while (period > 0 && !g.workers_finished) { --period; diff --git a/src/third_party/wiredtiger/test/format/backup.c b/src/third_party/wiredtiger/test/format/backup.c index 8ec113dd6d8..3d53b5324f7 100644 --- a/src/third_party/wiredtiger/test/format/backup.c +++ b/src/third_party/wiredtiger/test/format/backup.c @@ -71,12 +71,8 @@ check_copy(void) testutil_check(__wt_snprintf(path, len, "%s/BACKUP", g.home)); wts_open(path, &conn, &session, true); - /* - * Verify can return EBUSY if the handle isn't available. Don't yield and retry, in the case of - * LSM, the handle may not be available for a long time. - */ - ret = session->verify(session, g.uri, NULL); - testutil_assertfmt(ret == 0 || ret == EBUSY, "WT_SESSION.verify: %s: %s", path, g.uri); + /* Verify the objects. */ + tables_apply(wts_verify, conn); wts_close(&conn, &session); @@ -114,7 +110,7 @@ active_files_print(ACTIVE_FILES *active, const char *msg) if (active == NULL) return; - fprintf(stderr, "Active files: %s, %d entries\n", msg, (int)active->count); + fprintf(stderr, "Active files: %s, %" PRIu32 " entries\n", msg, active->count); for (i = 0; i < active->count; i++) fprintf(stderr, " %s\n", active->names[i]); } @@ -167,7 +163,7 @@ active_files_remove_missing(ACTIVE_FILES *prev, ACTIVE_FILES *cur) { uint32_t curpos, prevpos; int cmp; - char filename[1024]; + char filename[MAX_FORMAT_PATH]; if (prev == NULL) return; @@ -244,7 +240,7 @@ copy_blocks(WT_SESSION *session, WT_CURSOR *bkup_c, const char *name) ssize_t rdsize; uint64_t offset, size, this_size, total, type; int rfd, wfd1, wfd2; - char config[512], *tmp; + char config[MAX_FORMAT_PATH], *tmp; bool first_pass; tmp_sz = 0; @@ -366,7 +362,7 @@ restore_backup_info(WT_SESSION *session, ACTIVE_FILES *active) uint32_t i; char buf[512], *path; - testutil_assert(g.c_backup_incr_flag == INCREMENTAL_BLOCK); + testutil_assert(g.backup_incr_flag == INCREMENTAL_BLOCK); len = strlen(g.home) + strlen(BACKUP_INFO_FILE) + 2; path = dmalloc(len); testutil_check(__wt_snprintf(path, len, "%s/%s", g.home, BACKUP_INFO_FILE)); @@ -440,7 +436,7 @@ save_backup_info(ACTIVE_FILES *active, uint64_t id) uint32_t i; char *from_path, *to_path; - if (g.c_backup_incr_flag != INCREMENTAL_BLOCK) + if (g.backup_incr_flag != INCREMENTAL_BLOCK) return; len = strlen(g.home) + strlen(BACKUP_INFO_FILE_TMP) + 2; from_path = dmalloc(len); @@ -498,7 +494,7 @@ backup(void *arg) * that we can take an incremental backup and use the older id as a source identifier. We force * that only if the restore function was successful in restoring the backup information. */ - if (g.reopen && g.c_backup_incr_flag == INCREMENTAL_BLOCK && + if (g.reopen && g.backup_incr_flag == INCREMENTAL_BLOCK && restore_backup_info(session, &active[0]) == RESTORE_SUCCESS) { incr_full = false; full = false; @@ -529,7 +525,7 @@ backup(void *arg) break; } - if (g.c_backup_incr_flag == INCREMENTAL_BLOCK) { + if (g.backup_incr_flag == INCREMENTAL_BLOCK) { /* * If we're doing a full backup as the start of the incremental backup, only send in an * identifier for this one. Also set the block granularity. @@ -541,7 +537,7 @@ backup(void *arg) active_prev = NULL; testutil_check(__wt_snprintf(cfg, sizeof(cfg), "incremental=(enabled,granularity=%" PRIu32 "K,this_id=%" PRIu64 ")", - g.c_backup_incr_granularity, g.backup_id)); + GV(BACKUP_INCR_GRANULARITY), g.backup_id)); full = true; incr_full = false; } else { @@ -562,7 +558,7 @@ backup(void *arg) config = cfg; /* Free up the old active file list we're going to overwrite. */ active_files_free(active_now); - } else if (g.c_logging && g.c_backup_incr_flag == INCREMENTAL_LOG) { + } else if (GV(LOGGING) && g.backup_incr_flag == INCREMENTAL_LOG) { if (incr_full) { config = NULL; full = true; @@ -595,7 +591,7 @@ backup(void *arg) while ((ret = backup_cursor->next(backup_cursor)) == 0) { testutil_check(backup_cursor->get_key(backup_cursor, &key)); - if (g.c_backup_incr_flag == INCREMENTAL_BLOCK) { + if (g.backup_incr_flag == INCREMENTAL_BLOCK) { if (full) testutil_copy_file(session, key); else @@ -609,7 +605,7 @@ backup(void *arg) testutil_die(ret, "backup-cursor"); /* After a log-based incremental backup, truncate the log files. */ - if (g.c_backup_incr_flag == INCREMENTAL_LOG) + if (g.backup_incr_flag == INCREMENTAL_LOG) testutil_check(session->truncate(session, "log:", backup_cursor, NULL, NULL)); testutil_check(backup_cursor->close(backup_cursor)); @@ -628,9 +624,9 @@ backup(void *arg) */ if (full) { incremental = 1; - if (g.c_backup_incr_flag == INCREMENTAL_LOG) - incremental = g.c_logging_archive ? 1 : mmrand(NULL, 1, 8); - else if (g.c_backup_incr_flag == INCREMENTAL_BLOCK) + if (g.backup_incr_flag == INCREMENTAL_LOG) + incremental = GV(LOGGING_ARCHIVE) ? 1 : mmrand(NULL, 1, 8); + else if (g.backup_incr_flag == INCREMENTAL_BLOCK) incremental = mmrand(NULL, 1, 8); } if (--incremental == 0) { diff --git a/src/third_party/wiredtiger/test/format/bulk.c b/src/third_party/wiredtiger/test/format/bulk.c index d1864741a77..c242e62f8ee 100644 --- a/src/third_party/wiredtiger/test/format/bulk.c +++ b/src/third_party/wiredtiger/test/format/bulk.c @@ -73,8 +73,12 @@ bulk_rollback_transaction(WT_SESSION *session) testutil_check(session->rollback_transaction(session, NULL)); } +/* + * wts_load -- + * Bulk load a table. + */ void -wts_load(void) +wts_load(TABLE *table, void *arg) { WT_CONNECTION *conn; WT_CURSOR *cursor; @@ -82,27 +86,31 @@ wts_load(void) WT_ITEM key, value; WT_SESSION *session; uint32_t committed_keyno, keyno, v; + char track_buf[128]; bool is_bulk; + (void)arg; /* unused argument */ + conn = g.wts_conn; testutil_check(conn->open_session(conn, NULL, NULL, &session)); - trace_msg("%s", "=============== bulk load start"); + testutil_check(__wt_snprintf(track_buf, sizeof(track_buf), "table %u bulk load", table->id)); + trace_msg("=============== %s bulk load start", table->uri); /* * No bulk load with custom collators, the order of insertion will not match the collation * order. */ is_bulk = true; - if (g.c_reverse) + if (TV(BTREE_REVERSE)) is_bulk = false; /* * open_cursor can return EBUSY if concurrent with a metadata operation, retry in that case. */ while ((ret = session->open_cursor( - session, g.uri, NULL, is_bulk ? "bulk,append" : NULL, &cursor)) == EBUSY) + session, table->uri, NULL, is_bulk ? "bulk,append" : NULL, &cursor)) == EBUSY) __wt_yield(); testutil_check(ret); @@ -110,13 +118,13 @@ wts_load(void) key_gen_init(&key); val_gen_init(&value); - if (g.c_txn_timestamps) + if (g.transaction_timestamps_config) bulk_begin_transaction(session); - for (committed_keyno = keyno = 0; ++keyno <= g.c_rows;) { - val_gen(NULL, &value, keyno); + for (committed_keyno = keyno = 0; ++keyno <= table->rows_current;) { + val_gen(table, NULL, &value, keyno); - switch (g.type) { + switch (table->type) { case FIX: if (!is_bulk) cursor->set_key(cursor, keyno); @@ -132,7 +140,7 @@ wts_load(void) trace_msg("bulk %" PRIu32 " {%.*s}", keyno, (int)value.size, (char *)value.data); break; case ROW: - key_gen(&key, keyno); + key_gen(table, &key, keyno); cursor->set_key(cursor, &key); cursor->set_value(cursor, &value); if (g.trace_all) @@ -150,7 +158,7 @@ wts_load(void) if ((ret = cursor->insert(cursor)) != 0) { testutil_assert(ret == WT_CACHE_FULL || ret == WT_ROLLBACK); - if (g.c_txn_timestamps) { + if (g.transaction_timestamps_config) { bulk_rollback_transaction(session); bulk_begin_transaction(session); } @@ -161,13 +169,13 @@ wts_load(void) * simply modify the values because they have to equal 100 when the database is reopened * (we are going to rewrite the CONFIG file, too). */ - if (g.c_insert_pct > 5) { - g.c_delete_pct += g.c_insert_pct - 5; - g.c_insert_pct = 5; + if (TV(OPS_PCT_INSERT) > 5) { + TV(OPS_PCT_DELETE) += TV(OPS_PCT_INSERT) - 5; + TV(OPS_PCT_INSERT) = 5; } - v = g.c_write_pct / 2; - g.c_delete_pct += v; - g.c_write_pct -= v; + v = TV(OPS_PCT_WRITE) / 2; + TV(OPS_PCT_DELETE) += v; + TV(OPS_PCT_WRITE) -= v; break; } @@ -179,9 +187,9 @@ wts_load(void) */ if ((keyno < 5000 && keyno % 10 == 0) || keyno % 5000 == 0) { /* Report on progress. */ - track("bulk load", keyno, NULL); + track(track_buf, keyno); - if (g.c_txn_timestamps) { + if (g.transaction_timestamps_config) { bulk_commit_transaction(session); committed_keyno = keyno; bulk_begin_transaction(session); @@ -189,25 +197,24 @@ wts_load(void) } } - if (g.c_txn_timestamps) + if (g.transaction_timestamps_config) bulk_commit_transaction(session); /* * Ideally, the insert loop runs until the number of rows plus one, in which case row counts are - * correct. If the loop exited early, reset the counters and rewrite the CONFIG file (so reopens - * aren't surprised). + * correct. If the loop exited early, reset the table's row count and rewrite the CONFIG file + * (so reopens aren't surprised). */ - if (keyno != g.c_rows + 1) { - g.c_rows = g.c_txn_timestamps ? committed_keyno : (keyno - 1); - testutil_assert(g.c_rows > 0); - g.rows = g.c_rows; + if (keyno != table->rows_current + 1) { + table->rows_current = g.transaction_timestamps_config ? committed_keyno : (keyno - 1); + testutil_assert(table->rows_current > 0); config_print(false); } testutil_check(cursor->close(cursor)); - trace_msg("%s", "=============== bulk load stop"); + trace_msg("=============== %s bulk load stop", table->uri); testutil_check(session->close(session, NULL)); diff --git a/src/third_party/wiredtiger/test/format/checkpoint.c b/src/third_party/wiredtiger/test/format/checkpoint.c index dac538348a6..b2eef25bea0 100644 --- a/src/third_party/wiredtiger/test/format/checkpoint.c +++ b/src/third_party/wiredtiger/test/format/checkpoint.c @@ -35,7 +35,7 @@ void wts_checkpoints(void) { - char config[1024]; + char config[128]; /* * Configuring WiredTiger library checkpoints is done separately, rather than as part of the @@ -45,12 +45,12 @@ wts_checkpoints(void) * checkpoint running in the tree, and the cache can get stuck. That workload is unlikely enough * we're not going to fix it in the library, so configure it away by delaying checkpoint start. */ - if (g.c_checkpoint_flag != CHECKPOINT_WIREDTIGER) + if (g.checkpoint_config != CHECKPOINT_WIREDTIGER) return; testutil_check( __wt_snprintf(config, sizeof(config), ",checkpoint=(wait=%" PRIu32 ",log_size=%" PRIu32 ")", - g.c_checkpoint_wait, MEGABYTE(g.c_checkpoint_log_size))); + GV(CHECKPOINT_WAIT), MEGABYTE(GV(CHECKPOINT_LOG_SIZE)))); testutil_check(g.wts_conn->reconfigure(g.wts_conn, config)); } @@ -67,11 +67,13 @@ checkpoint(void *arg) u_int secs; char config_buf[64]; const char *ckpt_config; - bool backup_locked; + bool backup_locked, named_checkpoints; (void)arg; + conn = g.wts_conn; testutil_check(conn->open_session(conn, NULL, NULL, &session)); + named_checkpoints = !g.lsm_config; for (secs = mmrand(NULL, 1, 10); !g.workers_finished;) { if (secs > 0) { @@ -88,7 +90,7 @@ checkpoint(void *arg) */ ckpt_config = NULL; backup_locked = false; - if (!DATASOURCE("lsm")) + if (named_checkpoints) switch (mmrand(NULL, 1, 20)) { case 1: /* diff --git a/src/third_party/wiredtiger/test/format/compact.c b/src/third_party/wiredtiger/test/format/compact.c index 4de1d898bcc..f4b53f21f75 100644 --- a/src/third_party/wiredtiger/test/format/compact.c +++ b/src/third_party/wiredtiger/test/format/compact.c @@ -29,12 +29,13 @@ #include "format.h" /* - * compaction -- + * compact -- * Periodically do a compaction operation. */ WT_THREAD_RET compact(void *arg) { + TABLE *table; WT_CONNECTION *conn; WT_DECL_RET; WT_SESSION *session; @@ -66,7 +67,8 @@ compact(void *arg) * Compact returns ETIMEDOUT if the compaction doesn't finish in in some number of seconds. * We don't configure a timeout and occasionally exceed the default of 1200 seconds. */ - ret = session->compact(session, g.uri, NULL); + table = table_select(NULL); + ret = session->compact(session, table->uri, NULL); if (ret != 0 && ret != EBUSY && ret != ETIMEDOUT && ret != WT_ROLLBACK && ret != WT_CACHE_FULL) testutil_die(ret, "session.compact"); diff --git a/src/third_party/wiredtiger/test/format/config.c b/src/third_party/wiredtiger/test/format/config.c index 6cf7c795774..a61b2ec5851 100644 --- a/src/third_party/wiredtiger/test/format/config.c +++ b/src/third_party/wiredtiger/test/format/config.c @@ -27,7 +27,6 @@ */ #include "format.h" -#include "config.h" static void config_backup_incr(void); static void config_backup_incr_granularity(void); @@ -35,81 +34,124 @@ static void config_backup_incr_log_compatibility_check(void); static void config_backward_compatible(void); static void config_cache(void); static void config_checkpoint(void); -static void config_checksum(void); -static void config_compression(const char *); +static void config_checksum(TABLE *); +static void config_compression(TABLE *, const char *); static void config_directio(void); static void config_encryption(void); static const char *config_file_type(u_int); -static bool config_fix(void); +static bool config_explicit(TABLE *, const char *); +static bool config_fix(TABLE *); static void config_in_memory(void); static void config_in_memory_reset(void); -static int config_is_perm(const char *); -static void config_lsm_reset(void); +static void config_lsm_reset(TABLE *); static void config_map_backup_incr(const char *, u_int *); static void config_map_checkpoint(const char *, u_int *); -static void config_map_checksum(const char *, u_int *); -static void config_map_compression(const char *, u_int *); -static void config_map_encryption(const char *, u_int *); static void config_map_file_type(const char *, u_int *); -static void config_pct(void); -static void config_prefix(void); -static void config_reset(void); +static void config_pct(TABLE *); static void config_transaction(void); /* - * We currently disable random LSM testing, that is, it can be specified explicitly but we won't - * randomly choose LSM as a data_source configuration. - */ -#define DISABLE_RANDOM_LSM_TESTING 1 - -/* - * config_final -- - * Final run initialization. + * config_random -- + * Do random configuration on the remaining global or table space. */ -void -config_final(void) +static void +config_random(TABLE *table, bool table_only) { - config_print(false); + CONFIG *cp; + CONFIGV *v; + char buf[128]; - g.rows = g.c_rows; /* Set the key count. */ + for (cp = configuration_list; cp->name != NULL; ++cp) { + if (F_ISSET(cp, C_IGNORE)) + continue; + if (table_only && !F_ISSET(cp, C_TABLE)) + continue; + if (!table_only && F_ISSET(cp, C_TABLE)) + continue; + + /* + * Don't randomly configure runs.tables if we read a CONFIG file, that prevents us from + * turning old-style CONFIG files into multi-table tests. + */ + if (cp->off == V_GLOBAL_RUNS_TABLES && !g.multi_table_config) + continue; + + v = &table->v[cp->off]; + if (v->set) + continue; - key_init(); /* Initialize key/value information. */ - val_init(); + /* Configure key prefixes only rarely, 5% if the length isn't set explicitly. */ + if (cp->off == V_TABLE_BTREE_PREFIX_LEN && mmrand(NULL, 1, 100) > 5) + continue; + + /* + * Boolean flags are 0 or 1, where the variable's "min" value is the percent chance the flag + * is "on" (so "on" if random rolled <= N, otherwise "off"). + */ + if (F_ISSET(cp, C_BOOL)) + testutil_check(__wt_snprintf( + buf, sizeof(buf), "%s=%s", cp->name, mmrand(NULL, 1, 100) <= cp->min ? "on" : "off")); + else + testutil_check(__wt_snprintf( + buf, sizeof(buf), "%s=%" PRIu32, cp->name, mmrand(NULL, cp->min, cp->maxrand))); + config_single(table, buf, false); + } } /* - * config -- - * Initialize the configuration itself. + * config_promote -- + * Promote a base value to a table. */ -void -config_run(void) +static void +config_promote(TABLE *table, CONFIG *cp, CONFIGV *v) { - CONFIG *cp; char buf[128]; - /* Clear any temporary values. */ - config_reset(); + if (F_ISSET(cp, C_STRING)) + testutil_check(__wt_snprintf(buf, sizeof(buf), "%s=%s", cp->name, v->vstr)); + else + testutil_check(__wt_snprintf(buf, sizeof(buf), "%s=%" PRIu32, cp->name, v->v)); + config_single(table, buf, true); +} - /* Periodically run in-memory. */ - config_in_memory(); +/* + * We currently disable random LSM testing, that is, it can be specified explicitly but we won't + * randomly choose LSM as a data_source configuration. + */ +#define DISABLE_RANDOM_LSM_TESTING 1 + +/* + * config_table_am -- + * Configure the table's access methods (type and source). + */ +static void +config_table_am(TABLE *table) +{ + char buf[128]; /* - * Choose a file format and a data source: they're interrelated (LSM is only compatible with - * row-store) and other items depend on them. + * The runs.type configuration allows more than a single type, for example, choosing from either + * RS and VLCS but not FLCS. If there's no table value but there was a global value, re-evaluate + * the original global specification, not the choice set for the global table. */ - if (!config_is_perm("runs.type")) { - if (config_is_perm("runs.source") && DATASOURCE("lsm")) - config_single("runs.type=row", false); + if (!table->v[V_TABLE_RUNS_TYPE].set && tables[0]->v[V_TABLE_RUNS_TYPE].set) { + testutil_check(__wt_snprintf(buf, sizeof(buf), "runs.type=%s", g.runs_type)); + config_single(table, buf, true); + } + + if (!config_explicit(table, "runs.type")) { + if (config_explicit(table, "runs.source") && DATASOURCE(table, "lsm")) + config_single(table, "runs.type=row", false); else switch (mmrand(NULL, 1, 10)) { case 1: case 2: case 3: /* 30% */ - config_single("runs.type=var", false); + config_single(table, "runs.type=var", false); break; case 4: /* 10% */ - if (config_fix()) { - config_single("runs.type=fix", false); + if (config_fix(table)) { + config_single(table, "runs.type=fix", false); break; } /* FALLTHROUGH */ /* 60% */ @@ -119,118 +161,182 @@ config_run(void) case 8: case 9: case 10: - config_single("runs.type=row", false); + config_single(table, "runs.type=row", false); break; } } - if (!config_is_perm("runs.source")) { - config_single("runs.source=table", false); + if (!config_explicit(table, "runs.source")) switch (mmrand(NULL, 1, 5)) { case 1: /* 20% */ - config_single("runs.source=file", false); + config_single(table, "runs.source=file", false); break; case 2: /* 20% */ #if !defined(DISABLE_RANDOM_LSM_TESTING) /* - * LSM requires a row-store and backing disk. + * LSM requires a row-store and backing disk. Don't configure LSM if in-memory, + * timestamps or truncation are configured, they result in cache problems. * - * Configuring truncation or timestamps results in LSM cache problems, don't configure - * LSM if those set. - * - * XXX Remove the timestamp test when WT-4162 resolved. + * FIXME WT-4162: Remove the timestamp test when WT-4162 resolved. */ - if (g.type != ROW || g.c_in_memory) + if (table->type != ROW || GV(RUNS_IN_MEMORY)) + break; + if (config_explicit(table, "transaction.timestamps") && TV(TRANSACTION_TIMESTAMPS)) break; - if (config_is_perm("transaction.timestamps") && g.c_txn_timestamps) + if (GV(BACKUP) && config_explicit(table, "backup.incremental") && + g.backup_incr_flag == INCREMENTAL_BLOCK) break; - if (config_is_perm("ops.truncate") && g.c_truncate) + if (config_explicit(table, "ops.truncate") && TV(OPS_TRUNCATE)) break; - config_single("runs.source=lsm", false); + config_single(table, "runs.source=lsm", false); #endif - break; + /* FALLTHROUGH */ case 3: case 4: case 5: /* 60% */ + config_single(table, "runs.source=table", false); break; } - } - /* If data_source and file_type were both "permanent", we may still have a mismatch. */ - if (DATASOURCE("lsm") && g.type != ROW) - testutil_die( - EINVAL, "%s: lsm data_source is only compatible with row file_type\n", progname); + /* If data_source and file_type were both set explicitly, we may still have a mismatch. */ + if (DATASOURCE(table, "lsm") && table->type != ROW) + testutil_die(EINVAL, "%s: lsm data_source is only compatible with row file_type", progname); +} + +/* + * config_table -- + * Finish initialization of a single table. + */ +static void +config_table(TABLE *table, void *arg) +{ + CONFIG *cp; + + (void)arg; /* unused argument */ + + /* + * Choose a file format and a data source: they're interrelated (LSM is only compatible with + * row-store) and other items depend on them. + */ + config_table_am(table); /* * Build the top-level object name: we're overloading data_source in our configuration, LSM * objects are "tables", but files are tested as well. */ - g.uri = dmalloc(256); - strcpy(g.uri, DATASOURCE("file") ? "file:" : "table:"); - strcat(g.uri, WT_NAME); + if (ntables == 0) + testutil_check(__wt_snprintf(table->uri, sizeof(table->uri), "%s", + DATASOURCE(table, "file") ? "file:wt" : "table:wt")); + else + testutil_check(__wt_snprintf(table->uri, sizeof(table->uri), + DATASOURCE(table, "file") ? "file:F%05u" : "table:T%05u", table->id)); + testutil_check( + __wt_snprintf(table->track_prefix, sizeof(table->track_prefix), "table %u", table->id)); + + /* + * For any values set in the base configuration, export them to this table (where this table + * doesn't already have a value set). + */ + if (ntables != 0) + for (cp = configuration_list; cp->name != NULL; ++cp) + if (F_ISSET(cp, C_TABLE) && !table->v[cp->off].set && tables[0]->v[cp->off].set) + config_promote(table, cp, &tables[0]->v[cp->off]); /* Fill in random values for the rest of the run. */ - for (cp = c; cp->name != NULL; ++cp) { - if (F_ISSET(cp, C_IGNORE | C_PERM | C_TEMP)) - continue; + config_random(table, true); - /* - * Boolean flags are 0 or 1, where the variable's "min" value is the percent chance the flag - * is "on" (so "on" if random rolled <= N, otherwise "off"). - */ - if (F_ISSET(cp, C_BOOL)) - testutil_check(__wt_snprintf( - buf, sizeof(buf), "%s=%s", cp->name, mmrand(NULL, 1, 100) <= cp->min ? "on" : "off")); - else - testutil_check(__wt_snprintf( - buf, sizeof(buf), "%s=%" PRIu32, cp->name, mmrand(NULL, cp->min, cp->maxrand))); - config_single(buf, false); + /* Page sizes are configured using powers-of-two or megabytes, convert them. */ + table->max_intl_page = 1U << TV(BTREE_INTERNAL_PAGE_MAX); + table->max_leaf_page = 1U << TV(BTREE_LEAF_PAGE_MAX); + table->max_mem_page = MEGABYTE(TV(BTREE_MEMORY_PAGE_MAX)); + + /* + * Key/value minimum/maximum are related, correct unless specified by the configuration. Key + * sizes are a row-store consideration: column-store doesn't store keys, a constant of 8 will + * reserve a small amount of additional space. + */ + if (table->type == ROW) { + if (!config_explicit(table, "btree.key_min") && TV(BTREE_KEY_MIN) > TV(BTREE_KEY_MAX)) + TV(BTREE_KEY_MIN) = TV(BTREE_KEY_MAX); + if (!config_explicit(table, "btree.key_max") && TV(BTREE_KEY_MAX) < TV(BTREE_KEY_MIN)) + TV(BTREE_KEY_MAX) = TV(BTREE_KEY_MIN); + if (TV(BTREE_KEY_MIN) > TV(BTREE_KEY_MAX)) + testutil_die(EINVAL, "btree.key_min may not be larger than btree.key_max"); + } else + TV(BTREE_KEY_MIN) = TV(BTREE_KEY_MAX) = 8; + if (!config_explicit(table, "btree.value_min") && TV(BTREE_VALUE_MIN) > TV(BTREE_VALUE_MAX)) + TV(BTREE_VALUE_MIN) = TV(BTREE_VALUE_MAX); + if (!config_explicit(table, "btree.value_max") && TV(BTREE_VALUE_MAX) < TV(BTREE_VALUE_MIN)) + TV(BTREE_VALUE_MAX) = TV(BTREE_VALUE_MIN); + if (TV(BTREE_VALUE_MIN) > TV(BTREE_VALUE_MAX)) + testutil_die(EINVAL, "btree.value_min may not be larger than btree.value_max"); + + /* + * If common key prefixes are configured, add prefix compression if no explicit choice was made + * and track the largest common key prefix in the run. + */ + if (TV(BTREE_PREFIX_LEN) != 0) { + if (TV(BTREE_PREFIX_COMPRESSION) == 0 && + !config_explicit(table, "btree.prefix_compression")) + config_single(table, "btree.prefix_compression=on", false); + g.prefix_len_max = WT_MAX(g.prefix_len_max, TV(BTREE_PREFIX_LEN)); } - /* Only row-store tables support collation order. */ - if (g.type != ROW) - config_single("btree.reverse=off", false); - - /* First, transaction configuration, it configures other features. */ - config_transaction(); - - /* Simple selection. */ - config_backup_incr(); - config_checkpoint(); - config_checksum(); - config_compression("btree.compression"); - config_compression("logging.compression"); - config_encryption(); - config_prefix(); - - /* Configuration based on the configuration already chosen. */ - config_directio(); - config_pct(); - config_cache(); - - /* Give in-memory, LSM and backward compatible configurations a final review. */ - if (g.c_in_memory != 0) + config_checksum(table); + config_compression(table, "btree.compression"); + config_pct(table); + + /* The number of rows in the table can change, get a local copy of the starting value. */ + table->rows_current = TV(RUNS_ROWS); + + /* Column-store tables require special row insert resolution. */ + if (table->type != ROW) + g.column_store_config = true; + + /* Only row-store tables support a collation order. */ + if (table->type != ROW) + config_single(table, "btree.reverse=off", false); + + /* Give LSM a final review and flag if there's at least one LSM data source. */ + if (DATASOURCE(table, "lsm")) { + g.lsm_config = true; + config_lsm_reset(table); + } +} + +/* + * config_run -- + * Run initialization. + */ +void +config_run(void) +{ + config_in_memory(); /* Periodically run in-memory. */ + + config_random(tables[0], false); /* Configure the remaining global name space. */ + + tables_apply(config_table, NULL); /* Configure the tables. */ + + /* Order can be important, don't shuffle without careful consideration. */ + config_transaction(); /* Transactions */ + config_backup_incr(); /* Incremental backup */ + config_checkpoint(); /* Checkpoints */ + config_compression(NULL, "logging.compression"); /* Logging compression */ + config_directio(); /* Direct I/O */ + config_encryption(); /* Encryption */ + + /* If doing an in-memory run, make sure we haven't configured something that won't work. */ + if (GV(RUNS_IN_MEMORY)) config_in_memory_reset(); - if (DATASOURCE("lsm")) - config_lsm_reset(); - config_backward_compatible(); /* - * Key/value minimum/maximum are related, correct unless specified by the configuration. + * If built in a branch that doesn't support all current options, or creating a database for + * such an environment, strip out configurations that won't work. */ - if (!config_is_perm("btree.key_min") && g.c_key_min > g.c_key_max) - g.c_key_min = g.c_key_max; - if (!config_is_perm("btree.key_max") && g.c_key_max < g.c_key_min) - g.c_key_max = g.c_key_min; - if (g.c_key_min > g.c_key_max) - testutil_die(EINVAL, "key_min may not be larger than key_max"); - - if (!config_is_perm("btree.value_min") && g.c_value_min > g.c_value_max) - g.c_value_min = g.c_value_max; - if (!config_is_perm("btree.value_max") && g.c_value_max < g.c_value_min) - g.c_value_max = g.c_value_min; - if (g.c_value_min > g.c_value_max) - testutil_die(EINVAL, "value_min may not be larger than value_max"); + if (g.backward_compatible) + config_backward_compatible(); + + config_cache(); /* Cache */ /* * Run-length is configured by a number of operations and a timer. @@ -244,14 +350,14 @@ config_run(void) * operations but the rest of the configuration means operations take a long time to complete * (for example, a small cache and many worker threads), don't let it run forever. */ - if (config_is_perm("runs.timer")) { - if (!config_is_perm("runs.ops")) - config_single("runs.ops=0", false); + if (config_explicit(NULL, "runs.timer")) { + if (!config_explicit(NULL, "runs.ops")) + config_single(NULL, "runs.ops=0", false); } else { - if (!config_is_perm("runs.ops")) - config_single("runs.timer=30", false); + if (!config_explicit(NULL, "runs.ops")) + config_single(NULL, "runs.timer=30", false); else - config_single("runs.timer=360", false); + config_single(NULL, "runs.timer=360", false); } } @@ -263,10 +369,10 @@ static void config_backup_incr(void) { /* Incremental backup requires backup. */ - if (g.c_backups == 0) { - if (!config_is_perm("backup.incremental")) - config_single("backup.incremental=off", false); - if (g.c_backup_incr_flag != INCREMENTAL_OFF) + if (GV(BACKUP) == 0) { + if (!config_explicit(NULL, "backup.incremental")) + config_single(NULL, "backup.incremental=off", false); + if (g.backup_incr_flag != INCREMENTAL_OFF) testutil_die(EINVAL, "backup.incremental requires backups be configured"); return; } @@ -275,10 +381,10 @@ config_backup_incr(void) * Incremental backup using log files is incompatible with logging archival. Testing log file * archival doesn't seem as useful as testing backup, let the backup configuration override. */ - if (config_is_perm("backup.incremental")) { - if (g.c_backup_incr_flag == INCREMENTAL_LOG) + if (config_explicit(NULL, "backup.incremental")) { + if (g.backup_incr_flag == INCREMENTAL_LOG) config_backup_incr_log_compatibility_check(); - if (g.c_backup_incr_flag == INCREMENTAL_BLOCK) + if (g.backup_incr_flag == INCREMENTAL_BLOCK) config_backup_incr_granularity(); return; } @@ -291,15 +397,15 @@ config_backup_incr(void) case 1: /* 30% full backup only */ case 2: case 3: - config_single("backup.incremental=off", false); + config_single(NULL, "backup.incremental=off", false); break; case 4: /* 30% log based incremental */ case 5: case 6: - if (!g.c_logging_archive || !config_is_perm("logging.archive")) { - if (g.c_logging_archive) - config_single("logging.archive=0", false); - config_single("backup.incremental=log", false); + if (!GV(LOGGING_ARCHIVE) || !config_explicit(NULL, "logging.archive")) { + if (GV(LOGGING_ARCHIVE)) + config_single(NULL, "logging.archive=0", false); + config_single(NULL, "backup.incremental=log", false); break; } /* FALLTHROUGH */ @@ -307,7 +413,7 @@ config_backup_incr(void) case 8: case 9: case 10: - config_single("backup.incremental=block", false); + config_single(NULL, "backup.incremental=block", false); config_backup_incr_granularity(); break; } @@ -323,7 +429,7 @@ config_backup_incr_granularity(void) uint32_t granularity, i; char confbuf[128]; - if (config_is_perm("backup.incr_granularity")) + if (config_explicit(NULL, "backup.incr_granularity")) return; /* @@ -353,8 +459,27 @@ config_backup_incr_granularity(void) } testutil_check( - __wt_snprintf(confbuf, sizeof(confbuf), "backup.incr_granularity=%u", granularity)); - config_single(confbuf, false); + __wt_snprintf(confbuf, sizeof(confbuf), "backup.incr_granularity=%" PRIu32, granularity)); + config_single(NULL, confbuf, false); +} + +/* + * config_backward_compatible_table -- + * Backward compatibility configuration, per table. + */ +static void +config_backward_compatible_table(TABLE *table, void *arg) +{ + (void)arg; /* unused argument */ + +#undef BC_CHECK +#define BC_CHECK(name, flag) \ + if (TV(flag)) { \ + if (config_explicit(table, name)) \ + testutil_die(EINVAL, "%s not supported in backward compatibility mode", name); \ + config_single(table, #name "=off", false); \ + } + BC_CHECK("btree.prefix_len", BTREE_PREFIX_LEN); } /* @@ -364,51 +489,22 @@ config_backup_incr_granularity(void) static void config_backward_compatible(void) { - bool backward_compatible; - - /* - * If built in a branch that doesn't support all current options, or creating a database for - * such an environment, strip out configurations that won't work. - */ - backward_compatible = g.backward_compatible; -#if WIREDTIGER_VERSION_MAJOR < 10 - backward_compatible = true; -#endif - if (!backward_compatible) - return; - - if (g.c_mmap_all) { - if (config_is_perm("disk.mmap_all")) - testutil_die(EINVAL, "disk.mmap_all not supported in backward compatibility mode"); - config_single("disk.mmap_all=off", false); - } - - if (g.c_timing_stress_checkpoint_reserved_txnid_delay) { - if (config_is_perm("stress.checkpoint_reserved_txnid_delay")) - testutil_die(EINVAL, - "stress.checkpoint_reserved_txnid_delay not supported in backward compatibility " - "mode"); - config_single("stress.checkpoint_reserved_txnid_delay=off", false); - } - - if (g.c_timing_stress_hs_sweep) { - if (config_is_perm("stress.hs_sweep")) - testutil_die(EINVAL, "stress.hs_sweep not supported in backward compatibility mode"); - config_single("stress.hs_sweep=off", false); +#undef BC_CHECK +#define BC_CHECK(name, flag) \ + if (GV(flag)) { \ + if (config_explicit(NULL, name)) \ + testutil_die(EINVAL, "%s not supported in backward compatibility mode", name); \ + config_single(NULL, #name "=off", false); \ } - if (g.c_timing_stress_hs_checkpoint_delay) { - if (config_is_perm("stress.hs_checkpoint_delay")) - testutil_die( - EINVAL, "stress.hs_checkpoint_delay not supported in backward compatibility mode"); - config_single("stress.hs_checkpoint_delay=off", false); - } + BC_CHECK("disk.mmap_all", DISK_MMAP_ALL); + BC_CHECK("block_cache", BLOCK_CACHE); + BC_CHECK("stress.checkpoint_reserved_txnid_delay", STRESS_CHECKPOINT_RESERVED_TXNID_DELAY); + BC_CHECK("stress.hs_checkpoint_delay", STRESS_HS_CHECKPOINT_DELAY); + BC_CHECK("stress.hs_search", STRESS_HS_SEARCH); + BC_CHECK("stress.hs_sweep", STRESS_HS_SWEEP); - if (g.c_timing_stress_hs_search) { - if (config_is_perm("stress.hs_search")) - testutil_die(EINVAL, "stress.hs_search not supported in backward compatibility mode"); - config_single("stress.hs_search=off", false); - } + tables_apply(config_backward_compatible_table, NULL); } /* @@ -418,22 +514,38 @@ config_backward_compatible(void) static void config_cache(void) { - uint32_t required, workers; - - /* Page sizes are powers-of-two for bad historic reasons. */ - g.intl_page_max = 1U << g.c_intl_page_max; - g.leaf_page_max = 1U << g.c_leaf_page_max; + uint64_t cache; + uint32_t workers; - /* Check if a minimum cache size has been specified. */ - if (config_is_perm("cache")) { - if (config_is_perm("cache.minimum") && g.c_cache_minimum != 0 && - g.c_cache < g.c_cache_minimum) + /* Check if both min and max cache sizes have been specified and if they're consistent. */ + if (config_explicit(NULL, "cache")) { + if (config_explicit(NULL, "cache.minimum") && GV(CACHE) < GV(CACHE_MINIMUM)) testutil_die(EINVAL, "minimum cache set larger than cache (%" PRIu32 " > %" PRIu32 ")", - g.c_cache_minimum, g.c_cache); + GV(CACHE_MINIMUM), GV(CACHE)); return; } - g.c_cache = WT_MAX(g.c_cache, g.c_cache_minimum); + GV(CACHE) = GV(CACHE_MINIMUM); + + /* + * If it's an in-memory run, size the cache at 2x the maximum initial data set. This calculation + * is done in bytes, convert to megabytes before testing against the cache. + */ + if (GV(RUNS_IN_MEMORY)) { + cache = table_sumv(V_TABLE_BTREE_KEY_MAX) + table_sumv(V_TABLE_BTREE_VALUE_MAX); + cache *= table_sumv(V_TABLE_RUNS_ROWS); + cache *= 2; + cache /= WT_MEGABYTE; /* NOT in MB units, convert for cache test */ + if (GV(CACHE) < cache) + GV(CACHE) = (uint32_t)cache; + } + + /* Sum the number of workers. */ + workers = GV(RUNS_THREADS); + if (GV(OPS_HS_CURSOR)) + ++workers; + if (GV(OPS_RANDOM_CURSOR)) + ++workers; /* * Maximum internal/leaf page size sanity. @@ -442,33 +554,34 @@ config_cache(void) * cache with pinned pages, that is, every thread consuming an internal page and a leaf page (or * a pair of leaf pages for cursor movements). * - * Maximum memory pages are in units of MB. - * * This code is what dramatically increases the cache size when there are lots of threads, it * grows the cache to several megabytes per thread. */ - workers = g.c_threads; - if (g.c_hs_cursor) - ++workers; - if (g.c_random_cursor) - ++workers; - g.c_cache = WT_MAX(g.c_cache, 2 * workers * g.c_memory_page_max); + cache = table_sumv(V_TABLE_BTREE_MEMORY_PAGE_MAX); /* in MB units, no conversion to cache */ + cache *= workers; + cache *= 2; + if (GV(CACHE) < cache) + GV(CACHE) = (uint32_t)cache; /* - * Ensure cache size sanity for LSM runs. An LSM tree open requires 3 - * chunks plus a page for each participant in up to three concurrent - * merges. Integrate a thread count into that calculation by requiring - * 3 chunks/pages per configured thread. That might be overkill, but - * LSM runs are more sensitive to small caches than other runs, and a - * generous cache avoids stalls we're not interested in chasing. + * Ensure cache size sanity for LSM runs. An LSM tree open requires 3 chunks plus a page for + * each participant in up to three concurrent merges. Integrate a thread count into that + * calculation by requiring 3 chunks/pages per configured thread. That might be overkill, but + * LSM runs are more sensitive to small caches than other runs, and a generous cache avoids + * stalls we're not interested in chasing. */ - if (DATASOURCE("lsm")) { - required = WT_LSM_TREE_MINIMUM_SIZE( - g.c_chunk_size * WT_MEGABYTE, workers * g.c_merge_max, workers * g.leaf_page_max); - required = (required + (WT_MEGABYTE - 1)) / WT_MEGABYTE; - if (g.c_cache < required) - g.c_cache = required; + if (g.lsm_config) { + cache = WT_LSM_TREE_MINIMUM_SIZE(table_sumv(V_TABLE_LSM_CHUNK_SIZE) * WT_MEGABYTE, + workers * table_sumv(V_TABLE_LSM_MERGE_MAX), + workers * table_sumv(V_TABLE_BTREE_LEAF_PAGE_MAX) * WT_MEGABYTE); + cache = (cache + (WT_MEGABYTE - 1)) / WT_MEGABYTE; + if (GV(CACHE) < cache) + GV(CACHE) = (uint32_t)cache; } + + /* Give any block cache 20% of the total cache size, over and above the cache. */ + if (GV(BLOCK_CACHE) != 0) + GV(BLOCK_CACHE_SIZE) = (GV(CACHE) + 4) / 5; } /* @@ -479,19 +592,19 @@ static void config_checkpoint(void) { /* Choose a checkpoint mode if nothing was specified. */ - if (!config_is_perm("checkpoint")) + if (!config_explicit(NULL, "checkpoint")) switch (mmrand(NULL, 1, 20)) { case 1: case 2: case 3: case 4: /* 20% */ - config_single("checkpoint=wiredtiger", false); + config_single(NULL, "checkpoint=wiredtiger", false); break; case 5: /* 5 % */ - config_single("checkpoint=off", false); + config_single(NULL, "checkpoint=off", false); break; default: /* 75% */ - config_single("checkpoint=on", false); + config_single(NULL, "checkpoint=on", false); break; } } @@ -501,25 +614,25 @@ config_checkpoint(void) * Checksum configuration. */ static void -config_checksum(void) +config_checksum(TABLE *table) { /* Choose a checksum mode if nothing was specified. */ - if (!config_is_perm("disk.checksum")) + if (!config_explicit(table, "disk.checksum")) switch (mmrand(NULL, 1, 10)) { case 1: case 2: case 3: case 4: /* 40% */ - config_single("disk.checksum=on", false); + config_single(table, "disk.checksum=on", false); break; case 5: /* 10% */ - config_single("disk.checksum=off", false); + config_single(table, "disk.checksum=off", false); break; case 6: /* 10% */ - config_single("disk.checksum=uncompressed", false); + config_single(table, "disk.checksum=uncompressed", false); break; default: /* 40% */ - config_single("disk.checksum=unencrypted", false); + config_single(table, "disk.checksum=unencrypted", false); break; } } @@ -529,31 +642,26 @@ config_checksum(void) * Compression configuration. */ static void -config_compression(const char *conf_name) +config_compression(TABLE *table, const char *conf_name) { char confbuf[128]; const char *cstr; /* Return if already specified. */ - if (config_is_perm(conf_name)) + if (config_explicit(table, conf_name)) return; - /* - * Don't configure a compression engine for logging if logging isn't configured (it won't break, - * but it's confusing). - */ - cstr = "none"; - if (strcmp(conf_name, "logging.compression") == 0 && g.c_logging == 0) { - testutil_check(__wt_snprintf(confbuf, sizeof(confbuf), "%s=%s", conf_name, cstr)); - config_single(confbuf, false); + /* Ignore logging compression if we're not doing logging. */ + if (strcmp(conf_name, "logging.compression") == 0 && GV(LOGGING) == 0) { + config_single(NULL, "logging.compression=none", false); return; } /* - * Select a compression type from the list of built-in engines. - * - * Listed percentages are only correct if all of the possible engines are compiled in. + * Select a compression type from the list of built-in engines. Listed percentages are only + * correct if all of the possible engines are compiled in. */ + cstr = "none"; switch (mmrand(NULL, 1, 20)) { #ifdef HAVE_BUILTIN_EXTENSION_LZ4 case 1: @@ -566,9 +674,9 @@ config_compression(const char *conf_name) case 4: case 5: case 6: - case 7: /* 30% snappy */ + case 7: case 8: - case 9: + case 9: /* 30% snappy */ cstr = "snappy"; break; #endif @@ -596,11 +704,11 @@ config_compression(const char *conf_name) } testutil_check(__wt_snprintf(confbuf, sizeof(confbuf), "%s=%s", conf_name, cstr)); - config_single(confbuf, false); + config_single(table, confbuf, false); } /* - * config_directio + * config_directio -- * Direct I/O configuration. */ static void @@ -610,26 +718,24 @@ config_directio(void) * We don't roll the dice and set direct I/O, it has to be set explicitly. For that reason, any * incompatible "permanent" option set with direct I/O is a configuration error. */ - if (!g.c_direct_io) + if (!GV(DISK_DIRECT_IO)) return; /* * Direct I/O may not work with backups, doing copies through the buffer cache after configuring * direct I/O in Linux won't work. If direct I/O is configured, turn off backups. */ - if (g.c_backups) { - if (config_is_perm("backup")) + if (GV(BACKUP)) { + if (config_explicit(NULL, "backup")) testutil_die(EINVAL, "direct I/O is incompatible with backup configurations"); - config_single("backup=off", false); + config_single(NULL, "backup=off", false); } - /* - * Direct I/O may not work with imports for the same reason as for backups. - */ - if (g.c_import) { - if (config_is_perm("import")) + /* Direct I/O may not work with imports for the same reason as for backups. */ + if (GV(IMPORT)) { + if (config_explicit(NULL, "import")) testutil_die(EINVAL, "direct I/O is incompatible with import configurations"); - config_single("import=0", false); + config_single(NULL, "import=0", false); } /* @@ -637,10 +743,10 @@ config_directio(void) * the presence of shared cache configurations (including mmap), but we've seen file corruption * and it doesn't make much sense (the library disallows the combination). */ - if (g.c_mmap_all != 0) { - if (config_is_perm("disk.mmap_all")) + if (GV(DISK_MMAP_ALL) != 0) { + if (config_explicit(NULL, "disk.mmap_all")) testutil_die(EINVAL, "direct I/O is incompatible with mmap_all configurations"); - config_single("disk.mmap_all=off", false); + config_single(NULL, "disk.mmap_all=off", false); } /* @@ -649,10 +755,10 @@ config_directio(void) * format just hung, and the 15-minute timeout isn't effective. We could play games to handle * child process termination, but it's not worth the effort. */ - if (g.c_salvage) { - if (config_is_perm("ops.salvage")) + if (GV(OPS_SALVAGE)) { + if (config_explicit(NULL, "ops.salvage")) testutil_die(EINVAL, "direct I/O is incompatible with salvage configurations"); - config_single("ops.salvage=off", false); + config_single(NULL, "ops.salvage=off", false); } } @@ -668,7 +774,7 @@ config_encryption(void) /* * Encryption: choose something if encryption wasn't specified. */ - if (!config_is_perm("disk.encryption")) { + if (!config_explicit(NULL, "disk.encryption")) { cstr = "disk.encryption=none"; switch (mmrand(NULL, 1, 10)) { case 1: @@ -686,7 +792,7 @@ config_encryption(void) break; } - config_single(cstr, false); + config_single(NULL, cstr, false); } } @@ -695,12 +801,10 @@ config_encryption(void) * Fixed-length column-store configuration. */ static bool -config_fix(void) +config_fix(TABLE *table) { - /* Fixed-length column stores don't support the history store table, so no modify operations. */ - if (config_is_perm("ops.pct.modify")) - return (false); - return (true); + /* Fixed-length column stores don't support modify operations. */ + return (!config_explicit(table, "ops.pct.modify")); } /* @@ -716,29 +820,27 @@ config_in_memory(void) * don't have to configure in-memory every time we configure something like LSM, that's too * painful. */ - if (config_is_perm("backup")) + if (config_explicit(NULL, "backup")) return; - if (config_is_perm("btree.compression")) + if (config_explicit(NULL, "btree.compression")) return; - if (config_is_perm("checkpoint")) + if (config_explicit(NULL, "checkpoint")) return; - if (config_is_perm("format.abort")) + if (config_explicit(NULL, "format.abort")) return; - if (config_is_perm("import")) + if (config_explicit(NULL, "import")) return; - if (config_is_perm("logging")) + if (config_explicit(NULL, "logging")) return; - if (config_is_perm("ops.hs_cursor")) + if (config_explicit(NULL, "ops.hs_cursor")) return; - if (config_is_perm("ops.salvage")) + if (config_explicit(NULL, "ops.salvage")) return; - if (config_is_perm("ops.verify")) - return; - if (config_is_perm("runs.source") && DATASOURCE("lsm")) + if (config_explicit(NULL, "ops.verify")) return; - if (!config_is_perm("runs.in_memory") && mmrand(NULL, 1, 20) == 1) - g.c_in_memory = 1; + if (!config_explicit(NULL, "runs.in_memory") && mmrand(NULL, 1, 20) == 1) + config_single(NULL, "runs.in_memory=1", false); } /* @@ -748,54 +850,38 @@ config_in_memory(void) static void config_in_memory_reset(void) { - uint32_t cache; - /* Turn off a lot of stuff. */ - if (!config_is_perm("backup")) - config_single("backup=off", false); - if (!config_is_perm("btree.compression")) - config_single("btree.compression=none", false); - if (!config_is_perm("checkpoint")) - config_single("checkpoint=off", false); - if (!config_is_perm("import")) - config_single("import=off", false); - if (!config_is_perm("logging")) - config_single("logging=off", false); - if (!config_is_perm("ops.alter")) - config_single("ops.alter=off", false); - if (!config_is_perm("ops.hs_cursor")) - config_single("ops.hs_cursor=off", false); - if (!config_is_perm("ops.salvage")) - config_single("ops.salvage=off", false); - if (!config_is_perm("ops.verify")) - config_single("ops.verify=off", false); + if (!config_explicit(NULL, "backup")) + config_single(NULL, "backup=off", false); + if (!config_explicit(NULL, "btree.compression")) + config_single(NULL, "btree.compression=none", false); + if (!config_explicit(NULL, "checkpoint")) + config_single(NULL, "checkpoint=off", false); + if (!config_explicit(NULL, "import")) + config_single(NULL, "import=off", false); + if (!config_explicit(NULL, "logging")) + config_single(NULL, "logging=off", false); + if (!config_explicit(NULL, "ops.alter")) + config_single(NULL, "ops.alter=off", false); + if (!config_explicit(NULL, "ops.hs_cursor")) + config_single(NULL, "ops.hs_cursor=off", false); + if (!config_explicit(NULL, "ops.salvage")) + config_single(NULL, "ops.salvage=off", false); + if (!config_explicit(NULL, "ops.verify")) + config_single(NULL, "ops.verify=off", false); /* * Keep keys/values small, overflow items aren't an issue for in-memory configurations and it * keeps us from overflowing the cache. */ - if (!config_is_perm("btree.key_max")) - config_single("btree.key_max=32", false); - if (!config_is_perm("btree.value_max")) - config_single("btree.value_max=80", false); - - /* - * Size the cache relative to the initial data set, use 2x the base size as a minimum. - */ - if (!config_is_perm("cache")) { - cache = g.c_value_max; - if (g.type == ROW) - cache += g.c_key_max; - cache *= g.c_rows; - cache *= 2; - cache /= WT_MEGABYTE; - if (g.c_cache < cache) - g.c_cache = cache; - } + if (!config_explicit(NULL, "btree.key_max")) + config_single(NULL, "btree.key_max=32", false); + if (!config_explicit(NULL, "btree.value_max")) + config_single(NULL, "btree.value_max=80", false); } /* - * config_backup_incr_compatibility_check -- + * config_backup_incr_log_compatibility_check -- * Backup incremental log compatibility check. */ static void @@ -805,10 +891,10 @@ config_backup_incr_log_compatibility_check(void) * Incremental backup using log files is incompatible with logging archival. Disable logging * archival if log incremental backup is set. */ - if (g.c_logging_archive && config_is_perm("logging.archive")) + if (GV(LOGGING_ARCHIVE) && config_explicit(NULL, "logging.archive")) testutil_die(EINVAL, "backup.incremental=log is incompatible with logging.archive"); - if (g.c_logging_archive) - config_single("logging.archive=0", false); + if (GV(LOGGING_ARCHIVE)) + config_single(NULL, "logging.archive=0", false); } /* @@ -816,45 +902,39 @@ config_backup_incr_log_compatibility_check(void) * LSM configuration review. */ static void -config_lsm_reset(void) +config_lsm_reset(TABLE *table) { /* * Turn off truncate for LSM runs (some configurations with truncate always result in a * timeout). */ - if (!config_is_perm("ops.truncate")) - config_single("ops.truncate=off", false); + if (config_explicit(table, "ops.truncate")) { + if (DATASOURCE(table, "lsm")) + testutil_die(EINVAL, "LSM (currently) incompatible with truncate configurations"); + config_single(table, "ops.truncate=off", false); + } /* - * LSM doesn't currently play nicely with timestamps, don't choose the pair unless forced to. If - * we turn off timestamps, make sure we turn off prepare as well, it requires timestamps. Remove - * this code with WT-4162. + * Turn off prepare and timestamps for LSM runs (prepare requires timestamps). + * + * FIXME: WT-4162. */ - if (!config_is_perm("ops.prepare") && !config_is_perm("transaction.timestamps")) { - config_single("ops.prepare=off", false); - config_single("transaction.timestamps=off", false); - } + if (config_explicit(table, "ops.prepare")) + testutil_die(EINVAL, "LSM (currently) incompatible with prepare configurations"); + config_single(table, "ops.prepare=off", false); + if (config_explicit(table, "transaction.timestamps")) + testutil_die(EINVAL, "LSM (currently) incompatible with timestamp configurations"); + config_single(table, "transaction.timestamps=off", false); /* * LSM does not work with block-based incremental backup, change the incremental backup - * mechanism if block based in configured. + * mechanism if configured to be block based. */ - if (g.c_backups) { - if (config_is_perm("backup.incremental") && g.c_backup_incr_flag == INCREMENTAL_BLOCK) - testutil_die(EINVAL, "LSM does not work with backup.incremental=block configuration."); - - if (g.c_backup_incr_flag == INCREMENTAL_BLOCK) - switch (mmrand(NULL, 1, 2)) { - case 1: - /* 50% */ - config_single("backup.incremental=off", false); - break; - case 2: - /* 50% */ - config_single("backup.incremental=log", false); - config_backup_incr_log_compatibility_check(); - break; - } + if (GV(BACKUP)) { + if (config_explicit(table, "backup.incremental")) + testutil_die( + EINVAL, "LSM (currently) incompatible with incremental backup configurations"); + config_single(NULL, "backup.incremental=log", false); } } @@ -863,29 +943,39 @@ config_lsm_reset(void) * Configure operation percentages. */ static void -config_pct(void) +config_pct(TABLE *table) { - static struct { + struct { const char *name; /* Operation */ uint32_t *vp; /* Value store */ u_int order; /* Order of assignment */ - } list[] = { - {"ops.pct.delete", &g.c_delete_pct, 0}, - {"ops.pct.insert", &g.c_insert_pct, 0}, -#define CONFIG_MODIFY_ENTRY 2 - {"ops.pct.modify", &g.c_modify_pct, 0}, - {"ops.pct.read", &g.c_read_pct, 0}, - {"ops.pct.write", &g.c_write_pct, 0}, - }; + } list[5]; u_int i, max_order, max_slot, n, pct; +#define CONFIG_MODIFY_ENTRY 2 + list[0].name = "ops.pct.delete"; + list[0].vp = &TV(OPS_PCT_DELETE); + list[0].order = 0; + list[1].name = "ops.pct.insert"; + list[1].vp = &TV(OPS_PCT_INSERT); + list[1].order = 0; + list[2].name = "ops.pct.modify"; + list[2].vp = &TV(OPS_PCT_MODIFY); + list[2].order = 0; + list[3].name = "ops.pct.read"; + list[3].vp = &TV(OPS_PCT_READ); + list[3].order = 0; + list[4].name = "ops.pct.write"; + list[4].vp = &TV(OPS_PCT_WRITE); + list[4].order = 0; + /* * Walk the list of operations, checking for an illegal configuration and creating a random * order in the list. */ pct = 0; for (i = 0; i < WT_ELEMENTS(list); ++i) - if (config_is_perm(list[i].name)) + if (config_explicit(table, list[i].name)) pct += *list[i].vp; else list[i].order = mmrand(NULL, 1, 1000); @@ -893,8 +983,8 @@ config_pct(void) testutil_die(EINVAL, "operation percentages do not total to 100%%"); /* Cursor modify isn't possible for fixed-length column store. */ - if (g.type == FIX) { - if (config_is_perm("ops.pct.modify") && g.c_modify_pct != 0) + if (table->type == FIX) { + if (config_explicit(table, "ops.pct.modify") && TV(OPS_PCT_MODIFY) != 0) testutil_die(EINVAL, "WT_CURSOR.modify not supported by fixed-length column store"); list[CONFIG_MODIFY_ENTRY].order = 0; *list[CONFIG_MODIFY_ENTRY].vp = 0; @@ -927,21 +1017,9 @@ config_pct(void) pct -= *list[max_slot].vp; } - testutil_assert( - g.c_delete_pct + g.c_insert_pct + g.c_modify_pct + g.c_read_pct + g.c_write_pct == 100); -} - -/* - * config_prefix -- - * Prefix configuration. - */ -static void -config_prefix(void) -{ - /* Add prefix compression if prefixes are configured and no explicit choice was made. */ - if (g.c_prefix != 0 && g.c_prefix_compression == 0 && - !config_is_perm("btree.prefix_compression")) - config_single("btree.prefix_compression=on", false); + testutil_assert(TV(OPS_PCT_DELETE) + TV(OPS_PCT_INSERT) + TV(OPS_PCT_MODIFY) + + TV(OPS_PCT_READ) + TV(OPS_PCT_WRITE) == + 100); } /* @@ -952,21 +1030,21 @@ static void config_transaction(void) { /* Transaction prepare requires timestamps and is incompatible with logging. */ - if (g.c_prepare && config_is_perm("ops.prepare")) { - if (g.c_logging && config_is_perm("logging")) - testutil_die(EINVAL, "prepare is incompatible with logging"); - if (!g.c_txn_timestamps && config_is_perm("transaction.timestamps")) + if (GV(OPS_PREPARE) && config_explicit(NULL, "ops.prepare")) { + if (!GV(TRANSACTION_TIMESTAMPS) && config_explicit(NULL, "transaction.timestamps")) testutil_die(EINVAL, "prepare requires transaction timestamps"); + if (GV(LOGGING) && config_explicit(NULL, "logging")) + testutil_die(EINVAL, "prepare is incompatible with logging"); } /* Transaction timestamps are incompatible with implicit transactions. */ - if (g.c_txn_timestamps && config_is_perm("transaction.timestamps")) { - if (g.c_txn_implicit && config_is_perm("transaction.implicit")) + if (GV(TRANSACTION_TIMESTAMPS) && config_explicit(NULL, "transaction.timestamps")) { + if (GV(TRANSACTION_IMPLICIT) && config_explicit(NULL, "transaction.implicit")) testutil_die( EINVAL, "transaction.timestamps is incompatible with implicit transactions"); /* FIXME-WT-6431: temporarily disable salvage with timestamps. */ - if (g.c_salvage && config_is_perm("ops.salvage")) + if (GV(OPS_SALVAGE) && config_explicit(NULL, "ops.salvage")) testutil_die(EINVAL, "transaction.timestamps is incompatible with salvage"); } @@ -978,24 +1056,28 @@ config_transaction(void) * time we check logging, logging must have been required by the run if both logging and prepare * are still set, so we can just turn off prepare in that case). */ - if (g.c_prepare) { - if (!config_is_perm("logging")) - config_single("logging=off", false); - if (!config_is_perm("transaction.timestamps")) - config_single("transaction.timestamps=on", false); + if (GV(OPS_PREPARE)) { + if (!config_explicit(NULL, "logging")) + config_single(NULL, "logging=off", false); + if (!config_explicit(NULL, "transaction.timestamps")) + config_single(NULL, "transaction.timestamps=on", false); } - if (g.c_txn_timestamps) { - if (!config_is_perm("transaction.implicit")) - config_single("transaction.implicit=0", false); - if (!config_is_perm("ops.salvage")) - config_single("ops.salvage=off", false); + if (GV(TRANSACTION_TIMESTAMPS)) { + if (!config_explicit(NULL, "transaction.implicit")) + config_single(NULL, "transaction.implicit=0", false); + if (!config_explicit(NULL, "ops.salvage")) + config_single(NULL, "ops.salvage=off", false); } - if (g.c_logging) - config_single("ops.prepare=off", false); - if (g.c_txn_implicit) - config_single("transaction.timestamps=off", false); - if (g.c_salvage) - config_single("transaction.timestamps=off", false); + if (GV(LOGGING)) + config_single(NULL, "ops.prepare=off", false); + if (GV(TRANSACTION_IMPLICIT)) + config_single(NULL, "transaction.timestamps=off", false); + if (GV(OPS_SALVAGE)) + config_single(NULL, "transaction.timestamps=off", false); + + /* Transaction timestamps configures format behavior, flag it. */ + if (GV(TRANSACTION_TIMESTAMPS)) + g.transaction_timestamps_config = true; } /* @@ -1023,13 +1105,67 @@ config_error(void) fprintf(stderr, "\n"); fprintf(stderr, "=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=\n"); fprintf(stderr, "Configuration names:\n"); - for (max_name = 0, cp = c; cp->name != NULL; ++cp) + for (max_name = 0, cp = configuration_list; cp->name != NULL; ++cp) max_name = WT_MAX(max_name, strlen(cp->name)); - for (cp = c; cp->name != NULL; ++cp) + for (cp = configuration_list; cp->name != NULL; ++cp) fprintf(stderr, "%*s: %s\n", (int)max_name, cp->name, cp->desc); } /* + * config_print_one -- + * Print out a single configuration setting. + */ +static void +config_print_one(FILE *fp, CONFIG *cp, CONFIGV *v, const char *prefix) +{ + if (F_ISSET(cp, C_STRING)) + fprintf(fp, "%s%s=%s\n", prefix, cp->name, v->vstr == NULL ? "" : v->vstr); + else + fprintf(fp, "%s%s=%" PRIu32 "\n", prefix, cp->name, v->v); +} + +/* + * config_print_table -- + * Print per-table information. + */ +static void +config_print_table(FILE *fp, TABLE *table) +{ + CONFIG *cp; + CONFIGV *v, *gv; + char buf[128]; + bool lsm; + + testutil_check(__wt_snprintf(buf, sizeof(buf), "table%u.", table->id)); + fprintf(fp, "############################################\n"); + fprintf(fp, "# TABLE PARAMETERS: table %u\n", table->id); + fprintf(fp, "############################################\n"); + + lsm = DATASOURCE(table, "lsm"); + for (cp = configuration_list; cp->name != NULL; ++cp) { + /* Skip global items. */ + if (!F_ISSET(cp, C_TABLE)) + continue; + /* Skip mismatched objects and configurations. */ + if (!lsm && F_ISSET(cp, C_TYPE_LSM)) + continue; + if (!C_TYPE_MATCH(cp, table->type)) + continue; + + gv = &tables[0]->v[cp->off]; + v = &table->v[cp->off]; + + /* Skip entries that match any global setting. */ + if (gv->set && v->v == gv->v && + ((v->vstr == NULL && gv->vstr == NULL) || + (v->vstr != NULL && gv->vstr != NULL && strcmp(v->vstr, gv->vstr) == 0))) + continue; + + config_print_one(fp, cp, v, buf); + } +} + +/* * config_print -- * Print configuration information. */ @@ -1037,7 +1173,9 @@ void config_print(bool error_display) { CONFIG *cp; + CONFIGV *gv; FILE *fp; + uint32_t i; /* Reopening an existing database should leave the existing CONFIG file. */ if (g.reopen) @@ -1049,17 +1187,31 @@ config_print(bool error_display) testutil_die(errno, "fopen: %s", g.home_config); fprintf(fp, "############################################\n"); - fprintf(fp, "# RUN PARAMETERS: V2\n"); + fprintf(fp, "# RUN PARAMETERS: V3\n"); fprintf(fp, "############################################\n"); - /* Display configuration values. */ - for (cp = c; cp->name != NULL; ++cp) - if (F_ISSET(cp, C_STRING)) - fprintf(fp, "%s=%s\n", cp->name, *cp->vstr == NULL ? "" : *cp->vstr); - else - fprintf(fp, "%s=%" PRIu32 "\n", cp->name, *cp->v); + /* Display global configuration values. */ + for (cp = configuration_list; cp->name != NULL; ++cp) { + /* Skip mismatched objects and configurations. */ + if (!g.lsm_config && F_ISSET(cp, C_TYPE_LSM)) + continue; + /* Skip table count if tables not configured (implying an old-style CONFIG file). */ + if (ntables == 0 && cp->off == V_GLOBAL_RUNS_TABLES) + continue; - fprintf(fp, "############################################\n"); + /* + * Otherwise, print if we never configured any tables, if the global item was explicitly + * configured, or this isn't a table option. + */ + gv = &tables[0]->v[cp->off]; + if (ntables == 0 || gv->set || !F_ISSET(cp, C_TABLE)) + config_print_one(fp, cp, gv, ""); + } + + /* Display per-table configuration values. */ + if (ntables != 0) + for (i = 1; i <= ntables; ++i) + config_print_table(fp, tables[i]); /* Flush so we're up-to-date on error. */ (void)fflush(fp); @@ -1078,6 +1230,16 @@ config_file(const char *name) FILE *fp; char buf[256], *p, *t; + /* + * Turn off multi-table configuration for all configuration files, for backward compatibility. + * This doesn't stop multiple table configurations, using either "runs.tables" or an explicit + * mention of a table, it only prevents CONFIG files without a table reference from configuring + * tables. This should only affect putting some non-table-specific configurations into a file + * and running that file as a CONFIG, expecting a multi-table test, and means old-style CONFIG + * files don't suddenly turn into multiple table tests. + */ + g.multi_table_config = false; + if ((fp = fopen(name, "r")) == NULL) testutil_die(errno, "fopen: %s", name); @@ -1092,7 +1254,7 @@ config_file(const char *name) *p = '\0'; break; } - if (*p == '#') { /* Comment, skip the line */ + if (*p == '#') { /* Comment */ t = p; break; } @@ -1104,7 +1266,7 @@ config_file(const char *name) } if (*t == '\0' || *t == '#') continue; - config_single(t, true); + config_single(NULL, t, true); } fclose_and_clear(&fp); } @@ -1116,55 +1278,35 @@ config_file(const char *name) void config_clear(void) { - CONFIG *cp; + u_int i, j, slots; - /* Clear all allocated configuration data. */ - for (cp = c; cp->name != NULL; ++cp) - if (cp->vstr != NULL) { - free((void *)*cp->vstr); - *cp->vstr = NULL; - } - free(g.uri); - g.uri = NULL; -} - -/* - * config_reset -- - * Clear per-run configuration values. - */ -static void -config_reset(void) -{ - CONFIG *cp; + /* Clear all allocated configuration data in the tables array. */ + slots = ntables == 0 ? 1 : ntables; + for (i = 0; i < slots; ++i) { + free(tables[i]->val_base); - /* Clear temporary allocated configuration data. */ - for (cp = c; cp->name != NULL; ++cp) { - F_CLR(cp, C_TEMP); - if (!F_ISSET(cp, C_PERM) && cp->vstr != NULL) { - free((void *)*cp->vstr); - *cp->vstr = NULL; - } + for (j = 0; j < V_ELEMENT_COUNT; ++j) + free(tables[i]->v[j].vstr); + free(tables[i]); } - free(g.uri); - g.uri = NULL; } /* - * config_find - * Find a specific configuration entry. + * config_find -- + * Find a specific configuration entry. */ static CONFIG * config_find(const char *s, size_t len, bool fatal) { CONFIG *cp; - for (cp = c; cp->name != NULL; ++cp) + for (cp = configuration_list; cp->name != NULL; ++cp) if (strncmp(s, cp->name, len) == 0 && cp->name[len] == '\0') return (cp); /* Optionally ignore unknown keywords, it makes it easier to run old CONFIG files. */ if (fatal) - testutil_die(EINVAL, "%s: %s: unknown required configuration keyword\n", progname, s); + testutil_die(EINVAL, "%s: %s: unknown required configuration keyword", progname, s); fprintf(stderr, "%s: %s: WARNING, ignoring unknown configuration keyword\n", progname, s); return (NULL); @@ -1190,70 +1332,109 @@ config_value(const char *config, const char *p, int match) } /* + * config_table_extend -- + * Extend the tables array as necessary. + */ +static void +config_table_extend(u_int ntable) +{ + u_int i; + + if (ntable <= ntables) + return; + + /* + * Allocate any new tables structures. (We do it this way, rather than reallocating the whole + * tables array, because our caller doesn't know we're extending the list of tables, and is + * likely holding pointers into the current list of tables. Reallocating the whole array would + * require handling reallocation in our caller, and it's not worth the effort.) + * + * This might be the first extension, reset the base table's ID (for debugging, we should never + * be using a table with ID 0). + */ + for (i = 0; i <= ntable; ++i) { + if (tables[i] == NULL) + tables[i] = dcalloc(1, sizeof(TABLE)); + tables[i]->id = i; + } + ntables = ntable; +} + +/* * config_single -- * Set a single configuration structure value. */ void -config_single(const char *s, bool perm) +config_single(TABLE *table, const char *s, bool explicit) { enum { RANGE_FIXED, RANGE_NONE, RANGE_WEIGHTED } range; CONFIG *cp; + CONFIGV *v; uint32_t steps, v1, v2; + u_long ntable; u_int i; const char *equalp, *vp1, *vp2; + char *endptr; while (__wt_isspace((u_char)*s)) ++s; + /* + * If configuring a single table, the table argument will be non-NULL. The configuration itself + * may include a table reference, in which case we extend the table as necessary and select the + * table. + */ + if (table == NULL) { + table = tables[0]; + if (strncmp(s, "table", strlen("table")) == 0) { + errno = 0; + ntable = strtoul(s + strlen("table"), &endptr, 10); + testutil_assert(errno == 0 && endptr[0] == '.'); + config_table_extend((uint32_t)ntable); + table = tables[ntable]; + + s = endptr + 1; + } + } + + /* Process backward compatibility configuration. */ config_compat(&s); if ((equalp = strchr(s, '=')) == NULL) - testutil_die(EINVAL, "%s: %s: illegal configuration value\n", progname, s); + testutil_die(EINVAL, "%s: %s: configuration missing \'=\' character", progname, s); + /* Find the configuration value, and assert it's not a table/global mismatch. */ if ((cp = config_find(s, (size_t)(equalp - s), false)) == NULL) return; + testutil_assert(F_ISSET(cp, C_TABLE) || table == tables[0]); - F_SET(cp, perm ? C_PERM : C_TEMP); ++equalp; + v = &table->v[cp->off]; if (F_ISSET(cp, C_STRING)) { - /* - * Free the previous setting if a configuration has been passed in twice. - */ - if (*cp->vstr != NULL) { - free(*cp->vstr); - *cp->vstr = NULL; - } - - if (strncmp(s, "backup.incremental", strlen("backup.incremental")) == 0) { - config_map_backup_incr(equalp, &g.c_backup_incr_flag); - *cp->vstr = dstrdup(equalp); - } else if (strncmp(s, "checkpoint", strlen("checkpoint")) == 0) { - config_map_checkpoint(equalp, &g.c_checkpoint_flag); - *cp->vstr = dstrdup(equalp); - } else if (strncmp(s, "disk.checksum", strlen("disk.checksum")) == 0) { - config_map_checksum(equalp, &g.c_checksum_flag); - *cp->vstr = dstrdup(equalp); - } else if (strncmp(s, "btree.compression", strlen("btree.compression")) == 0) { - config_map_compression(equalp, &g.c_compression_flag); - *cp->vstr = dstrdup(equalp); - } else if (strncmp(s, "runs.source", strlen("runs.source")) == 0 && + if (strncmp(s, "backup.incremental", strlen("backup.incremental")) == 0) + config_map_backup_incr(equalp, &g.backup_incr_flag); + else if (strncmp(s, "checkpoint", strlen("checkpoint")) == 0) + config_map_checkpoint(equalp, &g.checkpoint_config); + else if (strncmp(s, "runs.source", strlen("runs.source")) == 0 && strncmp("file", equalp, strlen("file")) != 0 && strncmp("lsm", equalp, strlen("lsm")) != 0 && strncmp("table", equalp, strlen("table")) != 0) { - testutil_die(EINVAL, "Invalid data source option: %s\n", equalp); - } else if (strncmp(s, "disk.encryption", strlen("disk.encryption")) == 0) { - config_map_encryption(equalp, &g.c_encryption_flag); - *cp->vstr = dstrdup(equalp); + testutil_die(EINVAL, "Invalid data source option: %s", equalp); } else if (strncmp(s, "runs.type", strlen("runs.type")) == 0) { - config_map_file_type(equalp, &g.type); - *cp->vstr = dstrdup(config_file_type(g.type)); - } else if (strncmp(s, "logging.compression", strlen("logging.compression")) == 0) { - config_map_compression(equalp, &g.c_logging_compression_flag); - *cp->vstr = dstrdup(equalp); - } else - *cp->vstr = dstrdup(equalp); + /* Save any global configuration for later table configuration. */ + if (table == tables[0]) + testutil_check(__wt_snprintf(g.runs_type, sizeof(g.runs_type), "%s", equalp)); + + config_map_file_type(equalp, &table->type); + equalp = config_file_type(table->type); + } + /* Free the previous setting if a configuration has been passed in twice. */ + free(v->vstr); + + v->vstr = dstrdup(equalp); + v->set = explicit; return; } @@ -1268,7 +1449,8 @@ config_single(const char *s, bool perm) testutil_die(EINVAL, "%s: %s: value of boolean not 0 or 1", progname, s); } - *cp->v = v1; + v->v = v1; + v->set = explicit; return; } @@ -1288,18 +1470,21 @@ config_single(const char *s, bool perm) } v1 = config_value(s, vp1, range == RANGE_NONE ? '\0' : (range == RANGE_FIXED ? '-' : ':')); - if (v1 < cp->min || v1 > cp->maxset) - testutil_die(EINVAL, "%s: %s: value outside min/max values of %" PRIu32 "-%" PRIu32 "\n", + /* Zero may be an out-of-band "don't set this variable" value. */ + if (v1 == 0 && F_ISSET(cp, C_ZERO_NOTSET)) + return; + if (v1 < cp->min || v1 > cp->maxset) { + testutil_die(EINVAL, "%s: %s: value outside min/max values of %" PRIu32 "-%" PRIu32, progname, s, cp->min, cp->maxset); + } if (range != RANGE_NONE) { v2 = config_value(s, vp2, '\0'); if (v2 < cp->min || v2 > cp->maxset) - testutil_die(EINVAL, - "%s: %s: value outside min/max values of %" PRIu32 "-%" PRIu32 "\n", progname, s, - cp->min, cp->maxset); + testutil_die(EINVAL, "%s: %s: value outside min/max values of %" PRIu32 "-%" PRIu32, + progname, s, cp->min, cp->maxset); if (v1 > v2) - testutil_die(EINVAL, "%s: %s: illegal numeric range\n", progname, s); + testutil_die(EINVAL, "%s: %s: illegal numeric range", progname, s); if (range == RANGE_FIXED) v1 = mmrand(NULL, (u_int)v1, (u_int)v2); @@ -1318,7 +1503,11 @@ config_single(const char *s, bool perm) } } - *cp->v = v1; + v->v = v1; + v->set = explicit; + + if (strncmp(s, "runs.tables", strlen("runs.tables")) == 0) + config_table_extend((uint32_t)v1); } /* @@ -1418,77 +1607,38 @@ config_map_checkpoint(const char *s, u_int *vp) } /* - * config_map_checksum -- - * Map a checksum configuration to a flag. - */ -static void -config_map_checksum(const char *s, u_int *vp) -{ - if (strcmp(s, "on") == 0) - *vp = CHECKSUM_ON; - else if (strcmp(s, "off") == 0) - *vp = CHECKSUM_OFF; - else if (strcmp(s, "uncompressed") == 0) - *vp = CHECKSUM_UNCOMPRESSED; - else if (strcmp(s, "unencrypted") == 0) - *vp = CHECKSUM_UNENCRYPTED; - else - testutil_die(EINVAL, "illegal checksum configuration: %s", s); -} - -/* - * config_map_compression -- - * Map a compression configuration to a flag. - */ -static void -config_map_compression(const char *s, u_int *vp) -{ - if (strcmp(s, "none") == 0) - *vp = COMPRESS_NONE; - else if (strcmp(s, "lz4") == 0) - *vp = COMPRESS_LZ4; - else if (strcmp(s, "lz4-noraw") == 0) /* CONFIG compatibility */ - *vp = COMPRESS_LZ4; - else if (strcmp(s, "snappy") == 0) - *vp = COMPRESS_SNAPPY; - else if (strcmp(s, "zlib") == 0) - *vp = COMPRESS_ZLIB; - else if (strcmp(s, "zlib-noraw") == 0) /* CONFIG compatibility */ - *vp = COMPRESS_ZLIB; - else if (strcmp(s, "zstd") == 0) - *vp = COMPRESS_ZSTD; - else - testutil_die(EINVAL, "illegal compression configuration: %s", s); -} - -/* - * config_map_encryption -- - * Map a encryption configuration to a flag. - */ -static void -config_map_encryption(const char *s, u_int *vp) -{ - if (strcmp(s, "none") == 0) - *vp = ENCRYPT_NONE; - else if (strcmp(s, "rotn-7") == 0) - *vp = ENCRYPT_ROTN_7; - else if (strcmp(s, "sodium") == 0) - *vp = ENCRYPT_SODIUM; - else - testutil_die(EINVAL, "illegal encryption configuration: %s", s); -} - -/* - * config_is_perm - * Return if a specific configuration entry was permanently set. + * config_explicit -- + * Return if a configuration entry is explicitly set (as opposed to being randomly set). */ -static int -config_is_perm(const char *s) +static bool +config_explicit(TABLE *table, const char *s) { CONFIG *cp; + u_int i; + /* Look up the configuration option. */ cp = config_find(s, strlen(s), true); - return (F_ISSET(cp, C_PERM) ? 1 : 0); + + /* + * If it's a global option, assert our caller didn't ask for a table value, and return if it's + * set in the base values. + */ + if (!F_ISSET(cp, C_TABLE)) { + testutil_assert(table == NULL); + return (tables[0]->v[cp->off].set); + } + + /* If checking a single table, the table argument is non-NULL. */ + if (table != NULL) + return (table->v[cp->off].set); + + /* Otherwise, check if it's set in any table. */ + if (ntables == 0) + return (tables[0]->v[cp->off].set); + for (i = 1; i < ntables; ++i) + if (tables[i]->v[cp->off].set) + return (true); + return (false); } /* diff --git a/src/third_party/wiredtiger/test/format/config.h b/src/third_party/wiredtiger/test/format/config.h index d12c30ed756..471fccd1365 100644 --- a/src/third_party/wiredtiger/test/format/config.h +++ b/src/third_party/wiredtiger/test/format/config.h @@ -1,373 +1,137 @@ -/*- - * Public Domain 2014-present 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. - */ +/* DO NOT EDIT: automatically built by format/config.sh. */ + +#define C_TYPE_MATCH(cp, type) \ + (!F_ISSET(cp, (C_TYPE_FIX | C_TYPE_ROW | C_TYPE_VAR)) || \ + ((type) == FIX && F_ISSET(cp, C_TYPE_FIX)) || ((type) == ROW && F_ISSET(cp, C_TYPE_ROW)) || \ + ((type) == VAR && F_ISSET(cp, C_TYPE_VAR))) -/* - * Configuration for the wts program is an array of string-based parameters. This is the structure - * used to declare them. - */ typedef struct { const char *name; /* Configuration item */ const char *desc; /* Configuration description */ -/* Value is a boolean, yes if roll of 1-to-100 is <= CONFIG->min. */ -#define C_BOOL 0x01u - -/* Not a simple randomization, handle outside the main loop. */ -#define C_IGNORE 0x02u - -/* Value was set from command-line or file, ignore for all runs. */ -#define C_PERM 0x04u - -/* Value isn't random for this run, ignore just for this run. */ -#define C_TEMP 0x08u - -/* Value is a string. */ -#define C_STRING 0x20u - u_int flags; +#define C_BOOL 0x001u /* Boolean (true if roll of 1-to-100 is <= CONFIG->min) */ +#define C_IGNORE 0x002u /* Not a simple randomization, configured specially */ +#define C_STRING 0x004u /* String (rather than integral) */ +#define C_TABLE 0x008u /* Value is per table, not global */ +#define C_TYPE_FIX 0x010u /* Value is only relevant to FLCS */ +#define C_TYPE_LSM 0x020u /* Value is only relevant to LSM */ +#define C_TYPE_ROW 0x040u /* Value is only relevant to RS */ +#define C_TYPE_VAR 0x080u /* Value is only relevant to VLCS */ +#define C_ZERO_NOTSET 0x100u /* Ignore zero values */ + uint32_t flags; uint32_t min; /* Minimum value */ uint32_t maxrand; /* Maximum value randomly chosen */ uint32_t maxset; /* Maximum value explicitly set */ - uint32_t *v; /* Value for this run */ - char **vstr; /* Value for string options */ -} CONFIG; - -#define COMPRESSION_LIST " (none | lz4 | snappy | zlib | zstd)" - -static CONFIG c[] = { - /* 2% */ - {"assert.read_timestamp", "assert read_timestamp", C_BOOL, 2, 0, 0, &g.c_assert_read_timestamp, - NULL}, - - /* 2% */ - {"assert.write_timestamp", "set write_timestamp_usage and assert write_timestamp", C_BOOL, 2, 0, - 0, &g.c_assert_write_timestamp, NULL}, - - /* 20% */ - {"backup", "configure backups", C_BOOL, 20, 0, 0, &g.c_backups, NULL}, - - {"backup.incremental", "backup type (block | log | off)", C_IGNORE | C_STRING, 0, 0, 0, NULL, - &g.c_backup_incremental}, - - {"backup.incr_granularity", "incremental backup block granularity (KB)", 0x0, 4, 16384, 16384, - &g.c_backup_incr_granularity, NULL}, - - {"btree.bitcnt", "fixed-length column-store object size (number of bits)", 0x0, 1, 8, 8, - &g.c_bitcnt, NULL}, - - {"btree.compression", "compression type" COMPRESSION_LIST, C_IGNORE | C_STRING, 0, 0, 0, NULL, - &g.c_compression}, - - /* 20% */ - {"btree.dictionary", "configure dictionary compressed values", C_BOOL, 20, 0, 0, &g.c_dictionary, - NULL}, - - /* 20% */ - {"btree.huffman_value", "configure huffman encoded values", C_BOOL, 20, 0, 0, &g.c_huffman_value, - NULL}, - - /* 95% */ - {"btree.internal_key_truncation", "truncate internal keys", C_BOOL, 95, 0, 0, - &g.c_internal_key_truncation, NULL}, - - {"btree.internal_page_max", "btree internal node maximum size", 0x0, 9, 17, 27, - &g.c_intl_page_max, NULL}, - - {"btree.key_max", "maximum key size", 0x0, 20, 128, MEGABYTE(10), &g.c_key_max, NULL}, - - /* - * A minimum key size of 11 is necessary. Row-store keys have a leading 10-digit number and the - * 11 guarantees we never see a key that we can't convert to a numeric value without formatting - * it first because there's a trailing non-digit character in every key. - */ - {"btree.key_min", "minimum key size", 0x0, 11, 32, 256, &g.c_key_min, NULL}, - - {"btree.leaf_page_max", "btree leaf node maximum size", 0x0, 9, 17, 27, &g.c_leaf_page_max, NULL}, - - {"btree.memory_page_max", "maximum cache page size", 0x0, 1, 10, 128, &g.c_memory_page_max, NULL}, - - {"btree.prefix", "common key prefix", C_BOOL, 3, 0, 0, &g.c_prefix, NULL}, - - /* 80% */ - {"btree.prefix_compression", "configure prefix compressed keys", C_BOOL, 80, 0, 0, - &g.c_prefix_compression, NULL}, - - {"btree.prefix_compression_min", "minimum gain before prefix compression is used (bytes)", 0x0, 0, - 8, 256, &g.c_prefix_compression_min, NULL}, - - {"btree.repeat_data_pct", "duplicate values (percentage)", 0x0, 0, 90, 90, &g.c_repeat_data_pct, - NULL}, - - /* 10% */ - {"btree.reverse", "reverse order collation", C_BOOL, 10, 0, 0, &g.c_reverse, NULL}, - - {"btree.split_pct", "page split size as a percentage of the maximum page size", 0x0, 50, 100, 100, - &g.c_split_pct, NULL}, - - {"btree.value_max", "maximum value size", 0x0, 32, 4096, MEGABYTE(10), &g.c_value_max, NULL}, - - {"btree.value_min", "minimum value size", 0x0, 0, 20, 4096, &g.c_value_min, NULL}, - - {"cache", "cache size (MB)", 0x0, 1, 100, 100 * 1024, &g.c_cache, NULL}, - - {"cache.evict_max", "maximum number of eviction workers", 0x0, 0, 5, 100, &g.c_evict_max, NULL}, - - {"cache.minimum", "minimum cache size (MB)", C_IGNORE, 0, 0, 100 * 1024, &g.c_cache_minimum, - NULL}, - - {"checkpoint", "checkpoint type (on | off | wiredtiger)", C_IGNORE | C_STRING, 0, 0, 0, NULL, - &g.c_checkpoint}, - - {"checkpoint.log_size", "MB of log to wait if wiredtiger checkpoints configured", 0x0, 20, 200, - 1024, &g.c_checkpoint_log_size, NULL}, - - {"checkpoint.wait", "seconds to wait if wiredtiger checkpoints configured", 0x0, 5, 100, 3600, - &g.c_checkpoint_wait, NULL}, - - {"disk.checksum", "checksum type (on | off | uncompressed | unencrypted)", C_IGNORE | C_STRING, 0, - 0, 0, NULL, &g.c_checksum}, - - /* 5% */ - {"disk.data_extend", "configure data file extension", C_BOOL, 5, 0, 0, &g.c_data_extend, NULL}, - - /* 0% */ - {"disk.direct_io", "configure direct I/O for data objects", C_IGNORE | C_BOOL, 0, 0, 1, - &g.c_direct_io, NULL}, - - {"disk.encryption", "encryption type (none | rotn-7)", C_IGNORE | C_STRING, 0, 0, 0, NULL, - &g.c_encryption}, - - /* 10% */ - {"disk.firstfit", "configure first-fit allocation", C_BOOL, 10, 0, 0, &g.c_firstfit, NULL}, - - /* 90% */ - {"disk.mmap", "configure mmap operations (reads only)", C_BOOL, 90, 0, 0, &g.c_mmap, NULL}, - - /* 5% */ - {"disk.mmap_all", "configure mmap operations (read and write)", C_BOOL, 5, 0, 0, &g.c_mmap_all, - NULL}, - - /* 0% */ - {"format.abort", "drop core during timed run", C_BOOL, 0, 0, 0, &g.c_abort, NULL}, - - /* 75% */ - {"format.independent_thread_rng", "configure independent thread RNG space", C_BOOL, 75, 0, 0, - &g.c_independent_thread_rng, NULL}, - - {"format.major_timeout", "long-running operations timeout (minutes)", C_IGNORE, 0, 0, 1000, - &g.c_major_timeout, NULL}, - - /* - * 0% - * FIXME-WT-7418 and FIXME-WT-7510: Temporarily disable import until WT_ROLLBACK error and - * wt_copy_and_sync error is fixed. It should be (C_BOOL, 20, 0, 0). - */ - {"import", "import table from newly created database", C_BOOL, 0, 0, 0, &g.c_import, NULL}, - - /* 50% */ - {"logging", "configure logging", C_BOOL, 50, 0, 0, &g.c_logging, NULL}, - - /* 50% */ - {"logging.archive", "configure log file archival", C_BOOL, 50, 0, 0, &g.c_logging_archive, NULL}, - - {"logging.compression", "logging compression type" COMPRESSION_LIST, C_IGNORE | C_STRING, 0, 0, 0, - NULL, &g.c_logging_compression}, - - {"logging.file_max", "maximum log file size (KB)", 0x0, 100, 512000, 2097152, - &g.c_logging_file_max, NULL}, - - /* 50% */ - {"logging.prealloc", "configure log file pre-allocation", C_BOOL, 50, 0, 0, &g.c_logging_prealloc, - NULL}, - - /* 90% */ - {"lsm.auto_throttle", "throttle LSM inserts", C_BOOL, 90, 0, 0, &g.c_auto_throttle, NULL}, - - /* 95% */ - {"lsm.bloom", "configure bloom filters", C_BOOL, 95, 0, 0, &g.c_bloom, NULL}, - {"lsm.bloom_bit_count", "number of bits per item for bloom filters", 0x0, 4, 64, 1000, - &g.c_bloom_bit_count, NULL}, - - {"lsm.bloom_hash_count", "number of hash values per item for bloom filters", 0x0, 4, 32, 100, - &g.c_bloom_hash_count, NULL}, - - /* 10% */ - {"lsm.bloom_oldest", "configure bloom_oldest=true", C_BOOL, 10, 0, 0, &g.c_bloom_oldest, NULL}, - - {"lsm.chunk_size", "LSM chunk size (MB)", 0x0, 1, 10, 100, &g.c_chunk_size, NULL}, - - {"lsm.merge_max", "maximum number of chunks to include in an LSM merge operation", 0x0, 4, 20, - 100, &g.c_merge_max, NULL}, - - {"lsm.worker_threads", "number of LSM worker threads", 0x0, 3, 4, 20, &g.c_lsm_worker_threads, - NULL}, - - /* 10% */ - {"ops.alter", "configure table alterations", C_BOOL, 10, 0, 0, &g.c_alter, NULL}, - - /* 10% */ - {"ops.compaction", "configure compaction", C_BOOL, 10, 0, 0, &g.c_compact, NULL}, - - /* 50% */ - {"ops.hs_cursor", "configure history store cursor reads", C_BOOL, 50, 0, 0, &g.c_hs_cursor, NULL}, - - {"ops.pct.delete", "delete operations (percentage)", C_IGNORE, 0, 0, 100, &g.c_delete_pct, NULL}, - - {"ops.pct.insert", "insert operations (percentage)", C_IGNORE, 0, 0, 100, &g.c_insert_pct, NULL}, - - {"ops.pct.modify", "modify operations (percentage)", C_IGNORE, 0, 0, 100, &g.c_modify_pct, NULL}, - - {"ops.pct.read", "read operations (percentage)", C_IGNORE, 0, 0, 100, &g.c_read_pct, NULL}, - - {"ops.pct.write", "update operations (percentage)", C_IGNORE, 0, 0, 100, &g.c_write_pct, NULL}, - - /* 5% */ - {"ops.prepare", "configure transaction prepare", C_BOOL, 5, 0, 0, &g.c_prepare, NULL}, - - /* 10% */ - {"ops.random_cursor", "configure random cursor reads", C_BOOL, 10, 0, 0, &g.c_random_cursor, - NULL}, - - /* 100% */ - {"ops.salvage", "configure salvage", C_BOOL, 100, 1, 0, &g.c_salvage, NULL}, - - /* 100% */ - {"ops.truncate", "configure truncation", C_BOOL, 100, 0, 0, &g.c_truncate, NULL}, - - /* 100% */ - {"ops.verify", "configure verify", C_BOOL, 100, 1, 0, &g.c_verify, NULL}, - - {"quiet", "quiet run (same as -q)", C_IGNORE | C_BOOL, 0, 0, 1, &g.c_quiet, NULL}, - - {"runs", "number of runs", C_IGNORE, 0, 0, UINT_MAX, &g.c_runs, NULL}, - - {"runs.in_memory", "configure in-memory", C_IGNORE | C_BOOL, 0, 0, 1, &g.c_in_memory, NULL}, - - {"runs.ops", "operations per run", 0x0, 0, M(2), M(100), &g.c_ops, NULL}, - - {"runs.rows", "number of rows", 0x0, 10, M(1), M(100), &g.c_rows, NULL}, - - {"runs.source", "data source type (file | lsm | table)", C_IGNORE | C_STRING, 0, 0, 0, NULL, - &g.c_data_source}, - - {"runs.threads", "number of worker threads", 0x0, 1, 32, 128, &g.c_threads, NULL}, - - {"runs.timer", "run time (minutes)", C_IGNORE, 0, 0, UINT_MAX, &g.c_timer, NULL}, - - {"runs.type", "object type (fix | row | var)", C_IGNORE | C_STRING, 0, 0, 0, NULL, - &g.c_file_type}, - - {"runs.verify_failure_dump", "configure page dump on repeatable read error", C_IGNORE | C_BOOL, 0, - 0, 1, &g.c_verify_failure_dump, NULL}, - - /* 20% */ - {"statistics", "configure statistics", C_BOOL, 20, 0, 0, &g.c_statistics, NULL}, - - /* 5% */ - {"statistics.server", "configure statistics server thread", C_BOOL, 5, 0, 0, - &g.c_statistics_server, NULL}, - - /* 2% */ - {"stress.aggressive_sweep", "stress aggressive sweep", C_BOOL, 2, 0, 0, - &g.c_timing_stress_aggressive_sweep, NULL}, - - /* 2% */ - {"stress.checkpoint", "stress checkpoints", C_BOOL, 2, 0, 0, &g.c_timing_stress_checkpoint, NULL}, - - /* 2% */ - {"stress.checkpoint_reserved_txnid_delay", "stress checkpoint invisible transaction id delay", - C_BOOL, 2, 0, 0, &g.c_timing_stress_checkpoint_reserved_txnid_delay, NULL}, - - /* 2% */ - {"stress.checkpoint_prepare", "stress checkpoint prepare", C_BOOL, 2, 0, 0, - &g.c_timing_stress_checkpoint_prepare, NULL}, - - /* 30% */ - {"stress.failpoint_hs_delete_key_from_ts", "stress failpoint history store delete key from ts", - C_BOOL, 30, 0, 0, &g.c_timing_stress_failpoint_hs_delete_key_from_ts, NULL}, - - /* 30% */ - {"stress.failpoint_hs_insert_1", "stress failpoint history store insert (#1)", C_BOOL, 30, 0, 0, - &g.c_timing_stress_failpoint_hs_insert_1, NULL}, - - /* 30% */ - {"stress.failpoint_hs_insert_2", "stress failpoint history store insert (#2)", C_BOOL, 30, 0, 0, - &g.c_timing_stress_failpoint_hs_insert_2, NULL}, - - /* 2% */ - {"stress.hs_checkpoint_delay", "stress history store checkpoint delay", C_BOOL, 2, 0, 0, - &g.c_timing_stress_hs_checkpoint_delay, NULL}, - - /* 2% */ - {"stress.hs_search", "stress history store search", C_BOOL, 2, 0, 0, &g.c_timing_stress_hs_search, - NULL}, - - /* 2% */ - {"stress.hs_sweep", "stress history store sweep", C_BOOL, 2, 0, 0, &g.c_timing_stress_hs_sweep, - NULL}, - - /* 2% */ - {"stress.split_1", "stress splits (#1)", C_BOOL, 2, 0, 0, &g.c_timing_stress_split_1, NULL}, - - /* 2% */ - {"stress.split_2", "stress splits (#2)", C_BOOL, 2, 0, 0, &g.c_timing_stress_split_2, NULL}, - - /* 2% */ - {"stress.split_3", "stress splits (#3)", C_BOOL, 2, 0, 0, &g.c_timing_stress_split_3, NULL}, - - /* 2% */ - {"stress.split_4", "stress splits (#4)", C_BOOL, 2, 0, 0, &g.c_timing_stress_split_4, NULL}, - - /* 2% */ - {"stress.split_5", "stress splits (#5)", C_BOOL, 2, 0, 0, &g.c_timing_stress_split_5, NULL}, - - /* 2% */ - {"stress.split_6", "stress splits (#6)", C_BOOL, 2, 0, 0, &g.c_timing_stress_split_6, NULL}, - - /* 2% */ - {"stress.split_7", "stress splits (#7)", C_BOOL, 2, 0, 0, &g.c_timing_stress_split_7, NULL}, - - {"transaction.implicit", "implicit, without timestamps, transactions (percentage)", 0x0, 0, 100, - 100, &g.c_txn_implicit, NULL}, - - /* 70% */ - {"transaction.timestamps", "all transactions (or none), have timestamps", C_BOOL, 80, 0, 0, - &g.c_txn_timestamps, NULL}, - - {"wiredtiger.config", "wiredtiger_open API configuration string", C_IGNORE | C_STRING, 0, 0, 0, - NULL, &g.c_config_open}, - - /* 80% */ - {"wiredtiger.rwlock", "configure wiredtiger read/write mutexes", C_BOOL, 80, 0, 0, &g.c_wt_mutex, - NULL}, - - {"wiredtiger.leak_memory", "configure memory leaked on shutdown", C_BOOL, 0, 0, 0, - &g.c_leak_memory, NULL}, + u_int off; /* Value offset */ +} CONFIG; - {NULL, NULL, 0x0, 0, 0, 0, NULL, NULL}}; +#define V_MAX_TABLES_CONFIG 1000 + +#define V_GLOBAL_ASSERT_READ_TIMESTAMP 0 +#define V_GLOBAL_ASSERT_WRITE_TIMESTAMP 1 +#define V_GLOBAL_BACKUP 2 +#define V_GLOBAL_BACKUP_INCREMENTAL 3 +#define V_GLOBAL_BACKUP_INCR_GRANULARITY 4 +#define V_GLOBAL_BLOCK_CACHE 5 +#define V_GLOBAL_BLOCK_CACHE_CACHE_ON_CHECKPOINT 6 +#define V_GLOBAL_BLOCK_CACHE_CACHE_ON_WRITES 7 +#define V_GLOBAL_BLOCK_CACHE_SIZE 8 +#define V_TABLE_BTREE_BITCNT 9 +#define V_TABLE_BTREE_COMPRESSION 10 +#define V_TABLE_BTREE_DICTIONARY 11 +#define V_TABLE_BTREE_HUFFMAN_VALUE 12 +#define V_TABLE_BTREE_INTERNAL_KEY_TRUNCATION 13 +#define V_TABLE_BTREE_INTERNAL_PAGE_MAX 14 +#define V_TABLE_BTREE_KEY_MAX 15 +#define V_TABLE_BTREE_KEY_MIN 16 +#define V_TABLE_BTREE_LEAF_PAGE_MAX 17 +#define V_TABLE_BTREE_MEMORY_PAGE_MAX 18 +#define V_TABLE_BTREE_PREFIX_LEN 19 +#define V_TABLE_BTREE_PREFIX_COMPRESSION 20 +#define V_TABLE_BTREE_PREFIX_COMPRESSION_MIN 21 +#define V_TABLE_BTREE_REPEAT_DATA_PCT 22 +#define V_TABLE_BTREE_REVERSE 23 +#define V_TABLE_BTREE_SPLIT_PCT 24 +#define V_TABLE_BTREE_VALUE_MAX 25 +#define V_TABLE_BTREE_VALUE_MIN 26 +#define V_GLOBAL_CACHE 27 +#define V_GLOBAL_CACHE_EVICT_MAX 28 +#define V_GLOBAL_CACHE_MINIMUM 29 +#define V_GLOBAL_CHECKPOINT 30 +#define V_GLOBAL_CHECKPOINT_LOG_SIZE 31 +#define V_GLOBAL_CHECKPOINT_WAIT 32 +#define V_TABLE_DISK_CHECKSUM 33 +#define V_GLOBAL_DISK_DATA_EXTEND 34 +#define V_GLOBAL_DISK_DIRECT_IO 35 +#define V_GLOBAL_DISK_ENCRYPTION 36 +#define V_TABLE_DISK_FIRSTFIT 37 +#define V_GLOBAL_DISK_MMAP 38 +#define V_GLOBAL_DISK_MMAP_ALL 39 +#define V_GLOBAL_FORMAT_ABORT 40 +#define V_GLOBAL_FORMAT_INDEPENDENT_THREAD_RNG 41 +#define V_GLOBAL_FORMAT_MAJOR_TIMEOUT 42 +#define V_GLOBAL_IMPORT 43 +#define V_GLOBAL_LOGGING 44 +#define V_GLOBAL_LOGGING_ARCHIVE 45 +#define V_GLOBAL_LOGGING_COMPRESSION 46 +#define V_GLOBAL_LOGGING_FILE_MAX 47 +#define V_GLOBAL_LOGGING_PREALLOC 48 +#define V_TABLE_LSM_AUTO_THROTTLE 49 +#define V_TABLE_LSM_BLOOM 50 +#define V_TABLE_LSM_BLOOM_BIT_COUNT 51 +#define V_TABLE_LSM_BLOOM_HASH_COUNT 52 +#define V_TABLE_LSM_BLOOM_OLDEST 53 +#define V_TABLE_LSM_CHUNK_SIZE 54 +#define V_TABLE_LSM_MERGE_MAX 55 +#define V_GLOBAL_LSM_WORKER_THREADS 56 +#define V_GLOBAL_OPS_ALTER 57 +#define V_GLOBAL_OPS_COMPACTION 58 +#define V_GLOBAL_OPS_HS_CURSOR 59 +#define V_TABLE_OPS_PCT_DELETE 60 +#define V_TABLE_OPS_PCT_INSERT 61 +#define V_TABLE_OPS_PCT_MODIFY 62 +#define V_TABLE_OPS_PCT_READ 63 +#define V_TABLE_OPS_PCT_WRITE 64 +#define V_GLOBAL_OPS_PREPARE 65 +#define V_GLOBAL_OPS_RANDOM_CURSOR 66 +#define V_GLOBAL_OPS_SALVAGE 67 +#define V_TABLE_OPS_TRUNCATE 68 +#define V_GLOBAL_OPS_VERIFY 69 +#define V_GLOBAL_QUIET 70 +#define V_GLOBAL_RUNS_IN_MEMORY 71 +#define V_GLOBAL_RUNS_OPS 72 +#define V_TABLE_RUNS_ROWS 73 +#define V_TABLE_RUNS_SOURCE 74 +#define V_GLOBAL_RUNS_TABLES 75 +#define V_GLOBAL_RUNS_THREADS 76 +#define V_GLOBAL_RUNS_TIMER 77 +#define V_TABLE_RUNS_TYPE 78 +#define V_GLOBAL_RUNS_VERIFY_FAILURE_DUMP 79 +#define V_GLOBAL_STATISTICS 80 +#define V_GLOBAL_STATISTICS_SERVER 81 +#define V_GLOBAL_STRESS_AGGRESSIVE_SWEEP 82 +#define V_GLOBAL_STRESS_CHECKPOINT 83 +#define V_GLOBAL_STRESS_CHECKPOINT_RESERVED_TXNID_DELAY 84 +#define V_GLOBAL_STRESS_CHECKPOINT_PREPARE 85 +#define V_GLOBAL_STRESS_FAILPOINT_HS_DELETE_KEY_FROM_TS 86 +#define V_GLOBAL_STRESS_FAILPOINT_HS_INSERT_1 87 +#define V_GLOBAL_STRESS_FAILPOINT_HS_INSERT_2 88 +#define V_GLOBAL_STRESS_HS_CHECKPOINT_DELAY 89 +#define V_GLOBAL_STRESS_HS_SEARCH 90 +#define V_GLOBAL_STRESS_HS_SWEEP 91 +#define V_GLOBAL_STRESS_SPLIT_1 92 +#define V_GLOBAL_STRESS_SPLIT_2 93 +#define V_GLOBAL_STRESS_SPLIT_3 94 +#define V_GLOBAL_STRESS_SPLIT_4 95 +#define V_GLOBAL_STRESS_SPLIT_5 96 +#define V_GLOBAL_STRESS_SPLIT_6 97 +#define V_GLOBAL_STRESS_SPLIT_7 98 +#define V_GLOBAL_TRANSACTION_IMPLICIT 99 +#define V_GLOBAL_TRANSACTION_TIMESTAMPS 100 +#define V_GLOBAL_WIREDTIGER_CONFIG 101 +#define V_GLOBAL_WIREDTIGER_RWLOCK 102 +#define V_GLOBAL_WIREDTIGER_LEAK_MEMORY 103 + +#define V_ELEMENT_COUNT 104 diff --git a/src/third_party/wiredtiger/test/format/config.sh b/src/third_party/wiredtiger/test/format/config.sh new file mode 100644 index 00000000000..dce73364530 --- /dev/null +++ b/src/third_party/wiredtiger/test/format/config.sh @@ -0,0 +1,296 @@ +#! /bin/sh + +# This script creates format's config.h and config_def.c files. To change format's configuration, +# modify this file and then run it as a script. + +fc="config_def.c" +fh="config.h" + +cat<<END_OF_HEADER_FILE_PREFIX>$fh +/* DO NOT EDIT: automatically built by format/config.sh. */ + +#define C_TYPE_MATCH(cp, type) \\ + (!F_ISSET(cp, (C_TYPE_FIX | C_TYPE_ROW | C_TYPE_VAR)) || \\ + ((type) == FIX && F_ISSET(cp, C_TYPE_FIX)) || ((type) == ROW && F_ISSET(cp, C_TYPE_ROW)) || \\ + ((type) == VAR && F_ISSET(cp, C_TYPE_VAR))) + +typedef struct { + const char *name; /* Configuration item */ + const char *desc; /* Configuration description */ + +#define C_BOOL 0x001u /* Boolean (true if roll of 1-to-100 is <= CONFIG->min) */ +#define C_IGNORE 0x002u /* Not a simple randomization, configured specially */ +#define C_STRING 0x004u /* String (rather than integral) */ +#define C_TABLE 0x008u /* Value is per table, not global */ +#define C_TYPE_FIX 0x010u /* Value is only relevant to FLCS */ +#define C_TYPE_LSM 0x020u /* Value is only relevant to LSM */ +#define C_TYPE_ROW 0x040u /* Value is only relevant to RS */ +#define C_TYPE_VAR 0x080u /* Value is only relevant to VLCS */ +#define C_ZERO_NOTSET 0x100u /* Ignore zero values */ + uint32_t flags; + + uint32_t min; /* Minimum value */ + uint32_t maxrand; /* Maximum value randomly chosen */ + uint32_t maxset; /* Maximum value explicitly set */ + + u_int off; /* Value offset */ +} CONFIG; + +#define V_MAX_TABLES_CONFIG 1000 + +END_OF_HEADER_FILE_PREFIX + +n=0 +while IFS= read -r line; do + case "$line" in + '{"'*) + tag=`echo "$line" | + sed -e 's/{"//' \ + -e 's/",.*//' \ + -e 's/\./_/g' | + tr '[:lower:]' '[:upper:]'` + prefix="GLOBAL" + if `echo "$line" | grep 'C_TABLE' > /dev/null`; then + prefix="TABLE" + fi + def="V_""$prefix""_""$tag" + echo "$line" | + sed -e "s/}/, $def},/" \ + -e 's/\(^.*",\) \(.*\)/ \1\n \2/' + + echo "#define $def $n" >> $fh + + n=`expr $n + 1` + ;; + *) + echo "$line" + esac +done<<END_OF_INPUT>$fc +/* DO NOT EDIT: automatically built by format/config.sh. */ + +#include "format.h" + +CONFIG configuration_list[] = { +{"assert.read_timestamp", "assert read_timestamp", C_BOOL, 2, 0, 0} + +{"assert.write_timestamp", "set write_timestamp_usage and assert write_timestamp", C_BOOL, 2, 0, 0} + +{"backup", "configure backups", C_BOOL, 20, 0, 0} + +{"backup.incremental", "backup type (block | log | off)", C_IGNORE | C_STRING, 0, 0, 0} + +{"backup.incr_granularity", "incremental backup block granularity (KB)", 0x0, 4, 16384, 16384} + +{"block_cache", "enable the block cache", C_BOOL, 10, 0, 0} + +{"block_cache.cache_on_checkpoint", "block cache: cache checkpoint writes", C_BOOL, 30, 0, 0} + +{"block_cache.cache_on_writes", "block cache: populate the cache on writes", C_BOOL, 60, 0, 0} + +{"block_cache.size", "block cache size (MB)", 0x0, 1, 100, 100 * 1024} + +{"btree.bitcnt", "fixed-length column-store object size (number of bits)", C_TABLE | C_TYPE_FIX, 1, 8, 8} + +{"btree.compression", "data compression (none | lz4 | snappy | zlib | zstd)", C_IGNORE | C_STRING | C_TABLE, 0, 0, 0} + +{"btree.dictionary", "configure dictionary compressed values", C_BOOL | C_TABLE | C_TYPE_ROW | C_TYPE_VAR, 20, 0, 0} + +{"btree.huffman_value", "configure huffman encoded values", C_BOOL | C_TABLE | C_TYPE_ROW | C_TYPE_VAR, 20, 0, 0} + +{"btree.internal_key_truncation", "truncate internal keys", C_BOOL | C_TABLE, 95, 0, 0} + +{"btree.internal_page_max", "btree internal node maximum size", C_TABLE, 9, 17, 27} + +{"btree.key_max", "maximum key size", C_TABLE | C_TYPE_ROW, 20, 128, MEGABYTE(10)} + +{"btree.key_min", "minimum key size", C_TABLE | C_TYPE_ROW, KEY_LEN_CONFIG_MIN, 32, 256} + +{"btree.leaf_page_max", "btree leaf node maximum size", C_TABLE, 9, 17, 27} + +{"btree.memory_page_max", "maximum cache page size", C_TABLE, 1, 10, 128} + +{"btree.prefix_len", "common key prefix", C_TABLE | C_TYPE_ROW | C_ZERO_NOTSET, PREFIX_LEN_CONFIG_MIN, PREFIX_LEN_CONFIG_MAX, PREFIX_LEN_CONFIG_MAX} + +{"btree.prefix_compression", "configure prefix compressed keys", C_BOOL | C_TABLE | C_TYPE_ROW, 80, 0, 0} + +{"btree.prefix_compression_min", "minimum gain before prefix compression is used (bytes)", C_TABLE | C_TYPE_ROW, 0, 8, 256} + +{"btree.repeat_data_pct", "duplicate values (percentage)", C_TABLE | C_TYPE_VAR, 0, 90, 90} + +{"btree.reverse", "reverse order collation", C_BOOL | C_TABLE | C_TYPE_ROW, 10, 0, 0} + +{"btree.split_pct", "page split size as a percentage of the maximum page size", C_TABLE, 50, 100, 100} + +{"btree.value_max", "maximum value size", C_TABLE | C_TYPE_ROW | C_TYPE_VAR, 32, 4096, MEGABYTE(10)} + +{"btree.value_min", "minimum value size", C_TABLE | C_TYPE_ROW | C_TYPE_VAR, 0, 20, 4096} + +{"cache", "cache size (MB)", 0x0, 1, 100, 100 * 1024} + +{"cache.evict_max", "maximum number of eviction workers", 0x0, 0, 5, 100} + +{"cache.minimum", "minimum cache size (MB)", C_IGNORE, 0, 0, 100 * 1024} + +{"checkpoint", "checkpoint type (on | off | wiredtiger)", C_IGNORE | C_STRING, 0, 0, 0} + +{"checkpoint.log_size", "MB of log to wait if wiredtiger checkpoints configured", 0x0, 20, 200, 1024} + +{"checkpoint.wait", "seconds to wait if wiredtiger checkpoints configured", 0x0, 5, 100, 3600} + +{"disk.checksum", "checksum type (on | off | uncompressed | unencrypted)", C_IGNORE | C_STRING | C_TABLE, 0, 0, 0} + +{"disk.data_extend", "configure data file extension", C_BOOL, 5, 0, 0} + +{"disk.direct_io", "configure direct I/O for data objects", C_BOOL | C_IGNORE, 0, 0, 1} + +{"disk.encryption", "encryption type (none | rotn-7)", C_IGNORE | C_STRING, 0, 0, 0} + +{"disk.firstfit", "configure first-fit allocation", C_BOOL | C_TABLE, 10, 0, 0} + +{"disk.mmap", "configure mmap operations (reads only)", C_BOOL, 90, 0, 0} + +{"disk.mmap_all", "configure mmap operations (read and write)", C_BOOL, 5, 0, 0} + +{"format.abort", "drop core during timed run", C_BOOL, 0, 0, 0} + +{"format.independent_thread_rng", "configure independent thread RNG space", C_BOOL, 75, 0, 0} + +{"format.major_timeout", "long-running operations timeout (minutes)", C_IGNORE, 0, 0, 1000} + +/* + * 0% + * FIXME-WT-7510: Temporarily disable import until WT_ROLLBACK error and wt_copy_and_sync error is + * fixed. It should be (C_BOOL, 20, 0, 0). + */ +{"import", "import table from newly created database", C_BOOL, 0, 0, 0} + +{"logging", "configure logging", C_BOOL, 50, 0, 0} + +{"logging.archive", "configure log file archival", C_BOOL, 50, 0, 0} + +{"logging.compression", "logging compression (none | lz4 | snappy | zlib | zstd)", C_IGNORE | C_STRING, 0, 0, 0} + +{"logging.file_max", "maximum log file size (KB)", 0x0, 100, 512000, 2097152} + +{"logging.prealloc", "configure log file pre-allocation", C_BOOL, 50, 0, 0} + +{"lsm.auto_throttle", "throttle LSM inserts", C_BOOL | C_TABLE | C_TYPE_LSM, 90, 0, 0} + +{"lsm.bloom", "configure bloom filters", C_BOOL | C_TABLE | C_TYPE_LSM, 95, 0, 0} + +{"lsm.bloom_bit_count", "number of bits per item for bloom filters", C_TABLE | C_TYPE_LSM, 4, 64, 1000} + +{"lsm.bloom_hash_count", "number of hash values per item for bloom filters", C_TABLE | C_TYPE_LSM, 4, 32, 100} + +{"lsm.bloom_oldest", "configure bloom_oldest=true", C_BOOL | C_TABLE | C_TYPE_LSM, 10, 0, 0} + +{"lsm.chunk_size", "LSM chunk size (MB)", C_TABLE | C_TYPE_LSM, 1, 10, 100} + +{"lsm.merge_max", "maximum number of chunks to include in an LSM merge operation", C_TABLE | C_TYPE_LSM, 4, 20, 100} + +{"lsm.worker_threads", "number of LSM worker threads", C_TYPE_LSM, 3, 4, 20} + +{"ops.alter", "configure table alterations", C_BOOL, 10, 0, 0} + +{"ops.compaction", "configure compaction", C_BOOL, 10, 0, 0} + +{"ops.hs_cursor", "configure history store cursor reads", C_BOOL, 50, 0, 0} + +{"ops.pct.delete", "delete operations (percentage)", C_IGNORE | C_TABLE, 0, 0, 100} + +{"ops.pct.insert", "insert operations (percentage)", C_IGNORE | C_TABLE, 0, 0, 100} + +{"ops.pct.modify", "modify operations (percentage)", C_IGNORE | C_TABLE, 0, 0, 100} + +{"ops.pct.read", "read operations (percentage)", C_IGNORE | C_TABLE, 0, 0, 100} + +{"ops.pct.write", "update operations (percentage)", C_IGNORE | C_TABLE, 0, 0, 100} + +{"ops.prepare", "configure transaction prepare", C_BOOL, 5, 0, 0} + +{"ops.random_cursor", "configure random cursor reads", C_BOOL, 10, 0, 0} + +{"ops.salvage", "configure salvage", C_BOOL, 100, 1, 0} + +{"ops.truncate", "configure truncation", C_BOOL | C_TABLE, 100, 0, 0} + +{"ops.verify", "configure verify", C_BOOL, 100, 1, 0} + +{"quiet", "quiet run (same as -q)", C_BOOL | C_IGNORE, 0, 0, 1} + +{"runs.in_memory", "configure in-memory", C_BOOL | C_IGNORE, 0, 0, 1} + +{"runs.ops", "operations per run", 0x0, 0, M(2), M(100)} + +{"runs.rows", "number of rows", C_TABLE, 10, M(1), M(100)} + +{"runs.source", "data source type (file | lsm | table)", C_IGNORE | C_STRING | C_TABLE, 0, 0, 0} + +{"runs.tables", "number of tables", 0x0, 1, 32, V_MAX_TABLES_CONFIG} + +{"runs.threads", "number of worker threads", 0x0, 1, 32, 128} + +{"runs.timer", "run time (minutes)", C_IGNORE, 0, 0, UINT_MAX} + +{"runs.type", "object type (fix | row | var)", C_IGNORE | C_STRING | C_TABLE, 0, 0, 0} + +{"runs.verify_failure_dump", "configure page dump on repeatable read error", C_BOOL | C_IGNORE, 0, 0, 1} + +{"statistics", "configure statistics", C_BOOL, 20, 0, 0} + +{"statistics.server", "configure statistics server thread", C_BOOL, 5, 0, 0} + +{"stress.aggressive_sweep", "stress aggressive sweep", C_BOOL, 2, 0, 0} + +{"stress.checkpoint", "stress checkpoints", C_BOOL, 2, 0, 0} + +{"stress.checkpoint_reserved_txnid_delay", "stress checkpoint invisible transaction id delay", C_BOOL, 2, 0, 0} + +{"stress.checkpoint_prepare", "stress checkpoint prepare", C_BOOL, 2, 0, 0} + +{"stress.failpoint_hs_delete_key_from_ts", "stress failpoint history store delete key from ts", C_BOOL, 30, 0, 0} + +{"stress.failpoint_hs_insert_1", "stress failpoint history store insert (#1)", C_BOOL, 30, 0, 0} + +{"stress.failpoint_hs_insert_2", "stress failpoint history store insert (#2)", C_BOOL, 30, 0, 0} + +{"stress.hs_checkpoint_delay", "stress history store checkpoint delay", C_BOOL, 2, 0, 0} + +{"stress.hs_search", "stress history store search", C_BOOL, 2, 0, 0} + +{"stress.hs_sweep", "stress history store sweep", C_BOOL, 2, 0, 0} + +{"stress.split_1", "stress splits (#1)", C_BOOL, 2, 0, 0} + +{"stress.split_2", "stress splits (#2)", C_BOOL, 2, 0, 0} + +{"stress.split_3", "stress splits (#3)", C_BOOL, 2, 0, 0} + +{"stress.split_4", "stress splits (#4)", C_BOOL, 2, 0, 0} + +{"stress.split_5", "stress splits (#5)", C_BOOL, 2, 0, 0} + +{"stress.split_6", "stress splits (#6)", C_BOOL, 2, 0, 0} + +{"stress.split_7", "stress splits (#7)", C_BOOL, 2, 0, 0} + +{"transaction.implicit", "implicit, without timestamps, transactions (percentage)", 0, 0, 100, 100} + +{"transaction.timestamps", "all transactions (or none), have timestamps", C_BOOL, 80, 0, 0} + +{"wiredtiger.config", "wiredtiger_open API configuration string", C_IGNORE | C_STRING, 0, 0, 0} + +{"wiredtiger.rwlock", "configure wiredtiger read/write mutexes", C_BOOL, 80, 0, 0} + +{"wiredtiger.leak_memory", "leak memory on wiredtiger shutdown", C_BOOL, 0, 0, 0} +END_OF_INPUT + +( +echo +echo ' {NULL, NULL, 0x0, 0, 0, 0, 0}' +echo '};') >> $fc + +( +echo +echo "#define V_ELEMENT_COUNT $n") >> $fh + diff --git a/src/third_party/wiredtiger/test/format/config_def.c b/src/third_party/wiredtiger/test/format/config_def.c new file mode 100644 index 00000000000..22d650f9326 --- /dev/null +++ b/src/third_party/wiredtiger/test/format/config_def.c @@ -0,0 +1,324 @@ +/* DO NOT EDIT: automatically built by format/config.sh. */ + +#include "format.h" + +CONFIG configuration_list[] = { + {"assert.read_timestamp", "assert read_timestamp", + C_BOOL, 2, 0, 0, V_GLOBAL_ASSERT_READ_TIMESTAMP}, + + {"assert.write_timestamp", "set write_timestamp_usage and assert write_timestamp", + C_BOOL, 2, 0, 0, V_GLOBAL_ASSERT_WRITE_TIMESTAMP}, + + {"backup", "configure backups", + C_BOOL, 20, 0, 0, V_GLOBAL_BACKUP}, + + {"backup.incremental", "backup type (block | log | off)", + C_IGNORE | C_STRING, 0, 0, 0, V_GLOBAL_BACKUP_INCREMENTAL}, + + {"backup.incr_granularity", "incremental backup block granularity (KB)", + 0x0, 4, 16384, 16384, V_GLOBAL_BACKUP_INCR_GRANULARITY}, + + {"block_cache", "enable the block cache", + C_BOOL, 10, 0, 0, V_GLOBAL_BLOCK_CACHE}, + + {"block_cache.cache_on_checkpoint", "block cache: cache checkpoint writes", + C_BOOL, 30, 0, 0, V_GLOBAL_BLOCK_CACHE_CACHE_ON_CHECKPOINT}, + + {"block_cache.cache_on_writes", "block cache: populate the cache on writes", + C_BOOL, 60, 0, 0, V_GLOBAL_BLOCK_CACHE_CACHE_ON_WRITES}, + + {"block_cache.size", "block cache size (MB)", + 0x0, 1, 100, 100 * 1024, V_GLOBAL_BLOCK_CACHE_SIZE}, + + {"btree.bitcnt", "fixed-length column-store object size (number of bits)", + C_TABLE | C_TYPE_FIX, 1, 8, 8, V_TABLE_BTREE_BITCNT}, + + {"btree.compression", "data compression (none | lz4 | snappy | zlib | zstd)", + C_IGNORE | C_STRING | C_TABLE, 0, 0, 0, V_TABLE_BTREE_COMPRESSION}, + + {"btree.dictionary", "configure dictionary compressed values", + C_BOOL | C_TABLE | C_TYPE_ROW | C_TYPE_VAR, 20, 0, 0, V_TABLE_BTREE_DICTIONARY}, + + {"btree.huffman_value", "configure huffman encoded values", + C_BOOL | C_TABLE | C_TYPE_ROW | C_TYPE_VAR, 20, 0, 0, V_TABLE_BTREE_HUFFMAN_VALUE}, + + {"btree.internal_key_truncation", "truncate internal keys", + C_BOOL | C_TABLE, 95, 0, 0, V_TABLE_BTREE_INTERNAL_KEY_TRUNCATION}, + + {"btree.internal_page_max", "btree internal node maximum size", + C_TABLE, 9, 17, 27, V_TABLE_BTREE_INTERNAL_PAGE_MAX}, + + {"btree.key_max", "maximum key size", + C_TABLE | C_TYPE_ROW, 20, 128, MEGABYTE(10), V_TABLE_BTREE_KEY_MAX}, + + {"btree.key_min", "minimum key size", + C_TABLE | C_TYPE_ROW, KEY_LEN_CONFIG_MIN, 32, 256, V_TABLE_BTREE_KEY_MIN}, + + {"btree.leaf_page_max", "btree leaf node maximum size", + C_TABLE, 9, 17, 27, V_TABLE_BTREE_LEAF_PAGE_MAX}, + + {"btree.memory_page_max", "maximum cache page size", + C_TABLE, 1, 10, 128, V_TABLE_BTREE_MEMORY_PAGE_MAX}, + + {"btree.prefix_len", "common key prefix", + C_TABLE | C_TYPE_ROW | C_ZERO_NOTSET, PREFIX_LEN_CONFIG_MIN, PREFIX_LEN_CONFIG_MAX, PREFIX_LEN_CONFIG_MAX, V_TABLE_BTREE_PREFIX_LEN}, + + {"btree.prefix_compression", "configure prefix compressed keys", + C_BOOL | C_TABLE | C_TYPE_ROW, 80, 0, 0, V_TABLE_BTREE_PREFIX_COMPRESSION}, + + {"btree.prefix_compression_min", "minimum gain before prefix compression is used (bytes)", + C_TABLE | C_TYPE_ROW, 0, 8, 256, V_TABLE_BTREE_PREFIX_COMPRESSION_MIN}, + + {"btree.repeat_data_pct", "duplicate values (percentage)", + C_TABLE | C_TYPE_VAR, 0, 90, 90, V_TABLE_BTREE_REPEAT_DATA_PCT}, + + {"btree.reverse", "reverse order collation", + C_BOOL | C_TABLE | C_TYPE_ROW, 10, 0, 0, V_TABLE_BTREE_REVERSE}, + + {"btree.split_pct", "page split size as a percentage of the maximum page size", + C_TABLE, 50, 100, 100, V_TABLE_BTREE_SPLIT_PCT}, + + {"btree.value_max", "maximum value size", + C_TABLE | C_TYPE_ROW | C_TYPE_VAR, 32, 4096, MEGABYTE(10), V_TABLE_BTREE_VALUE_MAX}, + + {"btree.value_min", "minimum value size", + C_TABLE | C_TYPE_ROW | C_TYPE_VAR, 0, 20, 4096, V_TABLE_BTREE_VALUE_MIN}, + + {"cache", "cache size (MB)", + 0x0, 1, 100, 100 * 1024, V_GLOBAL_CACHE}, + + {"cache.evict_max", "maximum number of eviction workers", + 0x0, 0, 5, 100, V_GLOBAL_CACHE_EVICT_MAX}, + + {"cache.minimum", "minimum cache size (MB)", + C_IGNORE, 0, 0, 100 * 1024, V_GLOBAL_CACHE_MINIMUM}, + + {"checkpoint", "checkpoint type (on | off | wiredtiger)", + C_IGNORE | C_STRING, 0, 0, 0, V_GLOBAL_CHECKPOINT}, + + {"checkpoint.log_size", "MB of log to wait if wiredtiger checkpoints configured", + 0x0, 20, 200, 1024, V_GLOBAL_CHECKPOINT_LOG_SIZE}, + + {"checkpoint.wait", "seconds to wait if wiredtiger checkpoints configured", + 0x0, 5, 100, 3600, V_GLOBAL_CHECKPOINT_WAIT}, + + {"disk.checksum", "checksum type (on | off | uncompressed | unencrypted)", + C_IGNORE | C_STRING | C_TABLE, 0, 0, 0, V_TABLE_DISK_CHECKSUM}, + + {"disk.data_extend", "configure data file extension", + C_BOOL, 5, 0, 0, V_GLOBAL_DISK_DATA_EXTEND}, + + {"disk.direct_io", "configure direct I/O for data objects", + C_BOOL | C_IGNORE, 0, 0, 1, V_GLOBAL_DISK_DIRECT_IO}, + + {"disk.encryption", "encryption type (none | rotn-7)", + C_IGNORE | C_STRING, 0, 0, 0, V_GLOBAL_DISK_ENCRYPTION}, + + {"disk.firstfit", "configure first-fit allocation", + C_BOOL | C_TABLE, 10, 0, 0, V_TABLE_DISK_FIRSTFIT}, + + {"disk.mmap", "configure mmap operations (reads only)", + C_BOOL, 90, 0, 0, V_GLOBAL_DISK_MMAP}, + + {"disk.mmap_all", "configure mmap operations (read and write)", + C_BOOL, 5, 0, 0, V_GLOBAL_DISK_MMAP_ALL}, + + {"format.abort", "drop core during timed run", + C_BOOL, 0, 0, 0, V_GLOBAL_FORMAT_ABORT}, + + {"format.independent_thread_rng", "configure independent thread RNG space", + C_BOOL, 75, 0, 0, V_GLOBAL_FORMAT_INDEPENDENT_THREAD_RNG}, + + {"format.major_timeout", "long-running operations timeout (minutes)", + C_IGNORE, 0, 0, 1000, V_GLOBAL_FORMAT_MAJOR_TIMEOUT}, + +/* + * 0% + * FIXME-WT-7510: Temporarily disable import until WT_ROLLBACK error and wt_copy_and_sync error is + * fixed. It should be (C_BOOL, 20, 0, 0). + */ + {"import", "import table from newly created database", + C_BOOL, 0, 0, 0, V_GLOBAL_IMPORT}, + + {"logging", "configure logging", + C_BOOL, 50, 0, 0, V_GLOBAL_LOGGING}, + + {"logging.archive", "configure log file archival", + C_BOOL, 50, 0, 0, V_GLOBAL_LOGGING_ARCHIVE}, + + {"logging.compression", "logging compression (none | lz4 | snappy | zlib | zstd)", + C_IGNORE | C_STRING, 0, 0, 0, V_GLOBAL_LOGGING_COMPRESSION}, + + {"logging.file_max", "maximum log file size (KB)", + 0x0, 100, 512000, 2097152, V_GLOBAL_LOGGING_FILE_MAX}, + + {"logging.prealloc", "configure log file pre-allocation", + C_BOOL, 50, 0, 0, V_GLOBAL_LOGGING_PREALLOC}, + + {"lsm.auto_throttle", "throttle LSM inserts", + C_BOOL | C_TABLE | C_TYPE_LSM, 90, 0, 0, V_TABLE_LSM_AUTO_THROTTLE}, + + {"lsm.bloom", "configure bloom filters", + C_BOOL | C_TABLE | C_TYPE_LSM, 95, 0, 0, V_TABLE_LSM_BLOOM}, + + {"lsm.bloom_bit_count", "number of bits per item for bloom filters", + C_TABLE | C_TYPE_LSM, 4, 64, 1000, V_TABLE_LSM_BLOOM_BIT_COUNT}, + + {"lsm.bloom_hash_count", "number of hash values per item for bloom filters", + C_TABLE | C_TYPE_LSM, 4, 32, 100, V_TABLE_LSM_BLOOM_HASH_COUNT}, + + {"lsm.bloom_oldest", "configure bloom_oldest=true", + C_BOOL | C_TABLE | C_TYPE_LSM, 10, 0, 0, V_TABLE_LSM_BLOOM_OLDEST}, + + {"lsm.chunk_size", "LSM chunk size (MB)", + C_TABLE | C_TYPE_LSM, 1, 10, 100, V_TABLE_LSM_CHUNK_SIZE}, + + {"lsm.merge_max", "maximum number of chunks to include in an LSM merge operation", + C_TABLE | C_TYPE_LSM, 4, 20, 100, V_TABLE_LSM_MERGE_MAX}, + + {"lsm.worker_threads", "number of LSM worker threads", + C_TYPE_LSM, 3, 4, 20, V_GLOBAL_LSM_WORKER_THREADS}, + + {"ops.alter", "configure table alterations", + C_BOOL, 10, 0, 0, V_GLOBAL_OPS_ALTER}, + + {"ops.compaction", "configure compaction", + C_BOOL, 10, 0, 0, V_GLOBAL_OPS_COMPACTION}, + + {"ops.hs_cursor", "configure history store cursor reads", + C_BOOL, 50, 0, 0, V_GLOBAL_OPS_HS_CURSOR}, + + {"ops.pct.delete", "delete operations (percentage)", + C_IGNORE | C_TABLE, 0, 0, 100, V_TABLE_OPS_PCT_DELETE}, + + {"ops.pct.insert", "insert operations (percentage)", + C_IGNORE | C_TABLE, 0, 0, 100, V_TABLE_OPS_PCT_INSERT}, + + {"ops.pct.modify", "modify operations (percentage)", + C_IGNORE | C_TABLE, 0, 0, 100, V_TABLE_OPS_PCT_MODIFY}, + + {"ops.pct.read", "read operations (percentage)", + C_IGNORE | C_TABLE, 0, 0, 100, V_TABLE_OPS_PCT_READ}, + + {"ops.pct.write", "update operations (percentage)", + C_IGNORE | C_TABLE, 0, 0, 100, V_TABLE_OPS_PCT_WRITE}, + + {"ops.prepare", "configure transaction prepare", + C_BOOL, 5, 0, 0, V_GLOBAL_OPS_PREPARE}, + + {"ops.random_cursor", "configure random cursor reads", + C_BOOL, 10, 0, 0, V_GLOBAL_OPS_RANDOM_CURSOR}, + + {"ops.salvage", "configure salvage", + C_BOOL, 100, 1, 0, V_GLOBAL_OPS_SALVAGE}, + + {"ops.truncate", "configure truncation", + C_BOOL | C_TABLE, 100, 0, 0, V_TABLE_OPS_TRUNCATE}, + + {"ops.verify", "configure verify", + C_BOOL, 100, 1, 0, V_GLOBAL_OPS_VERIFY}, + + {"quiet", "quiet run (same as -q)", + C_BOOL | C_IGNORE, 0, 0, 1, V_GLOBAL_QUIET}, + + {"runs.in_memory", "configure in-memory", + C_BOOL | C_IGNORE, 0, 0, 1, V_GLOBAL_RUNS_IN_MEMORY}, + + {"runs.ops", "operations per run", + 0x0, 0, M(2), M(100), V_GLOBAL_RUNS_OPS}, + + {"runs.rows", "number of rows", + C_TABLE, 10, M(1), M(100), V_TABLE_RUNS_ROWS}, + + {"runs.source", "data source type (file | lsm | table)", + C_IGNORE | C_STRING | C_TABLE, 0, 0, 0, V_TABLE_RUNS_SOURCE}, + + {"runs.tables", "number of tables", + 0x0, 1, 32, V_MAX_TABLES_CONFIG, V_GLOBAL_RUNS_TABLES}, + + {"runs.threads", "number of worker threads", + 0x0, 1, 32, 128, V_GLOBAL_RUNS_THREADS}, + + {"runs.timer", "run time (minutes)", + C_IGNORE, 0, 0, UINT_MAX, V_GLOBAL_RUNS_TIMER}, + + {"runs.type", "object type (fix | row | var)", + C_IGNORE | C_STRING | C_TABLE, 0, 0, 0, V_TABLE_RUNS_TYPE}, + + {"runs.verify_failure_dump", "configure page dump on repeatable read error", + C_BOOL | C_IGNORE, 0, 0, 1, V_GLOBAL_RUNS_VERIFY_FAILURE_DUMP}, + + {"statistics", "configure statistics", + C_BOOL, 20, 0, 0, V_GLOBAL_STATISTICS}, + + {"statistics.server", "configure statistics server thread", + C_BOOL, 5, 0, 0, V_GLOBAL_STATISTICS_SERVER}, + + {"stress.aggressive_sweep", "stress aggressive sweep", + C_BOOL, 2, 0, 0, V_GLOBAL_STRESS_AGGRESSIVE_SWEEP}, + + {"stress.checkpoint", "stress checkpoints", + C_BOOL, 2, 0, 0, V_GLOBAL_STRESS_CHECKPOINT}, + + {"stress.checkpoint_reserved_txnid_delay", "stress checkpoint invisible transaction id delay", + C_BOOL, 2, 0, 0, V_GLOBAL_STRESS_CHECKPOINT_RESERVED_TXNID_DELAY}, + + {"stress.checkpoint_prepare", "stress checkpoint prepare", + C_BOOL, 2, 0, 0, V_GLOBAL_STRESS_CHECKPOINT_PREPARE}, + + {"stress.failpoint_hs_delete_key_from_ts", "stress failpoint history store delete key from ts", + C_BOOL, 30, 0, 0, V_GLOBAL_STRESS_FAILPOINT_HS_DELETE_KEY_FROM_TS}, + + {"stress.failpoint_hs_insert_1", "stress failpoint history store insert (#1)", + C_BOOL, 30, 0, 0, V_GLOBAL_STRESS_FAILPOINT_HS_INSERT_1}, + + {"stress.failpoint_hs_insert_2", "stress failpoint history store insert (#2)", + C_BOOL, 30, 0, 0, V_GLOBAL_STRESS_FAILPOINT_HS_INSERT_2}, + + {"stress.hs_checkpoint_delay", "stress history store checkpoint delay", + C_BOOL, 2, 0, 0, V_GLOBAL_STRESS_HS_CHECKPOINT_DELAY}, + + {"stress.hs_search", "stress history store search", + C_BOOL, 2, 0, 0, V_GLOBAL_STRESS_HS_SEARCH}, + + {"stress.hs_sweep", "stress history store sweep", + C_BOOL, 2, 0, 0, V_GLOBAL_STRESS_HS_SWEEP}, + + {"stress.split_1", "stress splits (#1)", + C_BOOL, 2, 0, 0, V_GLOBAL_STRESS_SPLIT_1}, + + {"stress.split_2", "stress splits (#2)", + C_BOOL, 2, 0, 0, V_GLOBAL_STRESS_SPLIT_2}, + + {"stress.split_3", "stress splits (#3)", + C_BOOL, 2, 0, 0, V_GLOBAL_STRESS_SPLIT_3}, + + {"stress.split_4", "stress splits (#4)", + C_BOOL, 2, 0, 0, V_GLOBAL_STRESS_SPLIT_4}, + + {"stress.split_5", "stress splits (#5)", + C_BOOL, 2, 0, 0, V_GLOBAL_STRESS_SPLIT_5}, + + {"stress.split_6", "stress splits (#6)", + C_BOOL, 2, 0, 0, V_GLOBAL_STRESS_SPLIT_6}, + + {"stress.split_7", "stress splits (#7)", + C_BOOL, 2, 0, 0, V_GLOBAL_STRESS_SPLIT_7}, + + {"transaction.implicit", "implicit, without timestamps, transactions (percentage)", + 0, 0, 100, 100, V_GLOBAL_TRANSACTION_IMPLICIT}, + + {"transaction.timestamps", "all transactions (or none), have timestamps", + C_BOOL, 80, 0, 0, V_GLOBAL_TRANSACTION_TIMESTAMPS}, + + {"wiredtiger.config", "wiredtiger_open API configuration string", + C_IGNORE | C_STRING, 0, 0, 0, V_GLOBAL_WIREDTIGER_CONFIG}, + + {"wiredtiger.rwlock", "configure wiredtiger read/write mutexes", + C_BOOL, 80, 0, 0, V_GLOBAL_WIREDTIGER_RWLOCK}, + + {"wiredtiger.leak_memory", "leak memory on wiredtiger shutdown", + C_BOOL, 0, 0, 0, V_GLOBAL_WIREDTIGER_LEAK_MEMORY}, + + {NULL, NULL, 0x0, 0, 0, 0, 0} +}; diff --git a/src/third_party/wiredtiger/test/format/format.h b/src/third_party/wiredtiger/test/format/format.h index cce28406706..a0224d35d2d 100644 --- a/src/third_party/wiredtiger/test/format/format.h +++ b/src/third_party/wiredtiger/test/format/format.h @@ -80,12 +80,12 @@ #undef MEGABYTE #define MEGABYTE(v) ((v)*WT_MEGABYTE) +/* Format isn't careful about path buffers, an easy to fix hard-coded length. */ +#define MAX_FORMAT_PATH 1024 + #define BACKUP_INFO_FILE "BACKUP_INFO" /* Format's backup information for restart */ #define BACKUP_INFO_FILE_TMP "BACKUP_INFO.TMP" /* Format's backup information for restart */ #define BACKUP_MAX_COPY MEGABYTE(64) /* Maximum size we'll read/write at a time */ -#define WT_NAME "wt" /* Object name */ - -#define DATASOURCE(v) (strcmp(v, g.c_data_source) == 0 ? 1 : 0) #define FORMAT_OPERATION_REPS 3 /* 3 thread operations sets */ @@ -105,45 +105,109 @@ typedef struct { #define LOCK_INITIALIZED(lock) ((lock)->lock_type != LOCK_NONE) +#include "config.h" +extern CONFIG configuration_list[]; + typedef struct { - char tidbuf[128]; /* thread ID in printable form */ + uint32_t v; /* integral value */ + char *vstr; /* string value */ + bool set; /* value explicitly set */ +} CONFIGV; + +typedef enum { FIX, ROW, VAR } table_type; +typedef struct { + u_int id; /* table ID */ + char uri[32]; /* table URI */ + table_type type; /* table type */ + char track_prefix[32]; /* table track message prefix */ + uint32_t max_intl_page; /* page size configurations converted to bytes */ + uint32_t max_leaf_page; + uint32_t max_mem_page; + + uint32_t rows_current; /* current row count */ + + uint64_t truncate_cnt; /* truncation operation counter */ + + uint32_t key_rand_len[1031]; /* key: lengths */ + char *val_base; /* value: base/original */ + uint32_t val_dup_data_len; /* value: length of duplicate data items */ + + CONFIGV v[V_ELEMENT_COUNT]; /* table configuration */ +} TABLE; + +/* + * We read the configuration in a single pass, which means we don't know the table count until the + * end, and it can be extended at any time. Start out with a single table, which contains all of the + * global/default values, stored in the first slot of the tables array. If tables are added during + * configuration, they are separately allocated, but we continue to use the first (base) table slot + * for non-specific table or global configurations. In other words, the base information and the + * only table's information are both in tables' slot 0 to start. If additional tables are + * configured, the per-table information for each table is stored in tables slots 1-N. The number of + * tables starts at 0, and if any tables are configured, it's incremented: in other words, if the + * number of tables is 0, all of the information is in tables' slot 0. If the number of tables is + * greater than 1, all of the base information is in tables slot 0, and tables slot 1 holds table + * #1's specific information, slot #2 holds table #2's specific information and so on. This allows + * general and table-specific information to be configured in any order, and as part of the final + * table configuration, if there's more than a single table, the information in tables' slot 0 is + * propagated out to the additional table slots. + */ +extern TABLE *tables[V_MAX_TABLES_CONFIG + 1]; /* Table array */ +extern u_int ntables; + +/* + * Global and table-specific macros to retrieve configuration information. All of the tables contain + * all of the possible configuration entries, but the first table slot contains all of the global + * configuration information. The offset names a prefixed with "V_GLOBAL" and "V_TABLE" to reduce + * the chance of a coding error retrieving the wrong configuration item. + */ +#define GV(off) (tables[0]->v[V_GLOBAL_##off].v) +#define GVS(off) (tables[0]->v[V_GLOBAL_##off].vstr) +#define TV(off) (table->v[V_TABLE_##off].v) +#define TVS(off) (table->v[V_TABLE_##off].vstr) + +#define DATASOURCE(table, ds) (strcmp((table)->v[V_TABLE_RUNS_SOURCE].vstr, ds) == 0) + +typedef struct { WT_CONNECTION *wts_conn; WT_CONNECTION *wts_conn_inmemory; WT_SESSION *wts_session; - char *uri; /* Object name */ - bool backward_compatible; /* Backward compatibility testing */ + bool configured; /* Configuration completed */ bool reopen; /* Reopen an existing database */ bool workers_finished; /* Operations completed */ char *home; /* Home directory */ char *home_config; /* Run CONFIG file path */ char *home_hsdump; /* HS dump filename */ - char *home_init; /* Initialize home command */ char *home_key; /* Key file filename */ - char *home_log; /* Operation log file path */ char *home_pagedump; /* Page dump filename */ - char *home_rand; /* RNG log file path */ char *home_stats; /* Statistics file path */ char *config_open; /* Command-line configuration */ - uint32_t run_cnt; /* Run counter */ - bool trace; /* trace operations */ bool trace_all; /* trace all operations */ bool trace_local; /* write trace to the primary database */ + char tidbuf[128]; /* thread ID in printable form */ WT_CONNECTION *trace_conn; /* optional tracing database */ WT_SESSION *trace_session; RWLOCK backup_lock; /* Backup running */ uint64_t backup_id; /* Block incremental id */ +#define INCREMENTAL_BLOCK 1 +#define INCREMENTAL_LOG 2 +#define INCREMENTAL_OFF 3 + u_int backup_incr_flag; /* Incremental backup configuration */ WT_RAND_STATE rnd; /* Global RNG state */ - uint32_t rts_no_check; /* track unsuccessful RTS checking */ + u_int rts_no_check; /* track unsuccessful RTS checking */ + + uint64_t timestamp; /* Counter for timestamps */ + uint64_t oldest_timestamp; /* Last timestamp used for oldest */ + uint64_t stable_timestamp; /* Last timestamp used for stable */ /* * Prepare will return an error if the prepare timestamp is less than any active read timestamp. @@ -153,6 +217,7 @@ typedef struct { * that requires locking out transactional ops that set a timestamp. */ RWLOCK ts_lock; + /* * Lock to prevent the stable timestamp from moving during the commit of prepared transactions. * Otherwise, it may panic if the stable timestamp is moved to greater than or equal to the @@ -160,163 +225,38 @@ typedef struct { */ RWLOCK prepare_commit_lock; - uint64_t timestamp; /* Counter for timestamps */ - uint64_t oldest_timestamp; /* Last timestamp used for oldest */ - uint64_t stable_timestamp; /* Last timestamp used for stable */ - - uint64_t truncate_cnt; /* Counter for truncation */ - /* - * Single-thread failure. Always use pthread lock rather than WT lock in case WT library is - * misbehaving. + * Single-thread failure. Not a WiredTiger library lock because it's set up before configuring + * anything. */ pthread_rwlock_t death_lock; + WT_CURSOR *page_dump_cursor; /* Snapshot isolation read failed, modifies failure handling. */ - uint32_t c_abort; /* Config values */ - uint32_t c_alter; - uint32_t c_assert_read_timestamp; - uint32_t c_assert_write_timestamp; - uint32_t c_auto_throttle; - char *c_backup_incremental; - uint32_t c_backup_incr_granularity; - uint32_t c_backups; - uint32_t c_bitcnt; - uint32_t c_bloom; - uint32_t c_bloom_bit_count; - uint32_t c_bloom_hash_count; - uint32_t c_bloom_oldest; - uint32_t c_cache; - uint32_t c_cache_minimum; - char *c_checkpoint; - uint32_t c_checkpoint_log_size; - uint32_t c_checkpoint_wait; - char *c_checksum; - uint32_t c_chunk_size; - uint32_t c_compact; - char *c_compression; - char *c_config_open; - uint32_t c_data_extend; - char *c_data_source; - uint32_t c_delete_pct; - uint32_t c_dictionary; - uint32_t c_direct_io; - char *c_encryption; - uint32_t c_evict_max; - char *c_file_type; - uint32_t c_firstfit; - uint32_t c_hs_cursor; - uint32_t c_huffman_value; - uint32_t c_import; - uint32_t c_in_memory; - uint32_t c_independent_thread_rng; - uint32_t c_insert_pct; - uint32_t c_internal_key_truncation; - uint32_t c_intl_page_max; - uint32_t c_key_max; - uint32_t c_key_min; - uint32_t c_leaf_page_max; - uint32_t c_leak_memory; - uint32_t c_logging; - uint32_t c_logging_archive; - char *c_logging_compression; - uint32_t c_logging_file_max; - uint32_t c_logging_prealloc; - uint32_t c_lsm_worker_threads; - uint32_t c_major_timeout; - uint32_t c_memory_page_max; - uint32_t c_merge_max; - uint32_t c_mmap; - uint32_t c_mmap_all; - uint32_t c_modify_pct; - uint32_t c_ops; - uint32_t c_prefix; - uint32_t c_prefix_compression; - uint32_t c_prefix_compression_min; - uint32_t c_prepare; - uint32_t c_quiet; - uint32_t c_random_cursor; - uint32_t c_read_pct; - uint32_t c_repeat_data_pct; - uint32_t c_reverse; - uint32_t c_rows; - uint32_t c_runs; - uint32_t c_salvage; - uint32_t c_split_pct; - uint32_t c_statistics; - uint32_t c_statistics_server; - uint32_t c_threads; - uint32_t c_timer; - uint32_t c_timing_stress_aggressive_sweep; - uint32_t c_timing_stress_checkpoint; - uint32_t c_timing_stress_checkpoint_reserved_txnid_delay; - uint32_t c_timing_stress_failpoint_hs_delete_key_from_ts; - uint32_t c_timing_stress_failpoint_hs_insert_1; - uint32_t c_timing_stress_failpoint_hs_insert_2; - uint32_t c_timing_stress_hs_checkpoint_delay; - uint32_t c_timing_stress_hs_search; - uint32_t c_timing_stress_hs_sweep; - uint32_t c_timing_stress_checkpoint_prepare; - uint32_t c_timing_stress_split_1; - uint32_t c_timing_stress_split_2; - uint32_t c_timing_stress_split_3; - uint32_t c_timing_stress_split_4; - uint32_t c_timing_stress_split_5; - uint32_t c_timing_stress_split_6; - uint32_t c_timing_stress_split_7; - uint32_t c_truncate; - uint32_t c_txn_implicit; - uint32_t c_txn_timestamps; - uint32_t c_value_max; - uint32_t c_value_min; - uint32_t c_verify; - uint32_t c_verify_failure_dump; - uint32_t c_write_pct; - uint32_t c_wt_mutex; - -#define FIX 1 -#define ROW 2 -#define VAR 3 - u_int type; /* File type's flag value */ + /* Any runs.type configuration. */ + char runs_type[64]; -#define INCREMENTAL_BLOCK 1 -#define INCREMENTAL_LOG 2 -#define INCREMENTAL_OFF 3 - u_int c_backup_incr_flag; /* Incremental backup flag value */ + /* + * The minimum key size: A minimum key size of 11 is necessary, row-store keys have a leading + * 10-digit number and the 11 guarantees we never see a key we can't immediately convert to a + * numeric value without modification (there's a trailing non-digit character after every key). + * + * Range of common key prefix selection and the maximum table prefix length. + */ +#define KEY_LEN_CONFIG_MIN 11 +#define PREFIX_LEN_CONFIG_MIN 15 +#define PREFIX_LEN_CONFIG_MAX 80 + uint32_t prefix_len_max; + + bool column_store_config; /* At least one column-store table configured */ + bool lsm_config; /* At least one LSM data source configured */ + bool multi_table_config; /* If configuring multiple tables */ + bool transaction_timestamps_config; /* If transaction timestamps configured on any table */ #define CHECKPOINT_OFF 1 #define CHECKPOINT_ON 2 #define CHECKPOINT_WIREDTIGER 3 - u_int c_checkpoint_flag; /* Checkpoint flag value */ - -#define CHECKSUM_OFF 1 -#define CHECKSUM_ON 2 -#define CHECKSUM_UNCOMPRESSED 3 -#define CHECKSUM_UNENCRYPTED 4 - u_int c_checksum_flag; /* Checksum flag value */ - -#define COMPRESS_NONE 1 -#define COMPRESS_LZ4 2 -#define COMPRESS_SNAPPY 3 -#define COMPRESS_ZLIB 4 -#define COMPRESS_ZSTD 5 - u_int c_compression_flag; /* Compression flag value */ - u_int c_logging_compression_flag; /* Log compression flag value */ - -#define ENCRYPT_NONE 1 -#define ENCRYPT_ROTN_7 2 -#define ENCRYPT_SODIUM 3 - u_int c_encryption_flag; /* Encryption flag value */ - -/* The page must be a multiple of the allocation size, and 512 always works. */ -#define BLOCK_ALLOCATION_SIZE 512 - uint32_t intl_page_max; /* Maximum page sizes */ - uint32_t leaf_page_max; - - uint64_t rows; /* Total rows */ - - uint32_t prefix_len; /* Common key prefix length */ - uint32_t key_rand_len[1031]; /* Key lengths */ + u_int checkpoint_config; /* Checkpoint configuration */ } GLOBAL; extern GLOBAL g; @@ -330,6 +270,7 @@ typedef struct { thread_op op; /* Operation */ uint64_t opid; /* Operation ID */ + uint32_t id; /* Table ID */ uint64_t keyno; /* Row number */ uint64_t ts; /* Read/commit timestamp */ @@ -373,7 +314,14 @@ typedef struct { uint64_t update; WT_SESSION *session; /* WiredTiger session */ - WT_CURSOR *cursor; /* WiredTiger cursor */ + WT_CURSOR **cursors; /* WiredTiger cursors, maps one-to-one to tables */ + WT_CURSOR *cursor; /* Current cursor */ + TABLE *table; /* Current table */ + + struct col_insert { + uint32_t insert_list[256]; /* Inserted column-store records, maps one-to-one to tables */ + u_int insert_list_cnt; + } * col_insert; WT_SESSION *trace; /* WiredTiger operations tracing session */ @@ -398,9 +346,6 @@ typedef struct { #define snap_first s->snap_state_first #define snap_list s->snap_state_list - uint64_t insert_list[256]; /* column-store inserted records */ - u_int insert_list_cnt; - WT_ITEM vprint; /* Temporary buffer for printable values */ WT_ITEM moda, modb; /* Temporary buffer for modify operations */ @@ -426,17 +371,16 @@ void config_clear(void); void config_compat(const char **); void config_error(void); void config_file(const char *); -void config_final(void); void config_print(bool); void config_run(void); -void config_single(const char *, bool); +void config_single(TABLE *, const char *, bool); void create_database(const char *home, WT_CONNECTION **connp); void fclose_and_clear(FILE **); bool fp_readv(FILE *, char *, uint32_t *); -void key_gen_common(WT_ITEM *, uint64_t, const char *); +void key_gen_common(TABLE *, WT_ITEM *, uint64_t, const char *); void key_gen_init(WT_ITEM *); void key_gen_teardown(WT_ITEM *); -void key_init(void); +void key_init(TABLE *, void *); void lock_destroy(WT_SESSION *, RWLOCK *); void lock_init(WT_SESSION *, RWLOCK *); void operations(u_int, bool); @@ -445,37 +389,40 @@ void set_alarm(u_int); void set_core_off(void); void set_oldest_timestamp(void); void snap_init(TINFO *); -void snap_teardown(TINFO *); void snap_op_init(TINFO *, uint64_t, bool); -void snap_repeat_rollback(WT_CURSOR *, TINFO **, size_t); -void snap_repeat_single(WT_CURSOR *, TINFO *); -int snap_repeat_txn(WT_CURSOR *, TINFO *); +void snap_repeat_rollback(TINFO **, size_t); +void snap_repeat_single(TINFO *); +int snap_repeat_txn(TINFO *); void snap_repeat_update(TINFO *, bool); +void snap_teardown(TINFO *); void snap_track(TINFO *, thread_op); void timestamp_init(void); void timestamp_once(WT_SESSION *, bool, bool); void timestamp_teardown(WT_SESSION *); -int trace_config(const char *); +void trace_config(const char *); void trace_init(void); void trace_ops_init(TINFO *); void trace_teardown(void); -void track(const char *, uint64_t, TINFO *); -void val_gen(WT_RAND_STATE *, WT_ITEM *, uint64_t); +void track(const char *, uint64_t); +void track_ops(TINFO *); +void val_gen(TABLE *, WT_RAND_STATE *, WT_ITEM *, uint64_t); void val_gen_init(WT_ITEM *); void val_gen_teardown(WT_ITEM *); -void val_init(void); +void val_init(TABLE *, void *); void wts_checkpoints(void); void wts_close(WT_CONNECTION **, WT_SESSION **); -void wts_create(const char *); +void wts_create_database(void); +void wts_create_home(void); void wts_dump(const char *, bool); -void wts_load(void); +void wts_load(TABLE *, void *); void wts_open(const char *, WT_CONNECTION **, WT_SESSION **, bool); -void wts_read_scan(void); +void wts_read_scan(TABLE *, void *); void wts_reopen(void); -void wts_salvage(void); +void wts_salvage(TABLE *, void *); void wts_stats(void); -void wts_verify(WT_CONNECTION *, const char *); +void wts_verify(TABLE *, void *); +/* Backward compatibility to older versions of the WiredTiger library. */ #if !defined(CUR2S) #define CUR2S(c) ((WT_SESSION_IMPL *)((WT_CURSOR *)c)->session) #endif diff --git a/src/third_party/wiredtiger/test/format/format.i b/src/third_party/wiredtiger/test/format/format.i index f0645ffb956..eb1d406ca79 100644 --- a/src/third_party/wiredtiger/test/format/format.i +++ b/src/third_party/wiredtiger/test/format/format.i @@ -147,6 +147,136 @@ random_sleep(WT_RAND_STATE *rnd, u_int max_seconds) } } +/* + * tables_apply - + * Call an underlying function on all tables. + */ +static inline void +tables_apply(void (*func)(TABLE *, void *), void *arg) +{ + u_int i; + + if (ntables == 0) + func(tables[0], arg); + else + for (i = 1; i <= ntables; ++i) + func(tables[i], arg); +} + +/* + * table_maxv -- + * Return the maximum value for a table configuration variable. + */ +static inline uint32_t +table_maxv(u_int off) +{ + uint32_t v; + u_int i; + + if (ntables == 0) + return (tables[0]->v[off].v); + + for (v = 0, i = 1; i <= ntables; ++i) + v = WT_MAX(v, tables[i]->v[off].v); + return (v); +} + +/* + * table_sumv -- + * Return the summed value for a table configuration variable. + */ +static inline uint32_t +table_sumv(u_int off) +{ + uint32_t v; + u_int i; + + if (ntables == 0) + return (tables[0]->v[off].v); + + for (v = 0, i = 1; i <= ntables; ++i) + v += tables[i]->v[off].v; + return (v); +} + +/* + * table_select -- + * Randomly select a table. + */ +static inline TABLE * +table_select(TINFO *tinfo) +{ + if (ntables == 0) + return (tables[0]); + + return (tables[mmrand(tinfo == NULL ? NULL : &tinfo->rnd, 1, ntables)]); +} + +/* + * table_select_type -- + * Randomly select a table of a specific type. + */ +static inline TABLE * +table_select_type(table_type type) +{ + u_int i; + + if (ntables == 0) + return (tables[0]->type == type ? tables[0] : NULL); + + for (i = mmrand(NULL, 1, ntables);; ++i) { + if (i > ntables) + i = 1; + if (tables[i]->type == type) + break; + } + return (tables[i]); +} + +/* + * wiredtiger_open_cursor -- + * Open a WiredTiger cursor. + */ +static inline void +wiredtiger_open_cursor( + WT_SESSION *session, const char *uri, const char *config, WT_CURSOR **cursorp) +{ + WT_DECL_RET; + + /* WT_SESSION.open_cursor can return EBUSY if concurrent with a metadata operation, retry. */ + while ((ret = session->open_cursor(session, uri, NULL, config, cursorp)) == EBUSY) + __wt_yield(); + testutil_checkfmt(ret, "%s", uri); +} + +/* + * table_cursor -- + * Return the cursor for a table, opening a new one if necessary. + */ +static inline WT_CURSOR * +table_cursor(TINFO *tinfo, u_int id) +{ + TABLE *table; + const char *config; + + testutil_assert(id > 0); + + /* The table ID is 1-based, the cursor array is 0-based. */ + table = tables[ntables == 0 ? 0 : id]; + --id; + + if (tinfo->cursors[id] == NULL) { + /* Configure "append", in the case of column stores, we append when inserting new rows. */ + config = table->type == ROW ? NULL : "append"; + wiredtiger_open_cursor(tinfo->session, table->uri, config, &tinfo->cursors[id]); + } + return (tinfo->cursors[id]); +} + +/* + * wiredtiger_begin_transaction -- + * Start a WiredTiger transaction. + */ static inline void wiredtiger_begin_transaction(WT_SESSION *session, const char *config) { @@ -166,9 +296,9 @@ wiredtiger_begin_transaction(WT_SESSION *session, const char *config) * Generate a key for lookup. */ static inline void -key_gen(WT_ITEM *key, uint64_t keyno) +key_gen(TABLE *table, WT_ITEM *key, uint64_t keyno) { - key_gen_common(key, keyno, "00"); + key_gen_common(table, key, keyno, "00"); } /* @@ -176,12 +306,12 @@ key_gen(WT_ITEM *key, uint64_t keyno) * Generate a key for insertion. */ static inline void -key_gen_insert(WT_RAND_STATE *rnd, WT_ITEM *key, uint64_t keyno) +key_gen_insert(TABLE *table, WT_RAND_STATE *rnd, WT_ITEM *key, uint64_t keyno) { static const char *const suffix[15] = { "01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12", "13", "14", "15"}; - key_gen_common(key, keyno, suffix[mmrand(rnd, 0, 14)]); + key_gen_common(table, key, keyno, suffix[mmrand(rnd, 0, 14)]); } /* @@ -193,11 +323,9 @@ lock_try_writelock(WT_SESSION *session, RWLOCK *lock) { testutil_assert(LOCK_INITIALIZED(lock)); - if (lock->lock_type == LOCK_WT) { + if (lock->lock_type == LOCK_WT) return (__wt_try_writelock((WT_SESSION_IMPL *)session, &lock->l.wt)); - } else { - return (pthread_rwlock_trywrlock(&lock->l.pthread)); - } + return (pthread_rwlock_trywrlock(&lock->l.pthread)); } /* @@ -209,11 +337,10 @@ lock_writelock(WT_SESSION *session, RWLOCK *lock) { testutil_assert(LOCK_INITIALIZED(lock)); - if (lock->lock_type == LOCK_WT) { + if (lock->lock_type == LOCK_WT) __wt_writelock((WT_SESSION_IMPL *)session, &lock->l.wt); - } else { + else testutil_check(pthread_rwlock_wrlock(&lock->l.pthread)); - } } /* @@ -225,11 +352,10 @@ lock_writeunlock(WT_SESSION *session, RWLOCK *lock) { testutil_assert(LOCK_INITIALIZED(lock)); - if (lock->lock_type == LOCK_WT) { + if (lock->lock_type == LOCK_WT) __wt_writeunlock((WT_SESSION_IMPL *)session, &lock->l.wt); - } else { + else testutil_check(pthread_rwlock_unlock(&lock->l.pthread)); - } } /* @@ -241,11 +367,10 @@ lock_readlock(WT_SESSION *session, RWLOCK *lock) { testutil_assert(LOCK_INITIALIZED(lock)); - if (lock->lock_type == LOCK_WT) { + if (lock->lock_type == LOCK_WT) __wt_readlock((WT_SESSION_IMPL *)session, &lock->l.wt); - } else { + else testutil_check(pthread_rwlock_rdlock(&lock->l.pthread)); - } } /* @@ -257,11 +382,10 @@ lock_readunlock(WT_SESSION *session, RWLOCK *lock) { testutil_assert(LOCK_INITIALIZED(lock)); - if (lock->lock_type == LOCK_WT) { + if (lock->lock_type == LOCK_WT) __wt_readunlock((WT_SESSION_IMPL *)session, &lock->l.wt); - } else { + else testutil_check(pthread_rwlock_unlock(&lock->l.pthread)); - } } #define trace_msg(fmt, ...) \ @@ -275,16 +399,16 @@ lock_readunlock(WT_SESSION *session, RWLOCK *lock) (uintmax_t)__ts.tv_nsec / WT_THOUSAND, g.tidbuf, __VA_ARGS__)); \ } \ } while (0) -#define trace_op(tinfo, fmt, ...) \ - do { \ - if (g.trace) { \ - struct timespec __ts; \ - WT_SESSION *__s = (tinfo)->trace; \ - __wt_epoch((WT_SESSION_IMPL *)__s, &__ts); \ - testutil_check( \ - __s->log_printf(__s, "[%" PRIuMAX ":%" PRIuMAX "][%s] " fmt, (uintmax_t)__ts.tv_sec, \ - (uintmax_t)__ts.tv_nsec / WT_THOUSAND, tinfo->tidbuf, __VA_ARGS__)); \ - } \ +#define trace_op(tinfo, fmt, ...) \ + do { \ + if (g.trace) { \ + struct timespec __ts; \ + WT_SESSION *__s = (tinfo)->trace; \ + __wt_epoch((WT_SESSION_IMPL *)__s, &__ts); \ + testutil_check(__s->log_printf(__s, "[%" PRIuMAX ":%" PRIuMAX "][%s]:%s " fmt, \ + (uintmax_t)__ts.tv_sec, (uintmax_t)__ts.tv_nsec / WT_THOUSAND, (tinfo)->tidbuf, \ + (tinfo)->table->uri, __VA_ARGS__)); \ + } \ } while (0) /* diff --git a/src/third_party/wiredtiger/test/format/format.sh b/src/third_party/wiredtiger/test/format/format.sh index 461875e33bb..8a04266ce86 100755 --- a/src/third_party/wiredtiger/test/format/format.sh +++ b/src/third_party/wiredtiger/test/format/format.sh @@ -45,17 +45,16 @@ smoke_base_2="$smoke_base_1 leaf_page_max=9 internal_page_max=9" smoke_list=( # Three access methods. "$smoke_base_1 file_type=row" - # Temporarily disabled + # Temporarily disabled: FIXME FLCS # "$smoke_base_1 file_type=fix" - # "$smoke_base_1 file_type=var" + "$smoke_base_1 file_type=var" # Huffman value encoding. "$smoke_base_1 file_type=row huffman_value=1" - # Temporarily disabled - # "$smoke_base_1 file_type=var huffman_value=1" + "$smoke_base_1 file_type=var huffman_value=1" # LSM - # Temporarily disabled + # Temporarily disabled: FIXME LSM # "$smoke_base_1 file_type=row runs.source=lsm" # Force the statistics server. @@ -64,8 +63,7 @@ smoke_list=( # Overflow testing. "$smoke_base_2 file_type=row key_min=256" "$smoke_base_2 file_type=row key_min=256 value_min=256" - # Temporarily disabled - # "$smoke_base_2 file_type=var value_min=256" + "$smoke_base_2 file_type=var value_min=256" ) smoke_next=0 @@ -392,18 +390,36 @@ resolve() echo "$name: original directory copied into $dir.RECOVER" echo) >> $log - # Everything is a table unless explicitly a file. - uri="table:wt" - grep 'runs.source=file' $dir/CONFIG > /dev/null && uri="file:wt" + # Verify the objects. In current format, it's a list of files named with a + # leading F or tables named with a leading T. Historically, it was a file + # or table named "wt". + verify_failed=0 + for i in $(ls $dir | sed -e 's/.*\///'); do + case $i in + F*) uri="file:$i";; + T*) uri="table:${i%.wt}";; + wt) uri="file:wt";; + wt.wt) uri="table:wt";; + *) continue;; + esac + + # Use the wt utility to recover & verify the object. + echo "verify: $wt_binary -m -R -h $dir verify $uri" >> $log + if $($wt_binary -m -R -h $dir verify $uri >> $log 2>&1); then + continue + fi + + verify_failed=1 + break + done - # Use the wt utility to recover & verify the object. - if $($wt_binary -m -R -h $dir verify $uri >> $log 2>&1); then - rm -rf $dir $dir.RECOVER $log - success=$(($success + 1)) - verbose "$name: job in $dir successfully completed" + if [[ $verify_failed -eq 0 ]]; then + rm -rf $dir $dir.RECOVER $log + success=$(($success + 1)) + verbose "$name: job in $dir successfully completed" else - echo "$name: job in $dir failed abort/recovery testing" - report_failure $dir + echo "$name: job in $dir failed abort/recovery testing" + report_failure $dir fi continue } @@ -498,7 +514,7 @@ format() live_record_binary="$live_record_binary --save-on=error" fi - cmd="$live_record_binary $format_binary -c "$config" -h "$dir" -1 $args quiet=1" + cmd="$live_record_binary $format_binary -c "$config" -h "$dir" $args quiet=1" echo "$name: $cmd" # Disassociate the command from the shell script so we can exit and let the command diff --git a/src/third_party/wiredtiger/test/format/import.c b/src/third_party/wiredtiger/test/format/import.c index 0b85515b806..1d22782ca3e 100644 --- a/src/third_party/wiredtiger/test/format/import.c +++ b/src/third_party/wiredtiger/test/format/import.c @@ -104,18 +104,14 @@ import(void *arg) copy_file_into_directory(import_session, "import.wt"); /* Perform import with either repair or file metadata. */ - memset(buf, 0, sizeof(buf)); import_value = mmrand(NULL, 0, 1); - if (import_value == 0) { + if (import_value == 0) testutil_check(__wt_snprintf(buf, sizeof(buf), "import=(enabled,repair=true)")); - if ((ret = session->create(session, IMPORT_URI, buf)) != 0) - testutil_die(ret, "session.import", ret); - } else { + else testutil_check(__wt_snprintf(buf, sizeof(buf), "%s,import=(enabled,repair=false,file_metadata=(%s))", table_config, file_config)); - if ((ret = session->create(session, IMPORT_URI, buf)) != 0) - testutil_die(ret, "session.import", ret); - } + if ((ret = session->create(session, IMPORT_URI, buf)) != 0) + testutil_die(ret, "session.import", ret); verify_import(session); diff --git a/src/third_party/wiredtiger/test/format/kv.c b/src/third_party/wiredtiger/test/format/kv.c index 32788b86ffb..54ed9732fb4 100644 --- a/src/third_party/wiredtiger/test/format/kv.c +++ b/src/third_party/wiredtiger/test/format/kv.c @@ -33,11 +33,16 @@ * Initialize the keys for a run. */ void -key_init(void) +key_init(TABLE *table, void *arg) { FILE *fp; size_t i; uint32_t max; + char buf[MAX_FORMAT_PATH]; + + (void)arg; /* unused argument */ + + testutil_check(__wt_snprintf(buf, sizeof(buf), "%s.%u", g.home_key, table->id)); /* * The key is a variable length item with a leading 10-digit value. Since we have to be able @@ -48,10 +53,10 @@ key_init(void) * Read in the values during reopen. */ if (g.reopen) { - if ((fp = fopen(g.home_key, "r")) == NULL) - testutil_die(errno, "%s", g.home_key); - for (i = 0; i < WT_ELEMENTS(g.key_rand_len); ++i) - fp_readv(fp, g.home_key, &g.key_rand_len[i]); + if ((fp = fopen(buf, "r")) == NULL) + testutil_die(errno, "%s", buf); + for (i = 0; i < WT_ELEMENTS(table->key_rand_len); ++i) + fp_readv(fp, buf, &table->key_rand_len[i]); fclose_and_clear(&fp); return; } @@ -62,23 +67,19 @@ key_init(void) * Focus on relatively small items, admitting the possibility of larger items. Pick a size close * to the minimum most of the time, only create a larger item 1 in 20 times. */ - for (i = 0; i < WT_ELEMENTS(g.key_rand_len); ++i) { - max = g.c_key_max; - if (i % 20 != 0 && max > g.c_key_min + 20) - max = g.c_key_min + 20; - g.key_rand_len[i] = mmrand(NULL, g.c_key_min, max); + for (i = 0; i < WT_ELEMENTS(table->key_rand_len); ++i) { + max = TV(BTREE_KEY_MAX); + if (i % 20 != 0 && max > TV(BTREE_KEY_MIN) + 20) + max = TV(BTREE_KEY_MIN) + 20; + table->key_rand_len[i] = mmrand(NULL, TV(BTREE_KEY_MIN), max); } /* Write out the values for a subsequent reopen. */ - if ((fp = fopen(g.home_key, "w")) == NULL) - testutil_die(errno, "%s", g.home_key); - for (i = 0; i < WT_ELEMENTS(g.key_rand_len); ++i) - fprintf(fp, "%" PRIu32 "\n", g.key_rand_len[i]); + if ((fp = fopen(buf, "w")) == NULL) + testutil_die(errno, "%s", buf); + for (i = 0; i < WT_ELEMENTS(table->key_rand_len); ++i) + fprintf(fp, "%" PRIu32 "\n", table->key_rand_len[i]); fclose_and_clear(&fp); - - /* Fill in the common key prefix length (which is added to the key min/max). */ - if (g.c_prefix != 0) - g.prefix_len = mmrand(NULL, 15, 80); } /* @@ -91,7 +92,7 @@ key_gen_init(WT_ITEM *key) size_t i, len; char *p; - len = WT_MAX(KILOBYTE(100), g.c_key_max + g.prefix_len); + len = WT_MAX(KILOBYTE(100), table_maxv(V_TABLE_BTREE_KEY_MAX) + g.prefix_len_max + 10); p = dmalloc(len); for (i = 0; i < len; ++i) p[i] = "abcdefghijklmnopqrstuvwxyz"[i % 26]; @@ -113,20 +114,22 @@ key_gen_teardown(WT_ITEM *key) memset(key, 0, sizeof(*key)); } +#define COMMON_PREFIX_CHAR 'C' + /* * key_gen_common -- * Row-store key generation code shared between normal and insert key generation. */ void -key_gen_common(WT_ITEM *key, uint64_t keyno, const char *const suffix) +key_gen_common(TABLE *table, WT_ITEM *key, uint64_t keyno, const char *const suffix) { + size_t i; uint64_t n; + uint32_t prefix_len; char *p; const char *bucket; - testutil_assert(g.type == ROW); - - p = key->mem; + testutil_assert(table->type == ROW); /* * The workload we're trying to mimic with a prefix is a long common prefix followed by a record @@ -138,16 +141,32 @@ key_gen_common(WT_ITEM *key, uint64_t keyno, const char *const suffix) * lexicographically, meaning the common key prefix will grow and shrink by a few bytes as the * number increments, which is a good thing for testing. */ - if (g.prefix_len > 0) { - bucket = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; - for (n = keyno; n > 0; n >>= 1) { - if (*bucket == 'z') - break; - ++bucket; + p = key->mem; + prefix_len = TV(BTREE_PREFIX_LEN); + if (g.prefix_len_max != 0) { + /* + * Not all tables have prefixes and prefixes may be of different lengths. If any table has a + * prefix, check if we need to reset the leading bytes in the key to their original values. + * It's an ugly test, but it avoids rewriting the key in a performance path. The variable is + * the largest prefix in the run, and the hard-coded 20 gets us past the key appended to + * that prefix. + */ + if (p[1] == COMMON_PREFIX_CHAR) { + for (i = 0; i < g.prefix_len_max + 20; ++i) + p[i] = "abcdefghijklmnopqrstuvwxyz"[i % 26]; + p = key->mem; + } + if (prefix_len != 0) { + bucket = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + for (n = keyno; n > 0; n >>= 1) { + if (*bucket == 'z') + break; + ++bucket; + } + p[0] = *bucket; + memset(p + 1, COMMON_PREFIX_CHAR, prefix_len - 1); + p += prefix_len; } - p[0] = *bucket; - memset(p + 1, 'C', g.prefix_len - 1); - p += g.prefix_len; } /* @@ -166,19 +185,19 @@ key_gen_common(WT_ITEM *key, uint64_t keyno, const char *const suffix) * the cache. Handle that here, use a really big key 1 in 2500 times. */ key->data = key->mem; - key->size = g.prefix_len; - key->size += keyno % 2500 == 0 && g.c_key_max < KILOBYTE(80) ? + key->size = prefix_len; + key->size += keyno % 2500 == 0 && TV(BTREE_KEY_MAX) < KILOBYTE(80) ? KILOBYTE(80) : - g.key_rand_len[keyno % WT_ELEMENTS(g.key_rand_len)]; + table->key_rand_len[keyno % WT_ELEMENTS(table->key_rand_len)]; testutil_assert(key->size <= key->memsize); } -static char *val_base; /* Base/original value */ -static uint32_t val_dup_data_len; /* Length of duplicate data items */ -static uint32_t val_len; /* Length of data items */ - +/* + * val_len -- + * Select and return the length for a value. + */ static inline uint32_t -value_len(WT_RAND_STATE *rnd, uint64_t keyno, uint32_t min, uint32_t max) +val_len(WT_RAND_STATE *rnd, uint64_t keyno, uint32_t min, uint32_t max) { /* * Focus on relatively small items, admitting the possibility of larger items. Pick a size close @@ -193,15 +212,22 @@ value_len(WT_RAND_STATE *rnd, uint64_t keyno, uint32_t min, uint32_t max) return (mmrand(rnd, min, max)); } +/* + * val_init -- + * Initialize the value structures for a table. + */ void -val_init(void) +val_init(TABLE *table, void *arg) { size_t i; + uint32_t len; + + (void)arg; /* unused argument */ /* Discard any previous value initialization. */ - free(val_base); - val_base = NULL; - val_dup_data_len = val_len = 0; + free(table->val_base); + table->val_base = NULL; + table->val_dup_data_len = 0; /* * Set initial buffer contents to recognizable text. @@ -209,23 +235,35 @@ val_init(void) * Add a few extra bytes in order to guarantee we can always offset into the buffer by a few * extra bytes, used to generate different data for column-store run-length encoded files. */ - val_len = WT_MAX(KILOBYTE(100), g.c_value_max) + 20; - val_base = dmalloc(val_len); - for (i = 0; i < val_len; ++i) - val_base[i] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"[i % 26]; + len = WT_MAX(KILOBYTE(100), table_maxv(V_TABLE_BTREE_VALUE_MAX)) + 20; + table->val_base = dmalloc(len); + for (i = 0; i < len; ++i) + table->val_base[i] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"[i % 26]; - val_dup_data_len = value_len(NULL, (uint64_t)mmrand(NULL, 1, 20), g.c_value_min, g.c_value_max); + table->val_dup_data_len = + val_len(NULL, (uint64_t)mmrand(NULL, 1, 20), TV(BTREE_VALUE_MIN), TV(BTREE_VALUE_MAX)); } +/* + * val_gen_init -- + * Initialize a single value structure. + */ void val_gen_init(WT_ITEM *value) { - value->mem = dmalloc(val_len); - value->memsize = val_len; + uint32_t len; + + len = WT_MAX(KILOBYTE(100), table_maxv(V_TABLE_BTREE_VALUE_MAX)) + 20; + value->mem = dmalloc(len); + value->memsize = len; value->data = value->mem; value->size = 0; } +/* + * val_gen_teardown -- + * Discard a single value structure. + */ void val_gen_teardown(WT_ITEM *value) { @@ -233,8 +271,12 @@ val_gen_teardown(WT_ITEM *value) memset(value, 0, sizeof(*value)); } +/* + * val_gen -- + * Generate a new value. + */ void -val_gen(WT_RAND_STATE *rnd, WT_ITEM *value, uint64_t keyno) +val_gen(TABLE *table, WT_RAND_STATE *rnd, WT_ITEM *value, uint64_t keyno) { char *p; @@ -244,8 +286,8 @@ val_gen(WT_RAND_STATE *rnd, WT_ITEM *value, uint64_t keyno) /* * Fixed-length records: take the low N bits from the last digit of the record number. */ - if (g.type == FIX) { - switch (g.c_bitcnt) { + if (table->type == FIX) { + switch (TV(BTREE_BITCNT)) { case 8: p[0] = (char)mmrand(rnd, 1, 0xff); break; @@ -289,14 +331,14 @@ val_gen(WT_RAND_STATE *rnd, WT_ITEM *value, uint64_t keyno) * Data items have unique leading numbers by default and random lengths; variable-length * column-stores use a duplicate data value to test RLE. */ - if (g.type == VAR && mmrand(rnd, 1, 100) < g.c_repeat_data_pct) { - value->size = val_dup_data_len; - memcpy(p, val_base, value->size); + if (table->type == VAR && mmrand(rnd, 1, 100) < TV(BTREE_REPEAT_DATA_PCT)) { + value->size = table->val_dup_data_len; + memcpy(p, table->val_base, value->size); (void)strcpy(p, "DUPLICATEV"); p[10] = '/'; } else { - value->size = value_len(rnd, keyno, g.c_value_min, g.c_value_max); - memcpy(p, val_base, value->size); + value->size = val_len(rnd, keyno, TV(BTREE_VALUE_MIN), TV(BTREE_VALUE_MAX)); + memcpy(p, table->val_base, value->size); u64_to_string_zf(keyno, p, 11); p[10] = '/'; } diff --git a/src/third_party/wiredtiger/test/format/ops.c b/src/third_party/wiredtiger/test/format/ops.c index 4c24ba9fd45..6d079eb12aa 100644 --- a/src/third_party/wiredtiger/test/format/ops.c +++ b/src/third_party/wiredtiger/test/format/ops.c @@ -28,23 +28,23 @@ #include "format.h" -static int col_insert(TINFO *, WT_CURSOR *); -static void col_insert_resolve(TINFO *); -static int col_modify(TINFO *, WT_CURSOR *, bool); -static int col_remove(TINFO *, WT_CURSOR *, bool); -static int col_reserve(TINFO *, WT_CURSOR *, bool); -static int col_truncate(TINFO *, WT_CURSOR *); -static int col_update(TINFO *, WT_CURSOR *, bool); -static int nextprev(TINFO *, WT_CURSOR *, bool); +static int col_insert(TINFO *); +static void col_insert_resolve(TABLE *, void *); +static int col_modify(TINFO *, bool); +static int col_remove(TINFO *, bool); +static int col_reserve(TINFO *, bool); +static int col_truncate(TINFO *); +static int col_update(TINFO *, bool); +static int nextprev(TINFO *, bool); static WT_THREAD_RET ops(void *); -static int read_row(TINFO *, WT_CURSOR *); -static int read_row_worker(WT_CURSOR *, TINFO *, uint64_t, WT_ITEM *, WT_ITEM *, bool); -static int row_insert(TINFO *, WT_CURSOR *, bool); -static int row_modify(TINFO *, WT_CURSOR *, bool); -static int row_remove(TINFO *, WT_CURSOR *, bool); -static int row_reserve(TINFO *, WT_CURSOR *, bool); -static int row_truncate(TINFO *, WT_CURSOR *); -static int row_update(TINFO *, WT_CURSOR *, bool); +static int read_row(TINFO *); +static int read_row_worker(TINFO *, TABLE *, WT_CURSOR *, uint64_t, WT_ITEM *, WT_ITEM *, bool); +static int row_insert(TINFO *, bool); +static int row_modify(TINFO *, bool); +static int row_remove(TINFO *, bool); +static int row_reserve(TINFO *, bool); +static int row_truncate(TINFO *); +static int row_update(TINFO *, bool); static char modify_repl[256]; @@ -113,13 +113,16 @@ tinfo_init(void) /* Allocate the thread structures separately to minimize false sharing. */ if (tinfo_list == NULL) { - tinfo_list = dcalloc((size_t)g.c_threads + 1, sizeof(TINFO *)); - for (i = 0; i < g.c_threads; ++i) { + tinfo_list = dcalloc((size_t)GV(RUNS_THREADS) + 1, sizeof(TINFO *)); + for (i = 0; i < GV(RUNS_THREADS); ++i) { tinfo_list[i] = dcalloc(1, sizeof(TINFO)); tinfo = tinfo_list[i]; tinfo->id = (int)i + 1; + tinfo->cursors = dcalloc(WT_MAX(ntables, 1), sizeof(tinfo->cursors[0])); + tinfo->col_insert = dcalloc(WT_MAX(ntables, 1), sizeof(tinfo->col_insert[0])); + /* Set up the default key and value buffers. */ tinfo->key = &tinfo->_key; key_gen_init(tinfo->key); @@ -133,7 +136,7 @@ tinfo_init(void) } /* Cleanup for each new run. */ - for (i = 0; i < g.c_threads; ++i) { + for (i = 0; i < GV(RUNS_THREADS); ++i) { tinfo = tinfo_list[i]; tinfo->ops = 0; @@ -147,10 +150,8 @@ tinfo_init(void) tinfo->update = 0; tinfo->session = NULL; - tinfo->cursor = NULL; - - memset(tinfo->insert_list, 0, sizeof(tinfo->insert_list)); - tinfo->insert_list_cnt = 0; + memset(tinfo->cursors, 0, WT_MAX(ntables, 1) * sizeof(tinfo->cursors[0])); + memset(tinfo->col_insert, 0, WT_MAX(ntables, 1) * sizeof(tinfo->col_insert[0])); tinfo->state = TINFO_RUNNING; tinfo->quit = false; @@ -167,19 +168,16 @@ tinfo_teardown(void) TINFO *tinfo; u_int i; - for (i = 0; i < g.c_threads; ++i) { + for (i = 0; i < GV(RUNS_THREADS); ++i) { tinfo = tinfo_list[i]; + free(tinfo->cursors); + free(tinfo->col_insert); + __wt_buf_free(NULL, &tinfo->vprint); __wt_buf_free(NULL, &tinfo->moda); __wt_buf_free(NULL, &tinfo->modb); - /* - * Assert records were not removed unless configured to do so, otherwise subsequent runs can - * incorrectly report scan errors. - */ - testutil_assert(g.c_delete_pct != 0 || tinfo->remove == 0); - snap_teardown(tinfo); key_gen_teardown(tinfo->key); val_gen_teardown(tinfo->value); @@ -192,25 +190,21 @@ tinfo_teardown(void) } /* - * tinfo_rollback_to_stable -- + * rollback_to_stable -- * Do a rollback to stable and verify operations. */ static void -tinfo_rollback_to_stable(WT_SESSION *session) +rollback_to_stable(void) { - WT_CURSOR *cursor; - - /* Rollback-to-stable only makes sense for timestamps and on-disk stores. */ - if (g.c_txn_timestamps == 0 || g.c_in_memory != 0) + /* Rollback-to-stable only makes sense for timestamps. */ + if (!g.transaction_timestamps_config) return; trace_msg("%-10s ts=%" PRIu64, "rts", g.stable_timestamp); testutil_check(g.wts_conn->rollback_to_stable(g.wts_conn, NULL)); /* Check the saved snap operations for consistency. */ - testutil_check(session->open_cursor(session, g.uri, NULL, NULL, &cursor)); - snap_repeat_rollback(cursor, tinfo_list, g.c_threads); - testutil_check(cursor->close(cursor)); + snap_repeat_rollback(tinfo_list, GV(RUNS_THREADS)); } /* @@ -252,12 +246,12 @@ operations(u_int ops_seconds, bool lastrun) * Calculate how many fourth-of-a-second sleeps until the timer expires. If the timer expires * and threads don't return in 15 minutes, assume there is something hung, and force the quit. */ - if (g.c_ops == 0) + if (GV(RUNS_OPS) == 0) thread_ops = -1; else { - if (g.c_ops < g.c_threads) - g.c_ops = g.c_threads; - thread_ops = g.c_ops / g.c_threads; + if (GV(RUNS_OPS) < GV(RUNS_THREADS)) + GV(RUNS_OPS) = GV(RUNS_THREADS); + thread_ops = GV(RUNS_OPS) / GV(RUNS_THREADS); } if (ops_seconds == 0) fourths = quit_fourths = -1; @@ -273,35 +267,35 @@ operations(u_int ops_seconds, bool lastrun) tinfo_init(); trace_msg("%s", "=============== thread ops start"); - for (i = 0; i < g.c_threads; ++i) { + for (i = 0; i < GV(RUNS_THREADS); ++i) { tinfo = tinfo_list[i]; testutil_check(__wt_thread_create(NULL, &tinfo->tid, ops, tinfo)); } /* Start optional special-purpose threads. */ - if (g.c_alter) + if (GV(OPS_ALTER)) testutil_check(__wt_thread_create(NULL, &alter_tid, alter, NULL)); - if (g.c_backups) + if (GV(BACKUP)) testutil_check(__wt_thread_create(NULL, &backup_tid, backup, NULL)); - if (g.c_compact) + if (GV(OPS_COMPACTION)) testutil_check(__wt_thread_create(NULL, &compact_tid, compact, NULL)); - if (g.c_hs_cursor) + if (GV(OPS_HS_CURSOR)) testutil_check(__wt_thread_create(NULL, &hs_tid, hs_cursor, NULL)); - if (g.c_import) + if (GV(IMPORT)) testutil_check(__wt_thread_create(NULL, &import_tid, import, NULL)); - if (g.c_random_cursor) + if (GV(OPS_RANDOM_CURSOR)) testutil_check(__wt_thread_create(NULL, &random_tid, random_kv, NULL)); - if (g.c_txn_timestamps) + if (g.transaction_timestamps_config) testutil_check(__wt_thread_create(NULL, ×tamp_tid, timestamp, tinfo_list)); - if (g.c_checkpoint_flag == CHECKPOINT_ON) + if (g.checkpoint_config == CHECKPOINT_ON) testutil_check(__wt_thread_create(NULL, &checkpoint_tid, checkpoint, NULL)); /* Spin on the threads, calculating the totals. */ for (;;) { /* Clear out the totals each pass. */ memset(&total, 0, sizeof(total)); - for (i = 0, running = false; i < g.c_threads; ++i) { + for (i = 0, running = false; i < GV(RUNS_THREADS); ++i) { tinfo = tinfo_list[i]; total.commit += tinfo->commit; total.insert += tinfo->insert; @@ -332,12 +326,12 @@ operations(u_int ops_seconds, bool lastrun) /* * On the last execution, optionally drop core for recovery testing. */ - if (lastrun && g.c_abort) + if (lastrun && GV(FORMAT_ABORT)) random_failure(); tinfo->quit = true; } } - track("ops", 0ULL, &total); + track_ops(&total); if (!running) break; __wt_sleep(0, 250000); /* 1/4th of a second */ @@ -363,21 +357,21 @@ operations(u_int ops_seconds, bool lastrun) /* Wait for the special-purpose threads. */ g.workers_finished = true; - if (g.c_alter) + if (GV(OPS_ALTER)) testutil_check(__wt_thread_join(NULL, &alter_tid)); - if (g.c_backups) + if (GV(BACKUP)) testutil_check(__wt_thread_join(NULL, &backup_tid)); - if (g.c_checkpoint_flag == CHECKPOINT_ON) + if (g.checkpoint_config == CHECKPOINT_ON) testutil_check(__wt_thread_join(NULL, &checkpoint_tid)); - if (g.c_compact) + if (GV(OPS_COMPACTION)) testutil_check(__wt_thread_join(NULL, &compact_tid)); - if (g.c_hs_cursor) + if (GV(OPS_HS_CURSOR)) testutil_check(__wt_thread_join(NULL, &hs_tid)); - if (g.c_import) + if (GV(IMPORT)) testutil_check(__wt_thread_join(NULL, &import_tid)); - if (g.c_random_cursor) + if (GV(OPS_RANDOM_CURSOR)) testutil_check(__wt_thread_join(NULL, &random_tid)); - if (g.c_txn_timestamps) + if (g.transaction_timestamps_config) testutil_check(__wt_thread_join(NULL, ×tamp_tid)); g.workers_finished = false; @@ -390,7 +384,7 @@ operations(u_int ops_seconds, bool lastrun) * close/re-open pair. Note we are not advancing the oldest timestamp, otherwise we wouldn't be * able to replay operations from after rollback-to-stable completes. */ - tinfo_rollback_to_stable(session); + rollback_to_stable(); if (lastrun) { tinfo_teardown(); @@ -496,12 +490,12 @@ commit_transaction(TINFO *tinfo, bool prepared) uint64_t ts; char buf[64]; - ++tinfo->commit; - session = tinfo->session; + ++tinfo->commit; + ts = 0; /* -Wconditional-uninitialized */ - if (g.c_txn_timestamps) { + if (g.transaction_timestamps_config) { if (prepared) lock_readlock(session, &g.prepare_commit_lock); @@ -628,47 +622,35 @@ prepare_transaction(TINFO *tinfo) } while (0) /* - * ops_open_session -- - * Create a new session/cursor pair for the thread. + * ops_session_open -- + * Create a new session for the thread. */ static void -ops_open_session(TINFO *tinfo) +ops_session_open(TINFO *tinfo) { WT_CONNECTION *conn; - WT_CURSOR *cursor; - WT_DECL_RET; WT_SESSION *session; conn = g.wts_conn; - /* Close any open session/cursor. */ + /* Close any open session (which closes all open cursors as well). */ if ((session = tinfo->session) != NULL) testutil_check(session->close(session, NULL)); + tinfo->session = NULL; + memset(tinfo->cursors, 0, WT_MAX(ntables, 1) * sizeof(tinfo->cursors[0])); - testutil_check(conn->open_session(conn, NULL, NULL, &session)); - - /* - * Configure "append", in the case of column stores, we append when inserting new rows. - * - * WT_SESSION.open_cursor can return EBUSY if concurrent with a metadata operation, retry. - */ - while ((ret = session->open_cursor(session, g.uri, NULL, "append", &cursor)) == EBUSY) - __wt_yield(); - testutil_checkfmt(ret, "%s", g.uri); - - tinfo->session = session; - tinfo->cursor = cursor; + testutil_check(conn->open_session(conn, NULL, NULL, &tinfo->session)); } /* Isolation configuration. */ typedef enum { + ISOLATION_IMPLICIT, ISOLATION_READ_COMMITTED, ISOLATION_READ_UNCOMMITTED, ISOLATION_SNAPSHOT } iso_level_t; -/* When in an explicit snapshot isolation transaction, track operations for later - * repetition. */ +/* When in an explicit snapshot isolation transaction, track operations for later repetition. */ #define SNAP_TRACK(tinfo, op) \ do { \ if (intxn && iso_level == ISOLATION_SNAPSHOT) \ @@ -682,14 +664,15 @@ typedef enum { static WT_THREAD_RET ops(void *arg) { + struct col_insert *cip; TINFO *tinfo; - WT_CURSOR *cursor; + TABLE *table; WT_DECL_RET; WT_SESSION *session; iso_level_t iso_level; thread_op op; - uint64_t max_rows, reset_op, session_op, truncate_op; - uint32_t range, rnd; + uint64_t reset_op, session_op, truncate_op; + uint32_t max_rows, range, rnd; u_int i, j; const char *iso_config; bool greater_than, intxn, next, positioned, prepared; @@ -703,29 +686,28 @@ ops(void *arg) * pound on the same key/value pairs, that is, by making them traverse the same RNG space. 75% * of the time we run in independent RNG space. */ - if (g.c_independent_thread_rng) + if (GV(FORMAT_INDEPENDENT_THREAD_RNG)) __wt_random_init_seed(NULL, &tinfo->rnd); else __wt_random_init(&tinfo->rnd); iso_level = ISOLATION_SNAPSHOT; /* -Wconditional-uninitialized */ - /* Set the first operation where we'll create sessions and cursors. */ - cursor = NULL; + /* Set the first operation where we'll create a new session and cursors. */ session = NULL; session_op = 0; /* Set the first operation where we'll reset the session. */ reset_op = mmrand(&tinfo->rnd, 100, 10000); /* Set the first operation where we'll truncate a range. */ - truncate_op = g.c_truncate == 0 ? UINT64_MAX : mmrand(&tinfo->rnd, 100, 10000); + truncate_op = mmrand(&tinfo->rnd, 100, 10000); /* Initialize operation trace. */ trace_ops_init(tinfo); for (intxn = false; !tinfo->quit; ++tinfo->ops) { /* Periodically open up a new session and cursors. */ - if (tinfo->ops > session_op || session == NULL || cursor == NULL) { + if (tinfo->ops > session_op || session == NULL) { /* * We can't swap sessions/cursors if in a transaction, resolve any running transaction. */ @@ -734,13 +716,11 @@ ops(void *arg) intxn = false; } - ops_open_session(tinfo); + ops_session_open(tinfo); + session = tinfo->session; /* Pick the next session/cursor close/open. */ session_op += mmrand(&tinfo->rnd, 100, 5000); - - session = tinfo->session; - cursor = tinfo->cursor; } /* @@ -752,20 +732,27 @@ ops(void *arg) testutil_check(session->reset(session)); /* Pick the next reset operation. */ - reset_op += mmrand(&tinfo->rnd, 20000, 50000); + reset_op += mmrand(&tinfo->rnd, 40000, 60000); } + /* Select a table, acquire a cursor. */ + tinfo->table = table = table_select(tinfo); + tinfo->cursor = table_cursor(tinfo, table->id); + /* - * If not in a transaction and in a timestamp world, occasionally repeat a timestamped - * operation. + * If not in a transaction and in a timestamp world, occasionally repeat timestamped + * operations. */ - if (!intxn && g.c_txn_timestamps && mmrand(&tinfo->rnd, 1, 15) == 1) { + if (!intxn && g.transaction_timestamps_config && mmrand(&tinfo->rnd, 1, 15) == 1) { ++tinfo->search; - snap_repeat_single(cursor, tinfo); + snap_repeat_single(tinfo); } - /* If not in a transaction and in a timestamp world, start a transaction. */ - if (!intxn && g.c_txn_timestamps) { + /* + * If not in a transaction and in a timestamp world, start a transaction (which is always at + * snapshot-isolation). + */ + if (!intxn && g.transaction_timestamps_config) { iso_level = ISOLATION_SNAPSHOT; begin_transaction_ts(tinfo); intxn = true; @@ -773,47 +760,52 @@ ops(void *arg) /* * If not in a transaction and not in a timestamp world, start a transaction some percentage - * of the time. + * of the time, otherwise it's an implicit transaction. */ - if (!intxn && mmrand(&tinfo->rnd, 1, 100) < g.c_txn_implicit) { - iso_level = ISOLATION_SNAPSHOT; - iso_config = "isolation=snapshot"; + if (!intxn && !g.transaction_timestamps_config) { + iso_level = ISOLATION_IMPLICIT; + + if (mmrand(&tinfo->rnd, 1, 100) < GV(TRANSACTION_IMPLICIT)) { + iso_level = ISOLATION_SNAPSHOT; + iso_config = "isolation=snapshot"; + + /* Occasionally do reads at an isolation level lower than snapshot. */ + switch (mmrand(NULL, 1, 20)) { + case 1: + iso_level = ISOLATION_READ_COMMITTED; /* 5% */ + iso_config = "isolation=read-committed"; + break; + case 2: + iso_level = ISOLATION_READ_UNCOMMITTED; /* 5% */ + iso_config = "isolation=read-uncommitted"; + break; + } - /* Occasionally do reads at an isolation level lower than snapshot. */ - switch (mmrand(NULL, 1, 20)) { - case 1: - iso_level = ISOLATION_READ_COMMITTED; /* 5% */ - iso_config = "isolation=read-committed"; - break; - case 2: - iso_level = ISOLATION_READ_UNCOMMITTED; /* 5% */ - iso_config = "isolation=read-uncommitted"; - break; + begin_transaction(tinfo, iso_config); + intxn = true; } - - begin_transaction(tinfo, iso_config); - intxn = true; } /* - * Select an operation: all updates must be in snapshot isolation, modify must be in an - * explicit transaction. + * Select an operation: updates cannot happen at lower isolation levels and modify must be + * in an explicit transaction. */ op = READ; - if (iso_level == ISOLATION_SNAPSHOT) { + if (iso_level == ISOLATION_IMPLICIT || iso_level == ISOLATION_SNAPSHOT) { i = mmrand(&tinfo->rnd, 1, 100); - if (i < g.c_delete_pct && tinfo->ops > truncate_op) { + if (TV(OPS_TRUNCATE) && i < TV(OPS_PCT_DELETE) && tinfo->ops > truncate_op) { op = TRUNCATE; /* Pick the next truncate operation. */ truncate_op += mmrand(&tinfo->rnd, 20000, 100000); - } else if (i < g.c_delete_pct) + } else if (i < TV(OPS_PCT_DELETE)) op = REMOVE; - else if (i < g.c_delete_pct + g.c_insert_pct) + else if (i < TV(OPS_PCT_DELETE) + TV(OPS_PCT_INSERT)) op = INSERT; - else if (intxn && i < g.c_delete_pct + g.c_insert_pct + g.c_modify_pct) + else if (intxn && i < TV(OPS_PCT_DELETE) + TV(OPS_PCT_INSERT) + TV(OPS_PCT_MODIFY)) op = MODIFY; - else if (i < g.c_delete_pct + g.c_insert_pct + g.c_modify_pct + g.c_write_pct) + else if (i < + TV(OPS_PCT_DELETE) + TV(OPS_PCT_INSERT) + TV(OPS_PCT_MODIFY) + TV(OPS_PCT_WRITE)) op = UPDATE; } @@ -821,7 +813,7 @@ ops(void *arg) * Select a row. Column-store extends the object, explicitly read the maximum row count and * then use a local variable so the value won't change inside the loop. */ - max_rows = (volatile uint64_t)g.rows; + WT_ORDERED_READ(max_rows, table->rows_current); tinfo->keyno = mmrand(&tinfo->rnd, 1, (u_int)max_rows); /* @@ -832,7 +824,7 @@ ops(void *arg) positioned = false; if (op != READ && mmrand(&tinfo->rnd, 1, 5) == 1) { ++tinfo->search; - ret = read_row(tinfo, cursor); + ret = read_row(tinfo); if (ret == 0) { positioned = true; SNAP_TRACK(tinfo, READ); @@ -841,17 +833,18 @@ ops(void *arg) } /* - * Optionally reserve a row, it's an update so it requires snapshot isolation. Reserving a - * row before a read isn't all that sensible, but not unexpected, either. + * If we're in a transaction, optionally reserve a row: it's an update so cannot be done at + * lower isolation levels. Reserving a row in an implicit transaction will work, but doesn't + * make sense. Reserving a row before a read isn't sensible either, but it's not unexpected. */ if (intxn && iso_level == ISOLATION_SNAPSHOT && mmrand(&tinfo->rnd, 0, 20) == 1) { - switch (g.type) { + switch (table->type) { case ROW: - ret = row_reserve(tinfo, cursor, positioned); + ret = row_reserve(tinfo, positioned); break; case FIX: case VAR: - ret = col_reserve(tinfo, cursor, positioned); + ret = col_reserve(tinfo, positioned); break; } if (ret == 0) { @@ -864,9 +857,9 @@ ops(void *arg) /* Perform the operation. */ switch (op) { case INSERT: - switch (g.type) { + switch (table->type) { case ROW: - ret = row_insert(tinfo, cursor, positioned); + ret = row_insert(tinfo, positioned); break; case FIX: case VAR: @@ -874,10 +867,11 @@ ops(void *arg) * We can only append so many new records, once we reach that limit, update a record * instead of inserting. */ - if (tinfo->insert_list_cnt >= WT_ELEMENTS(tinfo->insert_list)) + cip = &tinfo->col_insert[table->id - 1]; + if (cip->insert_list_cnt >= WT_ELEMENTS(cip->insert_list)) goto update_instead_of_chosen_op; - ret = col_insert(tinfo, cursor); + ret = col_insert(tinfo); break; } @@ -891,12 +885,16 @@ ops(void *arg) break; case MODIFY: ++tinfo->update; - switch (g.type) { + switch (table->type) { + case FIX: + testutil_die( + 0, "%s", "fixed-length column-store does not support modify operations"); + /* NOTREACHED */ case ROW: - ret = row_modify(tinfo, cursor, positioned); + ret = row_modify(tinfo, positioned); break; case VAR: - ret = col_modify(tinfo, cursor, positioned); + ret = col_modify(tinfo, positioned); break; } if (ret == 0) { @@ -907,7 +905,7 @@ ops(void *arg) break; case READ: ++tinfo->search; - ret = read_row(tinfo, cursor); + ret = read_row(tinfo); if (ret == 0) { positioned = true; SNAP_TRACK(tinfo, READ); @@ -915,13 +913,13 @@ ops(void *arg) READ_OP_FAILED(true); break; case REMOVE: - switch (g.type) { + switch (table->type) { case ROW: - ret = row_remove(tinfo, cursor, positioned); + ret = row_remove(tinfo, positioned); break; case FIX: case VAR: - ret = col_remove(tinfo, cursor, positioned); + ret = col_remove(tinfo, positioned); break; } if (ret == 0) { @@ -936,11 +934,11 @@ ops(void *arg) break; case TRUNCATE: /* - * A maximum of 2 truncation operations at a time, more than that can lead to serious - * thrashing. + * A maximum of 2 truncation operations at a time in an object, more than that can lead + * to serious thrashing. */ - if (__wt_atomic_addv64(&g.truncate_cnt, 1) > 2) { - (void)__wt_atomic_subv64(&g.truncate_cnt, 1); + if (__wt_atomic_addv64(&table->truncate_cnt, 1) > 2) { + (void)__wt_atomic_subv64(&table->truncate_cnt, 1); goto update_instead_of_chosen_op; } @@ -960,7 +958,7 @@ ops(void *arg) range = max_rows < 20 ? 0 : mmrand(&tinfo->rnd, 0, (u_int)max_rows / 20); tinfo->last = tinfo->keyno; if (greater_than) { - if (g.c_reverse) { + if (TV(BTREE_REVERSE)) { if (tinfo->keyno <= range) tinfo->last = 0; else @@ -971,7 +969,7 @@ ops(void *arg) tinfo->last = 0; } } else { - if (g.c_reverse) { + if (TV(BTREE_REVERSE)) { tinfo->keyno += range; if (tinfo->keyno > max_rows) tinfo->keyno = 0; @@ -982,16 +980,16 @@ ops(void *arg) tinfo->keyno -= range; } } - switch (g.type) { + switch (table->type) { case ROW: - ret = row_truncate(tinfo, cursor); + ret = row_truncate(tinfo); break; case FIX: case VAR: - ret = col_truncate(tinfo, cursor); + ret = col_truncate(tinfo); break; } - (void)__wt_atomic_subv64(&g.truncate_cnt, 1); + (void)__wt_atomic_subv64(&table->truncate_cnt, 1); /* Truncate never leaves the cursor positioned. */ positioned = false; @@ -1004,13 +1002,13 @@ ops(void *arg) case UPDATE: update_instead_of_chosen_op: ++tinfo->update; - switch (g.type) { + switch (table->type) { case ROW: - ret = row_update(tinfo, cursor, positioned); + ret = row_update(tinfo, positioned); break; case FIX: case VAR: - ret = col_update(tinfo, cursor, positioned); + ret = col_update(tinfo, positioned); break; } if (ret == 0) { @@ -1022,8 +1020,8 @@ update_instead_of_chosen_op: } /* If we have pending inserts, try and update the total rows. */ - if (tinfo->insert_list_cnt > 0) - col_insert_resolve(tinfo); + if (g.column_store_config) + tables_apply(col_insert_resolve, tinfo); /* * The cursor is positioned if we did any operation other than insert, do a small number of @@ -1033,7 +1031,7 @@ update_instead_of_chosen_op: next = mmrand(&tinfo->rnd, 0, 1) == 1; j = mmrand(&tinfo->rnd, 1, 100); for (i = 0; i < j; ++i) { - if ((ret = nextprev(tinfo, cursor, next)) == 0) + if ((ret = nextprev(tinfo, next)) == 0) continue; READ_OP_FAILED(true); @@ -1042,7 +1040,7 @@ update_instead_of_chosen_op: } /* Reset the cursor: there is no reason to keep pages pinned. */ - testutil_check(cursor->reset(cursor)); + testutil_check(tinfo->cursor->reset(tinfo->cursor)); /* * No post-operation work is needed outside of a transaction. If in a transaction, add more @@ -1052,21 +1050,24 @@ update_instead_of_chosen_op: continue; /* - * Ending a transaction. If the transaction was configured for snapshot isolation, repeat - * the operations and confirm the results are unchanged. + * Ending a transaction. If an explicit transaction was configured for snapshot isolation, + * repeat the operations and confirm the results are unchanged. */ if (intxn && iso_level == ISOLATION_SNAPSHOT) { __wt_yield(); /* Encourage races */ - ret = snap_repeat_txn(cursor, tinfo); + ret = snap_repeat_txn(tinfo); testutil_assert(ret == 0 || ret == WT_ROLLBACK || ret == WT_CACHE_FULL); if (ret == WT_ROLLBACK || ret == WT_CACHE_FULL) goto rollback; } - /* If prepare configured, prepare the transaction 10% of the time. */ + /* + * If prepare configured, prepare the transaction 10% of the time. Note prepare requires a + * timestamped world, which means we're in a snapshot-isolation transaction by definition. + */ prepared = false; - if (g.c_prepare && mmrand(&tinfo->rnd, 1, 10) == 1) { + if (GV(OPS_PREPARE) && mmrand(&tinfo->rnd, 1, 10) == 1) { if ((ret = prepare_transaction(tinfo)) != 0) WRITE_OP_FAILED(false); @@ -1096,11 +1097,10 @@ rollback: intxn = false; } - if (session != NULL) { + if (session != NULL) testutil_check(session->close(session, NULL)); - tinfo->cursor = NULL; - tinfo->session = NULL; - } + tinfo->session = NULL; + memset(tinfo->cursors, 0, WT_MAX(ntables, 1) * sizeof(tinfo->cursors[0])); tinfo->state = TINFO_COMPLETE; return (WT_THREAD_RET_VALUE); @@ -1111,7 +1111,7 @@ rollback: * Read and verify a subset of the elements in a file. */ void -wts_read_scan(void) +wts_read_scan(TABLE *table, void *arg) { WT_CONNECTION *conn; WT_CURSOR *cursor; @@ -1119,14 +1119,15 @@ wts_read_scan(void) WT_ITEM key, value; WT_SESSION *session; uint64_t keyno; + uint32_t max_rows; - conn = g.wts_conn; + conn = (WT_CONNECTION *)arg; /* * We're not configuring transactions or read timestamps, if there's a diagnostic check, skip * the scan. */ - if (g.c_assert_read_timestamp) + if (GV(ASSERT_READ_TIMESTAMP)) return; /* Set up the default key/value buffers. */ @@ -1135,20 +1136,16 @@ wts_read_scan(void) /* Open a session and cursor pair. */ testutil_check(conn->open_session(conn, NULL, NULL, &session)); - /* - * open_cursor can return EBUSY if concurrent with a metadata operation, retry in that case. - */ - while ((ret = session->open_cursor(session, g.uri, NULL, NULL, &cursor)) == EBUSY) - __wt_yield(); - testutil_check(ret); + wiredtiger_open_cursor(session, table->uri, NULL, &cursor); /* Check a random subset of the records using the key. */ - for (keyno = 0; keyno < g.rows;) { + WT_ORDERED_READ(max_rows, table->rows_current); + for (keyno = 0; keyno < max_rows;) { keyno += mmrand(NULL, 1, 1000); - if (keyno > g.rows) - keyno = g.rows; + if (keyno > max_rows) + keyno = max_rows; - switch (ret = read_row_worker(cursor, NULL, keyno, &key, &value, false)) { + switch (ret = read_row_worker(NULL, table, cursor, keyno, &key, &value, false)) { case 0: case WT_NOTFOUND: case WT_ROLLBACK: @@ -1171,20 +1168,20 @@ wts_read_scan(void) * Read and verify a single element in a row- or column-store file. */ static int -read_row_worker( - WT_CURSOR *cursor, TINFO *tinfo, uint64_t keyno, WT_ITEM *key, WT_ITEM *value, bool sn) +read_row_worker(TINFO *tinfo, TABLE *table, WT_CURSOR *cursor, uint64_t keyno, WT_ITEM *key, + WT_ITEM *value, bool sn) { uint8_t bitfield; int exact, ret; /* Retrieve the key/value pair by key. */ - switch (g.type) { + switch (table->type) { case FIX: case VAR: cursor->set_key(cursor, keyno); break; case ROW: - key_gen(key, keyno); + key_gen(table, key, keyno); cursor->set_key(cursor, key); break; } @@ -1197,7 +1194,7 @@ read_row_worker( ret = read_op(cursor, SEARCH, NULL); switch (ret) { case 0: - if (g.type == FIX) { + if (table->type == FIX) { testutil_check(cursor->get_value(cursor, &bitfield)); *(uint8_t *)(value->data) = bitfield; value->size = 1; @@ -1211,7 +1208,7 @@ read_row_worker( * The WiredTiger cursor has lost its position though, so we return not-found, the cursor * movement can't continue. */ - if (g.type == FIX) { + if (table->type == FIX) { *(uint8_t *)(value->data) = 0; value->size = 1; } @@ -1222,7 +1219,7 @@ read_row_worker( /* Log the operation */ if (ret == 0) - switch (g.type) { + switch (table->type) { case FIX: if (tinfo == NULL && g.trace_all) trace_msg("read %" PRIu64 " {0x%02x}", keyno, ((char *)value->data)[0]); @@ -1247,11 +1244,11 @@ read_row_worker( * Read and verify a single element in a row- or column-store file. */ static int -read_row(TINFO *tinfo, WT_CURSOR *cursor) +read_row(TINFO *tinfo) { /* 25% of the time we call search-near. */ - return (read_row_worker( - cursor, tinfo, tinfo->keyno, tinfo->key, tinfo->value, mmrand(&tinfo->rnd, 0, 3) == 1)); + return (read_row_worker(tinfo, tinfo->table, tinfo->cursor, tinfo->keyno, tinfo->key, + tinfo->value, mmrand(&tinfo->rnd, 0, 3) == 1)); } /* @@ -1259,22 +1256,26 @@ read_row(TINFO *tinfo, WT_CURSOR *cursor) * Read and verify the next/prev element in a row- or column-store file. */ static int -nextprev(TINFO *tinfo, WT_CURSOR *cursor, bool next) +nextprev(TINFO *tinfo, bool next) { + TABLE *table; + WT_CURSOR *cursor; WT_DECL_RET; WT_ITEM key, value; - uint64_t keyno, keyno_prev; + uint64_t keyno; uint8_t bitfield; int cmp; const char *which; - bool incrementing, record_gaps; + bool incrementing; + table = tinfo->table; + cursor = tinfo->cursor; keyno = 0; which = next ? "next" : "prev"; switch (ret = read_op(cursor, next ? NEXT : PREV, NULL)) { case 0: - switch (g.type) { + switch (table->type) { case FIX: if ((ret = cursor->get_key(cursor, &keyno)) == 0 && (ret = cursor->get_value(cursor, &bitfield)) == 0) { @@ -1294,64 +1295,26 @@ nextprev(TINFO *tinfo, WT_CURSOR *cursor, bool next) if (ret != 0) testutil_die(ret, "nextprev: get_key/get_value"); - /* Check that keys are never returned out-of-order. */ - /* - * XXX WT-3889 LSM has a bug that prevents cursor order checks from working, skip the test - * for now. - */ - if (DATASOURCE("lsm")) - break; - /* - * Compare the returned key with the previously returned key, and assert the order is - * correct. If not deleting keys, and the rows aren't in the column-store insert name space, - * also assert we don't skip groups of records (that's a page-split bug symptom). Note a - * previous run that performed salvage might have corrupted a chunk of space such that - * records were removed. If this is a reopen of an existing database, assume salvage might - * have happened. + * Check that keys are never returned out-of-order by comparing the returned key with the + * previously returned key, and assert the order is correct. */ - record_gaps = g.c_delete_pct != 0 || g.reopen; - switch (g.type) { + switch (table->type) { case FIX: case VAR: - if (tinfo->keyno > g.c_rows || keyno > g.c_rows) - record_gaps = true; - if (!next) { - if (tinfo->keyno < keyno || (!record_gaps && keyno != tinfo->keyno - 1)) - goto order_error_col; - } else if (tinfo->keyno > keyno || (!record_gaps && keyno != tinfo->keyno + 1)) - goto order_error_col; - if (0) { -order_error_col: + if ((next && tinfo->keyno > keyno) || (!next && tinfo->keyno < keyno)) testutil_die( 0, "%s returned %" PRIu64 " then %" PRIu64, which, tinfo->keyno, keyno); - } - tinfo->keyno = keyno; break; case ROW: - incrementing = (next && !g.c_reverse) || (!next && g.c_reverse); + incrementing = (next && !TV(BTREE_REVERSE)) || (!next && TV(BTREE_REVERSE)); cmp = memcmp(tinfo->key->data, key.data, WT_MIN(tinfo->key->size, key.size)); if (incrementing) { if (cmp > 0 || (cmp == 0 && tinfo->key->size < key.size)) goto order_error_row; } else if (cmp < 0 || (cmp == 0 && tinfo->key->size > key.size)) goto order_error_row; - if (!record_gaps) { - /* - * Convert the keys to record numbers and then compare less-than-or-equal. (It's not - * less-than, row-store inserts new rows in-between rows by appending a new suffix - * to the row's key.) Keys are strings with terminating '/' values, so absent key - * corruption, we can simply do the underlying string conversion on the key string. - */ - keyno_prev = strtoul((char *)tinfo->key->data + g.prefix_len, NULL, 10); - keyno = strtoul((char *)key.data + g.prefix_len, NULL, 10); - if (incrementing) { - if (keyno_prev != keyno && keyno_prev + 1 != keyno) - goto order_error_row; - } else if (keyno_prev != keyno && keyno_prev - 1 != keyno) - goto order_error_row; - } if (0) { order_error_row: #ifdef HAVE_DIAGNOSTIC @@ -1366,13 +1329,12 @@ order_error_row: } break; case WT_NOTFOUND: - break; default: return (ret); } - if (g.trace_all && ret == 0) - switch (g.type) { + if (g.trace_all) + switch (table->type) { case FIX: trace_op(tinfo, "%s %" PRIu64 " {0x%02x}", which, keyno, ((char *)value.data)[0]); break; @@ -1393,12 +1355,15 @@ order_error_row: * Reserve a row in a row-store file. */ static int -row_reserve(TINFO *tinfo, WT_CURSOR *cursor, bool positioned) +row_reserve(TINFO *tinfo, bool positioned) { + WT_CURSOR *cursor; WT_DECL_RET; + cursor = tinfo->cursor; + if (!positioned) { - key_gen(tinfo->key, tinfo->keyno); + key_gen(tinfo->table, tinfo->key, tinfo->keyno); cursor->set_key(cursor, tinfo->key); } @@ -1416,10 +1381,13 @@ row_reserve(TINFO *tinfo, WT_CURSOR *cursor, bool positioned) * Reserve a row in a column-store file. */ static int -col_reserve(TINFO *tinfo, WT_CURSOR *cursor, bool positioned) +col_reserve(TINFO *tinfo, bool positioned) { + WT_CURSOR *cursor; WT_DECL_RET; + cursor = tinfo->cursor; + if (!positioned) cursor->set_key(cursor, tinfo->keyno); @@ -1491,11 +1459,14 @@ modify(TINFO *tinfo, WT_CURSOR *cursor, bool positioned) * Modify a row in a row-store file. */ static int -row_modify(TINFO *tinfo, WT_CURSOR *cursor, bool positioned) +row_modify(TINFO *tinfo, bool positioned) { + WT_CURSOR *cursor; + + cursor = tinfo->cursor; if (!positioned) { - key_gen(tinfo->key, tinfo->keyno); + key_gen(tinfo->table, tinfo->key, tinfo->keyno); cursor->set_key(cursor, tinfo->key); } @@ -1512,8 +1483,12 @@ row_modify(TINFO *tinfo, WT_CURSOR *cursor, bool positioned) * Modify a row in a column-store file. */ static int -col_modify(TINFO *tinfo, WT_CURSOR *cursor, bool positioned) +col_modify(TINFO *tinfo, bool positioned) { + WT_CURSOR *cursor; + + cursor = tinfo->cursor; + if (!positioned) cursor->set_key(cursor, tinfo->keyno); @@ -1529,12 +1504,13 @@ col_modify(TINFO *tinfo, WT_CURSOR *cursor, bool positioned) * Truncate rows in a row-store file. */ static int -row_truncate(TINFO *tinfo, WT_CURSOR *cursor) +row_truncate(TINFO *tinfo) { - WT_CURSOR *c2; + WT_CURSOR *cursor, *c2; WT_DECL_RET; WT_SESSION *session; + cursor = tinfo->cursor; session = cursor->session; /* @@ -1544,28 +1520,26 @@ row_truncate(TINFO *tinfo, WT_CURSOR *cursor) c2 = NULL; if (tinfo->keyno == 0) { - key_gen(tinfo->key, tinfo->last); + key_gen(tinfo->table, tinfo->key, tinfo->last); cursor->set_key(cursor, tinfo->key); - ret = session->truncate(session, NULL, NULL, cursor, NULL); + WT_RET(session->truncate(session, NULL, NULL, cursor, NULL)); } else if (tinfo->last == 0) { - key_gen(tinfo->key, tinfo->keyno); + key_gen(tinfo->table, tinfo->key, tinfo->keyno); cursor->set_key(cursor, tinfo->key); - ret = session->truncate(session, NULL, cursor, NULL, NULL); + WT_RET(session->truncate(session, NULL, cursor, NULL, NULL)); } else { - key_gen(tinfo->key, tinfo->keyno); + key_gen(tinfo->table, tinfo->key, tinfo->keyno); cursor->set_key(cursor, tinfo->key); - testutil_check(session->open_cursor(session, g.uri, NULL, NULL, &c2)); - key_gen(tinfo->lastkey, tinfo->last); + testutil_check(session->open_cursor(session, tinfo->table->uri, NULL, NULL, &c2)); + key_gen(tinfo->table, tinfo->lastkey, tinfo->last); cursor->set_key(c2, tinfo->lastkey); ret = session->truncate(session, NULL, cursor, c2, NULL); testutil_check(c2->close(c2)); + WT_RET(ret); } - if (ret != 0) - return (ret); - trace_op(tinfo, "truncate %" PRIu64 ", %" PRIu64, "truncate", tinfo->keyno, tinfo->last); return (0); @@ -1576,12 +1550,13 @@ row_truncate(TINFO *tinfo, WT_CURSOR *cursor) * Truncate rows in a column-store file. */ static int -col_truncate(TINFO *tinfo, WT_CURSOR *cursor) +col_truncate(TINFO *tinfo) { - WT_CURSOR *c2; + WT_CURSOR *cursor, *c2; WT_DECL_RET; WT_SESSION *session; + cursor = tinfo->cursor; session = cursor->session; /* @@ -1599,7 +1574,7 @@ col_truncate(TINFO *tinfo, WT_CURSOR *cursor) } else { cursor->set_key(cursor, tinfo->keyno); - testutil_check(session->open_cursor(session, g.uri, NULL, NULL, &c2)); + testutil_check(session->open_cursor(session, tinfo->table->uri, NULL, NULL, &c2)); cursor->set_key(c2, tinfo->last); ret = session->truncate(session, NULL, cursor, c2, NULL); @@ -1618,15 +1593,18 @@ col_truncate(TINFO *tinfo, WT_CURSOR *cursor) * Update a row in a row-store file. */ static int -row_update(TINFO *tinfo, WT_CURSOR *cursor, bool positioned) +row_update(TINFO *tinfo, bool positioned) { + WT_CURSOR *cursor; WT_DECL_RET; + cursor = tinfo->cursor; + if (!positioned) { - key_gen(tinfo->key, tinfo->keyno); + key_gen(tinfo->table, tinfo->key, tinfo->keyno); cursor->set_key(cursor, tinfo->key); } - val_gen(&tinfo->rnd, tinfo->value, tinfo->keyno); + val_gen(tinfo->table, &tinfo->rnd, tinfo->value, tinfo->keyno); cursor->set_value(cursor, tinfo->value); if ((ret = cursor->update(cursor)) != 0) @@ -1643,14 +1621,19 @@ row_update(TINFO *tinfo, WT_CURSOR *cursor, bool positioned) * Update a row in a column-store file. */ static int -col_update(TINFO *tinfo, WT_CURSOR *cursor, bool positioned) +col_update(TINFO *tinfo, bool positioned) { + TABLE *table; + WT_CURSOR *cursor; WT_DECL_RET; + table = tinfo->table; + cursor = tinfo->cursor; + if (!positioned) cursor->set_key(cursor, tinfo->keyno); - val_gen(&tinfo->rnd, tinfo->value, tinfo->keyno); - if (g.type == FIX) + val_gen(table, &tinfo->rnd, tinfo->value, tinfo->keyno); + if (table->type == FIX) cursor->set_value(cursor, *(uint8_t *)tinfo->value->data); else cursor->set_value(cursor, tinfo->value); @@ -1658,7 +1641,7 @@ col_update(TINFO *tinfo, WT_CURSOR *cursor, bool positioned) if ((ret = cursor->update(cursor)) != 0) return (ret); - if (g.type == FIX) + if (table->type == FIX) trace_op(tinfo, "update %" PRIu64 " {0x%02" PRIx8 "}", tinfo->keyno, ((uint8_t *)tinfo->value->data)[0]); else @@ -1672,19 +1655,22 @@ col_update(TINFO *tinfo, WT_CURSOR *cursor, bool positioned) * Insert a row in a row-store file. */ static int -row_insert(TINFO *tinfo, WT_CURSOR *cursor, bool positioned) +row_insert(TINFO *tinfo, bool positioned) { + WT_CURSOR *cursor; WT_DECL_RET; + cursor = tinfo->cursor; + /* * If we positioned the cursor already, it's a test of an update using the insert method. * Otherwise, generate a unique key and insert. */ if (!positioned) { - key_gen_insert(&tinfo->rnd, tinfo->key, tinfo->keyno); + key_gen_insert(tinfo->table, &tinfo->rnd, tinfo->key, tinfo->keyno); cursor->set_key(cursor, tinfo->key); } - val_gen(&tinfo->rnd, tinfo->value, tinfo->keyno); + val_gen(tinfo->table, &tinfo->rnd, tinfo->value, tinfo->keyno); cursor->set_value(cursor, tinfo->value); if ((ret = cursor->insert(cursor)) != 0) @@ -1702,11 +1688,18 @@ row_insert(TINFO *tinfo, WT_CURSOR *cursor, bool positioned) * Resolve newly inserted records. */ static void -col_insert_resolve(TINFO *tinfo) +col_insert_resolve(TABLE *table, void *arg) { - uint64_t v, *p; + struct col_insert *cip; + TINFO *tinfo; + uint32_t max_rows, *p; u_int i; + tinfo = arg; + cip = &tinfo->col_insert[table->id - 1]; + if (cip->insert_list_cnt == 0) + return; + /* * We don't want to ignore column-store records we insert, which requires we update the "last * row" so other threads consider them. Threads allocating record numbers can race with other @@ -1722,17 +1715,17 @@ col_insert_resolve(TINFO *tinfo) * Process the existing records and advance the last row count until we can't go further. */ do { - WT_ORDERED_READ(v, g.rows); - for (i = 0, p = tinfo->insert_list; i < WT_ELEMENTS(tinfo->insert_list); ++i, ++p) { - if (*p == v + 1) { - testutil_assert(__wt_atomic_casv64(&g.rows, v, v + 1)); + WT_ORDERED_READ(max_rows, table->rows_current); + for (i = 0, p = cip->insert_list; i < WT_ELEMENTS(cip->insert_list); ++i, ++p) { + if (*p == max_rows + 1) { + testutil_assert(__wt_atomic_casv32(&table->rows_current, max_rows, max_rows + 1)); *p = 0; - --tinfo->insert_list_cnt; + --cip->insert_list_cnt; break; } - testutil_assert(*p == 0 || *p > v); + testutil_assert(*p == 0 || *p > max_rows); } - } while (tinfo->insert_list_cnt > 0 && i < WT_ELEMENTS(tinfo->insert_list)); + } while (cip->insert_list_cnt > 0 && i < WT_ELEMENTS(cip->insert_list)); } /* @@ -1742,16 +1735,18 @@ col_insert_resolve(TINFO *tinfo) static void col_insert_add(TINFO *tinfo) { + struct col_insert *cip; u_int i; - /* Add the inserted record to the array. */ - for (i = 0; i < WT_ELEMENTS(tinfo->insert_list); ++i) - if (tinfo->insert_list[i] == 0) { - tinfo->insert_list[i] = tinfo->keyno; - ++tinfo->insert_list_cnt; + /* Add the inserted record to the insert array. */ + cip = &tinfo->col_insert[tinfo->table->id - 1]; + for (i = 0; i < WT_ELEMENTS(cip->insert_list); ++i) + if (cip->insert_list[i] == 0) { + cip->insert_list[i] = (uint32_t)tinfo->keyno; + ++cip->insert_list_cnt; break; } - testutil_assert(i < WT_ELEMENTS(tinfo->insert_list)); + testutil_assert(i < WT_ELEMENTS(cip->insert_list)); } /* @@ -1759,12 +1754,19 @@ col_insert_add(TINFO *tinfo) * Insert an element in a column-store file. */ static int -col_insert(TINFO *tinfo, WT_CURSOR *cursor) +col_insert(TINFO *tinfo) { + TABLE *table; + WT_CURSOR *cursor; WT_DECL_RET; + uint64_t max_rows; - val_gen(&tinfo->rnd, tinfo->value, g.rows + 1); - if (g.type == FIX) + table = tinfo->table; + cursor = tinfo->cursor; + + WT_ORDERED_READ(max_rows, table->rows_current); + val_gen(table, &tinfo->rnd, tinfo->value, max_rows + 1); + if (table->type == FIX) cursor->set_value(cursor, *(uint8_t *)tinfo->value->data); else cursor->set_value(cursor, tinfo->value); @@ -1777,7 +1779,7 @@ col_insert(TINFO *tinfo, WT_CURSOR *cursor) col_insert_add(tinfo); /* Extend the object. */ - if (g.type == FIX) + if (table->type == FIX) trace_op(tinfo, "insert %" PRIu64 " {0x%02" PRIx8 "}", tinfo->keyno, ((uint8_t *)tinfo->value->data)[0]); else @@ -1791,12 +1793,15 @@ col_insert(TINFO *tinfo, WT_CURSOR *cursor) * Remove an row from a row-store file. */ static int -row_remove(TINFO *tinfo, WT_CURSOR *cursor, bool positioned) +row_remove(TINFO *tinfo, bool positioned) { + WT_CURSOR *cursor; WT_DECL_RET; + cursor = tinfo->cursor; + if (!positioned) { - key_gen(tinfo->key, tinfo->keyno); + key_gen(tinfo->table, tinfo->key, tinfo->keyno); cursor->set_key(cursor, tinfo->key); } @@ -1817,10 +1822,13 @@ row_remove(TINFO *tinfo, WT_CURSOR *cursor, bool positioned) * Remove a row from a column-store file. */ static int -col_remove(TINFO *tinfo, WT_CURSOR *cursor, bool positioned) +col_remove(TINFO *tinfo, bool positioned) { + WT_CURSOR *cursor; WT_DECL_RET; + cursor = tinfo->cursor; + if (!positioned) cursor->set_key(cursor, tinfo->keyno); diff --git a/src/third_party/wiredtiger/test/format/random.c b/src/third_party/wiredtiger/test/format/random.c index f5726ff8ea3..fba5e062079 100644 --- a/src/third_party/wiredtiger/test/format/random.c +++ b/src/third_party/wiredtiger/test/format/random.c @@ -35,6 +35,7 @@ WT_THREAD_RET random_kv(void *arg) { + TABLE *table; WT_CONNECTION *conn; WT_CURSOR *cursor; WT_DECL_RET; @@ -47,13 +48,19 @@ random_kv(void *arg) (void)(arg); /* Unused parameter */ - conn = g.wts_conn; - - /* Random cursor ops are only supported on row-store. */ - if (g.type != ROW) + /* Random cursor ops are only supported on row-store, make sure there's a row-store table. */ + if (ntables == 0 && tables[0]->type != ROW) return (WT_THREAD_RET_VALUE); + else { + for (i = 1; i < ntables; ++i) + if (tables[i]->type == ROW) + break; + if (i == ntables) + return (WT_THREAD_RET_VALUE); + } /* Open a session. */ + conn = g.wts_conn; testutil_check(conn->open_session(conn, NULL, NULL, &session)); for (simple = false;;) { @@ -61,12 +68,9 @@ random_kv(void *arg) config = simple ? "next_random=true" : "next_random=true,next_random_sample_size=37"; simple = !simple; - /* - * open_cursor can return EBUSY if concurrent with a metadata operation, retry in that case. - */ - while ((ret = session->open_cursor(session, g.uri, NULL, config, &cursor)) == EBUSY) - __wt_yield(); - testutil_check(ret); + /* Select a table and open a cursor. */ + table = table_select_type(ROW); + wiredtiger_open_cursor(session, table->uri, config, &cursor); /* This is just a smoke-test, get some key/value pairs. */ for (i = mmrand(NULL, 0, 1000); i > 0; --i) { diff --git a/src/third_party/wiredtiger/test/format/salvage.c b/src/third_party/wiredtiger/test/format/salvage.c index a383c121862..26589e18a3f 100644 --- a/src/third_party/wiredtiger/test/format/salvage.c +++ b/src/third_party/wiredtiger/test/format/salvage.c @@ -29,124 +29,126 @@ #include "format.h" /* + * uri_path -- + * Return the path to an object file, and optionally, the object name. + */ +static void +uri_path(TABLE *table, char **object_namep, char *buf, size_t len) +{ + char *p; + + /* + * It's a little tricky: if the data source is a file, we're looking for the table URI, if the + * data source is a table, we're looking for the table URI with a trailing ".wt". + */ + p = strchr(table->uri, ':'); + testutil_assert(p != NULL); + ++p; + + testutil_check(__wt_snprintf(buf, len, "%s/%s", g.home, p)); + if (object_namep != NULL) + *object_namep = strrchr(buf, '/') + 1; + if (!access(buf, F_OK)) + return; + testutil_check(__wt_snprintf(buf, len, "%s/%s.wt", g.home, p)); + if (object_namep != NULL) + *object_namep = strrchr(buf, '/') + 1; + if (!access(buf, F_OK)) + return; + testutil_die(0, "%s: unable to find file for salvage", table->uri); +} + +/* * corrupt -- * Corrupt the file in a random way. */ -static int -corrupt(void) +static void +corrupt(TABLE *table) { struct stat sb; FILE *fp; wt_off_t offset; size_t len, nw; - int fd, ret; - char copycmd[2 * 1024], path[1024]; + int fd; + char buf[MAX_FORMAT_PATH * 2], *object_name, path[MAX_FORMAT_PATH]; const char *smash; - /* - * If it's a single Btree file (not LSM), open the file, and corrupt roughly 2% of the file at a - * random spot, including the beginning of the file and overlapping the end. - * - * It's a little tricky: if the data source is a file, we're looking for "wt", if the data - * source is a table, we're looking for "wt.wt". - */ - testutil_check(__wt_snprintf(path, sizeof(path), "%s/%s", g.home, WT_NAME)); - if ((fd = open(path, O_RDWR)) != -1) { - testutil_check(__wt_snprintf(copycmd, sizeof(copycmd), - "cp %s/%s %s/SALVAGE.copy/%s.corrupted", g.home, WT_NAME, g.home, WT_NAME)); - goto found; - } - testutil_check(__wt_snprintf(path, sizeof(path), "%s/%s.wt", g.home, WT_NAME)); - if ((fd = open(path, O_RDWR)) != -1) { - testutil_check(__wt_snprintf(copycmd, sizeof(copycmd), - "cp %s/%s.wt %s/SALVAGE.copy/%s.wt.corrupted", g.home, WT_NAME, g.home, WT_NAME)); - goto found; - } - return (0); + uri_path(table, &object_name, path, sizeof(path)); -found: - if (fstat(fd, &sb) == -1) - testutil_die(errno, "salvage-corrupt: fstat"); + fd = open(path, O_RDWR); + testutil_assert(fd != -1); - offset = mmrand(NULL, 0, (u_int)sb.st_size); - len = (size_t)(20 + (sb.st_size / 100) * 2); - testutil_check(__wt_snprintf(path, sizeof(path), "%s/SALVAGE.corrupt", g.home)); - if ((fp = fopen(path, "w")) == NULL) - testutil_die(errno, "salvage-corrupt: open: %s", path); + /* + * Corrupt a chunk of the file at a random spot, including the first bytes of the file and + * possibly overlapping the end. The length of the corruption is roughly 2% of the file, not + * exceeding a megabyte (so we aren't just corrupting the whole file). + */ + testutil_check(fstat(fd, &sb)); + offset = mmrand(NULL, 0, (u_int)sb.st_size - 1024); + len = (size_t)(sb.st_size * 2) / 100; + len += 4 * 1024; + len = WT_MIN(len, WT_MEGABYTE); + + /* Log the corruption offset and length. */ + testutil_check(__wt_snprintf(buf, sizeof(buf), "%s/SALVAGE.corrupt", g.home)); + testutil_assert((fp = fopen(buf, "w")) != NULL); (void)fprintf(fp, "salvage-corrupt: offset %" PRIuMAX ", length %" WT_SIZET_FMT "\n", (uintmax_t)offset, len); fclose_and_clear(&fp); - if (lseek(fd, offset, SEEK_SET) == -1) - testutil_die(errno, "salvage-corrupt: lseek"); - + testutil_assert(lseek(fd, offset, SEEK_SET) != -1); smash = "!!! memory corrupted by format to test salvage "; for (; len > 0; len -= nw) { nw = (size_t)(len > strlen(smash) ? strlen(smash) : len); - if (write(fd, smash, nw) == -1) - testutil_die(errno, "salvage-corrupt: write"); + testutil_assert(write(fd, smash, nw) != -1); } - if (close(fd) == -1) - testutil_die(errno, "salvage-corrupt: close"); + testutil_check(close(fd)); - /* - * Save a copy of the corrupted file so we can replay the salvage step as necessary. - */ - if ((ret = system(copycmd)) != 0) - testutil_die(ret, "salvage corrupt copy step failed"); - - return (1); + /* Save a copy of the corrupted file so we can replay the salvage step as necessary. */ + testutil_check(__wt_snprintf( + buf, sizeof(buf), "cp %s %s/SALVAGE.copy/%s.corrupted", path, g.home, object_name)); + testutil_check(system(buf)); } -/* - * Salvage command, save the interesting files so we can replay the salvage command as necessary. - * - * Redirect the "cd" command to /dev/null so chatty cd implementations don't add the new working - * directory to our output. - */ -#define SALVAGE_COPY_CMD \ - "cd %s > /dev/null && " \ - "rm -rf SALVAGE.copy && mkdir SALVAGE.copy && " \ - "cp WiredTiger* wt* SALVAGE.copy/" +/* Salvage command, save the interesting files so we can replay the salvage command as necessary. */ +#define SALVAGE_COPY_CMD \ + "rm -rf %s/SALVAGE.copy && mkdir %s/SALVAGE.copy && cp %s/WiredTiger* %s %s/SALVAGE.copy/" /* * wts_salvage -- * Salvage testing. */ void -wts_salvage(void) +wts_salvage(TABLE *table, void *arg) { WT_CONNECTION *conn; - WT_DECL_RET; WT_SESSION *session; - size_t len; - char *cmd; + char buf[MAX_FORMAT_PATH * 5], path[MAX_FORMAT_PATH]; - if (g.c_salvage == 0) - return; + (void)arg; /* unused argument */ - track("salvage", 0ULL, NULL); + if (GV(OPS_SALVAGE) == 0 || DATASOURCE(table, "lsm")) + return; /* Save a copy of the interesting files so we can replay the salvage step as necessary. */ - len = strlen(g.home) + strlen(SALVAGE_COPY_CMD) + 1; - cmd = dmalloc(len); - testutil_check(__wt_snprintf(cmd, len, SALVAGE_COPY_CMD, g.home)); - if ((ret = system(cmd)) != 0) - testutil_die(ret, "salvage copy (\"%s\"), failed", cmd); - free(cmd); + uri_path(table, NULL, path, sizeof(path)); + testutil_check( + __wt_snprintf(buf, sizeof(buf), SALVAGE_COPY_CMD, g.home, g.home, g.home, path, g.home)); + testutil_check(system(buf)); /* Salvage, then verify. */ wts_open(g.home, &conn, &session, true); - testutil_check(session->salvage(session, g.uri, "force=true")); - wts_verify(conn, "post-salvage verify"); + session->app_private = table->track_prefix; + testutil_check(session->salvage(session, table->uri, "force=true")); + wts_verify(table, conn); wts_close(&conn, &session); /* Corrupt the file randomly, salvage, then verify. */ - if (corrupt()) { - wts_open(g.home, &conn, &session, false); - testutil_check(session->salvage(session, g.uri, "force=true")); - wts_verify(conn, "post-corrupt-salvage verify"); - wts_close(&conn, &session); - } + corrupt(table); + wts_open(g.home, &conn, &session, false); + testutil_check(session->salvage(session, table->uri, "force=true")); + wts_verify(table, conn); + + wts_close(&conn, &session); } diff --git a/src/third_party/wiredtiger/test/format/smoke.sh b/src/third_party/wiredtiger/test/format/smoke.sh index 151d1be37b3..ab2096dcd12 100755 --- a/src/third_party/wiredtiger/test/format/smoke.sh +++ b/src/third_party/wiredtiger/test/format/smoke.sh @@ -3,14 +3,16 @@ set -e # Smoke-test format as part of running "make check". -args="-1 -c . " +args="-c . " args="$args btree.compression=none " args="$args cache.minimum=40 " args="$args logging_compression=none" -args="$args runs.ops=500000 " args="$args runs.rows=100000 " args="$args runs.source=table " -args="$args runs.threads=4 " +args="$args runs.tables=3 " +args="$args runs.threads=6 " +args="$args runs.timer=1 " +args="$args transaction.timestamps=1 " # Temporarily disable LSM and FLCS. # $TEST_WRAPPER ./t $args runs.type=fix diff --git a/src/third_party/wiredtiger/test/format/snap.c b/src/third_party/wiredtiger/test/format/snap.c index e7709754523..298b82ec9a5 100644 --- a/src/third_party/wiredtiger/test/format/snap.c +++ b/src/third_party/wiredtiger/test/format/snap.c @@ -45,7 +45,7 @@ snap_init(TINFO *tinfo) * we can the secondary snap list to see the state of keys/values seen and updated at the time * of the rollback. */ - if (g.c_txn_timestamps) { + if (g.transaction_timestamps_config) { tinfo->s = &tinfo->snap_states[1]; tinfo->snap_list = dcalloc(SNAP_LIST_SIZE, sizeof(SNAP_OPS)); tinfo->snap_end = &tinfo->snap_list[SNAP_LIST_SIZE]; @@ -77,7 +77,7 @@ snap_teardown(TINFO *tinfo) } /* - * snap_clear -- + * snap_clear_one -- * Clear a single snap entry. */ static void @@ -110,7 +110,7 @@ snap_op_init(TINFO *tinfo, uint64_t read_ts, bool repeatable_reads) ++tinfo->opid; - if (g.c_txn_timestamps) { + if (g.transaction_timestamps_config) { /* * If the stable timestamp has changed and we've advanced beyond it, preserve the current * snapshot history up to this point, we'll use it verify rollback_to_stable. Switch our @@ -150,13 +150,14 @@ snap_track(TINFO *tinfo, thread_op op) snap = tinfo->snap_current; snap->op = op; snap->opid = tinfo->opid; + snap->id = tinfo->table->id; snap->keyno = tinfo->keyno; snap->ts = WT_TS_NONE; snap->repeatable = false; snap->last = op == TRUNCATE ? tinfo->last : 0; snap->ksize = snap->vsize = 0; - if (op == INSERT && g.type == ROW) { + if (op == INSERT && tinfo->table->type == ROW) { ip = tinfo->key; if (snap->kmemsize < ip->size) { snap->kdata = drealloc(snap->kdata, ip->size); @@ -196,11 +197,11 @@ snap_track(TINFO *tinfo, thread_op op) * Display a single data/size pair, with a tag. */ static void -print_item_data(const char *tag, const uint8_t *data, size_t size) +print_item_data(TABLE *table, const char *tag, const uint8_t *data, size_t size) { WT_ITEM tmp; - if (g.type == FIX) { + if (table->type == FIX) { fprintf(stderr, "%s {0x%02x}\n", tag, data[0]); return; } @@ -216,8 +217,10 @@ print_item_data(const char *tag, const uint8_t *data, size_t size) * Repeat a read and verify the contents. */ static int -snap_verify(WT_CURSOR *cursor, TINFO *tinfo, SNAP_OPS *snap) +snap_verify(TINFO *tinfo, SNAP_OPS *snap) { + TABLE *table; + WT_CURSOR *cursor; WT_DECL_RET; WT_ITEM *key, *value; uint64_t keyno; @@ -225,26 +228,28 @@ snap_verify(WT_CURSOR *cursor, TINFO *tinfo, SNAP_OPS *snap) testutil_assert(snap->op != TRUNCATE); + table = tables[ntables == 0 ? 0 : snap->id]; + cursor = table_cursor(tinfo, snap->id); + keyno = snap->keyno; key = tinfo->key; value = tinfo->value; - keyno = snap->keyno; /* * Retrieve the key/value pair by key. Row-store inserts have a unique generated key we saved, * else generate the key from the key number. */ - if (snap->op == INSERT && g.type == ROW) { + if (snap->op == INSERT && table->type == ROW) { key->data = snap->kdata; key->size = snap->ksize; cursor->set_key(cursor, key); } else { - switch (g.type) { + switch (table->type) { case FIX: case VAR: cursor->set_key(cursor, keyno); break; case ROW: - key_gen(key, keyno); + key_gen(table, key, keyno); cursor->set_key(cursor, key); break; } @@ -252,7 +257,7 @@ snap_verify(WT_CURSOR *cursor, TINFO *tinfo, SNAP_OPS *snap) switch (ret = read_op(cursor, SEARCH, NULL)) { case 0: - if (g.type == FIX) { + if (table->type == FIX) { testutil_check(cursor->get_value(cursor, &bitfield)); *(uint8_t *)(value->data) = bitfield; value->size = 1; @@ -279,15 +284,22 @@ snap_verify(WT_CURSOR *cursor, TINFO *tinfo, SNAP_OPS *snap) * In fixed length stores, zero values at the end of the key space are returned as not-found, * and not-found row reads are saved as zero values. Map back-and-forth for simplicity. */ - if (g.type == FIX) { + if (table->type == FIX) { if (ret == WT_NOTFOUND && snap->vsize == 1 && *(uint8_t *)snap->vdata == 0) return (0); if (snap->op == REMOVE && value->size == 1 && *(uint8_t *)value->data == 0) return (0); } - /* Things went pear-shaped. */ - switch (g.type) { + /* + * Things went pear-shaped. + * + * Dump the WiredTiger handle ID, it's useful in selecting trace records from the log. We have + * an open cursor on the handle, so while this is pretty ugly, I don't think it's unsafe. + */ + fprintf(stderr, "%s: WiredTiger trace ID: %u\n", table->uri, + (u_int)((WT_BTREE *)((WT_CURSOR_BTREE *)cursor)->dhandle->handle)->id); + switch (table->type) { case FIX: fprintf(stderr, "snapshot-isolation: %" PRIu64 " search: expected {0x%02x}, found {0x%02x}\n", keyno, @@ -301,11 +313,11 @@ snap_verify(WT_CURSOR *cursor, TINFO *tinfo, SNAP_OPS *snap) if (snap->op == REMOVE) fprintf(stderr, "expected {deleted}\n"); else - print_item_data("expected", snap->vdata, snap->vsize); + print_item_data(table, "expected", snap->vdata, snap->vsize); if (ret == WT_NOTFOUND) fprintf(stderr, " found {deleted}\n"); else - print_item_data(" found", value->data, value->size); + print_item_data(table, " found", value->data, value->size); break; case VAR: fprintf(stderr, "snapshot-isolation %" PRIu64 " search mismatch\n", keyno); @@ -313,11 +325,11 @@ snap_verify(WT_CURSOR *cursor, TINFO *tinfo, SNAP_OPS *snap) if (snap->op == REMOVE) fprintf(stderr, "expected {deleted}\n"); else - print_item_data("expected", snap->vdata, snap->vsize); + print_item_data(table, "expected", snap->vdata, snap->vsize); if (ret == WT_NOTFOUND) fprintf(stderr, " found {deleted}\n"); else - print_item_data(" found", value->data, value->size); + print_item_data(table, " found", value->data, value->size); break; } @@ -344,31 +356,40 @@ snap_ts_clear(TINFO *tinfo, uint64_t ts) } /* - * snap_repeat_ok_match -- - * Compare two operations and see if they modified the same record. + * snap_repeat_match -- + * Compare two operations and return if they modified the same record. */ static bool -snap_repeat_ok_match(SNAP_OPS *current, SNAP_OPS *a) +snap_repeat_match(SNAP_OPS *current, SNAP_OPS *a) { + TABLE *table; + bool reverse; + /* Reads are never a problem, there's no modification. */ if (a->op == READ) - return (true); + return (false); - /* Check for a matching single record modification. */ - if (a->keyno == current->keyno) + /* Check if the operations were in the same table. */ + if (a->id != current->id) return (false); + /* Check for a matching single record insert, modify, remove or update. */ + if (a->keyno == current->keyno) + return (true); + /* Truncates are slightly harder, make sure the ranges don't overlap. */ + table = tables[ntables == 0 ? 0 : a->id]; + reverse = TV(BTREE_REVERSE) != 0; if (a->op == TRUNCATE) { - if (g.c_reverse && (a->keyno == 0 || a->keyno >= current->keyno) && + if (reverse && (a->keyno == 0 || a->keyno >= current->keyno) && (a->last == 0 || a->last <= current->keyno)) - return (false); - if (!g.c_reverse && (a->keyno == 0 || a->keyno <= current->keyno) && + return (true); + if (!reverse && (a->keyno == 0 || a->keyno <= current->keyno) && (a->last == 0 || a->last >= current->keyno)) - return (false); + return (true); } - return (true); + return (false); } /* @@ -401,7 +422,7 @@ snap_repeat_ok_commit(TINFO *tinfo, SNAP_OPS *current) if (p->opid != tinfo->opid) break; - if (!snap_repeat_ok_match(current, p)) + if (snap_repeat_match(current, p)) return (false); } @@ -414,7 +435,7 @@ snap_repeat_ok_commit(TINFO *tinfo, SNAP_OPS *current) if (p->opid != tinfo->opid) break; - if (!snap_repeat_ok_match(current, p)) + if (snap_repeat_match(current, p)) return (false); } return (true); @@ -444,7 +465,7 @@ snap_repeat_ok_rollback(TINFO *tinfo, SNAP_OPS *current) if (p->opid != tinfo->opid) break; - if (!snap_repeat_ok_match(current, p)) + if (snap_repeat_match(current, p)) return (false); } return (true); @@ -455,7 +476,7 @@ snap_repeat_ok_rollback(TINFO *tinfo, SNAP_OPS *current) * Repeat each operation done within a snapshot isolation transaction. */ int -snap_repeat_txn(WT_CURSOR *cursor, TINFO *tinfo) +snap_repeat_txn(TINFO *tinfo) { SNAP_OPS *current; @@ -478,7 +499,7 @@ snap_repeat_txn(WT_CURSOR *cursor, TINFO *tinfo) * 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)); + WT_RET(snap_verify(tinfo, current)); } return (0); @@ -532,7 +553,7 @@ snap_repeat_update(TINFO *tinfo, bool committed) * Repeat one operation. */ static void -snap_repeat(WT_CURSOR *cursor, TINFO *tinfo, SNAP_OPS *snap) +snap_repeat(TINFO *tinfo, SNAP_OPS *snap) { WT_DECL_RET; WT_SESSION *session; @@ -540,7 +561,7 @@ snap_repeat(WT_CURSOR *cursor, TINFO *tinfo, SNAP_OPS *snap) u_int max_retry; char buf[64]; - session = cursor->session; + session = tinfo->session; trace_op(tinfo, "repeat %" PRIu64 " ts=%" PRIu64 " {%s}", snap->keyno, snap->ts, trace_bytes(tinfo, snap->vdata, snap->vsize)); @@ -563,7 +584,7 @@ snap_repeat(WT_CURSOR *cursor, TINFO *tinfo, SNAP_OPS *snap) * matter to us). Persist after rollback, as a repeatable read we should succeed, yield to * let eviction catch up. */ - if ((ret = snap_verify(cursor, tinfo, snap)) == 0) + if ((ret = snap_verify(tinfo, snap)) == 0) break; testutil_assert(ret == WT_ROLLBACK); @@ -579,7 +600,7 @@ snap_repeat(WT_CURSOR *cursor, TINFO *tinfo, SNAP_OPS *snap) * Repeat an historic operation. */ void -snap_repeat_single(WT_CURSOR *cursor, TINFO *tinfo) +snap_repeat_single(TINFO *tinfo) { SNAP_OPS *snap; u_int v; @@ -602,7 +623,7 @@ snap_repeat_single(WT_CURSOR *cursor, TINFO *tinfo) if (count == 0) return; - snap_repeat(cursor, tinfo, snap); + snap_repeat(tinfo, snap); } /* @@ -610,20 +631,27 @@ snap_repeat_single(WT_CURSOR *cursor, TINFO *tinfo) * Repeat all known operations after a rollback. */ void -snap_repeat_rollback(WT_CURSOR *cursor, TINFO **tinfo_array, size_t tinfo_count) +snap_repeat_rollback(TINFO **tinfo_array, size_t tinfo_count) { SNAP_OPS *snap; SNAP_STATE *state; TINFO *tinfo, **tinfop; + WT_SESSION *push_session, *session; uint32_t count; size_t i, statenum; - char buf[100]; + char buf[64]; count = 0; + session = NULL; - track("rollback_to_stable: checking", 0ULL, NULL); + track("rollback_to_stable: checking", 0ULL); for (i = 0, tinfop = tinfo_array; i < tinfo_count; ++i, ++tinfop) { tinfo = *tinfop; + if ((push_session = tinfo->session) == NULL) { + if (session == NULL) + testutil_check(g.wts_conn->open_session(g.wts_conn, NULL, NULL, &session)); + tinfo->session = session; + } /* * For this thread, walk through both sets of snaps ("states"), looking for entries that are @@ -636,28 +664,33 @@ snap_repeat_rollback(WT_CURSOR *cursor, TINFO **tinfo_array, size_t tinfo_count) state = &tinfo->snap_states[statenum]; for (snap = state->snap_state_list; snap < state->snap_state_end; ++snap) { if (snap->repeatable && snap->ts <= g.stable_timestamp) { - snap_repeat(cursor, tinfo, snap); + snap_repeat(tinfo, snap); ++count; if (count % 100 == 0) { testutil_check(__wt_snprintf( buf, sizeof(buf), "rollback_to_stable: %" PRIu32 " ops repeated", count)); - track(buf, 0ULL, NULL); + track(buf, 0ULL); } } snap_clear_one(snap); } } + + tinfo->session = push_session; } /* Show the final result and check that we're accomplishing some checking. */ testutil_check( __wt_snprintf(buf, sizeof(buf), "rollback_to_stable: %" PRIu32 " ops repeated", count)); - track(buf, 0ULL, NULL); + track(buf, 0ULL); if (count == 0) { #define WARN_RTS_NO_CHECK 5 if (++g.rts_no_check >= WARN_RTS_NO_CHECK) - fprintf(stderr, - "Warning: %" PRIu32 " consecutive runs with no rollback_to_stable checking\n", count); + fprintf( + stderr, "Warning: %u consecutive runs with no rollback_to_stable checking\n", count); } else g.rts_no_check = 0; + + if (session == NULL) + testutil_check(session->close(session, NULL)); } diff --git a/src/third_party/wiredtiger/test/format/t.c b/src/third_party/wiredtiger/test/format/t.c index 3656635e873..b8a926670fd 100644 --- a/src/third_party/wiredtiger/test/format/t.c +++ b/src/third_party/wiredtiger/test/format/t.c @@ -30,17 +30,22 @@ GLOBAL g; +TABLE *tables[V_MAX_TABLES_CONFIG + 1]; /* Table array */ +u_int ntables; + static void format_die(void); static void usage(void) WT_GCC_FUNC_DECL_ATTRIBUTE((noreturn)); extern int __wt_optind; extern char *__wt_optarg; +static void signal_handler(int signo) WT_GCC_FUNC_DECL_ATTRIBUTE((noreturn)); +static void signal_timer(int signo) WT_GCC_FUNC_DECL_ATTRIBUTE((noreturn)); + /* * signal_handler -- * Generic signal handler, report the signal and exit. */ -static void signal_handler(int signo) WT_GCC_FUNC_DECL_ATTRIBUTE((noreturn)); static void signal_handler(int signo) { @@ -53,7 +58,6 @@ signal_handler(int signo) * signal_timer -- * Alarm signal handler. */ -static void signal_timer(int signo) WT_GCC_FUNC_DECL_ATTRIBUTE((noreturn)); static void signal_timer(int signo) { @@ -61,7 +65,7 @@ signal_timer(int signo) * Direct I/O configurations can result in really long run times depending on how the test * machine is configured. If a direct I/O run timed out, don't bother dropping core. */ - if (g.c_direct_io) { + if (GV(DISK_DIRECT_IO)) { fprintf(stderr, "format direct I/O configuration timed out\n"); fprintf(stderr, "format caught signal %d, exiting with error\n", signo); fflush(stderr); @@ -118,9 +122,6 @@ format_process_env(void) (void)signal(SIGTERM, signal_handler); #endif - /* Initialize lock to ensure single threading during failure handling */ - testutil_check(pthread_rwlock_init(&g.death_lock, NULL)); - #if 0 /* Configure the GNU malloc for debugging. */ (void)setenv("MALLOC_CHECK_", "2", 1); @@ -135,13 +136,13 @@ format_process_env(void) * TIMED_MAJOR_OP -- * Set a timer and perform a major operation (for example, verify or salvage). */ -#define TIMED_MAJOR_OP(call) \ - do { \ - if (g.c_major_timeout != 0) \ - set_alarm(g.c_major_timeout * 60); \ - call; \ - if (g.c_major_timeout != 0) \ - set_alarm(0); \ +#define TIMED_MAJOR_OP(call) \ + do { \ + if (GV(FORMAT_MAJOR_TIMEOUT) != 0) \ + set_alarm(GV(FORMAT_MAJOR_TIMEOUT) * 60); \ + call; \ + if (GV(FORMAT_MAJOR_TIMEOUT) != 0) \ + set_alarm(0); \ } while (0) int @@ -151,7 +152,7 @@ main(int argc, char *argv[]) u_int ops_seconds; int ch, reps; const char *config, *home; - bool one_flag, quiet_flag; + bool quiet_flag; custom_die = format_die; /* Local death handler. */ @@ -165,15 +166,21 @@ main(int argc, char *argv[]) format_process_env(); - __wt_random_init_seed(NULL, &g.rnd); /* Initialize the RNG. */ + /* + * If built in a branch that doesn't support all current options, configure for backward + * compatibility. + */ +#if WIREDTIGER_VERSION_MAJOR < 10 + g.backward_compatible = true; +#endif /* Set values from the command line. */ home = NULL; - one_flag = quiet_flag = false; + quiet_flag = false; while ((ch = __wt_getopt(progname, argc, argv, "1BC:c:h:qRrT:t")) != EOF) switch (ch) { - case '1': /* One run and quit */ - one_flag = true; + case '1': + /* Ignored for backward compatibility. */ break; case 'B': /* Backward compatibility */ g.backward_compatible = true; @@ -194,11 +201,8 @@ main(int argc, char *argv[]) g.reopen = true; break; case 'T': /* Trace specifics. */ - if (trace_config(__wt_optarg) != 0) { - fprintf(stderr, "unexpected trace configuration \"%s\"\n", __wt_optarg); - usage(); - } - /* FALLTHROUGH */ + trace_config(__wt_optarg); + /* FALLTHROUGH */ case 't': /* Trace */ g.trace = true; break; @@ -207,14 +211,29 @@ main(int argc, char *argv[]) } argv += __wt_optind; + __wt_random_init_seed(NULL, &g.rnd); /* Initialize the RNG. */ + + /* Printable thread ID. */ + testutil_check(__wt_thread_str(g.tidbuf, sizeof(g.tidbuf))); + + /* Initialize lock to ensure single threading during failure handling */ + testutil_check(pthread_rwlock_init(&g.death_lock, NULL)); + + /* + * Initialize the tables array and default to multi-table testing if not in backward-compatible + * mode. + */ + tables[0] = dcalloc(1, sizeof(TABLE)); + tables[0]->id = 1; + g.multi_table_config = !g.backward_compatible; + /* Set up paths. */ path_setup(home); /* - * If it's a reopen, use the already existing home directory's CONFIG file. - * - * If we weren't given a configuration file, set values from "CONFIG", if it exists. Small hack - * to ignore any CONFIG file named ".", that just makes it possible to ignore any local CONFIG + * If it's a reopen, use the already existing home directory's CONFIG file. Otherwise, if we + * weren't given a configuration file, set values from "CONFIG", if it exists. Small hack to + * ignore any CONFIG file named ".", that just makes it possible to ignore any local CONFIG * file, used when running checks. */ if (g.reopen) { @@ -233,16 +252,58 @@ main(int argc, char *argv[]) * which can lead to a lot of hurt if you're not careful. */ for (; *argv != NULL; ++argv) - config_single(*argv, true); + config_single(NULL, *argv, true); /* - * Let the command line -1 and -q flags override values configured from other sources. - * Regardless, don't go all verbose if we're not talking to a terminal. + * Let the command line -q flag override values configured from other sources. Regardless, don't + * go all verbose if we're not talking to a terminal. */ - if (one_flag) - g.c_runs = 1; if (quiet_flag || !isatty(1)) - g.c_quiet = 1; + GV(QUIET) = 1; + + /* Configure the run. */ + config_run(); + g.configured = true; + + /* Initialize locks to single-thread backups and timestamps. */ + lock_init(g.wts_session, &g.backup_lock); + lock_init(g.wts_session, &g.ts_lock); + lock_init(g.wts_session, &g.prepare_commit_lock); + + __wt_seconds(NULL, &start); + track("starting up", 0ULL); + + /* Create and open, or reopen the database. */ + if (g.reopen) { + if (GV(RUNS_IN_MEMORY)) + testutil_die(0, "reopen impossible after in-memory run"); + wts_open(g.home, &g.wts_conn, &g.wts_session, true); + timestamp_init(); + set_oldest_timestamp(); + } else { + wts_create_home(); + config_print(false); + wts_create_database(); + wts_open(g.home, &g.wts_conn, &g.wts_session, true); + timestamp_init(); + } + + trace_init(); /* Initialize operation tracing. */ + + /* + * Initialize key/value information. Load and verify initial records (at least a brief scan if + * not doing a full verify). + */ + tables_apply(key_init, NULL); + tables_apply(val_init, NULL); + if (!g.reopen) + TIMED_MAJOR_OP(tables_apply(wts_load, NULL)); + TIMED_MAJOR_OP(tables_apply(wts_verify, g.wts_conn)); + if (GV(OPS_VERIFY) == 0) + TIMED_MAJOR_OP(tables_apply(wts_read_scan, g.wts_conn)); + + /* Optionally start checkpoints. */ + wts_checkpoints(); /* * Calculate how long each operations loop should run. Take any timer value and convert it to @@ -253,101 +314,58 @@ main(int argc, char *argv[]) * even if we run out of time, otherwise it won't get done. So, in summary pick a reasonable * time and then don't check for timer expiration once the main operations loop completes. */ - ops_seconds = g.c_timer == 0 ? 0 : ((g.c_timer * 60) - 15) / FORMAT_OPERATION_REPS; - - testutil_check(__wt_thread_str(g.tidbuf, sizeof(g.tidbuf))); - - while (++g.run_cnt <= g.c_runs || g.c_runs == 0) { - __wt_seconds(NULL, &start); - track("starting up", 0ULL, NULL); - - config_run(); - - if (g.reopen) { - config_final(); - wts_open(g.home, &g.wts_conn, &g.wts_session, true); - timestamp_init(); - set_oldest_timestamp(); - } else { - wts_create(g.home); - config_final(); - wts_open(g.home, &g.wts_conn, &g.wts_session, true); - timestamp_init(); - - trace_init(); - } - - /* Initialize locks to single-thread backups, failures, and timestamp updates. */ - lock_init(g.wts_session, &g.backup_lock); - lock_init(g.wts_session, &g.ts_lock); - lock_init(g.wts_session, &g.prepare_commit_lock); - - if (!g.reopen) - TIMED_MAJOR_OP(wts_load()); /* Load and verify initial records */ - - TIMED_MAJOR_OP(wts_verify(g.wts_conn, "verify")); - TIMED_MAJOR_OP(wts_read_scan()); - - wts_checkpoints(); + ops_seconds = GV(RUNS_TIMER) == 0 ? 0 : ((GV(RUNS_TIMER) * 60) - 15) / FORMAT_OPERATION_REPS; + for (reps = 1; reps <= FORMAT_OPERATION_REPS; ++reps) + operations(ops_seconds, reps == FORMAT_OPERATION_REPS); - /* Operations. */ - for (reps = 1; reps <= FORMAT_OPERATION_REPS; ++reps) - operations(ops_seconds, reps == FORMAT_OPERATION_REPS); + /* Copy out the run's statistics. */ + TIMED_MAJOR_OP(wts_stats()); - /* Copy out the run's statistics. */ - TIMED_MAJOR_OP(wts_stats()); - - /* - * Verify the objects. Verify closes the underlying handle and discards the statistics, read - * them first. - */ - TIMED_MAJOR_OP(wts_verify(g.wts_conn, "post-ops verify")); - - lock_destroy(g.wts_session, &g.backup_lock); - lock_destroy(g.wts_session, &g.ts_lock); - lock_destroy(g.wts_session, &g.prepare_commit_lock); - - track("shutting down", 0ULL, NULL); - wts_close(&g.wts_conn, &g.wts_session); + /* + * Verify the objects. Verify closes the underlying handle and discards the statistics, read + * them first. + */ + TIMED_MAJOR_OP(tables_apply(wts_verify, g.wts_conn)); - /* - * Salvage testing. - */ - TIMED_MAJOR_OP(wts_salvage()); + track("shutting down", 0ULL); + wts_close(&g.wts_conn, &g.wts_session); - trace_teardown(); + /* Salvage testing. */ + TIMED_MAJOR_OP(tables_apply(wts_salvage, NULL)); - /* Overwrite the progress line with a completion line. */ - if (!g.c_quiet) - printf("\r%78s\r", " "); - __wt_seconds(NULL, &now); - printf("%4" PRIu32 ": %s, %s (%" PRIu64 " seconds)\n", g.run_cnt, g.c_data_source, - g.c_file_type, now - start); - fflush(stdout); - } + trace_teardown(); - config_print(false); + /* Overwrite the progress line with a completion line. */ + if (!GV(QUIET)) + printf("\r%78s\r", " "); + __wt_seconds(NULL, &now); + printf("%s: successful run completed (%" PRIu64 " seconds)\n ", progname, now - start); + fflush(stdout); config_clear(); - printf("%s: successful run completed\n", progname); + lock_destroy(g.wts_session, &g.backup_lock); + lock_destroy(g.wts_session, &g.ts_lock); + lock_destroy(g.wts_session, &g.prepare_commit_lock); return (EXIT_SUCCESS); } /* - * die -- + * format_die -- * Report an error, dumping the configuration. */ static void format_die(void) { /* - * Turn off progress reports so we don't obscure the error message. The lock we're about to - * acquire will act as a barrier to schedule the write. This is really a "best effort" more than - * a guarantee, there's too much stuff in flight to be sure. + * Turn off progress reports and tracing so we don't obscure the error message or drop core when + * using a session that's being closed. The lock we're about to acquire will act as a barrier to + * schedule the write. This is really a "best effort" more than a guarantee, there's too much + * stuff in flight to be sure. */ - g.c_quiet = 1; + GV(QUIET) = 1; + g.trace = 0; /* * Single-thread error handling, our caller exits after calling us (we never release the lock). @@ -359,14 +377,17 @@ format_die(void) fflush(stderr); fflush(stdout); + /* Display the configuration that failed. */ + if (g.configured) + config_print(true); + /* Flush the logs, they may contain debugging information. */ - trace_teardown(); - if (g.c_logging && g.wts_session != NULL) + if (GV(LOGGING) && g.wts_session != NULL) testutil_check(g.wts_session->log_flush(g.wts_session, "sync=off")); - /* Display the configuration that failed. */ - if (g.run_cnt) - config_print(true); + /* Now about to close shared resources, give them a chance to empty. */ + __wt_sleep(2, 0); + trace_teardown(); #ifdef HAVE_DIAGNOSTIC /* @@ -379,7 +400,7 @@ format_die(void) * which can potentially be very large. If it becomes a problem, this can be modified to just * dump out the page this key is on. */ - if (g.c_verify_failure_dump && g.page_dump_cursor != NULL) { + if (GV(RUNS_VERIFY_FAILURE_DUMP) && g.page_dump_cursor != NULL) { set_core_off(); fprintf(stderr, "snapshot-isolation error: Dumping page to %s\n", g.home_pagedump); diff --git a/src/third_party/wiredtiger/test/format/trace.c b/src/third_party/wiredtiger/test/format/trace.c index 24971032279..b365505a05c 100644 --- a/src/third_party/wiredtiger/test/format/trace.c +++ b/src/third_party/wiredtiger/test/format/trace.c @@ -31,10 +31,13 @@ #define TRACE_DIR "OPS.TRACE" #define TRACE_INIT_CMD "rm -rf %s/" TRACE_DIR " && mkdir %s/" TRACE_DIR -int +/* + * trace_config -- + * Configure operation tracing. + */ +void trace_config(const char *config) { - WT_DECL_RET; char *copy, *p; copy = dstrdup(config); @@ -53,15 +56,16 @@ trace_config(const char *config) } for (p = copy; *p != '\0'; ++p) - if (*p != ',' && !__wt_isspace((u_char)*p)) { - ret = EINVAL; - break; - } + if (*p != ',' && !__wt_isspace((u_char)*p)) + testutil_assertfmt(0, "unexpected trace configuration \"%s\"\n", config); free(copy); - return (ret); } +/* + * trace_init -- + * Initialize operation tracing. + */ void trace_init(void) { @@ -76,7 +80,7 @@ trace_init(void) /* Write traces to a separate database by default, optionally write traces to the primary. */ if (g.trace_local) { - if (!g.c_logging) + if (!GV(LOGGING)) testutil_die(EINVAL, "operation logging to the primary database requires logging be configured for that " "database"); @@ -107,6 +111,10 @@ trace_init(void) g.trace_session = session; } +/* + * trace_teardown -- + * Close operation tracing. + */ void trace_teardown(void) { @@ -115,12 +123,14 @@ trace_teardown(void) conn = g.trace_conn; g.trace_conn = NULL; - if (!g.trace || g.trace_local || conn == NULL) - return; - - testutil_check(conn->close(conn, NULL)); + if (conn != NULL) + testutil_check(conn->close(conn, NULL)); } +/* + * trace_ops_init -- + * Per thread operation tracing setup. + */ void trace_ops_init(TINFO *tinfo) { diff --git a/src/third_party/wiredtiger/test/format/util.c b/src/third_party/wiredtiger/test/format/util.c index 42e6bb8c2ef..45f4f809815 100644 --- a/src/third_party/wiredtiger/test/format/util.c +++ b/src/third_party/wiredtiger/test/format/util.c @@ -56,85 +56,109 @@ track_ts_dots(u_int dot_count) } /* - * track -- + * track_write -- + * Write out a tracking message. + */ +static void +track_write(char *msg, size_t len) +{ + static size_t last_len; /* callers must be single-threaded */ + + if (last_len > len) { + memset(msg + len, ' ', (size_t)(last_len - len)); + msg[last_len] = '\0'; + } + last_len = len; + + if (printf("%s\r", msg) < 0) + testutil_die(EIO, "printf"); + if (fflush(stdout) == EOF) + testutil_die(errno, "fflush"); +} + +/* + * track_ops -- * Show a status line of operations and time stamp progress. */ void -track(const char *tag, uint64_t cnt, TINFO *tinfo) +track_ops(TINFO *tinfo) { - static size_t last_len; static uint64_t last_cur, last_old, last_stable; static u_int cur_dot_cnt, old_dot_cnt, stable_dot_cnt; size_t len; uint64_t cur_ts, old_ts, stable_ts; char msg[128], ts_msg[64]; - if (g.c_quiet || tag == NULL) + if (GV(QUIET)) return; - if (tinfo == NULL && cnt == 0) - testutil_check( - __wt_snprintf_len_set(msg, sizeof(msg), &len, "%4" PRIu32 ": %s", g.run_cnt, tag)); - else if (tinfo == NULL) - testutil_check(__wt_snprintf_len_set( - msg, sizeof(msg), &len, "%4" PRIu32 ": %s: %" PRIu64, g.run_cnt, tag, cnt)); - else { - ts_msg[0] = '\0'; - if (g.c_txn_timestamps) { - /* - * Don't worry about having a completely consistent set of timestamps. - */ - old_ts = g.oldest_timestamp; - stable_ts = g.stable_timestamp; - cur_ts = g.timestamp; - - if (old_ts != last_old) { - ++old_dot_cnt; - last_old = old_ts; - } - if (stable_ts != last_stable) { - ++stable_dot_cnt; - last_stable = stable_ts; - } - if (cur_ts != last_cur) { - ++cur_dot_cnt; - last_cur = cur_ts; - } - - testutil_check(__wt_snprintf(ts_msg, sizeof(ts_msg), - " old%s" - "stb%s%s" - "ts%s%s", - track_ts_dots(old_dot_cnt), track_ts_diff(old_ts, stable_ts), - track_ts_dots(stable_dot_cnt), track_ts_diff(stable_ts, cur_ts), - track_ts_dots(cur_dot_cnt))); + ts_msg[0] = '\0'; + if (g.transaction_timestamps_config) { + /* + * Don't worry about having a completely consistent set of timestamps. + */ + old_ts = g.oldest_timestamp; + stable_ts = g.stable_timestamp; + cur_ts = g.timestamp; + + if (old_ts != last_old) { + ++old_dot_cnt; + last_old = old_ts; } - testutil_check( - __wt_snprintf_len_set(msg, sizeof(msg), &len, - "%4" PRIu32 ": %s: " - "S %" PRIu64 "%s, " - "I %" PRIu64 "%s, " - "U %" PRIu64 "%s, " - "R %" PRIu64 "%s%s", - g.run_cnt, tag, tinfo->search > M(9) ? tinfo->search / M(1) : tinfo->search, - tinfo->search > M(9) ? "M" : "", - tinfo->insert > M(9) ? tinfo->insert / M(1) : tinfo->insert, - tinfo->insert > M(9) ? "M" : "", - tinfo->update > M(9) ? tinfo->update / M(1) : tinfo->update, - tinfo->update > M(9) ? "M" : "", - tinfo->remove > M(9) ? tinfo->remove / M(1) : tinfo->remove, - tinfo->remove > M(9) ? "M" : "", ts_msg)); - } - if (last_len > len) { - memset(msg + len, ' ', (size_t)(last_len - len)); - msg[last_len] = '\0'; + if (stable_ts != last_stable) { + ++stable_dot_cnt; + last_stable = stable_ts; + } + if (cur_ts != last_cur) { + ++cur_dot_cnt; + last_cur = cur_ts; + } + + testutil_check(__wt_snprintf(ts_msg, sizeof(ts_msg), + " old%s" + "stb%s%s" + "ts%s%s", + track_ts_dots(old_dot_cnt), track_ts_diff(old_ts, stable_ts), + track_ts_dots(stable_dot_cnt), track_ts_diff(stable_ts, cur_ts), + track_ts_dots(cur_dot_cnt))); } - last_len = len; + testutil_check(__wt_snprintf_len_set(msg, sizeof(msg), &len, + "ops: " + "S %" PRIu64 + "%s, " + "I %" PRIu64 + "%s, " + "U %" PRIu64 + "%s, " + "R %" PRIu64 "%s%s", + tinfo->search > M(9) ? tinfo->search / M(1) : tinfo->search, tinfo->search > M(9) ? "M" : "", + tinfo->insert > M(9) ? tinfo->insert / M(1) : tinfo->insert, tinfo->insert > M(9) ? "M" : "", + tinfo->update > M(9) ? tinfo->update / M(1) : tinfo->update, tinfo->update > M(9) ? "M" : "", + tinfo->remove > M(9) ? tinfo->remove / M(1) : tinfo->remove, tinfo->remove > M(9) ? "M" : "", + ts_msg)); + + track_write(msg, len); +} - if (printf("%s\r", msg) < 0) - testutil_die(EIO, "printf"); - if (fflush(stdout) == EOF) - testutil_die(errno, "fflush"); +/* + * track -- + * Show general operation progress. + */ +void +track(const char *tag, uint64_t cnt) +{ + size_t len; + char msg[128]; + + if (GV(QUIET)) + return; + + if (cnt == 0) + testutil_check(__wt_snprintf_len_set(msg, sizeof(msg), &len, "%s", tag)); + else + testutil_check(__wt_snprintf_len_set(msg, sizeof(msg), &len, "%s: %" PRIu64, tag, cnt)); + + track_write(msg, len); } /* @@ -162,12 +186,6 @@ path_setup(const char *home) g.home_key = dmalloc(len); testutil_check(__wt_snprintf(g.home_key, len, "%s/%s", g.home, name)); - /* RNG log file. */ - name = "CONFIG.rand"; - len = strlen(g.home) + strlen(name) + 2; - g.home_rand = dmalloc(len); - testutil_check(__wt_snprintf(g.home_rand, len, "%s/%s", g.home, name)); - /* History store dump file. */ name = "FAIL.HSdump"; len = strlen(g.home) + strlen(name) + 2; @@ -195,7 +213,7 @@ bool fp_readv(FILE *fp, char *name, uint32_t *vp) { u_long ulv; - char *endptr, buf[100]; + char *endptr, buf[64]; if (fgets(buf, sizeof(buf), fp) == NULL) testutil_die(errno, "%s: read-value error", name); @@ -239,7 +257,7 @@ timestamp_parse(const char *p, uint64_t *tsp) } /* - * timestamp_stable -- + * timestamp_init -- * Set the stable timestamp on open. */ void @@ -317,7 +335,8 @@ timestamp(void *arg) WT_CONNECTION *conn; WT_SESSION *session; - (void)(arg); + (void)arg; /* Unused argument */ + conn = g.wts_conn; /* Locks need session */ @@ -390,7 +409,7 @@ lock_init(WT_SESSION *session, RWLOCK *lock) { testutil_assert(lock->lock_type == LOCK_NONE); - if (g.c_wt_mutex) { + if (GV(WIREDTIGER_RWLOCK)) { testutil_check(__wt_rwlock_init((WT_SESSION_IMPL *)session, &lock->l.wt)); lock->lock_type = LOCK_WT; } else { @@ -408,10 +427,9 @@ lock_destroy(WT_SESSION *session, RWLOCK *lock) { testutil_assert(LOCK_INITIALIZED(lock)); - if (lock->lock_type == LOCK_WT) { + if (lock->lock_type == LOCK_WT) __wt_rwlock_destroy((WT_SESSION_IMPL *)session, &lock->l.wt); - } else { + else testutil_check(pthread_rwlock_destroy(&lock->l.pthread)); - } lock->lock_type = LOCK_NONE; } diff --git a/src/third_party/wiredtiger/test/format/wts.c b/src/third_party/wiredtiger/test/format/wts.c index 6bb12e063fe..bb75d1b138a 100644 --- a/src/third_party/wiredtiger/test/format/wts.c +++ b/src/third_party/wiredtiger/test/format/wts.c @@ -28,74 +28,27 @@ #include "format.h" -/* - * Home directory initialize command: create the directory if it doesn't exist, else remove - * everything except the RNG log file. - * - * Redirect the "cd" command to /dev/null so chatty cd implementations don't add the new working - * directory to our output. - */ -#define FORMAT_HOME_INIT_CMD \ - "test -e %s || mkdir %s; " \ - "cd %s > /dev/null && rm -rf `ls | sed /CONFIG.rand/d`" - -/* - * compressor -- - * Configure compression. - */ -static const char * -compressor(uint32_t compress_flag) -{ - const char *p; - - p = "unrecognized compressor flag"; - switch (compress_flag) { - case COMPRESS_NONE: - p = "none"; - break; - case COMPRESS_LZ4: - p = "lz4"; - break; - case COMPRESS_SNAPPY: - p = "snappy"; - break; - case COMPRESS_ZLIB: - p = "zlib"; - break; - case COMPRESS_ZSTD: - p = "zstd"; - break; - default: - testutil_die(EINVAL, "illegal compression flag: %#" PRIx32, compress_flag); - /* NOTREACHED */ - } - return (p); -} +static void create_object(TABLE *, void *); /* * encryptor -- * Configure encryption. */ static const char * -encryptor(uint32_t encrypt_flag) +encryptor(void) { + char *s; const char *p; - p = "unrecognized encryptor flag"; - switch (encrypt_flag) { - case ENCRYPT_NONE: + s = GVS(DISK_ENCRYPTION); + if (strcmp(s, "none") == 0) p = "none"; - break; - case ENCRYPT_ROTN_7: + else if (strcmp(s, "rotn-7") == 0) p = "rotn,keyid=7"; - break; - case ENCRYPT_SODIUM: + else if (strcmp(s, "sodium") == 0) p = "sodium,secretkey=" SODIUM_TESTKEY; - break; - default: - testutil_die(EINVAL, "illegal encryption flag: %#" PRIx32, encrypt_flag); - /* NOTREACHED */ - } + else + testutil_die(EINVAL, "illegal encryption configuration: %s", s); return (p); } @@ -106,34 +59,35 @@ encryptor(uint32_t encrypt_flag) * This must set any secretkey. When keyids are in use it can return NULL. */ static const char * -encryptor_at_open(uint32_t encrypt_flag) +encryptor_at_open(void) { + char *s; const char *p; - p = NULL; - switch (encrypt_flag) { - case ENCRYPT_NONE: - break; - case ENCRYPT_ROTN_7: - break; - case ENCRYPT_SODIUM: + s = GVS(DISK_ENCRYPTION); + if (strcmp(s, "none") == 0) + p = NULL; + else if (strcmp(s, "rotn-7") == 0) + p = NULL; + else if (strcmp(s, "sodium") == 0) p = "sodium,secretkey=" SODIUM_TESTKEY; - break; - default: - testutil_die(EINVAL, "illegal encryption flag: %#" PRIx32, encrypt_flag); - /* NOTREACHED */ - } + else + testutil_die(EINVAL, "illegal encryption configuration: %s", s); return (p); } +/* + * handle_message -- + * Event handler for verbose and error messages. + */ static int handle_message(WT_EVENT_HANDLER *handler, WT_SESSION *session, const char *message) { WT_DECL_RET; int nw; - (void)(handler); - (void)(session); + (void)handler; + (void)session; /* * WiredTiger logs a verbose message when the read timestamp is set to a value older than the @@ -149,14 +103,26 @@ handle_message(WT_EVENT_HANDLER *handler, WT_SESSION *session, const char *messa return (nw < 0 ? EIO : (ret == EOF ? errno : 0)); } +/* + * handle_progress -- + * Event handler for progress messages. + */ static int handle_progress( WT_EVENT_HANDLER *handler, WT_SESSION *session, const char *operation, uint64_t progress) { - (void)(handler); - (void)(session); + char buf[256]; - track(operation, progress, NULL); + (void)handler; + (void)session; + + if (session->app_private == NULL) + track(operation, progress); + else { + testutil_check( + __wt_snprintf(buf, sizeof(buf), "%s %s", (char *)session->app_private, operation)); + track(buf, progress); + } return (0); } @@ -182,39 +148,39 @@ static void configure_timing_stress(char *p, size_t max) { CONFIG_APPEND(p, ",timing_stress_for_test=["); - if (g.c_timing_stress_aggressive_sweep) + if (GV(STRESS_AGGRESSIVE_SWEEP)) CONFIG_APPEND(p, ",aggressive_sweep"); - if (g.c_timing_stress_checkpoint) + if (GV(STRESS_CHECKPOINT)) CONFIG_APPEND(p, ",checkpoint_slow"); - if (g.c_timing_stress_checkpoint_prepare) + if (GV(STRESS_CHECKPOINT_PREPARE)) CONFIG_APPEND(p, ",prepare_checkpoint_delay"); - if (g.c_timing_stress_checkpoint_reserved_txnid_delay) + if (GV(STRESS_CHECKPOINT_RESERVED_TXNID_DELAY)) CONFIG_APPEND(p, ",checkpoint_reserved_txnid_delay"); - if (g.c_timing_stress_failpoint_hs_delete_key_from_ts) + if (GV(STRESS_FAILPOINT_HS_DELETE_KEY_FROM_TS)) CONFIG_APPEND(p, ",failpoint_history_store_delete_key_from_ts"); - if (g.c_timing_stress_failpoint_hs_insert_1) + if (GV(STRESS_FAILPOINT_HS_INSERT_1)) CONFIG_APPEND(p, ",failpoint_history_store_insert_1"); - if (g.c_timing_stress_failpoint_hs_insert_2) + if (GV(STRESS_FAILPOINT_HS_INSERT_2)) CONFIG_APPEND(p, ",failpoint_history_store_insert_2"); - if (g.c_timing_stress_hs_checkpoint_delay) + if (GV(STRESS_HS_CHECKPOINT_DELAY)) CONFIG_APPEND(p, ",history_store_checkpoint_delay"); - if (g.c_timing_stress_hs_search) + if (GV(STRESS_HS_SEARCH)) CONFIG_APPEND(p, ",history_store_search"); - if (g.c_timing_stress_hs_sweep) + if (GV(STRESS_HS_SWEEP)) CONFIG_APPEND(p, ",history_store_sweep_race"); - if (g.c_timing_stress_split_1) + if (GV(STRESS_SPLIT_1)) CONFIG_APPEND(p, ",split_1"); - if (g.c_timing_stress_split_2) + if (GV(STRESS_SPLIT_2)) CONFIG_APPEND(p, ",split_2"); - if (g.c_timing_stress_split_3) + if (GV(STRESS_SPLIT_3)) CONFIG_APPEND(p, ",split_3"); - if (g.c_timing_stress_split_4) + if (GV(STRESS_SPLIT_4)) CONFIG_APPEND(p, ",split_4"); - if (g.c_timing_stress_split_5) + if (GV(STRESS_SPLIT_5)) CONFIG_APPEND(p, ",split_5"); - if (g.c_timing_stress_split_6) + if (GV(STRESS_SPLIT_6)) CONFIG_APPEND(p, ",split_6"); - if (g.c_timing_stress_split_7) + if (GV(STRESS_SPLIT_7)) CONFIG_APPEND(p, ",split_7"); CONFIG_APPEND(p, "]"); } @@ -228,7 +194,7 @@ create_database(const char *home, WT_CONNECTION **connp) { WT_CONNECTION *conn; size_t max; - char config[8 * 1024], *p; + char config[8 * 1024], *p, *s; const char *enc; p = config; @@ -241,72 +207,82 @@ create_database(const char *home, WT_CONNECTION **connp) ",checkpoint_sync=false" ",error_prefix=\"%s\"" ",operation_timeout_ms=2000", - g.c_cache, progname); + GV(CACHE), progname); /* In-memory configuration. */ - if (g.c_in_memory != 0) + if (GV(RUNS_IN_MEMORY) != 0) CONFIG_APPEND(p, ",in_memory=1"); + /* Block cache configuration. */ + CONFIG_APPEND(p, + ",block_cache=(enabled=%s,type=\"dram\"" + ",cache_on_checkpoint=%s" + ",cache_on_writes=%s" + ",size=%" PRIu32 "MB)", + GV(BLOCK_CACHE) == 0 ? "false" : "true", + GV(BLOCK_CACHE_CACHE_ON_CHECKPOINT) == 0 ? "false" : "true", + GV(BLOCK_CACHE_CACHE_ON_WRITES) == 0 ? "false" : "true", GV(BLOCK_CACHE_SIZE)); + /* LSM configuration. */ - if (DATASOURCE("lsm")) - CONFIG_APPEND(p, ",lsm_manager=(worker_thread_max=%" PRIu32 "),", g.c_lsm_worker_threads); + if (g.lsm_config) + CONFIG_APPEND(p, ",lsm_manager=(worker_thread_max=%" PRIu32 "),", GV(LSM_WORKER_THREADS)); - if (DATASOURCE("lsm") || g.c_cache < 20) + if (g.lsm_config || GV(CACHE) < 20) CONFIG_APPEND(p, ",eviction_dirty_trigger=95"); - /* Eviction worker configuration. */ - if (g.c_evict_max != 0) - CONFIG_APPEND(p, ",eviction=(threads_max=%" PRIu32 ")", g.c_evict_max); + /* Eviction configuration. */ + if (GV(CACHE_EVICT_MAX) != 0) + CONFIG_APPEND(p, ",eviction=(threads_max=%" PRIu32 ")", GV(CACHE_EVICT_MAX)); /* Logging configuration. */ - if (g.c_logging) + if (GV(LOGGING)) { + s = GVS(LOGGING_COMPRESSION); CONFIG_APPEND(p, ",log=(enabled=true,archive=%d,prealloc=%d,file_max=%" PRIu32 ",compressor=\"%s\")", - g.c_logging_archive ? 1 : 0, g.c_logging_prealloc ? 1 : 0, KILOBYTE(g.c_logging_file_max), - compressor(g.c_logging_compression_flag)); + GV(LOGGING_ARCHIVE) ? 1 : 0, GV(LOGGING_PREALLOC) ? 1 : 0, KILOBYTE(GV(LOGGING_FILE_MAX)), + s == NULL ? "none" : s); + } /* Encryption. */ - if (g.c_encryption) { - enc = encryptor(g.c_encryption_flag); - if (enc != NULL) - CONFIG_APPEND(p, ",encryption=(name=%s)", enc); - } + enc = encryptor(); + if (enc != NULL) + CONFIG_APPEND(p, ",encryption=(name=%s)", enc); /* Miscellaneous. */ #ifdef HAVE_POSIX_MEMALIGN CONFIG_APPEND(p, ",buffer_alignment=512"); #endif - if (g.c_mmap) + if (GV(DISK_MMAP)) CONFIG_APPEND(p, ",mmap=1"); - if (g.c_mmap_all) + if (GV(DISK_MMAP_ALL)) CONFIG_APPEND(p, ",mmap_all=1"); - if (g.c_direct_io) + if (GV(DISK_DIRECT_IO)) CONFIG_APPEND(p, ",direct_io=(data)"); - if (g.c_data_extend) + if (GV(DISK_DATA_EXTEND)) CONFIG_APPEND(p, ",file_extend=(data=8MB)"); /* * Run the statistics server and/or maintain statistics in the engine. Sometimes specify a set * of sources just to exercise that code. */ - if (g.c_statistics_server) { - if (mmrand(NULL, 0, 5) == 1 && memcmp(g.uri, "file:", strlen("file:")) == 0) + if (GV(STATISTICS_SERVER)) { + if (mmrand(NULL, 0, 20) == 1) CONFIG_APPEND( p, ",statistics=(fast),statistics_log=(json,on_close,wait=5,sources=(\"file:\"))"); else CONFIG_APPEND(p, ",statistics=(fast),statistics_log=(json,on_close,wait=5)"); } else - CONFIG_APPEND(p, ",statistics=(%s)", g.c_statistics ? "fast" : "none"); + CONFIG_APPEND(p, ",statistics=(%s)", GV(STATISTICS) ? "fast" : "none"); /* Optional timing stress. */ configure_timing_stress(p, max); /* Extensions. */ CONFIG_APPEND(p, ",extensions=[\"%s\", \"%s\", \"%s\", \"%s\", \"%s\", \"%s\", \"%s\"],", - g.c_reverse ? REVERSE_PATH : "", access(LZ4_PATH, R_OK) == 0 ? LZ4_PATH : "", + REVERSE_PATH, access(LZ4_PATH, R_OK) == 0 ? LZ4_PATH : "", access(ROTN_PATH, R_OK) == 0 ? ROTN_PATH : "", access(SNAPPY_PATH, R_OK) == 0 ? SNAPPY_PATH : "", access(ZLIB_PATH, R_OK) == 0 ? ZLIB_PATH : "", access(ZSTD_PATH, R_OK) == 0 ? ZSTD_PATH : "", @@ -316,8 +292,9 @@ create_database(const char *home, WT_CONNECTION **connp) * Put configuration file configuration options second to last. Put command line configuration * options at the end. Do this so they override the standard configuration. */ - if (g.c_config_open != NULL) - CONFIG_APPEND(p, ",%s", g.c_config_open); + s = GVS(WIREDTIGER_CONFIG); + if (s != NULL) + CONFIG_APPEND(p, ",%s", s); if (g.config_open != NULL) CONFIG_APPEND(p, ",%s", g.config_open); @@ -334,102 +311,96 @@ create_database(const char *home, WT_CONNECTION **connp) * Create the database object. */ static void -create_object(WT_CONNECTION *conn) +create_object(TABLE *table, void *arg) { + WT_CONNECTION *conn; WT_SESSION *session; size_t max; uint32_t maxintlkey, maxleafkey, maxleafvalue; - char config[4096], *p; + char config[4096], *p, *s; + conn = (WT_CONNECTION *)arg; p = config; max = sizeof(config); +/* The page must be a multiple of the allocation size, and 512 always works. */ +#define BLOCK_ALLOCATION_SIZE 512 CONFIG_APPEND(p, "key_format=%s,allocation_size=%d,%s,internal_page_max=%" PRIu32 ",leaf_page_max=%" PRIu32 ",memory_page_max=%" PRIu32, - (g.type == ROW) ? "u" : "r", BLOCK_ALLOCATION_SIZE, - g.c_firstfit ? "block_allocation=first" : "", g.intl_page_max, g.leaf_page_max, - MEGABYTE(g.c_memory_page_max)); + (table->type == ROW) ? "u" : "r", BLOCK_ALLOCATION_SIZE, + TV(DISK_FIRSTFIT) ? "block_allocation=first" : "", table->max_intl_page, table->max_leaf_page, + table->max_mem_page); /* * Configure the maximum key/value sizes, but leave it as the default if we come up with * something crazy. */ - maxintlkey = mmrand(NULL, g.intl_page_max / 50, g.intl_page_max / 40); + maxintlkey = mmrand(NULL, table->max_intl_page / 50, table->max_intl_page / 40); if (maxintlkey > 20) CONFIG_APPEND(p, ",internal_key_max=%" PRIu32, maxintlkey); - maxleafkey = mmrand(NULL, g.leaf_page_max / 50, g.leaf_page_max / 40); + maxleafkey = mmrand(NULL, table->max_leaf_page / 50, table->max_leaf_page / 40); if (maxleafkey > 20) CONFIG_APPEND(p, ",leaf_key_max=%" PRIu32, maxleafkey); - maxleafvalue = mmrand(NULL, g.leaf_page_max * 10, g.leaf_page_max / 40); + maxleafvalue = mmrand(NULL, table->max_leaf_page * 10, table->max_leaf_page / 40); if (maxleafvalue > 40 && maxleafvalue < 100 * 1024) CONFIG_APPEND(p, ",leaf_value_max=%" PRIu32, maxleafvalue); - switch (g.type) { + switch (table->type) { case FIX: - CONFIG_APPEND(p, ",value_format=%" PRIu32 "t", g.c_bitcnt); + CONFIG_APPEND(p, ",value_format=%" PRIu32 "t", TV(BTREE_BITCNT)); break; case ROW: CONFIG_APPEND(p, ",prefix_compression=%s,prefix_compression_min=%" PRIu32, - g.c_prefix_compression == 0 ? "false" : "true", g.c_prefix_compression_min); - if (g.c_reverse) + TV(BTREE_PREFIX_COMPRESSION) == 0 ? "false" : "true", TV(BTREE_PREFIX_COMPRESSION_MIN)); + if (TV(BTREE_REVERSE)) CONFIG_APPEND(p, ",collator=reverse"); /* FALLTHROUGH */ case VAR: - if (g.c_huffman_value) + if (TV(BTREE_HUFFMAN_VALUE)) CONFIG_APPEND(p, ",huffman_value=english"); - if (g.c_dictionary) + if (TV(BTREE_DICTIONARY)) CONFIG_APPEND(p, ",dictionary=%" PRIu32, mmrand(NULL, 123, 517)); break; } /* Configure checksums. */ - switch (g.c_checksum_flag) { - case CHECKSUM_OFF: - CONFIG_APPEND(p, ",checksum=\"off\""); - break; - case CHECKSUM_ON: - CONFIG_APPEND(p, ",checksum=\"on\""); - break; - case CHECKSUM_UNCOMPRESSED: - CONFIG_APPEND(p, ",checksum=\"uncompressed\""); - break; - case CHECKSUM_UNENCRYPTED: - CONFIG_APPEND(p, ",checksum=\"unencrypted\""); - break; - } + if ((s = TVS(DISK_CHECKSUM)) != NULL) + CONFIG_APPEND(p, ",checksum=\"%s\"", s); /* Configure compression. */ - if (g.c_compression_flag != COMPRESS_NONE) - CONFIG_APPEND(p, ",block_compressor=\"%s\"", compressor(g.c_compression_flag)); + if ((s = TVS(BTREE_COMPRESSION)) != NULL) + CONFIG_APPEND(p, ",block_compressor=\"%s\"", s); /* Configure Btree. */ - CONFIG_APPEND(p, ",internal_key_truncate=%s", g.c_internal_key_truncation ? "true" : "false"); - CONFIG_APPEND(p, ",split_pct=%" PRIu32, g.c_split_pct); + CONFIG_APPEND( + p, ",internal_key_truncate=%s", TV(BTREE_INTERNAL_KEY_TRUNCATION) ? "true" : "false"); + CONFIG_APPEND(p, ",split_pct=%" PRIu32, TV(BTREE_SPLIT_PCT)); /* Assertions: assertions slow down the code for additional diagnostic checking. */ - if (g.c_assert_read_timestamp) - CONFIG_APPEND(p, ",assert=(read_timestamp=%s)", g.c_txn_timestamps ? "always" : "never"); - if (g.c_assert_write_timestamp) + if (GV(ASSERT_READ_TIMESTAMP)) + CONFIG_APPEND( + p, ",assert=(read_timestamp=%s)", g.transaction_timestamps_config ? "always" : "never"); + if (GV(ASSERT_WRITE_TIMESTAMP)) CONFIG_APPEND(p, ",assert=(write_timestamp=on),write_timestamp_usage=%s", - g.c_txn_timestamps ? "key_consistent" : "never"); + g.transaction_timestamps_config ? "key_consistent" : "never"); /* Configure LSM. */ - if (DATASOURCE("lsm")) { + if (DATASOURCE(table, "lsm")) { CONFIG_APPEND(p, ",type=lsm,lsm=("); - CONFIG_APPEND(p, "auto_throttle=%s,", g.c_auto_throttle ? "true" : "false"); - CONFIG_APPEND(p, "chunk_size=%" PRIu32 "MB,", g.c_chunk_size); + CONFIG_APPEND(p, "auto_throttle=%s,", TV(LSM_AUTO_THROTTLE) ? "true" : "false"); + CONFIG_APPEND(p, "chunk_size=%" PRIu32 "MB,", TV(LSM_CHUNK_SIZE)); /* * We can't set bloom_oldest without bloom, and we want to test with Bloom filters on most * of the time anyway. */ - if (g.c_bloom_oldest) - g.c_bloom = 1; - CONFIG_APPEND(p, "bloom=%s,", g.c_bloom ? "true" : "false"); - CONFIG_APPEND(p, "bloom_bit_count=%" PRIu32 ",", g.c_bloom_bit_count); - CONFIG_APPEND(p, "bloom_hash_count=%" PRIu32 ",", g.c_bloom_hash_count); - CONFIG_APPEND(p, "bloom_oldest=%s,", g.c_bloom_oldest ? "true" : "false"); - CONFIG_APPEND(p, "merge_max=%" PRIu32 ",", g.c_merge_max); + if (TV(LSM_BLOOM_OLDEST)) + TV(LSM_BLOOM) = 1; + CONFIG_APPEND(p, "bloom=%s,", TV(LSM_BLOOM) ? "true" : "false"); + CONFIG_APPEND(p, "bloom_bit_count=%" PRIu32 ",", TV(LSM_BLOOM_BIT_COUNT)); + CONFIG_APPEND(p, "bloom_hash_count=%" PRIu32 ",", TV(LSM_BLOOM_HASH_COUNT)); + CONFIG_APPEND(p, "bloom_oldest=%s,", TV(LSM_BLOOM_OLDEST) ? "true" : "false"); + CONFIG_APPEND(p, "merge_max=%" PRIu32 ",", TV(LSM_MERGE_MAX)); CONFIG_APPEND(p, ",)"); } @@ -440,35 +411,41 @@ create_object(WT_CONNECTION *conn) * Create the underlying store. */ testutil_check(conn->open_session(conn, NULL, NULL, &session)); - testutil_checkfmt(session->create(session, g.uri, config), "%s", g.uri); + testutil_checkfmt(session->create(session, table->uri, config), "%s", table->uri); testutil_check(session->close(session, NULL)); } /* - * wts_create -- - * Create the database home and objects. + * wts_create_home -- + * Remove and re-create the directory. + */ +void +wts_create_home(void) +{ + char buf[MAX_FORMAT_PATH * 2]; + + testutil_check(__wt_snprintf(buf, sizeof(buf), "rm -rf %s && mkdir %s", g.home, g.home)); + testutil_checkfmt(system(buf), "database home creation (\"%s\") failed", buf); +} + +/* + * wts_create_database -- + * Create the database. */ void -wts_create(const char *home) +wts_create_database(void) { WT_CONNECTION *conn; - WT_DECL_RET; - size_t len; - char *cmd; - - len = strlen(g.home) * 3 + strlen(FORMAT_HOME_INIT_CMD) + 1; - cmd = dmalloc(len); - testutil_check(__wt_snprintf(cmd, len, FORMAT_HOME_INIT_CMD, g.home, g.home, g.home)); - if ((ret = system(cmd)) != 0) - testutil_die(ret, "home initialization (\"%s\") failed", cmd); - free(cmd); - - create_database(home, &conn); - create_object(conn); - if (g.c_in_memory != 0) - g.wts_conn_inmemory = conn; + + create_database(g.home, &conn); + + g.wts_conn = conn; + tables_apply(create_object, g.wts_conn); + if (GV(RUNS_IN_MEMORY) != 0) + g.wts_conn_inmemory = g.wts_conn; else testutil_check(conn->close(conn, NULL)); + g.wts_conn = NULL; } /* @@ -491,7 +468,7 @@ wts_open(const char *home, WT_CONNECTION **connp, WT_SESSION **sessionp, bool al config[0] = '\0'; /* Configuration settings that are not persistent between open calls. */ - enc = encryptor_at_open(g.c_encryption_flag); + enc = encryptor_at_open(); if (enc != NULL) CONFIG_APPEND(p, ",encryption=(name=%s)", enc); @@ -501,11 +478,11 @@ wts_open(const char *home, WT_CONNECTION **connp, WT_SESSION **sessionp, bool al configure_timing_stress(p, max); /* If in-memory, there's only a single, shared WT_CONNECTION handle. */ - if (g.c_in_memory != 0) + if (GV(RUNS_IN_MEMORY) != 0) conn = g.wts_conn_inmemory; else { #if WIREDTIGER_VERSION_MAJOR >= 10 - if (g.c_verify && allow_verify) + if (GV(OPS_VERIFY) && allow_verify) CONFIG_APPEND(p, ",verify_metadata=true"); #else WT_UNUSED(allow_verify); @@ -517,6 +494,10 @@ wts_open(const char *home, WT_CONNECTION **connp, WT_SESSION **sessionp, bool al *connp = conn; } +/* + * wts_close -- + * Close the open database. + */ void wts_close(WT_CONNECTION **connp, WT_SESSION **sessionp) { @@ -538,34 +519,74 @@ wts_close(WT_CONNECTION **connp, WT_SESSION **sessionp) if (g.backward_compatible) testutil_check(conn->reconfigure(conn, "compatibility=(release=3.3)")); - testutil_check(conn->close(conn, g.c_leak_memory ? "leak_memory" : NULL)); + testutil_check(conn->close(conn, GV(WIREDTIGER_LEAK_MEMORY) ? "leak_memory" : NULL)); } +/* + * wts_verify -- + * Verify a table. + */ void -wts_verify(WT_CONNECTION *conn, const char *tag) +wts_verify(TABLE *table, void *arg) { + WT_CONNECTION *conn; WT_DECL_RET; WT_SESSION *session; - if (g.c_verify == 0) - return; - - track("verify", 0ULL, NULL); + conn = (WT_CONNECTION *)arg; - testutil_check(conn->open_session(conn, NULL, NULL, &session)); - trace_msg("%s", "=============== verify start"); + if (GV(OPS_VERIFY) == 0) + return; /* * Verify can return EBUSY if the handle isn't available. Don't yield and retry, in the case of * LSM, the handle may not be available for a long time. */ - ret = session->verify(session, g.uri, "strict"); - testutil_assertfmt(ret == 0 || ret == EBUSY, "session.verify: %s: %s", g.uri, tag); - - trace_msg("%s", "=============== verify stop"); + testutil_check(conn->open_session(conn, NULL, NULL, &session)); + session->app_private = table->track_prefix; + ret = session->verify(session, table->uri, "strict"); + testutil_assert(ret == 0 || ret == EBUSY); testutil_check(session->close(session, NULL)); } +struct stats_args { + FILE *fp; + WT_SESSION *session; +}; + +/* + * stats_data_source -- + * Dump each data source's statistics. + */ +static void +stats_data_source(TABLE *table, void *arg) +{ + struct stats_args *args; + FILE *fp; + WT_CURSOR *cursor; + WT_DECL_RET; + WT_SESSION *session; + uint64_t v; + char buf[1024]; + const char *desc, *pval; + + args = arg; + fp = args->fp; + session = args->session; + + fprintf(fp, "\n\n====== Data source statistics: %s\n", table->uri); + + testutil_check(__wt_snprintf(buf, sizeof(buf), "statistics:%s", table->uri)); + testutil_check(session->open_cursor(session, buf, NULL, NULL, &cursor)); + while ( + (ret = cursor->next(cursor)) == 0 && (ret = cursor->get_value(cursor, &desc, &pval, &v)) == 0) + if (fprintf(fp, "%s=%s\n", desc, pval) < 0) + testutil_die(errno, "fprintf"); + + testutil_assert(ret == WT_NOTFOUND); + testutil_check(cursor->close(cursor)); +} + /* * wts_stats -- * Dump the run's statistics. @@ -573,22 +594,21 @@ wts_verify(WT_CONNECTION *conn, const char *tag) void wts_stats(void) { + struct stats_args args; FILE *fp; WT_CONNECTION *conn; WT_CURSOR *cursor; WT_DECL_RET; WT_SESSION *session; - size_t len; uint64_t v; - char *stat_name; const char *desc, *pval; /* Ignore statistics if they're not configured. */ - if (g.c_statistics == 0) + if (GV(STATISTICS) == 0) return; conn = g.wts_conn; - track("stat", 0ULL, NULL); + track("stat", 0ULL); testutil_check(conn->open_session(conn, NULL, NULL, &session)); @@ -603,27 +623,13 @@ wts_stats(void) (ret = cursor->next(cursor)) == 0 && (ret = cursor->get_value(cursor, &desc, &pval, &v)) == 0) if (fprintf(fp, "%s=%s\n", desc, pval) < 0) testutil_die(errno, "fprintf"); - - if (ret != WT_NOTFOUND) - testutil_die(ret, "cursor.next"); + testutil_assert(ret == WT_NOTFOUND); testutil_check(cursor->close(cursor)); /* Data source statistics. */ - fprintf(fp, "\n\n====== Data source statistics:\n"); - len = strlen("statistics:") + strlen(g.uri) + 1; - stat_name = dmalloc(len); - testutil_check(__wt_snprintf(stat_name, len, "statistics:%s", g.uri)); - testutil_check(session->open_cursor(session, stat_name, NULL, NULL, &cursor)); - free(stat_name); - - while ( - (ret = cursor->next(cursor)) == 0 && (ret = cursor->get_value(cursor, &desc, &pval, &v)) == 0) - if (fprintf(fp, "%s=%s\n", desc, pval) < 0) - testutil_die(errno, "fprintf"); - - if (ret != WT_NOTFOUND) - testutil_die(ret, "cursor.next"); - testutil_check(cursor->close(cursor)); + args.fp = fp; + args.session = session; + tables_apply(stats_data_source, &args); fclose_and_clear(&fp); diff --git a/src/third_party/wiredtiger/test/utility/misc.c b/src/third_party/wiredtiger/test/utility/misc.c index 4826a0b02ff..4d2d989a997 100644 --- a/src/third_party/wiredtiger/test/utility/misc.c +++ b/src/third_party/wiredtiger/test/utility/misc.c @@ -69,7 +69,7 @@ testutil_die(int e, const char *fmt, ...) fprintf(stderr, "\n"); fprintf(stderr, "%s: process aborting\n", progname); - abort(); + __wt_abort(NULL); } /* diff --git a/src/third_party/wiredtiger/test/utility/test_util.h b/src/third_party/wiredtiger/test/utility/test_util.h index 54444684ddf..31283084400 100644 --- a/src/third_party/wiredtiger/test/utility/test_util.h +++ b/src/third_party/wiredtiger/test/utility/test_util.h @@ -245,7 +245,8 @@ extern void (*custom_die)(void); #ifdef _WIN32 __declspec(noreturn) #endif - void testutil_die(int, const char *, ...) WT_GCC_FUNC_DECL_ATTRIBUTE((noreturn)); + void testutil_die(int, const char *, ...) WT_GCC_FUNC_ATTRIBUTE((cold)) + WT_GCC_FUNC_DECL_ATTRIBUTE((noreturn)); void *dcalloc(size_t, size_t); void *dmalloc(size_t); |