diff options
Diffstat (limited to 'src/third_party/wiredtiger/test/format/format_inline.h')
-rw-r--r-- | src/third_party/wiredtiger/test/format/format_inline.h | 431 |
1 files changed, 431 insertions, 0 deletions
diff --git a/src/third_party/wiredtiger/test/format/format_inline.h b/src/third_party/wiredtiger/test/format/format_inline.h new file mode 100644 index 00000000000..07f33f5319c --- /dev/null +++ b/src/third_party/wiredtiger/test/format/format_inline.h @@ -0,0 +1,431 @@ +/*- + * 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. + */ + +#define FORMAT_PREPARE_TIMEOUT 120 + +/* + * read_op -- + * Perform a read operation, waiting out prepare conflicts. + */ +static inline int +read_op(WT_CURSOR *cursor, read_operation op, int *exactp) +{ + WT_DECL_RET; + uint64_t start, now; + + switch (op) { + case NEXT: + ret = cursor->next(cursor); + break; + case PREV: + ret = cursor->prev(cursor); + break; + case SEARCH: + ret = cursor->search(cursor); + break; + case SEARCH_NEAR: + ret = cursor->search_near(cursor, exactp); + break; + } + if (ret != WT_PREPARE_CONFLICT) + return (ret); + + /* + * Read operations wait out prepare-conflicts. (As part of the snapshot isolation checks, we + * repeat reads that succeeded before, they should be repeatable.) + */ + __wt_seconds(NULL, &start); + switch (op) { + case NEXT: + while ((ret = cursor->next(cursor)) == WT_PREPARE_CONFLICT) { + __wt_yield(); + + /* Ignore clock reset. */ + __wt_seconds(NULL, &now); + testutil_assertfmt(now < start || now - start < FORMAT_PREPARE_TIMEOUT, + "%s: timed out with prepare-conflict", "WT_CURSOR.next"); + } + break; + case PREV: + while ((ret = cursor->prev(cursor)) == WT_PREPARE_CONFLICT) { + __wt_yield(); + + /* Ignore clock reset. */ + __wt_seconds(NULL, &now); + testutil_assertfmt(now < start || now - start < FORMAT_PREPARE_TIMEOUT, + "%s: timed out with prepare-conflict", "WT_CURSOR.prev"); + } + break; + case SEARCH: + while ((ret = cursor->search(cursor)) == WT_PREPARE_CONFLICT) { + __wt_yield(); + + /* Ignore clock reset. */ + __wt_seconds(NULL, &now); + testutil_assertfmt(now < start || now - start < FORMAT_PREPARE_TIMEOUT, + "%s: timed out with prepare-conflict", "WT_CURSOR.search"); + } + break; + case SEARCH_NEAR: + while ((ret = cursor->search_near(cursor, exactp)) == WT_PREPARE_CONFLICT) { + __wt_yield(); + + /* Ignore clock reset. */ + __wt_seconds(NULL, &now); + testutil_assertfmt(now < start || now - start < FORMAT_PREPARE_TIMEOUT, + "%s: timed out with prepare-conflict", "WT_CURSOR.search_near"); + } + break; + } + return (ret); +} + +/* + * rng -- + * Return a random number. + */ +static inline uint32_t +rng(WT_RAND_STATE *rnd) +{ + /* Threaded operations have their own RNG information, otherwise we use the default. */ + if (rnd == NULL) + rnd = &g.rnd; + + return (__wt_random(rnd)); +} + +/* + * mmrand -- + * Return a random value between a min/max pair, inclusive. + */ +static inline uint32_t +mmrand(WT_RAND_STATE *rnd, u_int min, u_int max) +{ + uint32_t v; + u_int range; + + /* + * Test runs with small row counts can easily pass a max of 0 (for example, "g.rows / 20"). + * Avoid the problem. + */ + if (max <= min) + return (min); + + v = rng(rnd); + range = (max - min) + 1; + v %= range; + v += min; + return (v); +} + +/* + * random_sleep -- + * Randomly select a time to sleep between 0 and a maximum number of seconds, favoring shorter + * sleep times. + */ +static inline void +random_sleep(WT_RAND_STATE *rnd, u_int max_seconds) +{ + uint64_t i, micro_seconds; + + /* + * We need a fast way to choose a sleep time. We want to sleep a short period most of the time, + * but occasionally wait longer. Divide the maximum period of time into 10 buckets (where bucket + * 0 doesn't sleep at all), and roll dice, advancing to the next bucket 50% of the time. That + * means we'll hit the maximum roughly every 1K calls. + */ + for (i = 0;;) + if (rng(rnd) & 0x1 || ++i > 9) + break; + + if (i == 0) + __wt_yield(); + else { + micro_seconds = (uint64_t)max_seconds * WT_MILLION; + __wt_sleep(0, i * (micro_seconds / 10)); + } +} + +/* + * 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]); +} + +/* + * wt_wrap_open_cursor -- + * Open a WiredTiger cursor. + */ +static inline void +wt_wrap_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"; + wt_wrap_open_cursor(tinfo->session, table->uri, config, &tinfo->cursors[id]); + } + return (tinfo->cursors[id]); +} + +/* + * wt_wrap_begin_transaction -- + * Start a WiredTiger transaction. + */ +static inline void +wt_wrap_begin_transaction(WT_SESSION *session, const char *config) +{ + WT_DECL_RET; + + /* + * Keep trying to start a new transaction if it's timing out. There are no resources pinned, it + * should succeed eventually. + */ + while ((ret = session->begin_transaction(session, config)) == WT_CACHE_FULL) + __wt_yield(); + testutil_check(ret); +} + +/* + * key_gen -- + * Generate a key for lookup. + */ +static inline void +key_gen(TABLE *table, WT_ITEM *key, uint64_t keyno) +{ + key_gen_common(table, key, keyno, "00"); +} + +/* + * key_gen_insert -- + * Generate a key for insertion. + */ +static inline void +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(table, key, keyno, suffix[mmrand(rnd, 0, 14)]); +} + +/* + * lock_try_writelock -- + * Try to get exclusive lock. Fail immediately if not available. + */ +static inline int +lock_try_writelock(WT_SESSION *session, RWLOCK *lock) +{ + if (lock->lock_type == LOCK_WT) + return (__wt_try_writelock((WT_SESSION_IMPL *)session, &lock->l.wt)); + return (pthread_rwlock_trywrlock(&lock->l.pthread)); +} + +/* + * lock_writelock -- + * Wait to get exclusive lock. + */ +static inline void +lock_writelock(WT_SESSION *session, RWLOCK *lock) +{ + if (lock->lock_type == LOCK_WT) + __wt_writelock((WT_SESSION_IMPL *)session, &lock->l.wt); + else + testutil_check(pthread_rwlock_wrlock(&lock->l.pthread)); +} + +/* + * lock_writeunlock -- + * Release an exclusive lock. + */ +static inline void +lock_writeunlock(WT_SESSION *session, RWLOCK *lock) +{ + if (lock->lock_type == LOCK_WT) + __wt_writeunlock((WT_SESSION_IMPL *)session, &lock->l.wt); + else + testutil_check(pthread_rwlock_unlock(&lock->l.pthread)); +} + +/* + * lock_readlock -- + * Wait to get read lock. + */ +static inline void +lock_readlock(WT_SESSION *session, RWLOCK *lock) +{ + if (lock->lock_type == LOCK_WT) + __wt_readlock((WT_SESSION_IMPL *)session, &lock->l.wt); + else + testutil_check(pthread_rwlock_rdlock(&lock->l.pthread)); +} + +/* + * lock_readunlock -- + * Release a shared lock. + */ +static inline void +lock_readunlock(WT_SESSION *session, RWLOCK *lock) +{ + if (lock->lock_type == LOCK_WT) + __wt_readunlock((WT_SESSION_IMPL *)session, &lock->l.wt); + else + testutil_check(pthread_rwlock_unlock(&lock->l.pthread)); +} + +#define trace_msg(s, fmt, ...) \ + do { \ + if (FLD_ISSET(g.trace_flags, TRACE)) \ + __wt_verbose_worker( \ + (WT_SESSION_IMPL *)(s), WT_VERB_TEMPORARY, WT_VERBOSE_INFO, fmt, __VA_ARGS__); \ + } while (0) +#define trace_uri_op(tinfo, uri, fmt, ...) \ + do { \ + WT_SESSION_IMPL *__s; \ + uint32_t __i; \ + __s = (WT_SESSION_IMPL *)(tinfo)->session; \ + if (FLD_ISSET(g.trace_flags, TRACE)) { \ + __wt_verbose_worker(__s, WT_VERB_TEMPORARY, WT_VERBOSE_INFO, "%" PRIu64 " %s%s" fmt, \ + (tinfo)->opid, (uri) == NULL ? "" : (uri), (uri) == NULL ? "" : ": ", __VA_ARGS__); \ + if (FLD_ISSET(g.trace_flags, TRACE_TXN)) { \ + __wt_verbose_worker(__s, WT_VERB_TEMPORARY, WT_VERBOSE_INFO, \ + "%s%s txn id %" PRIu64 " snap_min %" PRIu64 " snap_max %" PRIu64 \ + " snap count %" PRIu32, \ + (uri) == NULL ? "" : (uri), (uri) == NULL ? "" : ": ", __s->txn->id, \ + __s->txn->snap_min, __s->txn->snap_max, __s->txn->snapshot_count); \ + for (__i = 0; __i < __s->txn->snapshot_count; ++__i) \ + __wt_verbose_worker(__s, WT_VERB_TEMPORARY, WT_VERBOSE_INFO, \ + "%s%s txn snapshot[%" PRIu32 "]: %" PRIu64, (uri) == NULL ? "" : (uri), \ + (uri) == NULL ? "" : ": ", __i, __s->txn->snapshot[__i]); \ + } \ + } \ + } while (0) +#define trace_op(tinfo, fmt, ...) trace_uri_op(tinfo, (tinfo)->table->uri, fmt, __VA_ARGS__) |