summaryrefslogtreecommitdiff
path: root/src/third_party/wiredtiger/test/format/ops.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/third_party/wiredtiger/test/format/ops.c')
-rw-r--r--src/third_party/wiredtiger/test/format/ops.c2955
1 files changed, 1430 insertions, 1525 deletions
diff --git a/src/third_party/wiredtiger/test/format/ops.c b/src/third_party/wiredtiger/test/format/ops.c
index 7adfb795694..a03b42e427b 100644
--- a/src/third_party/wiredtiger/test/format/ops.c
+++ b/src/third_party/wiredtiger/test/format/ops.c
@@ -28,50 +28,50 @@
#include "format.h"
-static int col_insert(TINFO *, WT_CURSOR *);
-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 *, WT_CURSOR *);
+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 WT_THREAD_RET ops(void *);
-static int read_row(TINFO *, WT_CURSOR *);
-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 void table_append_init(void);
+static int read_row(TINFO *, WT_CURSOR *);
+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 void table_append_init(void);
static char modify_repl[256];
/*
* modify_repl_init --
- * Initialize the replacement information.
+ * Initialize the replacement information.
*/
static void
modify_repl_init(void)
{
- size_t i;
+ size_t i;
- for (i = 0; i < sizeof(modify_repl); ++i)
- modify_repl[i] = "zyxwvutsrqponmlkjihgfedcba"[i % 26];
+ for (i = 0; i < sizeof(modify_repl); ++i)
+ modify_repl[i] = "zyxwvutsrqponmlkjihgfedcba"[i % 26];
}
static void
set_alarm(void)
{
#ifdef HAVE_TIMER_CREATE
- struct itimerspec timer_val;
- timer_t timer_id;
-
- testutil_check(timer_create(CLOCK_REALTIME, NULL, &timer_id));
- memset(&timer_val, 0, sizeof(timer_val));
- timer_val.it_value.tv_sec = 60 * 2;
- timer_val.it_value.tv_nsec = 0;
- testutil_check(timer_settime(timer_id, 0, &timer_val, NULL));
+ struct itimerspec timer_val;
+ timer_t timer_id;
+
+ testutil_check(timer_create(CLOCK_REALTIME, NULL, &timer_id));
+ memset(&timer_val, 0, sizeof(timer_val));
+ timer_val.it_value.tv_sec = 60 * 2;
+ timer_val.it_value.tv_nsec = 0;
+ testutil_check(timer_settime(timer_id, 0, &timer_val, NULL));
#endif
}
@@ -79,341 +79,319 @@ TINFO **tinfo_list;
/*
* wts_ops --
- * Perform a number of operations in a set of threads.
+ * Perform a number of operations in a set of threads.
*/
void
wts_ops(bool lastrun)
{
- TINFO *tinfo, total;
- WT_CONNECTION *conn;
- WT_SESSION *session;
- wt_thread_t alter_tid, backup_tid, checkpoint_tid, compact_tid, lrt_tid;
- wt_thread_t timestamp_tid;
- int64_t fourths, quit_fourths, thread_ops;
- uint32_t i;
- bool running;
-
- conn = g.wts_conn;
-
- session = NULL; /* -Wconditional-uninitialized */
- memset(&alter_tid, 0, sizeof(alter_tid));
- memset(&backup_tid, 0, sizeof(backup_tid));
- memset(&checkpoint_tid, 0, sizeof(checkpoint_tid));
- memset(&compact_tid, 0, sizeof(compact_tid));
- memset(&lrt_tid, 0, sizeof(lrt_tid));
- memset(&timestamp_tid, 0, sizeof(timestamp_tid));
-
- modify_repl_init();
-
- /*
- * There are two mechanisms to specify the length of the run, a number
- * of operations and a timer, when either expire the run terminates.
- *
- * Each thread does an equal share of the total operations (and make
- * sure that it's not 0).
- *
- * 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)
- 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 (g.c_timer == 0)
- fourths = quit_fourths = -1;
- else {
- fourths = ((int64_t)g.c_timer * 4 * 60) / FORMAT_OPERATION_REPS;
- quit_fourths = fourths + 15 * 4 * 60;
- }
-
- /* Initialize the table extension code. */
- table_append_init();
-
- /*
- * We support replay of threaded runs, but don't log random numbers
- * after threaded operations start, there's no point.
- */
- if (!SINGLETHREADED)
- g.rand_log_stop = true;
-
- /* Logging requires a session. */
- if (g.logging)
- testutil_check(conn->open_session(conn, NULL, NULL, &session));
- logop(session, "%s", "=============== thread ops start");
-
- /*
- * Create the per-thread structures and start the worker threads.
- * Allocate the thread structures separately to minimize false sharing.
- */
- tinfo_list = dcalloc((size_t)g.c_threads + 1, sizeof(TINFO *));
- for (i = 0; i < g.c_threads; ++i) {
- tinfo_list[i] = tinfo = dcalloc(1, sizeof(TINFO));
-
- tinfo->id = (int)i + 1;
-
- /*
- * Characterize the per-thread random number generator. Normally
- * we want independent behavior so threads start in different
- * parts of the RNG space, but we've found bugs by having the
- * threads 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)
- __wt_random_init_seed(
- (WT_SESSION_IMPL *)session, &tinfo->rnd);
- else
- __wt_random_init(&tinfo->rnd);
-
- tinfo->state = TINFO_RUNNING;
- testutil_check(
- __wt_thread_create(NULL, &tinfo->tid, ops, tinfo));
- }
-
- /*
- * If a multi-threaded run, start optional backup, compaction and
- * long-running reader threads.
- */
- if (g.c_alter)
- testutil_check(
- __wt_thread_create(NULL, &alter_tid, alter, NULL));
- if (g.c_backups)
- testutil_check(
- __wt_thread_create(NULL, &backup_tid, backup, NULL));
- if (g.c_checkpoint_flag == CHECKPOINT_ON)
- testutil_check(__wt_thread_create(
- NULL, &checkpoint_tid, checkpoint, NULL));
- if (g.c_compact)
- testutil_check(
- __wt_thread_create(NULL, &compact_tid, compact, NULL));
- if (!SINGLETHREADED && g.c_long_running_txn)
- testutil_check(__wt_thread_create(NULL, &lrt_tid, lrt, NULL));
- if (g.c_txn_timestamps)
- testutil_check(__wt_thread_create(
- NULL, &timestamp_tid, timestamp, tinfo_list));
-
- /* 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) {
- tinfo = tinfo_list[i];
- total.commit += tinfo->commit;
- total.insert += tinfo->insert;
- total.prepare += tinfo->prepare;
- total.remove += tinfo->remove;
- total.rollback += tinfo->rollback;
- total.search += tinfo->search;
- total.truncate += tinfo->truncate;
- total.update += tinfo->update;
-
- switch (tinfo->state) {
- case TINFO_RUNNING:
- running = true;
- break;
- case TINFO_COMPLETE:
- tinfo->state = TINFO_JOINED;
- testutil_check(
- __wt_thread_join(NULL, &tinfo->tid));
- break;
- case TINFO_JOINED:
- break;
- }
-
- /*
- * If the timer has expired or this thread has completed
- * its operations, notify the thread it should quit.
- */
- if (fourths == 0 ||
- (thread_ops != -1 &&
- tinfo->ops >= (uint64_t)thread_ops)) {
- /*
- * On the last execution, optionally drop core
- * for recovery testing.
- */
- if (lastrun && g.c_abort) {
- static char *core = NULL;
- *core = 0;
- }
- tinfo->quit = true;
- }
- }
- track("ops", 0ULL, &total);
- if (!running)
- break;
- __wt_sleep(0, 250000); /* 1/4th of a second */
- if (fourths != -1)
- --fourths;
- if (quit_fourths != -1 && --quit_fourths == 0) {
- fprintf(stderr, "%s\n",
- "format run more than 15 minutes past the maximum "
- "time");
- fprintf(stderr, "%s\n",
- "format run dumping cache and transaction state, "
- "then aborting the process");
-
- /*
- * If the library is deadlocked, we might just join the
- * mess, set a timer to limit our exposure.
- */
- set_alarm();
-
- (void)conn->debug_info(conn, "txn");
- (void)conn->debug_info(conn, "cache");
-
- __wt_abort(NULL);
- }
- }
-
- /* Wait for the other threads. */
- g.workers_finished = true;
- if (g.c_alter)
- testutil_check(__wt_thread_join(NULL, &alter_tid));
- if (g.c_backups)
- testutil_check(__wt_thread_join(NULL, &backup_tid));
- if (g.c_checkpoint_flag == CHECKPOINT_ON)
- testutil_check(__wt_thread_join(NULL, &checkpoint_tid));
- if (g.c_compact)
- testutil_check(__wt_thread_join(NULL, &compact_tid));
- if (!SINGLETHREADED && g.c_long_running_txn)
- testutil_check(__wt_thread_join(NULL, &lrt_tid));
- if (g.c_txn_timestamps)
- testutil_check(__wt_thread_join(NULL, &timestamp_tid));
- g.workers_finished = false;
-
- logop(session, "%s", "=============== thread ops stop");
- if (g.logging)
- testutil_check(session->close(session, NULL));
-
- for (i = 0; i < g.c_threads; ++i)
- free(tinfo_list[i]);
- free(tinfo_list);
+ TINFO *tinfo, total;
+ WT_CONNECTION *conn;
+ WT_SESSION *session;
+ wt_thread_t alter_tid, backup_tid, checkpoint_tid, compact_tid, lrt_tid;
+ wt_thread_t timestamp_tid;
+ int64_t fourths, quit_fourths, thread_ops;
+ uint32_t i;
+ bool running;
+
+ conn = g.wts_conn;
+
+ session = NULL; /* -Wconditional-uninitialized */
+ memset(&alter_tid, 0, sizeof(alter_tid));
+ memset(&backup_tid, 0, sizeof(backup_tid));
+ memset(&checkpoint_tid, 0, sizeof(checkpoint_tid));
+ memset(&compact_tid, 0, sizeof(compact_tid));
+ memset(&lrt_tid, 0, sizeof(lrt_tid));
+ memset(&timestamp_tid, 0, sizeof(timestamp_tid));
+
+ modify_repl_init();
+
+ /*
+ * There are two mechanisms to specify the length of the run, a number
+ * of operations and a timer, when either expire the run terminates.
+ *
+ * Each thread does an equal share of the total operations (and make
+ * sure that it's not 0).
+ *
+ * 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)
+ 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 (g.c_timer == 0)
+ fourths = quit_fourths = -1;
+ else {
+ fourths = ((int64_t)g.c_timer * 4 * 60) / FORMAT_OPERATION_REPS;
+ quit_fourths = fourths + 15 * 4 * 60;
+ }
+
+ /* Initialize the table extension code. */
+ table_append_init();
+
+ /*
+ * We support replay of threaded runs, but don't log random numbers after threaded operations
+ * start, there's no point.
+ */
+ if (!SINGLETHREADED)
+ g.rand_log_stop = true;
+
+ /* Logging requires a session. */
+ if (g.logging)
+ testutil_check(conn->open_session(conn, NULL, NULL, &session));
+ logop(session, "%s", "=============== thread ops start");
+
+ /*
+ * Create the per-thread structures and start the worker threads. Allocate the thread structures
+ * separately to minimize false sharing.
+ */
+ tinfo_list = dcalloc((size_t)g.c_threads + 1, sizeof(TINFO *));
+ for (i = 0; i < g.c_threads; ++i) {
+ tinfo_list[i] = tinfo = dcalloc(1, sizeof(TINFO));
+
+ tinfo->id = (int)i + 1;
+
+ /*
+ * Characterize the per-thread random number generator. Normally we want independent
+ * behavior so threads start in different parts of the RNG space, but we've found bugs by
+ * having the threads 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)
+ __wt_random_init_seed((WT_SESSION_IMPL *)session, &tinfo->rnd);
+ else
+ __wt_random_init(&tinfo->rnd);
+
+ tinfo->state = TINFO_RUNNING;
+ testutil_check(__wt_thread_create(NULL, &tinfo->tid, ops, tinfo));
+ }
+
+ /*
+ * If a multi-threaded run, start optional backup, compaction and long-running reader threads.
+ */
+ if (g.c_alter)
+ testutil_check(__wt_thread_create(NULL, &alter_tid, alter, NULL));
+ if (g.c_backups)
+ testutil_check(__wt_thread_create(NULL, &backup_tid, backup, NULL));
+ if (g.c_checkpoint_flag == CHECKPOINT_ON)
+ testutil_check(__wt_thread_create(NULL, &checkpoint_tid, checkpoint, NULL));
+ if (g.c_compact)
+ testutil_check(__wt_thread_create(NULL, &compact_tid, compact, NULL));
+ if (!SINGLETHREADED && g.c_long_running_txn)
+ testutil_check(__wt_thread_create(NULL, &lrt_tid, lrt, NULL));
+ if (g.c_txn_timestamps)
+ testutil_check(__wt_thread_create(NULL, &timestamp_tid, timestamp, tinfo_list));
+
+ /* 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) {
+ tinfo = tinfo_list[i];
+ total.commit += tinfo->commit;
+ total.insert += tinfo->insert;
+ total.prepare += tinfo->prepare;
+ total.remove += tinfo->remove;
+ total.rollback += tinfo->rollback;
+ total.search += tinfo->search;
+ total.truncate += tinfo->truncate;
+ total.update += tinfo->update;
+
+ switch (tinfo->state) {
+ case TINFO_RUNNING:
+ running = true;
+ break;
+ case TINFO_COMPLETE:
+ tinfo->state = TINFO_JOINED;
+ testutil_check(__wt_thread_join(NULL, &tinfo->tid));
+ break;
+ case TINFO_JOINED:
+ break;
+ }
+
+ /*
+ * If the timer has expired or this thread has completed its operations, notify the
+ * thread it should quit.
+ */
+ if (fourths == 0 || (thread_ops != -1 && tinfo->ops >= (uint64_t)thread_ops)) {
+ /*
+ * On the last execution, optionally drop core for recovery testing.
+ */
+ if (lastrun && g.c_abort) {
+ static char *core = NULL;
+ *core = 0;
+ }
+ tinfo->quit = true;
+ }
+ }
+ track("ops", 0ULL, &total);
+ if (!running)
+ break;
+ __wt_sleep(0, 250000); /* 1/4th of a second */
+ if (fourths != -1)
+ --fourths;
+ if (quit_fourths != -1 && --quit_fourths == 0) {
+ fprintf(stderr, "%s\n",
+ "format run more than 15 minutes past the maximum "
+ "time");
+ fprintf(stderr, "%s\n",
+ "format run dumping cache and transaction state, "
+ "then aborting the process");
+
+ /*
+ * If the library is deadlocked, we might just join the mess, set a timer to limit our
+ * exposure.
+ */
+ set_alarm();
+
+ (void)conn->debug_info(conn, "txn");
+ (void)conn->debug_info(conn, "cache");
+
+ __wt_abort(NULL);
+ }
+ }
+
+ /* Wait for the other threads. */
+ g.workers_finished = true;
+ if (g.c_alter)
+ testutil_check(__wt_thread_join(NULL, &alter_tid));
+ if (g.c_backups)
+ testutil_check(__wt_thread_join(NULL, &backup_tid));
+ if (g.c_checkpoint_flag == CHECKPOINT_ON)
+ testutil_check(__wt_thread_join(NULL, &checkpoint_tid));
+ if (g.c_compact)
+ testutil_check(__wt_thread_join(NULL, &compact_tid));
+ if (!SINGLETHREADED && g.c_long_running_txn)
+ testutil_check(__wt_thread_join(NULL, &lrt_tid));
+ if (g.c_txn_timestamps)
+ testutil_check(__wt_thread_join(NULL, &timestamp_tid));
+ g.workers_finished = false;
+
+ logop(session, "%s", "=============== thread ops stop");
+ if (g.logging)
+ testutil_check(session->close(session, NULL));
+
+ for (i = 0; i < g.c_threads; ++i)
+ free(tinfo_list[i]);
+ free(tinfo_list);
}
/*
* begin_transaction_ts --
- * Begin a timestamped transaction.
+ * Begin a timestamped transaction.
*/
static void
begin_transaction_ts(TINFO *tinfo, u_int *iso_configp)
{
- TINFO **tlp;
- WT_DECL_RET;
- WT_SESSION *session;
- uint64_t ts;
- const char *config;
- char buf[64];
-
- session = tinfo->session;
-
- config = "isolation=snapshot";
- *iso_configp = ISOLATION_SNAPSHOT;
-
- /*
- * Transaction reads are normally repeatable, but WiredTiger timestamps
- * allow rewriting commits, that is, applications can specify at commit
- * time the timestamp at which the commit happens. If that happens, our
- * read might no longer be repeatable. Test in both modes: pick a read
- * timestamp we know is repeatable (because it's at least as old as the
- * oldest resolved commit timestamp in any thread), and pick a current
- * timestamp, 50% of the time.
- */
- ts = 0;
- if (mmrand(&tinfo->rnd, 1, 2) == 1)
- for (ts = UINT64_MAX, tlp = tinfo_list; *tlp != NULL; ++tlp)
- ts = WT_MIN(ts, (*tlp)->commit_ts);
- if (ts != 0) {
- wiredtiger_begin_transaction(session, config);
-
- /*
- * If the timestamp has aged out of the system, we'll get EINVAL
- * when we try and set it. That kills the transaction, we have
- * to restart.
- */
- testutil_check(__wt_snprintf(
- buf, sizeof(buf), "read_timestamp=%" PRIx64, ts));
- ret = session->timestamp_transaction(session, buf);
- if (ret == 0) {
- snap_init(tinfo, ts, true);
- logop(session,
- "begin snapshot read-ts=%" PRIu64 " (repeatable)",
- ts);
- return;
- }
- if (ret != EINVAL)
- testutil_check(ret);
-
- testutil_check(session->rollback_transaction(session, NULL));
- }
-
- wiredtiger_begin_transaction(session, config);
-
- /*
- * Otherwise, pick a current timestamp.
- *
- * Prepare returns an error if the prepare timestamp is less
- * than any active read timestamp, single-thread transaction
- * prepare and begin.
- *
- * Lock out the oldest timestamp update.
- */
- testutil_check(pthread_rwlock_wrlock(&g.ts_lock));
-
- ts = __wt_atomic_addv64(&g.timestamp, 1);
- testutil_check(__wt_snprintf(
- buf, sizeof(buf), "read_timestamp=%" PRIx64, ts));
- testutil_check(session->timestamp_transaction(session, buf));
-
- testutil_check(pthread_rwlock_unlock(&g.ts_lock));
-
- snap_init(tinfo, ts, false);
- logop(session,
- "begin snapshot read-ts=%" PRIu64 " (not repeatable)", ts);
+ TINFO **tlp;
+ WT_DECL_RET;
+ WT_SESSION *session;
+ uint64_t ts;
+ const char *config;
+ char buf[64];
+
+ session = tinfo->session;
+
+ config = "isolation=snapshot";
+ *iso_configp = ISOLATION_SNAPSHOT;
+
+ /*
+ * Transaction reads are normally repeatable, but WiredTiger timestamps allow rewriting commits,
+ * that is, applications can specify at commit time the timestamp at which the commit happens.
+ * If that happens, our read might no longer be repeatable. Test in both modes: pick a read
+ * timestamp we know is repeatable (because it's at least as old as the oldest resolved commit
+ * timestamp in any thread), and pick a current timestamp, 50% of the time.
+ */
+ ts = 0;
+ if (mmrand(&tinfo->rnd, 1, 2) == 1)
+ for (ts = UINT64_MAX, tlp = tinfo_list; *tlp != NULL; ++tlp)
+ ts = WT_MIN(ts, (*tlp)->commit_ts);
+ if (ts != 0) {
+ wiredtiger_begin_transaction(session, config);
+
+ /*
+ * If the timestamp has aged out of the system, we'll get EINVAL when we try and set it.
+ * That kills the transaction, we have to restart.
+ */
+ testutil_check(__wt_snprintf(buf, sizeof(buf), "read_timestamp=%" PRIx64, ts));
+ ret = session->timestamp_transaction(session, buf);
+ if (ret == 0) {
+ snap_init(tinfo, ts, true);
+ logop(session, "begin snapshot read-ts=%" PRIu64 " (repeatable)", ts);
+ return;
+ }
+ if (ret != EINVAL)
+ testutil_check(ret);
+
+ testutil_check(session->rollback_transaction(session, NULL));
+ }
+
+ wiredtiger_begin_transaction(session, config);
+
+ /*
+ * Otherwise, pick a current timestamp.
+ *
+ * Prepare returns an error if the prepare timestamp is less
+ * than any active read timestamp, single-thread transaction
+ * prepare and begin.
+ *
+ * Lock out the oldest timestamp update.
+ */
+ testutil_check(pthread_rwlock_wrlock(&g.ts_lock));
+
+ ts = __wt_atomic_addv64(&g.timestamp, 1);
+ testutil_check(__wt_snprintf(buf, sizeof(buf), "read_timestamp=%" PRIx64, ts));
+ testutil_check(session->timestamp_transaction(session, buf));
+
+ testutil_check(pthread_rwlock_unlock(&g.ts_lock));
+
+ snap_init(tinfo, ts, false);
+ logop(session, "begin snapshot read-ts=%" PRIu64 " (not repeatable)", ts);
}
/*
* begin_transaction --
- * Choose an isolation configuration and begin a transaction.
+ * Choose an isolation configuration and begin a transaction.
*/
static void
begin_transaction(TINFO *tinfo, u_int *iso_configp)
{
- WT_SESSION *session;
- u_int v;
- const char *config, *log;
-
- session = tinfo->session;
-
- if ((v = g.c_isolation_flag) == ISOLATION_RANDOM)
- v = mmrand(&tinfo->rnd, 1, 3);
- switch (v) {
- case 1:
- v = ISOLATION_READ_UNCOMMITTED;
- log = "read-uncommitted";
- config = "isolation=read-uncommitted";
- break;
- case 2:
- v = ISOLATION_READ_COMMITTED;
- log = "read-committed";
- config = "isolation=read-committed";
- break;
- case 3:
- default:
- v = ISOLATION_SNAPSHOT;
- log = "snapshot";
- config = "isolation=snapshot";
- break;
- }
- *iso_configp = v;
-
- wiredtiger_begin_transaction(session, config);
-
- snap_init(tinfo, WT_TS_NONE, false);
- logop(session, "begin %s", log);
+ WT_SESSION *session;
+ u_int v;
+ const char *config, *log;
+
+ session = tinfo->session;
+
+ if ((v = g.c_isolation_flag) == ISOLATION_RANDOM)
+ v = mmrand(&tinfo->rnd, 1, 3);
+ switch (v) {
+ case 1:
+ v = ISOLATION_READ_UNCOMMITTED;
+ log = "read-uncommitted";
+ config = "isolation=read-uncommitted";
+ break;
+ case 2:
+ v = ISOLATION_READ_COMMITTED;
+ log = "read-committed";
+ config = "isolation=read-committed";
+ break;
+ case 3:
+ default:
+ v = ISOLATION_SNAPSHOT;
+ log = "snapshot";
+ config = "isolation=snapshot";
+ break;
+ }
+ *iso_configp = v;
+
+ wiredtiger_begin_transaction(session, config);
+
+ snap_init(tinfo, WT_TS_NONE, false);
+ logop(session, "begin %s", log);
}
/*
@@ -423,41 +401,37 @@ begin_transaction(TINFO *tinfo, u_int *iso_configp)
static void
commit_transaction(TINFO *tinfo, bool prepared)
{
- WT_SESSION *session;
- uint64_t ts;
- char buf[64];
+ WT_SESSION *session;
+ uint64_t ts;
+ char buf[64];
- ++tinfo->commit;
+ ++tinfo->commit;
- session = tinfo->session;
+ session = tinfo->session;
- ts = 0; /* -Wconditional-uninitialized */
- if (g.c_txn_timestamps) {
- /* Lock out the oldest timestamp update. */
- testutil_check(pthread_rwlock_wrlock(&g.ts_lock));
+ ts = 0; /* -Wconditional-uninitialized */
+ if (g.c_txn_timestamps) {
+ /* Lock out the oldest timestamp update. */
+ testutil_check(pthread_rwlock_wrlock(&g.ts_lock));
- ts = __wt_atomic_addv64(&g.timestamp, 1);
- testutil_check(__wt_snprintf(
- buf, sizeof(buf), "commit_timestamp=%" PRIx64, ts));
- testutil_check(session->timestamp_transaction(session, buf));
+ ts = __wt_atomic_addv64(&g.timestamp, 1);
+ testutil_check(__wt_snprintf(buf, sizeof(buf), "commit_timestamp=%" PRIx64, ts));
+ testutil_check(session->timestamp_transaction(session, buf));
- if (prepared) {
- testutil_check(__wt_snprintf(buf, sizeof(buf),
- "durable_timestamp=%" PRIx64, ts));
- testutil_check(
- session->timestamp_transaction(session, buf));
- }
+ if (prepared) {
+ testutil_check(__wt_snprintf(buf, sizeof(buf), "durable_timestamp=%" PRIx64, ts));
+ testutil_check(session->timestamp_transaction(session, buf));
+ }
- testutil_check(pthread_rwlock_unlock(&g.ts_lock));
- }
- testutil_check(session->commit_transaction(session, NULL));
+ testutil_check(pthread_rwlock_unlock(&g.ts_lock));
+ }
+ testutil_check(session->commit_transaction(session, NULL));
- /* Remember our oldest commit timestamp. */
- tinfo->commit_ts = ts;
+ /* Remember our oldest commit timestamp. */
+ tinfo->commit_ts = ts;
- logop(session,
- "commit read-ts=%" PRIu64 ", commit-ts=%" PRIu64,
- tinfo->read_ts, tinfo->commit_ts);
+ logop(
+ session, "commit read-ts=%" PRIu64 ", commit-ts=%" PRIu64, tinfo->read_ts, tinfo->commit_ts);
}
/*
@@ -467,16 +441,15 @@ commit_transaction(TINFO *tinfo, bool prepared)
static void
rollback_transaction(TINFO *tinfo)
{
- WT_SESSION *session;
+ WT_SESSION *session;
- session = tinfo->session;
+ session = tinfo->session;
- ++tinfo->rollback;
+ ++tinfo->rollback;
- testutil_check(session->rollback_transaction(session, NULL));
+ testutil_check(session->rollback_transaction(session, NULL));
- logop(session,
- "abort read-ts=%" PRIu64, tinfo->read_ts);
+ logop(session, "abort read-ts=%" PRIu64, tinfo->read_ts);
}
/*
@@ -486,131 +459,130 @@ rollback_transaction(TINFO *tinfo)
static int
prepare_transaction(TINFO *tinfo)
{
- WT_DECL_RET;
- WT_SESSION *session;
- uint64_t ts;
- char buf[64];
-
- session = tinfo->session;
-
- ++tinfo->prepare;
-
- /*
- * Prepare timestamps must be less than or equal to the eventual commit
- * timestamp. Set the prepare timestamp to whatever the global value is
- * now. The subsequent commit will increment it, ensuring correctness.
- *
- * Prepare returns an error if the prepare timestamp is less than any
- * active read timestamp, single-thread transaction prepare and begin.
- *
- * Lock out the oldest timestamp update.
- */
- testutil_check(pthread_rwlock_wrlock(&g.ts_lock));
-
- ts = __wt_atomic_addv64(&g.timestamp, 1);
- testutil_check(__wt_snprintf(
- buf, sizeof(buf), "prepare_timestamp=%" PRIx64, ts));
- ret = session->prepare_transaction(session, buf);
-
- testutil_check(pthread_rwlock_unlock(&g.ts_lock));
-
- return (ret);
+ WT_DECL_RET;
+ WT_SESSION *session;
+ uint64_t ts;
+ char buf[64];
+
+ session = tinfo->session;
+
+ ++tinfo->prepare;
+
+ /*
+ * Prepare timestamps must be less than or equal to the eventual commit
+ * timestamp. Set the prepare timestamp to whatever the global value is
+ * now. The subsequent commit will increment it, ensuring correctness.
+ *
+ * Prepare returns an error if the prepare timestamp is less than any
+ * active read timestamp, single-thread transaction prepare and begin.
+ *
+ * Lock out the oldest timestamp update.
+ */
+ testutil_check(pthread_rwlock_wrlock(&g.ts_lock));
+
+ ts = __wt_atomic_addv64(&g.timestamp, 1);
+ testutil_check(__wt_snprintf(buf, sizeof(buf), "prepare_timestamp=%" PRIx64, ts));
+ ret = session->prepare_transaction(session, buf);
+
+ testutil_check(pthread_rwlock_unlock(&g.ts_lock));
+
+ return (ret);
}
/*
* OP_FAILED --
* General error handling.
*/
-#define OP_FAILED(notfound_ok) do { \
- positioned = false; \
- if (intxn && (ret == WT_CACHE_FULL || ret == WT_ROLLBACK)) \
- goto rollback; \
- testutil_assert((notfound_ok && ret == WT_NOTFOUND) || \
- ret == WT_CACHE_FULL || ret == WT_ROLLBACK); \
-} while (0)
+#define OP_FAILED(notfound_ok) \
+ do { \
+ positioned = false; \
+ if (intxn && (ret == WT_CACHE_FULL || ret == WT_ROLLBACK)) \
+ goto rollback; \
+ testutil_assert( \
+ (notfound_ok && ret == WT_NOTFOUND) || ret == WT_CACHE_FULL || ret == WT_ROLLBACK); \
+ } while (0)
/*
- * Rollback updates returning prepare-conflict, they're unlikely to succeed
- * unless the prepare aborts. Reads wait out the error, so it's unexpected.
+ * Rollback updates returning prepare-conflict, they're unlikely to succeed unless the prepare
+ * aborts. Reads wait out the error, so it's unexpected.
*/
-#define READ_OP_FAILED(notfound_ok) \
- OP_FAILED(notfound_ok)
-#define WRITE_OP_FAILED(notfound_ok) do { \
- if (ret == WT_PREPARE_CONFLICT) \
- ret = WT_ROLLBACK; \
- OP_FAILED(notfound_ok); \
-} while (0)
+#define READ_OP_FAILED(notfound_ok) OP_FAILED(notfound_ok)
+#define WRITE_OP_FAILED(notfound_ok) \
+ do { \
+ if (ret == WT_PREPARE_CONFLICT) \
+ ret = WT_ROLLBACK; \
+ OP_FAILED(notfound_ok); \
+ } while (0)
/*
- * When in a transaction on the live table with snapshot isolation, track
- * operations for later repetition.
+ * When in a transaction on the live table with snapshot isolation, track operations for later
+ * repetition.
*/
-#define SNAP_TRACK(tinfo, op) do { \
- if (intxn && !ckpt_handle && iso_config == ISOLATION_SNAPSHOT) \
- snap_track(tinfo, op); \
-} while (0)
+#define SNAP_TRACK(tinfo, op) \
+ do { \
+ if (intxn && !ckpt_handle && iso_config == ISOLATION_SNAPSHOT) \
+ snap_track(tinfo, op); \
+ } while (0)
/*
* ops_open_session --
- * Create a new session/cursor pair for the thread.
+ * Create a new session/cursor pair for the thread.
*/
static void
ops_open_session(TINFO *tinfo, bool *ckpt_handlep)
{
- WT_CONNECTION *conn;
- WT_CURSOR *cursor;
- WT_DECL_RET;
- WT_SESSION *session;
-
- conn = g.wts_conn;
-
- /* Close any open session/cursor. */
- if ((session = tinfo->session) != NULL)
- testutil_check(session->close(session, NULL));
-
- testutil_check(conn->open_session(conn, NULL, NULL, &session));
-
- /*
- * 10% of the time, perform some read-only operations from a checkpoint.
- * Skip if we are using data-sources or LSM, they don't support reading
- * from checkpoints.
- */
- cursor = NULL;
- if (!DATASOURCE("lsm") && mmrand(&tinfo->rnd, 1, 10) == 1) {
- /*
- * WT_SESSION.open_cursor can return EBUSY if concurrent with a
- * metadata operation, retry.
- */
- while ((ret = session->open_cursor(session, g.uri, NULL,
- "checkpoint=WiredTigerCheckpoint", &cursor)) == EBUSY)
- __wt_yield();
-
- /*
- * If the checkpoint hasn't been created yet, ignore the error.
- */
- if (ret != ENOENT) {
- testutil_check(ret);
- *ckpt_handlep = true;
- }
- }
- if (cursor == NULL) {
- /*
- * 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_check(ret);
- *ckpt_handlep = false;
- }
-
- tinfo->session = session;
- tinfo->cursor = cursor;
+ WT_CONNECTION *conn;
+ WT_CURSOR *cursor;
+ WT_DECL_RET;
+ WT_SESSION *session;
+
+ conn = g.wts_conn;
+
+ /* Close any open session/cursor. */
+ if ((session = tinfo->session) != NULL)
+ testutil_check(session->close(session, NULL));
+
+ testutil_check(conn->open_session(conn, NULL, NULL, &session));
+
+ /*
+ * 10% of the time, perform some read-only operations from a checkpoint.
+ * Skip if we are using data-sources or LSM, they don't support reading
+ * from checkpoints.
+ */
+ cursor = NULL;
+ if (!DATASOURCE("lsm") && mmrand(&tinfo->rnd, 1, 10) == 1) {
+ /*
+ * WT_SESSION.open_cursor can return EBUSY if concurrent with a metadata operation, retry.
+ */
+ while ((ret = session->open_cursor(
+ session, g.uri, NULL, "checkpoint=WiredTigerCheckpoint", &cursor)) == EBUSY)
+ __wt_yield();
+
+ /*
+ * If the checkpoint hasn't been created yet, ignore the error.
+ */
+ if (ret != ENOENT) {
+ testutil_check(ret);
+ *ckpt_handlep = true;
+ }
+ }
+ if (cursor == NULL) {
+ /*
+ * 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_check(ret);
+ *ckpt_handlep = false;
+ }
+
+ tinfo->session = session;
+ tinfo->cursor = cursor;
}
/*
@@ -620,1238 +592,1171 @@ ops_open_session(TINFO *tinfo, bool *ckpt_handlep)
static WT_THREAD_RET
ops(void *arg)
{
- TINFO *tinfo;
- WT_CURSOR *cursor;
- WT_DECL_RET;
- WT_SESSION *session;
- thread_op op;
- uint64_t reset_op, session_op, truncate_op;
- uint32_t range, rnd;
- u_int i, j, iso_config;
- bool ckpt_handle, greater_than, intxn, next, positioned, prepared;
-
- tinfo = arg;
-
- iso_config = ISOLATION_RANDOM; /* -Wconditional-uninitialized */
- ckpt_handle = false; /* -Wconditional-uninitialized */
-
- /* Tracking of transactional snapshot isolation operations. */
- tinfo->snap = tinfo->snap_first = tinfo->snap_list;
-
- /* Set up the default key and value buffers. */
- tinfo->key = &tinfo->_key;
- key_gen_init(tinfo->key);
- tinfo->value = &tinfo->_value;
- val_gen_init(tinfo->value);
- tinfo->lastkey = &tinfo->_lastkey;
- key_gen_init(tinfo->lastkey);
- tinfo->tbuf = &tinfo->_tbuf;
-
- /* Set the first operation where we'll create sessions and cursors. */
- cursor = NULL;
- 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);
-
- for (intxn = false; !tinfo->quit; ++tinfo->ops) {
- /* Periodically open up a new session and cursors. */
- if (tinfo->ops > session_op ||
- session == NULL || cursor == NULL) {
- /*
- * We can't swap sessions/cursors if in a transaction,
- * resolve any running transaction.
- */
- if (intxn) {
- commit_transaction(tinfo, false);
- intxn = false;
- }
-
- ops_open_session(tinfo, &ckpt_handle);
-
- /* Pick the next session/cursor close/open. */
- session_op += mmrand(&tinfo->rnd, 100, 5000);
-
- session = tinfo->session;
- cursor = tinfo->cursor;
- }
-
- /*
- * If not in a transaction, reset the session now and then, just
- * to make sure that operation gets tested. The test is not for
- * equality, we have to do the reset outside of a transaction so
- * we aren't likely to get an exact match.
- */
- if (!intxn && tinfo->ops > reset_op) {
- testutil_check(session->reset(session));
-
- /* Pick the next reset operation. */
- reset_op += mmrand(&tinfo->rnd, 20000, 50000);
- }
-
- /*
- * If not in a transaction, have a live handle and running in a
- * timestamp world, occasionally repeat a timestamped operation.
- */
- if (!intxn && !ckpt_handle &&
- g.c_txn_timestamps && mmrand(&tinfo->rnd, 1, 15) == 1) {
- ++tinfo->search;
- snap_repeat_single(cursor, tinfo);
- }
-
- /*
- * If not in a transaction and have a live handle, choose an
- * isolation level and start a transaction some percentage of
- * the time.
- */
- if (!intxn && (g.c_txn_timestamps ||
- mmrand(&tinfo->rnd, 1, 100) <= g.c_txn_freq)) {
- if (g.c_txn_timestamps)
- begin_transaction_ts(tinfo, &iso_config);
- else
- begin_transaction(tinfo, &iso_config);
- intxn = true;
- }
-
- /* Select an operation. */
- op = READ;
- if (!ckpt_handle) {
- i = mmrand(&tinfo->rnd, 1, 100);
- if (i < g.c_delete_pct && 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)
- op = REMOVE;
- else if (i < g.c_delete_pct + g.c_insert_pct)
- op = INSERT;
- else if (i < g.c_delete_pct +
- g.c_insert_pct + g.c_modify_pct)
- op = MODIFY;
- else if (i < g.c_delete_pct +
- g.c_insert_pct + g.c_modify_pct + g.c_write_pct)
- op = UPDATE;
- }
-
- /* Select a row. */
- tinfo->keyno = mmrand(&tinfo->rnd, 1, (u_int)g.rows);
-
- /*
- * Inserts, removes and updates can be done following a cursor
- * set-key, or based on a cursor position taken from a previous
- * search. If not already doing a read, position the cursor at
- * an existing point in the tree 20% of the time.
- */
- positioned = false;
- if (op != READ && mmrand(&tinfo->rnd, 1, 5) == 1) {
- ++tinfo->search;
- ret = read_row(tinfo, cursor);
- if (ret == 0) {
- positioned = true;
- SNAP_TRACK(tinfo, READ);
- } else
- READ_OP_FAILED(true);
- }
-
- /*
- * Optionally reserve a row. Reserving a row before a read isn't
- * all that sensible, but not unexpected, either.
- */
- if (intxn && !ckpt_handle && mmrand(&tinfo->rnd, 0, 20) == 1) {
- switch (g.type) {
- case ROW:
- ret = row_reserve(tinfo, cursor, positioned);
- break;
- case FIX:
- case VAR:
- ret = col_reserve(tinfo, cursor, positioned);
- break;
- }
- if (ret == 0) {
- positioned = true;
-
- __wt_yield(); /* Let other threads proceed. */
- } else
- WRITE_OP_FAILED(true);
- }
-
- /* Perform the operation. */
- switch (op) {
- case INSERT:
- switch (g.type) {
- case ROW:
- ret = row_insert(tinfo, cursor, positioned);
- break;
- case FIX:
- case VAR:
- /*
- * We can only append so many new records, once
- * we reach that limit, update a record instead
- * of inserting.
- */
- if (g.append_cnt >= g.append_max)
- goto update_instead_of_chosen_op;
-
- ret = col_insert(tinfo, cursor);
- break;
- }
-
- /* Insert never leaves the cursor positioned. */
- positioned = false;
- if (ret == 0) {
- ++tinfo->insert;
- SNAP_TRACK(tinfo, INSERT);
- } else
- WRITE_OP_FAILED(false);
- break;
- case MODIFY:
- /*
- * Change modify into update if not part of a snapshot
- * isolation transaction, modify isn't supported in
- * those cases.
- */
- if (!intxn || iso_config != ISOLATION_SNAPSHOT)
- goto update_instead_of_chosen_op;
-
- ++tinfo->update;
- switch (g.type) {
- case ROW:
- ret = row_modify(tinfo, cursor, positioned);
- break;
- case VAR:
- ret = col_modify(tinfo, cursor, positioned);
- break;
- }
- if (ret == 0) {
- positioned = true;
- SNAP_TRACK(tinfo, MODIFY);
- } else
- WRITE_OP_FAILED(true);
- break;
- case READ:
- ++tinfo->search;
- ret = read_row(tinfo, cursor);
- if (ret == 0) {
- positioned = true;
- SNAP_TRACK(tinfo, READ);
- } else
- READ_OP_FAILED(true);
- break;
- case REMOVE:
-remove_instead_of_truncate:
- switch (g.type) {
- case ROW:
- ret = row_remove(tinfo, cursor, positioned);
- break;
- case FIX:
- case VAR:
- ret = col_remove(tinfo, cursor, positioned);
- break;
- }
- if (ret == 0) {
- ++tinfo->remove;
- /*
- * Don't set positioned: it's unchanged from the
- * previous state, but not necessarily set.
- */
- SNAP_TRACK(tinfo, REMOVE);
- } else
- WRITE_OP_FAILED(true);
- break;
- case TRUNCATE:
- /*
- * A maximum of 2 truncation operations at a time, 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);
- goto remove_instead_of_truncate;
- }
-
- if (!positioned)
- tinfo->keyno =
- mmrand(&tinfo->rnd, 1, (u_int)g.rows);
-
- /*
- * Truncate up to 5% of the table. If the range overlaps
- * the beginning/end of the table, set the key to 0 (the
- * truncate function then sets a cursor to NULL so that
- * code is tested).
- *
- * This gets tricky: there are 2 directions (truncating
- * from lower keys to the current position or from
- * the current position to higher keys), and collation
- * order (truncating from lower keys to higher keys or
- * vice-versa).
- */
- greater_than = mmrand(&tinfo->rnd, 0, 1) == 1;
- range = g.rows < 20 ?
- 0 : mmrand(&tinfo->rnd, 0, (u_int)g.rows / 20);
- tinfo->last = tinfo->keyno;
- if (greater_than) {
- if (g.c_reverse) {
- if (tinfo->keyno <= range)
- tinfo->last = 0;
- else
- tinfo->last -= range;
- } else {
- tinfo->last += range;
- if (tinfo->last > g.rows)
- tinfo->last = 0;
- }
- } else {
- if (g.c_reverse) {
- tinfo->keyno += range;
- if (tinfo->keyno > g.rows)
- tinfo->keyno = 0;
- } else {
- if (tinfo->keyno <= range)
- tinfo->keyno = 0;
- else
- tinfo->keyno -= range;
- }
- }
- switch (g.type) {
- case ROW:
- ret = row_truncate(tinfo, cursor);
- break;
- case FIX:
- case VAR:
- ret = col_truncate(tinfo, cursor);
- break;
- }
- (void)__wt_atomic_subv64(&g.truncate_cnt, 1);
-
- /* Truncate never leaves the cursor positioned. */
- positioned = false;
- if (ret == 0) {
- ++tinfo->truncate;
- SNAP_TRACK(tinfo, TRUNCATE);
- } else
- WRITE_OP_FAILED(false);
- break;
- case UPDATE:
-update_instead_of_chosen_op:
- ++tinfo->update;
- switch (g.type) {
- case ROW:
- ret = row_update(tinfo, cursor, positioned);
- break;
- case FIX:
- case VAR:
- ret = col_update(tinfo, cursor, positioned);
- break;
- }
- if (ret == 0) {
- positioned = true;
- SNAP_TRACK(tinfo, UPDATE);
- } else
- WRITE_OP_FAILED(false);
- break;
- }
-
- /*
- * The cursor is positioned if we did any operation other than
- * insert, do a small number of next/prev cursor operations in
- * a random direction.
- */
- if (positioned) {
- 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)
- continue;
-
- READ_OP_FAILED(true);
- break;
- }
- }
-
- /* Reset the cursor: there is no reason to keep pages pinned. */
- testutil_check(cursor->reset(cursor));
-
- /*
- * Continue if not in a transaction, else add more operations
- * to the transaction half the time.
- */
- if (!intxn || (rnd = mmrand(&tinfo->rnd, 1, 10)) > 5)
- continue;
-
- /*
- * Ending a transaction. If on a live handle and the transaction
- * was configured for snapshot isolation, repeat the operations
- * and confirm the results are unchanged.
- */
- if (intxn && !ckpt_handle && iso_config == ISOLATION_SNAPSHOT) {
- __wt_yield(); /* Encourage races */
-
- ret = snap_repeat_txn(cursor, tinfo);
- testutil_assert(ret == 0 || ret == WT_ROLLBACK);
- if (ret == WT_ROLLBACK)
- goto rollback;
- }
-
- /*
- * If prepare configured, prepare the transaction 10% of the
- * time.
- */
- prepared = false;
- if (g.c_prepare && mmrand(&tinfo->rnd, 1, 10) == 1) {
- ret = prepare_transaction(tinfo);
- if (ret != 0)
- WRITE_OP_FAILED(false);
-
- __wt_yield(); /* Encourage races */
- prepared = true;
- }
-
- /*
- * If we're in a transaction, commit 40% of the time and
- * rollback 10% of the time.
- */
- switch (rnd) {
- case 1: case 2: case 3: case 4: /* 40% */
- commit_transaction(tinfo, prepared);
- snap_repeat_update(tinfo, true);
- break;
- case 5: /* 10% */
-rollback: rollback_transaction(tinfo);
- snap_repeat_update(tinfo, false);
- break;
- }
-
- intxn = false;
- }
-
- if (session != NULL)
- testutil_check(session->close(session, NULL));
-
- for (i = 0; i < WT_ELEMENTS(tinfo->snap_list); ++i) {
- free(tinfo->snap_list[i].kdata);
- free(tinfo->snap_list[i].vdata);
- }
- key_gen_teardown(tinfo->key);
- val_gen_teardown(tinfo->value);
- key_gen_teardown(tinfo->lastkey);
- free(tinfo->tbuf->mem);
-
- tinfo->state = TINFO_COMPLETE;
- return (WT_THREAD_RET_VALUE);
+ TINFO *tinfo;
+ WT_CURSOR *cursor;
+ WT_DECL_RET;
+ WT_SESSION *session;
+ thread_op op;
+ uint64_t reset_op, session_op, truncate_op;
+ uint32_t range, rnd;
+ u_int i, j, iso_config;
+ bool ckpt_handle, greater_than, intxn, next, positioned, prepared;
+
+ tinfo = arg;
+
+ iso_config = ISOLATION_RANDOM; /* -Wconditional-uninitialized */
+ ckpt_handle = false; /* -Wconditional-uninitialized */
+
+ /* Tracking of transactional snapshot isolation operations. */
+ tinfo->snap = tinfo->snap_first = tinfo->snap_list;
+
+ /* Set up the default key and value buffers. */
+ tinfo->key = &tinfo->_key;
+ key_gen_init(tinfo->key);
+ tinfo->value = &tinfo->_value;
+ val_gen_init(tinfo->value);
+ tinfo->lastkey = &tinfo->_lastkey;
+ key_gen_init(tinfo->lastkey);
+ tinfo->tbuf = &tinfo->_tbuf;
+
+ /* Set the first operation where we'll create sessions and cursors. */
+ cursor = NULL;
+ 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);
+
+ for (intxn = false; !tinfo->quit; ++tinfo->ops) {
+ /* Periodically open up a new session and cursors. */
+ if (tinfo->ops > session_op || session == NULL || cursor == NULL) {
+ /*
+ * We can't swap sessions/cursors if in a transaction, resolve any running transaction.
+ */
+ if (intxn) {
+ commit_transaction(tinfo, false);
+ intxn = false;
+ }
+
+ ops_open_session(tinfo, &ckpt_handle);
+
+ /* Pick the next session/cursor close/open. */
+ session_op += mmrand(&tinfo->rnd, 100, 5000);
+
+ session = tinfo->session;
+ cursor = tinfo->cursor;
+ }
+
+ /*
+ * If not in a transaction, reset the session now and then, just to make sure that operation
+ * gets tested. The test is not for equality, we have to do the reset outside of a
+ * transaction so we aren't likely to get an exact match.
+ */
+ if (!intxn && tinfo->ops > reset_op) {
+ testutil_check(session->reset(session));
+
+ /* Pick the next reset operation. */
+ reset_op += mmrand(&tinfo->rnd, 20000, 50000);
+ }
+
+ /*
+ * If not in a transaction, have a live handle and running in a timestamp world,
+ * occasionally repeat a timestamped operation.
+ */
+ if (!intxn && !ckpt_handle && g.c_txn_timestamps && mmrand(&tinfo->rnd, 1, 15) == 1) {
+ ++tinfo->search;
+ snap_repeat_single(cursor, tinfo);
+ }
+
+ /*
+ * If not in a transaction and have a live handle, choose an isolation level and start a
+ * transaction some percentage of the time.
+ */
+ if (!intxn && (g.c_txn_timestamps || mmrand(&tinfo->rnd, 1, 100) <= g.c_txn_freq)) {
+ if (g.c_txn_timestamps)
+ begin_transaction_ts(tinfo, &iso_config);
+ else
+ begin_transaction(tinfo, &iso_config);
+ intxn = true;
+ }
+
+ /* Select an operation. */
+ op = READ;
+ if (!ckpt_handle) {
+ i = mmrand(&tinfo->rnd, 1, 100);
+ if (i < g.c_delete_pct && 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)
+ op = REMOVE;
+ else if (i < g.c_delete_pct + g.c_insert_pct)
+ op = INSERT;
+ else if (i < g.c_delete_pct + g.c_insert_pct + g.c_modify_pct)
+ op = MODIFY;
+ else if (i < g.c_delete_pct + g.c_insert_pct + g.c_modify_pct + g.c_write_pct)
+ op = UPDATE;
+ }
+
+ /* Select a row. */
+ tinfo->keyno = mmrand(&tinfo->rnd, 1, (u_int)g.rows);
+
+ /*
+ * Inserts, removes and updates can be done following a cursor set-key, or based on a cursor
+ * position taken from a previous search. If not already doing a read, position the cursor
+ * at an existing point in the tree 20% of the time.
+ */
+ positioned = false;
+ if (op != READ && mmrand(&tinfo->rnd, 1, 5) == 1) {
+ ++tinfo->search;
+ ret = read_row(tinfo, cursor);
+ if (ret == 0) {
+ positioned = true;
+ SNAP_TRACK(tinfo, READ);
+ } else
+ READ_OP_FAILED(true);
+ }
+
+ /*
+ * Optionally reserve a row. Reserving a row before a read isn't all that sensible, but not
+ * unexpected, either.
+ */
+ if (intxn && !ckpt_handle && mmrand(&tinfo->rnd, 0, 20) == 1) {
+ switch (g.type) {
+ case ROW:
+ ret = row_reserve(tinfo, cursor, positioned);
+ break;
+ case FIX:
+ case VAR:
+ ret = col_reserve(tinfo, cursor, positioned);
+ break;
+ }
+ if (ret == 0) {
+ positioned = true;
+
+ __wt_yield(); /* Let other threads proceed. */
+ } else
+ WRITE_OP_FAILED(true);
+ }
+
+ /* Perform the operation. */
+ switch (op) {
+ case INSERT:
+ switch (g.type) {
+ case ROW:
+ ret = row_insert(tinfo, cursor, positioned);
+ break;
+ case FIX:
+ case VAR:
+ /*
+ * We can only append so many new records, once we reach that limit, update a record
+ * instead of inserting.
+ */
+ if (g.append_cnt >= g.append_max)
+ goto update_instead_of_chosen_op;
+
+ ret = col_insert(tinfo, cursor);
+ break;
+ }
+
+ /* Insert never leaves the cursor positioned. */
+ positioned = false;
+ if (ret == 0) {
+ ++tinfo->insert;
+ SNAP_TRACK(tinfo, INSERT);
+ } else
+ WRITE_OP_FAILED(false);
+ break;
+ case MODIFY:
+ /*
+ * Change modify into update if not part of a snapshot isolation transaction, modify
+ * isn't supported in those cases.
+ */
+ if (!intxn || iso_config != ISOLATION_SNAPSHOT)
+ goto update_instead_of_chosen_op;
+
+ ++tinfo->update;
+ switch (g.type) {
+ case ROW:
+ ret = row_modify(tinfo, cursor, positioned);
+ break;
+ case VAR:
+ ret = col_modify(tinfo, cursor, positioned);
+ break;
+ }
+ if (ret == 0) {
+ positioned = true;
+ SNAP_TRACK(tinfo, MODIFY);
+ } else
+ WRITE_OP_FAILED(true);
+ break;
+ case READ:
+ ++tinfo->search;
+ ret = read_row(tinfo, cursor);
+ if (ret == 0) {
+ positioned = true;
+ SNAP_TRACK(tinfo, READ);
+ } else
+ READ_OP_FAILED(true);
+ break;
+ case REMOVE:
+ remove_instead_of_truncate:
+ switch (g.type) {
+ case ROW:
+ ret = row_remove(tinfo, cursor, positioned);
+ break;
+ case FIX:
+ case VAR:
+ ret = col_remove(tinfo, cursor, positioned);
+ break;
+ }
+ if (ret == 0) {
+ ++tinfo->remove;
+ /*
+ * Don't set positioned: it's unchanged from the previous state, but not necessarily
+ * set.
+ */
+ SNAP_TRACK(tinfo, REMOVE);
+ } else
+ WRITE_OP_FAILED(true);
+ break;
+ case TRUNCATE:
+ /*
+ * A maximum of 2 truncation operations at a time, 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);
+ goto remove_instead_of_truncate;
+ }
+
+ if (!positioned)
+ tinfo->keyno = mmrand(&tinfo->rnd, 1, (u_int)g.rows);
+
+ /*
+ * Truncate up to 5% of the table. If the range overlaps
+ * the beginning/end of the table, set the key to 0 (the
+ * truncate function then sets a cursor to NULL so that
+ * code is tested).
+ *
+ * This gets tricky: there are 2 directions (truncating
+ * from lower keys to the current position or from
+ * the current position to higher keys), and collation
+ * order (truncating from lower keys to higher keys or
+ * vice-versa).
+ */
+ greater_than = mmrand(&tinfo->rnd, 0, 1) == 1;
+ range = g.rows < 20 ? 0 : mmrand(&tinfo->rnd, 0, (u_int)g.rows / 20);
+ tinfo->last = tinfo->keyno;
+ if (greater_than) {
+ if (g.c_reverse) {
+ if (tinfo->keyno <= range)
+ tinfo->last = 0;
+ else
+ tinfo->last -= range;
+ } else {
+ tinfo->last += range;
+ if (tinfo->last > g.rows)
+ tinfo->last = 0;
+ }
+ } else {
+ if (g.c_reverse) {
+ tinfo->keyno += range;
+ if (tinfo->keyno > g.rows)
+ tinfo->keyno = 0;
+ } else {
+ if (tinfo->keyno <= range)
+ tinfo->keyno = 0;
+ else
+ tinfo->keyno -= range;
+ }
+ }
+ switch (g.type) {
+ case ROW:
+ ret = row_truncate(tinfo, cursor);
+ break;
+ case FIX:
+ case VAR:
+ ret = col_truncate(tinfo, cursor);
+ break;
+ }
+ (void)__wt_atomic_subv64(&g.truncate_cnt, 1);
+
+ /* Truncate never leaves the cursor positioned. */
+ positioned = false;
+ if (ret == 0) {
+ ++tinfo->truncate;
+ SNAP_TRACK(tinfo, TRUNCATE);
+ } else
+ WRITE_OP_FAILED(false);
+ break;
+ case UPDATE:
+ update_instead_of_chosen_op:
+ ++tinfo->update;
+ switch (g.type) {
+ case ROW:
+ ret = row_update(tinfo, cursor, positioned);
+ break;
+ case FIX:
+ case VAR:
+ ret = col_update(tinfo, cursor, positioned);
+ break;
+ }
+ if (ret == 0) {
+ positioned = true;
+ SNAP_TRACK(tinfo, UPDATE);
+ } else
+ WRITE_OP_FAILED(false);
+ break;
+ }
+
+ /*
+ * The cursor is positioned if we did any operation other than insert, do a small number of
+ * next/prev cursor operations in a random direction.
+ */
+ if (positioned) {
+ 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)
+ continue;
+
+ READ_OP_FAILED(true);
+ break;
+ }
+ }
+
+ /* Reset the cursor: there is no reason to keep pages pinned. */
+ testutil_check(cursor->reset(cursor));
+
+ /*
+ * Continue if not in a transaction, else add more operations to the transaction half the
+ * time.
+ */
+ if (!intxn || (rnd = mmrand(&tinfo->rnd, 1, 10)) > 5)
+ continue;
+
+ /*
+ * Ending a transaction. If on a live handle and the transaction was configured for snapshot
+ * isolation, repeat the operations and confirm the results are unchanged.
+ */
+ if (intxn && !ckpt_handle && iso_config == ISOLATION_SNAPSHOT) {
+ __wt_yield(); /* Encourage races */
+
+ ret = snap_repeat_txn(cursor, tinfo);
+ testutil_assert(ret == 0 || ret == WT_ROLLBACK);
+ if (ret == WT_ROLLBACK)
+ goto rollback;
+ }
+
+ /*
+ * If prepare configured, prepare the transaction 10% of the time.
+ */
+ prepared = false;
+ if (g.c_prepare && mmrand(&tinfo->rnd, 1, 10) == 1) {
+ ret = prepare_transaction(tinfo);
+ if (ret != 0)
+ WRITE_OP_FAILED(false);
+
+ __wt_yield(); /* Encourage races */
+ prepared = true;
+ }
+
+ /*
+ * If we're in a transaction, commit 40% of the time and rollback 10% of the time.
+ */
+ switch (rnd) {
+ case 1:
+ case 2:
+ case 3:
+ case 4: /* 40% */
+ commit_transaction(tinfo, prepared);
+ snap_repeat_update(tinfo, true);
+ break;
+ case 5: /* 10% */
+rollback:
+ rollback_transaction(tinfo);
+ snap_repeat_update(tinfo, false);
+ break;
+ }
+
+ intxn = false;
+ }
+
+ if (session != NULL)
+ testutil_check(session->close(session, NULL));
+
+ for (i = 0; i < WT_ELEMENTS(tinfo->snap_list); ++i) {
+ free(tinfo->snap_list[i].kdata);
+ free(tinfo->snap_list[i].vdata);
+ }
+ key_gen_teardown(tinfo->key);
+ val_gen_teardown(tinfo->value);
+ key_gen_teardown(tinfo->lastkey);
+ free(tinfo->tbuf->mem);
+
+ tinfo->state = TINFO_COMPLETE;
+ return (WT_THREAD_RET_VALUE);
}
/*
* wts_read_scan --
- * Read and verify a subset of the elements in a file.
+ * Read and verify a subset of the elements in a file.
*/
void
wts_read_scan(void)
{
- WT_CONNECTION *conn;
- WT_CURSOR *cursor;
- WT_DECL_RET;
- WT_ITEM key, value;
- WT_SESSION *session;
- uint64_t keyno, last_keyno;
-
- conn = g.wts_conn;
-
- /*
- * We're not configuring transactions or read timestamps, if there's a
- * diagnostic check, skip the scan.
- */
- if (g.c_assert_read_timestamp)
- return;
-
- /* Set up the default key/value buffers. */
- key_gen_init(&key);
- val_gen_init(&value);
-
- /* 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);
-
- /* Check a random subset of the records using the key. */
- for (last_keyno = keyno = 0; keyno < g.key_cnt;) {
- keyno += mmrand(NULL, 1, 17);
- if (keyno > g.rows)
- keyno = g.rows;
- if (keyno - last_keyno > 1000) {
- track("read row scan", keyno, NULL);
- last_keyno = keyno;
- }
-
- switch (ret = read_row_worker(
- cursor, keyno, &key, &value, false)) {
- case 0:
- case WT_NOTFOUND:
- case WT_ROLLBACK:
- case WT_PREPARE_CONFLICT:
- break;
- default:
- testutil_die(
- ret, "wts_read_scan: read row %" PRIu64, keyno);
- }
- }
-
- testutil_check(session->close(session, NULL));
-
- key_gen_teardown(&key);
- val_gen_teardown(&value);
+ WT_CONNECTION *conn;
+ WT_CURSOR *cursor;
+ WT_DECL_RET;
+ WT_ITEM key, value;
+ WT_SESSION *session;
+ uint64_t keyno, last_keyno;
+
+ conn = g.wts_conn;
+
+ /*
+ * We're not configuring transactions or read timestamps, if there's a diagnostic check, skip
+ * the scan.
+ */
+ if (g.c_assert_read_timestamp)
+ return;
+
+ /* Set up the default key/value buffers. */
+ key_gen_init(&key);
+ val_gen_init(&value);
+
+ /* 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);
+
+ /* Check a random subset of the records using the key. */
+ for (last_keyno = keyno = 0; keyno < g.key_cnt;) {
+ keyno += mmrand(NULL, 1, 17);
+ if (keyno > g.rows)
+ keyno = g.rows;
+ if (keyno - last_keyno > 1000) {
+ track("read row scan", keyno, NULL);
+ last_keyno = keyno;
+ }
+
+ switch (ret = read_row_worker(cursor, keyno, &key, &value, false)) {
+ case 0:
+ case WT_NOTFOUND:
+ case WT_ROLLBACK:
+ case WT_PREPARE_CONFLICT:
+ break;
+ default:
+ testutil_die(ret, "wts_read_scan: read row %" PRIu64, keyno);
+ }
+ }
+
+ testutil_check(session->close(session, NULL));
+
+ key_gen_teardown(&key);
+ val_gen_teardown(&value);
}
/*
* read_row_worker --
- * Read and verify a single element in a row- or column-store file.
+ * Read and verify a single element in a row- or column-store file.
*/
int
-read_row_worker(
- WT_CURSOR *cursor, uint64_t keyno, WT_ITEM *key, WT_ITEM *value, bool sn)
+read_row_worker(WT_CURSOR *cursor, uint64_t keyno, WT_ITEM *key, WT_ITEM *value, bool sn)
{
- WT_SESSION *session;
- uint8_t bitfield;
- int exact, ret;
-
- session = cursor->session;
-
- /* Retrieve the key/value pair by key. */
- switch (g.type) {
- case FIX:
- case VAR:
- cursor->set_key(cursor, keyno);
- break;
- case ROW:
- key_gen(key, keyno);
- cursor->set_key(cursor, key);
- break;
- }
-
- if (sn) {
- ret = read_op(cursor, SEARCH_NEAR, &exact);
- if (ret == 0 && exact != 0)
- ret = WT_NOTFOUND;
- } else
- ret = read_op(cursor, SEARCH, NULL);
- switch (ret) {
- case 0:
- if (g.type == FIX) {
- testutil_check(cursor->get_value(cursor, &bitfield));
- *(uint8_t *)(value->data) = bitfield;
- value->size = 1;
- } else
- testutil_check(cursor->get_value(cursor, value));
- break;
- case WT_NOTFOUND:
- /*
- * In fixed length stores, zero values at the end of the key
- * space are returned as not-found. Treat this the same as
- * a zero value in the key space, to match BDB's behavior.
- * The WiredTiger cursor has lost its position though, so
- * we return not-found, the cursor movement can't continue.
- */
- if (g.type == FIX) {
- *(uint8_t *)(value->data) = 0;
- value->size = 1;
- }
- break;
- default:
- return (ret);
- }
-
- /* Log the operation */
- if (ret == 0)
- switch (g.type) {
- case FIX:
- logop(session, "%-10s%" PRIu64 " {0x%02x}",
- "read", keyno, ((char *)value->data)[0]);
- break;
- case ROW:
- case VAR:
- logop(session, "%-10s%" PRIu64 " {%.*s}",
- "read", keyno,
- (int)value->size, (char *)value->data);
- break;
- }
-
- return (ret);
+ WT_SESSION *session;
+ uint8_t bitfield;
+ int exact, ret;
+
+ session = cursor->session;
+
+ /* Retrieve the key/value pair by key. */
+ switch (g.type) {
+ case FIX:
+ case VAR:
+ cursor->set_key(cursor, keyno);
+ break;
+ case ROW:
+ key_gen(key, keyno);
+ cursor->set_key(cursor, key);
+ break;
+ }
+
+ if (sn) {
+ ret = read_op(cursor, SEARCH_NEAR, &exact);
+ if (ret == 0 && exact != 0)
+ ret = WT_NOTFOUND;
+ } else
+ ret = read_op(cursor, SEARCH, NULL);
+ switch (ret) {
+ case 0:
+ if (g.type == FIX) {
+ testutil_check(cursor->get_value(cursor, &bitfield));
+ *(uint8_t *)(value->data) = bitfield;
+ value->size = 1;
+ } else
+ testutil_check(cursor->get_value(cursor, value));
+ break;
+ case WT_NOTFOUND:
+ /*
+ * In fixed length stores, zero values at the end of the key space are returned as
+ * not-found. Treat this the same as a zero value in the key space, to match BDB's behavior.
+ * The WiredTiger cursor has lost its position though, so we return not-found, the cursor
+ * movement can't continue.
+ */
+ if (g.type == FIX) {
+ *(uint8_t *)(value->data) = 0;
+ value->size = 1;
+ }
+ break;
+ default:
+ return (ret);
+ }
+
+ /* Log the operation */
+ if (ret == 0)
+ switch (g.type) {
+ case FIX:
+ logop(session, "%-10s%" PRIu64 " {0x%02x}", "read", keyno, ((char *)value->data)[0]);
+ break;
+ case ROW:
+ case VAR:
+ logop(session, "%-10s%" PRIu64 " {%.*s}", "read", keyno, (int)value->size,
+ (char *)value->data);
+ break;
+ }
+
+ return (ret);
}
/*
* read_row --
- * Read and verify a single element in a row- or column-store file.
+ * Read and verify a single element in a row- or column-store file.
*/
static int
read_row(TINFO *tinfo, WT_CURSOR *cursor)
{
- /* 25% of the time we call search-near. */
- return (read_row_worker(cursor, tinfo->keyno,
- tinfo->key, tinfo->value, mmrand(&tinfo->rnd, 0, 3) == 1));
+ /* 25% of the time we call search-near. */
+ return (read_row_worker(
+ cursor, tinfo->keyno, tinfo->key, tinfo->value, mmrand(&tinfo->rnd, 0, 3) == 1));
}
/*
* nextprev --
- * Read and verify the next/prev element in a row- or column-store file.
+ * Read and verify the next/prev element in a row- or column-store file.
*/
static int
nextprev(TINFO *tinfo, WT_CURSOR *cursor, bool next)
{
- WT_DECL_RET;
- WT_ITEM key, value;
- uint64_t keyno, keyno_prev;
- uint8_t bitfield;
- int cmp;
- const char *which;
- bool incrementing, record_gaps;
-
- keyno = 0;
- which = next ? "next" : "prev";
-
- switch (ret = read_op(cursor, next ? NEXT : PREV, NULL)) {
- case 0:
- switch (g.type) {
- case FIX:
- if ((ret = cursor->get_key(cursor, &keyno)) == 0 &&
- (ret = cursor->get_value(cursor, &bitfield)) == 0) {
- value.data = &bitfield;
- value.size = 1;
- }
- break;
- case ROW:
- if ((ret = cursor->get_key(cursor, &key)) == 0)
- ret = cursor->get_value(cursor, &value);
- break;
- case VAR:
- if ((ret = cursor->get_key(cursor, &keyno)) == 0)
- ret = cursor->get_value(cursor, &value);
- break;
- }
- 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).
- */
- record_gaps = g.c_delete_pct != 0;
- switch (g.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:
- 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);
- 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. (Not less-than,
- * row-store inserts new rows in-between rows
- * by append a new suffix to the row's key.)
- */
- testutil_check(__wt_buf_fmt(
- (WT_SESSION_IMPL *)cursor->session,
- tinfo->tbuf, "%.*s",
- (int)tinfo->key->size,
- (char *)tinfo->key->data));
- keyno_prev =
- strtoul(tinfo->tbuf->data, NULL, 10);
- testutil_check(__wt_buf_fmt(
- (WT_SESSION_IMPL *)cursor->session,
- tinfo->tbuf, "%.*s",
- (int)key.size, (char *)key.data));
- keyno = strtoul(tinfo->tbuf->data, 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:
- testutil_die(0,
- "%s returned {%.*s} then {%.*s}",
- which,
- (int)tinfo->key->size,
- (char *)tinfo->key->data,
- (int)key.size, (char *)key.data);
- }
-
- testutil_check(__wt_buf_set((WT_SESSION_IMPL *)
- cursor->session, tinfo->key, key.data, key.size));
- break;
- }
- break;
- case WT_NOTFOUND:
- break;
- default:
- return (ret);
- }
-
- if (ret == 0)
- switch (g.type) {
- case FIX:
- logop(cursor->session, "%-10s%" PRIu64 " {0x%02x}",
- which, keyno, ((char *)value.data)[0]);
- break;
- case ROW:
- logop(cursor->session,
- "%-10s%" PRIu64 " {%.*s}, {%.*s}", which, keyno,
- (int)key.size, (char *)key.data,
- (int)value.size, (char *)value.data);
- break;
- case VAR:
- logop(cursor->session, "%-10s%" PRIu64 " {%.*s}",
- which, keyno, (int)value.size, (char *)value.data);
- break;
- }
-
- return (ret);
+ WT_DECL_RET;
+ WT_ITEM key, value;
+ uint64_t keyno, keyno_prev;
+ uint8_t bitfield;
+ int cmp;
+ const char *which;
+ bool incrementing, record_gaps;
+
+ keyno = 0;
+ which = next ? "next" : "prev";
+
+ switch (ret = read_op(cursor, next ? NEXT : PREV, NULL)) {
+ case 0:
+ switch (g.type) {
+ case FIX:
+ if ((ret = cursor->get_key(cursor, &keyno)) == 0 &&
+ (ret = cursor->get_value(cursor, &bitfield)) == 0) {
+ value.data = &bitfield;
+ value.size = 1;
+ }
+ break;
+ case ROW:
+ if ((ret = cursor->get_key(cursor, &key)) == 0)
+ ret = cursor->get_value(cursor, &value);
+ break;
+ case VAR:
+ if ((ret = cursor->get_key(cursor, &keyno)) == 0)
+ ret = cursor->get_value(cursor, &value);
+ break;
+ }
+ 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).
+ */
+ record_gaps = g.c_delete_pct != 0;
+ switch (g.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:
+ 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);
+ 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. (Not
+ * less-than, row-store inserts new rows in-between rows by append a new suffix to
+ * the row's key.)
+ */
+ testutil_check(__wt_buf_fmt((WT_SESSION_IMPL *)cursor->session, tinfo->tbuf, "%.*s",
+ (int)tinfo->key->size, (char *)tinfo->key->data));
+ keyno_prev = strtoul(tinfo->tbuf->data, NULL, 10);
+ testutil_check(__wt_buf_fmt((WT_SESSION_IMPL *)cursor->session, tinfo->tbuf, "%.*s",
+ (int)key.size, (char *)key.data));
+ keyno = strtoul(tinfo->tbuf->data, 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:
+ testutil_die(0, "%s returned {%.*s} then {%.*s}", which, (int)tinfo->key->size,
+ (char *)tinfo->key->data, (int)key.size, (char *)key.data);
+ }
+
+ testutil_check(
+ __wt_buf_set((WT_SESSION_IMPL *)cursor->session, tinfo->key, key.data, key.size));
+ break;
+ }
+ break;
+ case WT_NOTFOUND:
+ break;
+ default:
+ return (ret);
+ }
+
+ if (ret == 0)
+ switch (g.type) {
+ case FIX:
+ logop(
+ cursor->session, "%-10s%" PRIu64 " {0x%02x}", which, keyno, ((char *)value.data)[0]);
+ break;
+ case ROW:
+ logop(cursor->session, "%-10s%" PRIu64 " {%.*s}, {%.*s}", which, keyno, (int)key.size,
+ (char *)key.data, (int)value.size, (char *)value.data);
+ break;
+ case VAR:
+ logop(cursor->session, "%-10s%" PRIu64 " {%.*s}", which, keyno, (int)value.size,
+ (char *)value.data);
+ break;
+ }
+
+ return (ret);
}
/*
* row_reserve --
- * Reserve a row in a row-store file.
+ * Reserve a row in a row-store file.
*/
static int
row_reserve(TINFO *tinfo, WT_CURSOR *cursor, bool positioned)
{
- WT_DECL_RET;
+ WT_DECL_RET;
- if (!positioned) {
- key_gen(tinfo->key, tinfo->keyno);
- cursor->set_key(cursor, tinfo->key);
- }
+ if (!positioned) {
+ key_gen(tinfo->key, tinfo->keyno);
+ cursor->set_key(cursor, tinfo->key);
+ }
- if ((ret = cursor->reserve(cursor)) != 0)
- return (ret);
+ if ((ret = cursor->reserve(cursor)) != 0)
+ return (ret);
- logop(cursor->session,
- "%-10s%" PRIu64 " {%.*s}", "reserve",
- tinfo->keyno, (int)tinfo->key->size, (char *)tinfo->key->data);
+ logop(cursor->session, "%-10s%" PRIu64 " {%.*s}", "reserve", tinfo->keyno,
+ (int)tinfo->key->size, (char *)tinfo->key->data);
- return (0);
+ return (0);
}
/*
* col_reserve --
- * Reserve a row in a column-store file.
+ * Reserve a row in a column-store file.
*/
static int
col_reserve(TINFO *tinfo, WT_CURSOR *cursor, bool positioned)
{
- WT_DECL_RET;
+ WT_DECL_RET;
- if (!positioned)
- cursor->set_key(cursor, tinfo->keyno);
+ if (!positioned)
+ cursor->set_key(cursor, tinfo->keyno);
- if ((ret = cursor->reserve(cursor)) != 0)
- return (ret);
+ if ((ret = cursor->reserve(cursor)) != 0)
+ return (ret);
- logop(cursor->session, "%-10s%" PRIu64, "reserve", tinfo->keyno);
+ logop(cursor->session, "%-10s%" PRIu64, "reserve", tinfo->keyno);
- return (0);
+ return (0);
}
/*
* modify_build --
- * Generate a set of modify vectors.
+ * Generate a set of modify vectors.
*/
static void
modify_build(TINFO *tinfo, WT_MODIFY *entries, int *nentriesp)
{
- int i, nentries;
-
- /* Randomly select a number of byte changes, offsets and lengths. */
- nentries = (int)mmrand(&tinfo->rnd, 1, MAX_MODIFY_ENTRIES);
- for (i = 0; i < nentries; ++i) {
- entries[i].data.data = modify_repl +
- mmrand(&tinfo->rnd, 1, sizeof(modify_repl) - 10);
- entries[i].data.size = (size_t)mmrand(&tinfo->rnd, 0, 10);
- /*
- * Start at least 11 bytes into the buffer so we skip leading
- * key information.
- */
- entries[i].offset = (size_t)mmrand(&tinfo->rnd, 20, 40);
- entries[i].size = (size_t)mmrand(&tinfo->rnd, 0, 10);
- }
-
- *nentriesp = (int)nentries;
+ int i, nentries;
+
+ /* Randomly select a number of byte changes, offsets and lengths. */
+ nentries = (int)mmrand(&tinfo->rnd, 1, MAX_MODIFY_ENTRIES);
+ for (i = 0; i < nentries; ++i) {
+ entries[i].data.data = modify_repl + mmrand(&tinfo->rnd, 1, sizeof(modify_repl) - 10);
+ entries[i].data.size = (size_t)mmrand(&tinfo->rnd, 0, 10);
+ /*
+ * Start at least 11 bytes into the buffer so we skip leading key information.
+ */
+ entries[i].offset = (size_t)mmrand(&tinfo->rnd, 20, 40);
+ entries[i].size = (size_t)mmrand(&tinfo->rnd, 0, 10);
+ }
+
+ *nentriesp = (int)nentries;
}
/*
* row_modify --
- * Modify a row in a row-store file.
+ * Modify a row in a row-store file.
*/
static int
row_modify(TINFO *tinfo, WT_CURSOR *cursor, bool positioned)
{
- WT_DECL_RET;
- WT_MODIFY entries[MAX_MODIFY_ENTRIES];
- int nentries;
+ WT_DECL_RET;
+ WT_MODIFY entries[MAX_MODIFY_ENTRIES];
+ int nentries;
- if (!positioned) {
- key_gen(tinfo->key, tinfo->keyno);
- cursor->set_key(cursor, tinfo->key);
- }
+ if (!positioned) {
+ key_gen(tinfo->key, tinfo->keyno);
+ cursor->set_key(cursor, tinfo->key);
+ }
- modify_build(tinfo, entries, &nentries);
- if ((ret = cursor->modify(cursor, entries, nentries)) != 0)
- return (ret);
+ modify_build(tinfo, entries, &nentries);
+ if ((ret = cursor->modify(cursor, entries, nentries)) != 0)
+ return (ret);
- testutil_check(cursor->get_value(cursor, tinfo->value));
+ testutil_check(cursor->get_value(cursor, tinfo->value));
- logop(cursor->session, "%-10s%" PRIu64 " {%.*s}, {%.*s}", "modify",
- tinfo->keyno,
- (int)tinfo->key->size, (char *)tinfo->key->data,
- (int)tinfo->value->size, (char *)tinfo->value->data);
+ logop(cursor->session, "%-10s%" PRIu64 " {%.*s}, {%.*s}", "modify", tinfo->keyno,
+ (int)tinfo->key->size, (char *)tinfo->key->data, (int)tinfo->value->size,
+ (char *)tinfo->value->data);
- return (0);
+ return (0);
}
/*
* col_modify --
- * Modify a row in a column-store file.
+ * Modify a row in a column-store file.
*/
static int
col_modify(TINFO *tinfo, WT_CURSOR *cursor, bool positioned)
{
- WT_DECL_RET;
- WT_MODIFY entries[MAX_MODIFY_ENTRIES];
- int nentries;
+ WT_DECL_RET;
+ WT_MODIFY entries[MAX_MODIFY_ENTRIES];
+ int nentries;
- if (!positioned)
- cursor->set_key(cursor, tinfo->keyno);
+ if (!positioned)
+ cursor->set_key(cursor, tinfo->keyno);
- modify_build(tinfo, entries, &nentries);
- if ((ret = cursor->modify(cursor, entries, nentries)) != 0)
- return (ret);
+ modify_build(tinfo, entries, &nentries);
+ if ((ret = cursor->modify(cursor, entries, nentries)) != 0)
+ return (ret);
- testutil_check(cursor->get_value(cursor, tinfo->value));
+ testutil_check(cursor->get_value(cursor, tinfo->value));
- logop(cursor->session, "%-10s%" PRIu64 ", {%.*s}", "modify",
- tinfo->keyno, (int)tinfo->value->size, (char *)tinfo->value->data);
+ logop(cursor->session, "%-10s%" PRIu64 ", {%.*s}", "modify", tinfo->keyno,
+ (int)tinfo->value->size, (char *)tinfo->value->data);
- return (0);
+ return (0);
}
/*
* row_truncate --
- * Truncate rows in a row-store file.
+ * Truncate rows in a row-store file.
*/
static int
row_truncate(TINFO *tinfo, WT_CURSOR *cursor)
{
- WT_CURSOR *c2;
- WT_DECL_RET;
- WT_SESSION *session;
-
- session = cursor->session;
-
- /*
- * The code assumes we're never truncating the entire object, assert
- * that fact.
- */
- testutil_assert(tinfo->keyno != 0 || tinfo->last != 0);
-
- c2 = NULL;
- if (tinfo->keyno == 0) {
- key_gen(tinfo->key, tinfo->last);
- cursor->set_key(cursor, tinfo->key);
- ret = session->truncate(session, NULL, NULL, cursor, NULL);
- } else if (tinfo->last == 0) {
- key_gen(tinfo->key, tinfo->keyno);
- cursor->set_key(cursor, tinfo->key);
- ret = session->truncate(session, NULL, cursor, NULL, NULL);
- } else {
- key_gen(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);
- cursor->set_key(c2, tinfo->lastkey);
-
- ret = session->truncate(session, NULL, cursor, c2, NULL);
- testutil_check(c2->close(c2));
- }
-
- if (ret != 0)
- return (ret);
-
- logop(session, "%-10s%" PRIu64 ", %" PRIu64,
- "truncate", tinfo->keyno, tinfo->last);
-
- return (0);
+ WT_CURSOR *c2;
+ WT_DECL_RET;
+ WT_SESSION *session;
+
+ session = cursor->session;
+
+ /*
+ * The code assumes we're never truncating the entire object, assert that fact.
+ */
+ testutil_assert(tinfo->keyno != 0 || tinfo->last != 0);
+
+ c2 = NULL;
+ if (tinfo->keyno == 0) {
+ key_gen(tinfo->key, tinfo->last);
+ cursor->set_key(cursor, tinfo->key);
+ ret = session->truncate(session, NULL, NULL, cursor, NULL);
+ } else if (tinfo->last == 0) {
+ key_gen(tinfo->key, tinfo->keyno);
+ cursor->set_key(cursor, tinfo->key);
+ ret = session->truncate(session, NULL, cursor, NULL, NULL);
+ } else {
+ key_gen(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);
+ cursor->set_key(c2, tinfo->lastkey);
+
+ ret = session->truncate(session, NULL, cursor, c2, NULL);
+ testutil_check(c2->close(c2));
+ }
+
+ if (ret != 0)
+ return (ret);
+
+ logop(session, "%-10s%" PRIu64 ", %" PRIu64, "truncate", tinfo->keyno, tinfo->last);
+
+ return (0);
}
/*
* col_truncate --
- * Truncate rows in a column-store file.
+ * Truncate rows in a column-store file.
*/
static int
col_truncate(TINFO *tinfo, WT_CURSOR *cursor)
{
- WT_CURSOR *c2;
- WT_DECL_RET;
- WT_SESSION *session;
-
- session = cursor->session;
-
- /*
- * The code assumes we're never truncating the entire object, assert
- * that fact.
- */
- testutil_assert(tinfo->keyno != 0 || tinfo->last != 0);
-
- c2 = NULL;
- if (tinfo->keyno == 0) {
- cursor->set_key(cursor, tinfo->last);
- ret = session->truncate(session, NULL, NULL, cursor, NULL);
- } else if (tinfo->last == 0) {
- cursor->set_key(cursor, tinfo->keyno);
- ret = session->truncate(session, NULL, cursor, NULL, NULL);
- } else {
- cursor->set_key(cursor, tinfo->keyno);
-
- testutil_check(
- session->open_cursor(session, g.uri, NULL, NULL, &c2));
- cursor->set_key(c2, tinfo->last);
-
- ret = session->truncate(session, NULL, cursor, c2, NULL);
- testutil_check(c2->close(c2));
- }
- if (ret != 0)
- return (ret);
-
- logop(session,
- "%-10s%" PRIu64 "-%" PRIu64, "truncate", tinfo->keyno, tinfo->last);
-
- return (0);
+ WT_CURSOR *c2;
+ WT_DECL_RET;
+ WT_SESSION *session;
+
+ session = cursor->session;
+
+ /*
+ * The code assumes we're never truncating the entire object, assert that fact.
+ */
+ testutil_assert(tinfo->keyno != 0 || tinfo->last != 0);
+
+ c2 = NULL;
+ if (tinfo->keyno == 0) {
+ cursor->set_key(cursor, tinfo->last);
+ ret = session->truncate(session, NULL, NULL, cursor, NULL);
+ } else if (tinfo->last == 0) {
+ cursor->set_key(cursor, tinfo->keyno);
+ ret = session->truncate(session, NULL, cursor, NULL, NULL);
+ } else {
+ cursor->set_key(cursor, tinfo->keyno);
+
+ testutil_check(session->open_cursor(session, g.uri, NULL, NULL, &c2));
+ cursor->set_key(c2, tinfo->last);
+
+ ret = session->truncate(session, NULL, cursor, c2, NULL);
+ testutil_check(c2->close(c2));
+ }
+ if (ret != 0)
+ return (ret);
+
+ logop(session, "%-10s%" PRIu64 "-%" PRIu64, "truncate", tinfo->keyno, tinfo->last);
+
+ return (0);
}
/*
* row_update --
- * Update a row in a row-store file.
+ * Update a row in a row-store file.
*/
static int
row_update(TINFO *tinfo, WT_CURSOR *cursor, bool positioned)
{
- WT_DECL_RET;
+ WT_DECL_RET;
- if (!positioned) {
- key_gen(tinfo->key, tinfo->keyno);
- cursor->set_key(cursor, tinfo->key);
- }
- val_gen(&tinfo->rnd, tinfo->value, tinfo->keyno);
- cursor->set_value(cursor, tinfo->value);
+ if (!positioned) {
+ key_gen(tinfo->key, tinfo->keyno);
+ cursor->set_key(cursor, tinfo->key);
+ }
+ val_gen(&tinfo->rnd, tinfo->value, tinfo->keyno);
+ cursor->set_value(cursor, tinfo->value);
- if ((ret = cursor->update(cursor)) != 0)
- return (ret);
+ if ((ret = cursor->update(cursor)) != 0)
+ return (ret);
- logop(cursor->session, "%-10s%" PRIu64 " {%.*s}, {%.*s}", "update",
- tinfo->keyno,
- (int)tinfo->key->size, (char *)tinfo->key->data,
- (int)tinfo->value->size, (char *)tinfo->value->data);
+ logop(cursor->session, "%-10s%" PRIu64 " {%.*s}, {%.*s}", "update", tinfo->keyno,
+ (int)tinfo->key->size, (char *)tinfo->key->data, (int)tinfo->value->size,
+ (char *)tinfo->value->data);
- return (0);
+ return (0);
}
/*
* col_update --
- * Update a row in a column-store file.
+ * Update a row in a column-store file.
*/
static int
col_update(TINFO *tinfo, WT_CURSOR *cursor, bool positioned)
{
- WT_DECL_RET;
-
- if (!positioned)
- cursor->set_key(cursor, tinfo->keyno);
- val_gen(&tinfo->rnd, tinfo->value, tinfo->keyno);
- if (g.type == FIX)
- cursor->set_value(cursor, *(uint8_t *)tinfo->value->data);
- else
- cursor->set_value(cursor, tinfo->value);
-
- if ((ret = cursor->update(cursor)) != 0)
- return (ret);
-
- if (g.type == FIX)
- logop(cursor->session, "%-10s%" PRIu64 " {0x%02" PRIx8 "}",
- "update", tinfo->keyno, ((uint8_t *)tinfo->value->data)[0]);
- else
- logop(cursor->session, "%-10s%" PRIu64 " {%.*s}",
- "update", tinfo->keyno,
- (int)tinfo->value->size, (char *)tinfo->value->data);
-
- return (0);
+ WT_DECL_RET;
+
+ if (!positioned)
+ cursor->set_key(cursor, tinfo->keyno);
+ val_gen(&tinfo->rnd, tinfo->value, tinfo->keyno);
+ if (g.type == FIX)
+ cursor->set_value(cursor, *(uint8_t *)tinfo->value->data);
+ else
+ cursor->set_value(cursor, tinfo->value);
+
+ if ((ret = cursor->update(cursor)) != 0)
+ return (ret);
+
+ if (g.type == FIX)
+ logop(cursor->session, "%-10s%" PRIu64 " {0x%02" PRIx8 "}", "update", tinfo->keyno,
+ ((uint8_t *)tinfo->value->data)[0]);
+ else
+ logop(cursor->session, "%-10s%" PRIu64 " {%.*s}", "update", tinfo->keyno,
+ (int)tinfo->value->size, (char *)tinfo->value->data);
+
+ return (0);
}
/*
* table_append_init --
- * Re-initialize the appended records list.
+ * Re-initialize the appended records list.
*/
static void
table_append_init(void)
{
- /* Append up to 10 records per thread before waiting on resolution. */
- g.append_max = (size_t)g.c_threads * 10;
- g.append_cnt = 0;
+ /* Append up to 10 records per thread before waiting on resolution. */
+ g.append_max = (size_t)g.c_threads * 10;
+ g.append_cnt = 0;
- free(g.append);
- g.append = dcalloc(g.append_max, sizeof(uint64_t));
+ free(g.append);
+ g.append = dcalloc(g.append_max, sizeof(uint64_t));
}
/*
* table_append --
- * Resolve the appended records.
+ * Resolve the appended records.
*/
static void
table_append(uint64_t keyno)
{
- uint64_t *ep, *p;
- int done;
-
- ep = g.append + g.append_max;
-
- /*
- * We don't want to ignore records we append, which requires we update
- * the "last row" as we insert new records. Threads allocating record
- * numbers can race with other threads, so the thread allocating record
- * N may return after the thread allocating N + 1. We can't update a
- * record before it's been inserted, and so we can't leave gaps when the
- * count of records in the table is incremented.
- *
- * The solution is the append table, which contains an unsorted list of
- * appended records. Every time we finish appending a record, process
- * the table, trying to update the total records in the object.
- *
- * First, enter the new key into the append list.
- *
- * It's technically possible to race: we allocated space for 10 records
- * per thread, but the check for the maximum number of records being
- * appended doesn't lock. If a thread allocated a new record and went
- * to sleep (so the append table fills up), then N threads of control
- * used the same g.append_cnt value to decide there was an available
- * slot in the append table and both allocated new records, we could run
- * out of space in the table. It's unfortunately not even unlikely in
- * the case of a large number of threads all inserting as fast as they
- * can and a single thread going to sleep for an unexpectedly long time.
- * If it happens, sleep and retry until earlier records are resolved
- * and we find a slot.
- */
- for (done = 0;;) {
- testutil_check(pthread_rwlock_wrlock(&g.append_lock));
-
- /*
- * If this is the thread we've been waiting for, and its record
- * won't fit, we'd loop infinitely. If there are many append
- * operations and a thread goes to sleep for a little too long,
- * it can happen.
- */
- if (keyno == g.rows + 1) {
- g.rows = keyno;
- done = 1;
-
- /*
- * Clean out the table, incrementing the total count of
- * records until we don't find the next key.
- */
- for (;;) {
- for (p = g.append; p < ep; ++p)
- if (*p == g.rows + 1) {
- g.rows = *p;
- *p = 0;
- --g.append_cnt;
- break;
- }
- if (p == ep)
- break;
- }
- } else
- /* Enter the key into the table. */
- for (p = g.append; p < ep; ++p)
- if (*p == 0) {
- *p = keyno;
- ++g.append_cnt;
- done = 1;
- break;
- }
-
- testutil_check(pthread_rwlock_unlock(&g.append_lock));
-
- if (done)
- break;
- __wt_sleep(1, 0);
- }
+ uint64_t *ep, *p;
+ int done;
+
+ ep = g.append + g.append_max;
+
+ /*
+ * We don't want to ignore records we append, which requires we update
+ * the "last row" as we insert new records. Threads allocating record
+ * numbers can race with other threads, so the thread allocating record
+ * N may return after the thread allocating N + 1. We can't update a
+ * record before it's been inserted, and so we can't leave gaps when the
+ * count of records in the table is incremented.
+ *
+ * The solution is the append table, which contains an unsorted list of
+ * appended records. Every time we finish appending a record, process
+ * the table, trying to update the total records in the object.
+ *
+ * First, enter the new key into the append list.
+ *
+ * It's technically possible to race: we allocated space for 10 records
+ * per thread, but the check for the maximum number of records being
+ * appended doesn't lock. If a thread allocated a new record and went
+ * to sleep (so the append table fills up), then N threads of control
+ * used the same g.append_cnt value to decide there was an available
+ * slot in the append table and both allocated new records, we could run
+ * out of space in the table. It's unfortunately not even unlikely in
+ * the case of a large number of threads all inserting as fast as they
+ * can and a single thread going to sleep for an unexpectedly long time.
+ * If it happens, sleep and retry until earlier records are resolved
+ * and we find a slot.
+ */
+ for (done = 0;;) {
+ testutil_check(pthread_rwlock_wrlock(&g.append_lock));
+
+ /*
+ * If this is the thread we've been waiting for, and its record won't fit, we'd loop
+ * infinitely. If there are many append operations and a thread goes to sleep for a little
+ * too long, it can happen.
+ */
+ if (keyno == g.rows + 1) {
+ g.rows = keyno;
+ done = 1;
+
+ /*
+ * Clean out the table, incrementing the total count of records until we don't find the
+ * next key.
+ */
+ for (;;) {
+ for (p = g.append; p < ep; ++p)
+ if (*p == g.rows + 1) {
+ g.rows = *p;
+ *p = 0;
+ --g.append_cnt;
+ break;
+ }
+ if (p == ep)
+ break;
+ }
+ } else
+ /* Enter the key into the table. */
+ for (p = g.append; p < ep; ++p)
+ if (*p == 0) {
+ *p = keyno;
+ ++g.append_cnt;
+ done = 1;
+ break;
+ }
+
+ testutil_check(pthread_rwlock_unlock(&g.append_lock));
+
+ if (done)
+ break;
+ __wt_sleep(1, 0);
+ }
}
/*
* row_insert --
- * Insert a row in a row-store file.
+ * Insert a row in a row-store file.
*/
static int
row_insert(TINFO *tinfo, WT_CURSOR *cursor, bool positioned)
{
- WT_DECL_RET;
-
- /*
- * 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);
- cursor->set_key(cursor, tinfo->key);
- }
- val_gen(&tinfo->rnd, tinfo->value, tinfo->keyno);
- cursor->set_value(cursor, tinfo->value);
-
- if ((ret = cursor->insert(cursor)) != 0)
- return (ret);
-
- /* Log the operation */
- logop(cursor->session, "%-10s%" PRIu64 " {%.*s}, {%.*s}", "insert",
- tinfo->keyno,
- (int)tinfo->key->size, (char *)tinfo->key->data,
- (int)tinfo->value->size, (char *)tinfo->value->data);
-
- return (0);
+ WT_DECL_RET;
+
+ /*
+ * 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);
+ cursor->set_key(cursor, tinfo->key);
+ }
+ val_gen(&tinfo->rnd, tinfo->value, tinfo->keyno);
+ cursor->set_value(cursor, tinfo->value);
+
+ if ((ret = cursor->insert(cursor)) != 0)
+ return (ret);
+
+ /* Log the operation */
+ logop(cursor->session, "%-10s%" PRIu64 " {%.*s}, {%.*s}", "insert", tinfo->keyno,
+ (int)tinfo->key->size, (char *)tinfo->key->data, (int)tinfo->value->size,
+ (char *)tinfo->value->data);
+
+ return (0);
}
/*
* col_insert --
- * Insert an element in a column-store file.
+ * Insert an element in a column-store file.
*/
static int
col_insert(TINFO *tinfo, WT_CURSOR *cursor)
{
- WT_DECL_RET;
+ WT_DECL_RET;
- val_gen(&tinfo->rnd, tinfo->value, g.rows + 1);
- if (g.type == FIX)
- cursor->set_value(cursor, *(uint8_t *)tinfo->value->data);
- else
- cursor->set_value(cursor, tinfo->value);
+ val_gen(&tinfo->rnd, tinfo->value, g.rows + 1);
+ if (g.type == FIX)
+ cursor->set_value(cursor, *(uint8_t *)tinfo->value->data);
+ else
+ cursor->set_value(cursor, tinfo->value);
- if ((ret = cursor->insert(cursor)) != 0)
- return (ret);
+ if ((ret = cursor->insert(cursor)) != 0)
+ return (ret);
- testutil_check(cursor->get_key(cursor, &tinfo->keyno));
+ testutil_check(cursor->get_key(cursor, &tinfo->keyno));
- table_append(tinfo->keyno); /* Extend the object. */
+ table_append(tinfo->keyno); /* Extend the object. */
- if (g.type == FIX)
- logop(cursor->session, "%-10s%" PRIu64 " {0x%02" PRIx8 "}",
- "insert", tinfo->keyno, ((uint8_t *)tinfo->value->data)[0]);
- else
- logop(cursor->session, "%-10s%" PRIu64 " {%.*s}",
- "insert", tinfo->keyno,
- (int)tinfo->value->size, (char *)tinfo->value->data);
+ if (g.type == FIX)
+ logop(cursor->session, "%-10s%" PRIu64 " {0x%02" PRIx8 "}", "insert", tinfo->keyno,
+ ((uint8_t *)tinfo->value->data)[0]);
+ else
+ logop(cursor->session, "%-10s%" PRIu64 " {%.*s}", "insert", tinfo->keyno,
+ (int)tinfo->value->size, (char *)tinfo->value->data);
- return (0);
+ return (0);
}
/*
* row_remove --
- * Remove an row from a row-store file.
+ * Remove an row from a row-store file.
*/
static int
row_remove(TINFO *tinfo, WT_CURSOR *cursor, bool positioned)
{
- WT_DECL_RET;
+ WT_DECL_RET;
- if (!positioned) {
- key_gen(tinfo->key, tinfo->keyno);
- cursor->set_key(cursor, tinfo->key);
- }
+ if (!positioned) {
+ key_gen(tinfo->key, tinfo->keyno);
+ cursor->set_key(cursor, tinfo->key);
+ }
- /* We use the cursor in overwrite mode, check for existence. */
- if ((ret = read_op(cursor, SEARCH, NULL)) == 0)
- ret = cursor->remove(cursor);
+ /* We use the cursor in overwrite mode, check for existence. */
+ if ((ret = read_op(cursor, SEARCH, NULL)) == 0)
+ ret = cursor->remove(cursor);
- if (ret != 0 && ret != WT_NOTFOUND)
- return (ret);
+ if (ret != 0 && ret != WT_NOTFOUND)
+ return (ret);
- logop(cursor->session, "%-10s%" PRIu64, "remove", tinfo->keyno);
+ logop(cursor->session, "%-10s%" PRIu64, "remove", tinfo->keyno);
- return (ret);
+ return (ret);
}
/*
* col_remove --
- * Remove a row from a column-store file.
+ * Remove a row from a column-store file.
*/
static int
col_remove(TINFO *tinfo, WT_CURSOR *cursor, bool positioned)
{
- WT_DECL_RET;
+ WT_DECL_RET;
- if (!positioned)
- cursor->set_key(cursor, tinfo->keyno);
+ if (!positioned)
+ cursor->set_key(cursor, tinfo->keyno);
- /* We use the cursor in overwrite mode, check for existence. */
- if ((ret = read_op(cursor, SEARCH, NULL)) == 0)
- ret = cursor->remove(cursor);
+ /* We use the cursor in overwrite mode, check for existence. */
+ if ((ret = read_op(cursor, SEARCH, NULL)) == 0)
+ ret = cursor->remove(cursor);
- if (ret != 0 && ret != WT_NOTFOUND)
- return (ret);
+ if (ret != 0 && ret != WT_NOTFOUND)
+ return (ret);
- logop(cursor->session, "%-10s%" PRIu64, "remove", tinfo->keyno);
+ logop(cursor->session, "%-10s%" PRIu64, "remove", tinfo->keyno);
- return (ret);
+ return (ret);
}