diff options
Diffstat (limited to 'libs/libmdbx/src/test/test.cc')
-rw-r--r-- | libs/libmdbx/src/test/test.cc | 755 |
1 files changed, 0 insertions, 755 deletions
diff --git a/libs/libmdbx/src/test/test.cc b/libs/libmdbx/src/test/test.cc deleted file mode 100644 index 9af04ac9b0..0000000000 --- a/libs/libmdbx/src/test/test.cc +++ /dev/null @@ -1,755 +0,0 @@ -/* - * Copyright 2017-2020 Leonid Yuriev <leo@yuriev.ru> - * and other libmdbx authors: please see AUTHORS file. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted only as authorized by the OpenLDAP - * Public License. - * - * A copy of this license is available in the file LICENSE in the - * top-level directory of the distribution or, alternatively, at - * <http://www.OpenLDAP.org/license.html>. - */ - -#include "test.h" - -const char *testcase2str(const actor_testcase testcase) { - switch (testcase) { - default: - assert(false); - return "?!"; - case ac_none: - return "none"; - case ac_hill: - return "hill"; - case ac_deadread: - return "deadread"; - case ac_deadwrite: - return "deadwrite"; - case ac_jitter: - return "jitter"; - case ac_try: - return "try"; - case ac_copy: - return "copy"; - case ac_append: - return "append"; - case ac_ttl: - return "ttl"; - case ac_nested: - return "nested"; - } -} - -const char *status2str(actor_status status) { - switch (status) { - default: - assert(false); - return "?!"; - case as_debugging: - return "debugging"; - case as_running: - return "running"; - case as_successful: - return "successful"; - case as_killed: - return "killed"; - case as_failed: - return "failed"; - case as_coredump: - return "coredump"; - } -} - -const char *keygencase2str(const keygen_case keycase) { - switch (keycase) { - default: - assert(false); - return "?!"; - case kc_random: - return "random"; - case kc_dashes: - return "dashes"; - case kc_custom: - return "custom"; - } -} - -//----------------------------------------------------------------------------- - -int testcase::oom_callback(MDBX_env *env, mdbx_pid_t pid, mdbx_tid_t tid, - uint64_t txn, unsigned gap, size_t space, - int retry) { - - testcase *self = (testcase *)mdbx_env_get_userctx(env); - - if (retry == 0) - log_notice("oom_callback: waitfor pid %lu, thread %" PRIuPTR - ", txn #%" PRIu64 ", gap %d, scape %zu", - (long)pid, (size_t)tid, txn, gap, space); - - if (self->should_continue(true)) { - osal_yield(); - if (retry > 0) - osal_udelay(retry * 100); - return 0 /* always retry */; - } - - return -1; -} - -void testcase::db_prepare() { - log_trace(">> db_prepare"); - assert(!db_guard); - - MDBX_env *env = nullptr; - int rc = mdbx_env_create(&env); - if (unlikely(rc != MDBX_SUCCESS)) - failure_perror("mdbx_env_create()", rc); - - assert(env != nullptr); - db_guard.reset(env); - - rc = mdbx_env_set_userctx(env, this); - if (unlikely(rc != MDBX_SUCCESS)) - failure_perror("mdbx_env_set_userctx()", rc); - - rc = mdbx_env_set_maxreaders(env, config.params.max_readers); - if (unlikely(rc != MDBX_SUCCESS)) - failure_perror("mdbx_env_set_maxreaders()", rc); - - rc = mdbx_env_set_maxdbs(env, config.params.max_tables); - if (unlikely(rc != MDBX_SUCCESS)) - failure_perror("mdbx_env_set_maxdbs()", rc); - - rc = mdbx_env_set_oomfunc(env, testcase::oom_callback); - if (unlikely(rc != MDBX_SUCCESS)) - failure_perror("mdbx_env_set_oomfunc()", rc); - - rc = mdbx_env_set_geometry( - env, config.params.size_lower, config.params.size_now, - config.params.size_upper, config.params.growth_step, - config.params.shrink_threshold, config.params.pagesize); - if (unlikely(rc != MDBX_SUCCESS)) - failure_perror("mdbx_env_set_mapsize()", rc); - - log_trace("<< db_prepare"); -} - -void testcase::db_open() { - log_trace(">> db_open"); - - if (!db_guard) - db_prepare(); - - jitter_delay(true); - - unsigned mode = (unsigned)config.params.mode_flags; - if (config.params.random_writemap && flipcoin()) - mode ^= MDBX_WRITEMAP; - - actual_db_mode = mode; - int rc = mdbx_env_open(db_guard.get(), config.params.pathname_db.c_str(), - mode, 0640); - if (unlikely(rc != MDBX_SUCCESS)) - failure_perror("mdbx_env_open()", rc); - - log_trace("<< db_open"); -} - -void testcase::db_close() { - log_trace(">> db_close"); - cursor_guard.reset(); - txn_guard.reset(); - db_guard.reset(); - log_trace("<< db_close"); -} - -void testcase::txn_begin(bool readonly, unsigned flags) { - assert((flags & MDBX_RDONLY) == 0); - log_trace(">> txn_begin(%s, 0x%04X)", readonly ? "read-only" : "read-write", - flags); - assert(!txn_guard); - - MDBX_txn *txn = nullptr; - int rc = mdbx_txn_begin(db_guard.get(), nullptr, - readonly ? flags | MDBX_RDONLY : flags, &txn); - if (unlikely(rc != MDBX_SUCCESS)) - failure_perror("mdbx_txn_begin()", rc); - txn_guard.reset(txn); - need_speculum_assign = config.params.speculum && !readonly; - - log_trace("<< txn_begin(%s, 0x%04X)", readonly ? "read-only" : "read-write", - flags); -} - -int testcase::breakable_commit() { - log_trace(">> txn_commit"); - assert(txn_guard); - - MDBX_txn *txn = txn_guard.release(); - txn_inject_writefault(txn); - int rc = mdbx_txn_commit(txn); - if (unlikely(rc != MDBX_SUCCESS) && - (rc != MDBX_MAP_FULL || !config.params.ignore_dbfull)) - failure_perror("mdbx_txn_commit()", rc); - - if (need_speculum_assign) { - need_speculum_assign = false; - if (unlikely(rc != MDBX_SUCCESS)) - speculum = speculum_commited; - else - speculum_commited = speculum; - } - - log_trace("<< txn_commit: %s", rc ? "failed" : "Ok"); - return rc; -} - -unsigned testcase::txn_underutilization_x256(MDBX_txn *txn) const { - if (txn) { - MDBX_txn_info info; - int err = mdbx_txn_info(txn, &info, false); - if (unlikely(err != MDBX_SUCCESS)) - failure_perror("mdbx_txn_info()", err); - const size_t left = size_t(info.txn_space_leftover); - const size_t total = - size_t(info.txn_space_leftover) + size_t(info.txn_space_dirty); - return (unsigned)(left / (total >> 8)); - } - return 0; -} - -void testcase::txn_end(bool abort) { - log_trace(">> txn_end(%s)", abort ? "abort" : "commit"); - assert(txn_guard); - - MDBX_txn *txn = txn_guard.release(); - if (abort) { - int err = mdbx_txn_abort(txn); - if (unlikely(err != MDBX_SUCCESS)) - failure_perror("mdbx_txn_abort()", err); - if (need_speculum_assign) - speculum = speculum_commited; - } else { - txn_inject_writefault(txn); - int err = mdbx_txn_commit(txn); - if (unlikely(err != MDBX_SUCCESS)) - failure_perror("mdbx_txn_commit()", err); - if (need_speculum_assign) - speculum_commited = speculum; - } - - log_trace("<< txn_end(%s)", abort ? "abort" : "commit"); -} - -void testcase::cursor_open(MDBX_dbi handle) { - log_trace(">> cursor_open(%u)", handle); - assert(!cursor_guard); - assert(txn_guard); - - MDBX_cursor *cursor = nullptr; - int rc = mdbx_cursor_open(txn_guard.get(), handle, &cursor); - if (unlikely(rc != MDBX_SUCCESS)) - failure_perror("mdbx_cursor_open()", rc); - cursor_guard.reset(cursor); - - log_trace("<< cursor_open(%u)", handle); -} - -void testcase::cursor_close() { - log_trace(">> cursor_close()"); - assert(cursor_guard); - MDBX_cursor *cursor = cursor_guard.release(); - mdbx_cursor_close(cursor); - log_trace("<< cursor_close()"); -} - -int testcase::breakable_restart() { - int rc = MDBX_SUCCESS; - if (txn_guard) - rc = breakable_commit(); - if (cursor_guard) - cursor_close(); - txn_begin(false, 0); - return rc; -} - -void testcase::txn_restart(bool abort, bool readonly, unsigned flags) { - if (txn_guard) - txn_end(abort); - if (cursor_guard) - cursor_close(); - txn_begin(readonly, flags); -} - -void testcase::txn_inject_writefault(void) { - if (txn_guard) - txn_inject_writefault(txn_guard.get()); -} - -void testcase::txn_inject_writefault(MDBX_txn *txn) { - if (config.params.inject_writefaultn && txn) { - if (config.params.inject_writefaultn <= nops_completed && - (mdbx_txn_flags(txn) & MDBX_RDONLY) == 0) { - log_verbose( - "== txn_inject_writefault(): got %u nops or more, inject FAULT", - config.params.inject_writefaultn); - log_flush(); -#if defined(_WIN32) || defined(_WIN64) || defined(_WINDOWS) - TerminateProcess(GetCurrentProcess(), 42); -#else - raise(SIGKILL); -#endif - } - } -} - -bool testcase::wait4start() { - if (config.wait4id) { - log_trace(">> wait4start(%u)", config.wait4id); - assert(!global::singlemode); - int rc = osal_waitfor(config.wait4id); - if (rc) { - log_trace("<< wait4start(%u), failed %s", config.wait4id, - test_strerror(rc)); - return false; - } - } else { - log_trace("== skip wait4start: not needed"); - } - - if (config.params.delaystart) { - int rc = osal_delay(config.params.delaystart); - if (rc) { - log_trace("<< delay(%u), failed %s", config.params.delaystart, - test_strerror(rc)); - return false; - } - } else { - log_trace("== skip delay: not needed"); - } - - return true; -} - -void testcase::kick_progress(bool active) const { - if (!global::config::progress_indicator) - return; - logging::progress_canary(active); -} - -void testcase::report(size_t nops_done) { - assert(nops_done > 0); - if (!nops_done) - return; - - nops_completed += nops_done; - log_debug("== complete +%" PRIuPTR " iteration, total %" PRIu64 " done", - nops_done, nops_completed); - - kick_progress(true); - - if (config.signal_nops && !signalled && - config.signal_nops <= nops_completed) { - log_trace(">> signal(n-ops %" PRIu64 ")", nops_completed); - if (!global::singlemode) - osal_broadcast(config.actor_id); - signalled = true; - log_trace("<< signal(n-ops %" PRIu64 ")", nops_completed); - } -} - -void testcase::signal() { - if (!signalled) { - log_trace(">> signal(forced)"); - if (!global::singlemode) - osal_broadcast(config.actor_id); - signalled = true; - log_trace("<< signal(forced)"); - } -} - -bool testcase::setup() { - db_prepare(); - if (!wait4start()) - return false; - - start_timestamp = chrono::now_motonic(); - nops_completed = 0; - return true; -} - -bool testcase::teardown() { - log_trace(">> testcase::teardown"); - signal(); - db_close(); - log_trace("<< testcase::teardown"); - return true; -} - -bool testcase::should_continue(bool check_timeout_only) const { - bool result = true; - - if (config.params.test_duration) { - chrono::time since; - since.fixedpoint = - chrono::now_motonic().fixedpoint - start_timestamp.fixedpoint; - if (since.seconds() >= config.params.test_duration) - result = false; - } - - if (!check_timeout_only && config.params.test_nops && - nops_completed >= config.params.test_nops) - result = false; - - if (result) - kick_progress(false); - - return result; -} - -void testcase::fetch_canary() { - mdbx_canary canary_now; - log_trace(">> fetch_canary"); - - int rc = mdbx_canary_get(txn_guard.get(), &canary_now); - if (unlikely(rc != MDBX_SUCCESS)) - failure_perror("mdbx_canary_get()", rc); - - if (canary_now.v < last.canary.v) - failure("fetch_canary: %" PRIu64 "(canary-now.v) < %" PRIu64 - "(canary-last.v)", - canary_now.v, last.canary.v); - if (canary_now.y < last.canary.y) - failure("fetch_canary: %" PRIu64 "(canary-now.y) < %" PRIu64 - "(canary-last.y)", - canary_now.y, last.canary.y); - - last.canary = canary_now; - log_trace("<< fetch_canary: db-sequence %" PRIu64 - ", db-sequence.txnid %" PRIu64, - last.canary.y, last.canary.v); -} - -void testcase::update_canary(uint64_t increment) { - mdbx_canary canary_now = last.canary; - - log_trace(">> update_canary: sequence %" PRIu64 " += %" PRIu64, canary_now.y, - increment); - canary_now.y += increment; - - int rc = mdbx_canary_put(txn_guard.get(), &canary_now); - if (unlikely(rc != MDBX_SUCCESS)) - failure_perror("mdbx_canary_put()", rc); - - log_trace("<< update_canary: sequence = %" PRIu64, canary_now.y); -} - -int testcase::db_open__begin__table_create_open_clean(MDBX_dbi &handle) { - db_open(); - - int err, retry_left = 42; - for (;;) { - txn_begin(false); - handle = db_table_open(true); - db_table_clear(handle); - err = breakable_commit(); - if (likely(err == MDBX_SUCCESS)) { - txn_begin(false); - return MDBX_SUCCESS; - } - if (--retry_left == 0) - break; - jitter_delay(true); - } - log_notice("db_begin_table_create_open_clean: bailout due '%s'", - mdbx_strerror(err)); - return err; -} - -MDBX_dbi testcase::db_table_open(bool create) { - log_trace(">> testcase::db_table_create"); - - char tablename_buf[16]; - const char *tablename = nullptr; - if (config.space_id) { - int rc = snprintf(tablename_buf, sizeof(tablename_buf), "TBL%04u", - config.space_id); - if (rc < 4 || rc >= (int)sizeof(tablename_buf) - 1) - failure("snprintf(tablename): %d", rc); - tablename = tablename_buf; - } - log_debug("use %s table", tablename ? tablename : "MAINDB"); - - MDBX_dbi handle = 0; - int rc = mdbx_dbi_open(txn_guard.get(), tablename, - (create ? MDBX_CREATE : 0) | config.params.table_flags, - &handle); - if (unlikely(rc != MDBX_SUCCESS)) - failure_perror("mdbx_dbi_open()", rc); - - log_trace("<< testcase::db_table_create, handle %u", handle); - return handle; -} - -void testcase::db_table_drop(MDBX_dbi handle) { - log_trace(">> testcase::db_table_drop, handle %u", handle); - - if (config.params.drop_table) { - int rc = mdbx_drop(txn_guard.get(), handle, true); - if (unlikely(rc != MDBX_SUCCESS)) - failure_perror("mdbx_drop(delete=true)", rc); - speculum.clear(); - log_trace("<< testcase::db_table_drop"); - } else { - log_trace("<< testcase::db_table_drop: not needed"); - } -} - -void testcase::db_table_clear(MDBX_dbi handle, MDBX_txn *txn) { - log_trace(">> testcase::db_table_clear, handle %u", handle); - int rc = mdbx_drop(txn ? txn : txn_guard.get(), handle, false); - if (unlikely(rc != MDBX_SUCCESS)) - failure_perror("mdbx_drop(delete=false)", rc); - speculum.clear(); - log_trace("<< testcase::db_table_clear"); -} - -void testcase::db_table_close(MDBX_dbi handle) { - log_trace(">> testcase::db_table_close, handle %u", handle); - assert(!txn_guard); - int rc = mdbx_dbi_close(db_guard.get(), handle); - if (unlikely(rc != MDBX_SUCCESS)) - failure_perror("mdbx_dbi_close()", rc); - log_trace("<< testcase::db_table_close"); -} - -void testcase::checkdata(const char *step, MDBX_dbi handle, MDBX_val key2check, - MDBX_val expected_valued) { - MDBX_val actual_value = expected_valued; - int rc = mdbx_get_nearest(txn_guard.get(), handle, &key2check, &actual_value); - if (unlikely(rc != MDBX_SUCCESS)) - failure_perror(step, rc); - if (!is_samedata(&actual_value, &expected_valued)) - failure("%s data mismatch", step); -} - -//----------------------------------------------------------------------------- - -bool test_execute(const actor_config &config_const) { - const mdbx_pid_t pid = osal_getpid(); - actor_config config = config_const; - - if (global::singlemode) { - logging::setup(format("single_%s", testcase2str(config.testcase))); - } else { - logging::setup((logging::loglevel)config.params.loglevel, - format("child_%u.%u", config.actor_id, config.space_id)); - log_trace(">> wait4barrier"); - osal_wait4barrier(); - log_trace("<< wait4barrier"); - } - - try { - std::unique_ptr<testcase> test; - switch (config.testcase) { - case ac_hill: - test.reset(new testcase_hill(config, pid)); - break; - case ac_deadread: - test.reset(new testcase_deadread(config, pid)); - break; - case ac_deadwrite: - test.reset(new testcase_deadwrite(config, pid)); - break; - case ac_jitter: - test.reset(new testcase_jitter(config, pid)); - break; - case ac_try: - test.reset(new testcase_try(config, pid)); - break; - case ac_copy: - test.reset(new testcase_copy(config, pid)); - break; - case ac_append: - test.reset(new testcase_append(config, pid)); - break; - case ac_ttl: - test.reset(new testcase_ttl(config, pid)); - break; - case ac_nested: - test.reset(new testcase_nested(config, pid)); - break; - default: - test.reset(new testcase(config, pid)); - break; - } - - size_t iter = 0; - do { - iter++; - if (!test->setup()) { - log_notice("test setup failed"); - return false; - } - if (!test->run()) { - log_notice("test failed"); - return false; - } - if (!test->teardown()) { - log_notice("test teardown failed"); - return false; - } - - if (config.params.nrepeat == 1) - log_verbose("test successed"); - else { - if (config.params.nrepeat) - log_verbose("test successed (iteration %zi of %zi)", iter, - size_t(config.params.nrepeat)); - else - log_verbose("test successed (iteration %zi)", iter); - config.params.keygen.seed += INT32_C(0xA4F4D37B); - } - - } while (config.params.nrepeat == 0 || iter < config.params.nrepeat); - return true; - } catch (const std::exception &pipets) { - failure("***** Exception: %s *****", pipets.what()); - return false; - } -} - -//----------------------------------------------------------------------------- - -int testcase::insert(const keygen::buffer &akey, const keygen::buffer &adata, - unsigned flags) { - int err = mdbx_put(txn_guard.get(), dbi, &akey->value, &adata->value, flags); - if (err == MDBX_SUCCESS && config.params.speculum) { - const auto S_key = S(akey); - const auto S_data = S(adata); - const bool inserted = speculum.emplace(S_key, S_data).second; - assert(inserted); - (void)inserted; - } - return err; -} - -int testcase::replace(const keygen::buffer &akey, - const keygen::buffer &new_data, - const keygen::buffer &old_data, unsigned flags) { - if (config.params.speculum) { - const auto S_key = S(akey); - const auto S_old = S(old_data); - const auto S_new = S(new_data); - const auto removed = speculum.erase(SET::key_type(S_key, S_old)); - assert(removed == 1); - (void)removed; - const bool inserted = speculum.emplace(S_key, S_new).second; - assert(inserted); - (void)inserted; - } - return mdbx_replace(txn_guard.get(), dbi, &akey->value, &new_data->value, - &old_data->value, flags); -} - -int testcase::remove(const keygen::buffer &akey, const keygen::buffer &adata) { - if (config.params.speculum) { - const auto S_key = S(akey); - const auto S_data = S(adata); - const auto removed = speculum.erase(SET::key_type(S_key, S_data)); - assert(removed == 1); - (void)removed; - } - return mdbx_del(txn_guard.get(), dbi, &akey->value, &adata->value); -} - -bool testcase::speculum_verify() { - if (!config.params.speculum) - return true; - - if (!txn_guard) - txn_begin(true); - - char dump_key[128], dump_value[128]; - char dump_mkey[128], dump_mvalue[128]; - - MDBX_cursor *cursor; - int err = mdbx_cursor_open(txn_guard.get(), dbi, &cursor); - if (err != MDBX_SUCCESS) - failure_perror("mdbx_cursor_open()", err); - - bool rc = true; - MDBX_val akey, avalue; - MDBX_val mkey, mvalue; - err = mdbx_cursor_get(cursor, &akey, &avalue, MDBX_FIRST); - - unsigned extra = 0, lost = 0, n = 0; - assert(std::is_sorted(speculum.cbegin(), speculum.cend(), ItemCompare(this))); - auto it = speculum.cbegin(); - while (true) { - if (err != MDBX_SUCCESS) { - akey.iov_len = avalue.iov_len = 0; - akey.iov_base = avalue.iov_base = nullptr; - } - const auto S_key = S(akey); - const auto S_data = S(avalue); - if (it != speculum.cend()) { - mkey.iov_base = (void *)it->first.c_str(); - mkey.iov_len = it->first.size(); - mvalue.iov_base = (void *)it->second.c_str(); - mvalue.iov_len = it->second.size(); - } - if (err == MDBX_SUCCESS && it != speculum.cend() && S_key == it->first && - S_data == it->second) { - ++it; - err = mdbx_cursor_get(cursor, &akey, &avalue, MDBX_NEXT); - } else if (err == MDBX_SUCCESS && - (it == speculum.cend() || S_key < it->first || - (S_key == it->first && S_data < it->second))) { - extra += 1; - if (it != speculum.cend()) { - log_error("extra pair %u/%u: db{%s, %s} < mi{%s, %s}", n, extra, - mdbx_dump_val(&akey, dump_key, sizeof(dump_key)), - mdbx_dump_val(&avalue, dump_value, sizeof(dump_value)), - mdbx_dump_val(&mkey, dump_mkey, sizeof(dump_mkey)), - mdbx_dump_val(&mvalue, dump_mvalue, sizeof(dump_mvalue))); - } else { - log_error("extra pair %u/%u: db{%s, %s} < mi.END", n, extra, - mdbx_dump_val(&akey, dump_key, sizeof(dump_key)), - mdbx_dump_val(&avalue, dump_value, sizeof(dump_value))); - } - err = mdbx_cursor_get(cursor, &akey, &avalue, MDBX_NEXT); - rc = false; - } else if (it != speculum.cend() && - (err == MDBX_NOTFOUND || S_key > it->first || - (S_key == it->first && S_data > it->second))) { - lost += 1; - if (err == MDBX_NOTFOUND) { - log_error("lost pair %u/%u: db.END > mi{%s, %s}", n, lost, - mdbx_dump_val(&mkey, dump_mkey, sizeof(dump_mkey)), - mdbx_dump_val(&mvalue, dump_mvalue, sizeof(dump_mvalue))); - } else { - log_error("lost pair %u/%u: db{%s, %s} > mi{%s, %s}", n, lost, - mdbx_dump_val(&akey, dump_key, sizeof(dump_key)), - mdbx_dump_val(&avalue, dump_value, sizeof(dump_value)), - mdbx_dump_val(&mkey, dump_mkey, sizeof(dump_mkey)), - mdbx_dump_val(&mvalue, dump_mvalue, sizeof(dump_mvalue))); - } - ++it; - rc = false; - } else if (err == MDBX_NOTFOUND && it == speculum.cend()) { - break; - } else if (err != MDBX_SUCCESS) { - failure_perror("mdbx_cursor_get()", err); - } else { - assert(!"WTF?"); - } - n += 1; - } - - mdbx_cursor_close(cursor); - return rc; -} |