/* * Copyright 2017-2020 Leonid Yuriev * 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 * . */ #pragma once #include "base.h" #include "chrono.h" #include "config.h" #include "keygen.h" #include "log.h" #include "osal.h" #include "utils.h" #include #include #include #include #ifndef HAVE_cxx17_std_string_view #if __cplusplus >= 201703L && __has_include() #include #define HAVE_cxx17_std_string_view 1 #else #define HAVE_cxx17_std_string_view 0 #endif #endif /* HAVE_cxx17_std_string_view */ #if HAVE_cxx17_std_string_view #include #endif bool test_execute(const actor_config &config); std::string thunk_param(const actor_config &config); void testcase_setup(const char *casename, actor_params ¶ms, unsigned &last_space_id); void configure_actor(unsigned &last_space_id, const actor_testcase testcase, const char *space_id_cstr, const actor_params ¶ms); void keycase_setup(const char *casename, actor_params ¶ms); namespace global { extern const char thunk_param_prefix[]; extern std::vector actors; extern std::unordered_map events; extern std::unordered_map pid2actor; extern std::set databases; extern unsigned nactors; extern chrono::time start_motonic; extern chrono::time deadline_motonic; extern bool singlemode; namespace config { extern unsigned timeout_duration_seconds; extern bool dump_config; extern bool cleanup_before; extern bool cleanup_after; extern bool failfast; extern bool progress_indicator; extern bool console_mode; } /* namespace config */ } /* namespace global */ //----------------------------------------------------------------------------- struct db_deleter /* : public std::unary_function */ { void operator()(MDBX_env *env) const { mdbx_env_close(env); } }; struct txn_deleter /* : public std::unary_function */ { void operator()(MDBX_txn *txn) const { int rc = mdbx_txn_abort(txn); if (rc) log_trouble(__func__, "mdbx_txn_abort()", rc); } }; struct cursor_deleter /* : public std::unary_function */ { void operator()(MDBX_cursor *cursor) const { mdbx_cursor_close(cursor); } }; typedef std::unique_ptr scoped_db_guard; typedef std::unique_ptr scoped_txn_guard; typedef std::unique_ptr scoped_cursor_guard; //----------------------------------------------------------------------------- class testcase { protected: #if HAVE_cxx17_std_string_view using data_view = std::string_view; #else using data_view = std::string; #endif static inline data_view S(const MDBX_val &v) { return (v.iov_base && v.iov_len) ? data_view(static_cast(v.iov_base), v.iov_len) : data_view(); } static inline data_view S(const keygen::buffer &b) { return S(b->value); } using Item = std::pair; struct ItemCompare { const testcase *context; ItemCompare(const testcase *owner) : context(owner) {} bool operator()(const Item &a, const Item &b) const { MDBX_val va, vb; va.iov_base = (void *)a.first.data(); va.iov_len = a.first.size(); vb.iov_base = (void *)b.first.data(); vb.iov_len = b.first.size(); int cmp = mdbx_cmp(context->txn_guard.get(), context->dbi, &va, &vb); if (cmp == 0 && (context->config.params.table_flags & MDBX_DUPSORT) != 0) { va.iov_base = (void *)a.second.data(); va.iov_len = a.second.size(); vb.iov_base = (void *)b.second.data(); vb.iov_len = b.second.size(); cmp = mdbx_dcmp(context->txn_guard.get(), context->dbi, &va, &vb); } return cmp < 0; } }; // for simplify the set> // is used instead of multimap using SET = std::set; const actor_config &config; const mdbx_pid_t pid; MDBX_dbi dbi{0}; scoped_db_guard db_guard; scoped_txn_guard txn_guard; scoped_cursor_guard cursor_guard; bool signalled{false}; bool need_speculum_assign{false}; uint64_t nops_completed{0}; chrono::time start_timestamp; keygen::buffer key; keygen::buffer data; keygen::maker keyvalue_maker; struct { MDBX_canary canary; } last; SET speculum{ItemCompare(this)}, speculum_commited{ItemCompare(this)}; bool speculum_verify(); int insert(const keygen::buffer &akey, const keygen::buffer &adata, unsigned flags); int replace(const keygen::buffer &akey, const keygen::buffer &new_value, const keygen::buffer &old_value, unsigned flags); int remove(const keygen::buffer &akey, const keygen::buffer &adata); static int oom_callback(MDBX_env *env, mdbx_pid_t pid, mdbx_tid_t tid, uint64_t txn, unsigned gap, size_t space, int retry); unsigned actual_db_mode{0}; bool is_nested_txn_available() const { return (actual_db_mode & MDBX_WRITEMAP) == 0; } void kick_progress(bool active) const; void db_prepare(); void db_open(); void db_close(); void txn_begin(bool readonly, unsigned flags = 0); int breakable_commit(); void txn_end(bool abort); int breakable_restart(); void txn_restart(bool abort, bool readonly, unsigned flags = 0); void cursor_open(MDBX_dbi handle); void cursor_close(); void txn_inject_writefault(void); void txn_inject_writefault(MDBX_txn *txn); void fetch_canary(); void update_canary(uint64_t increment); void checkdata(const char *step, MDBX_dbi handle, MDBX_val key2check, MDBX_val expected_valued); unsigned txn_underutilization_x256(MDBX_txn *txn) const; MDBX_dbi db_table_open(bool create); void db_table_drop(MDBX_dbi handle); void db_table_clear(MDBX_dbi handle, MDBX_txn *txn = nullptr); void db_table_close(MDBX_dbi handle); int db_open__begin__table_create_open_clean(MDBX_dbi &handle); bool wait4start(); void report(size_t nops_done); void signal(); bool should_continue(bool check_timeout_only = false) const; void generate_pair(const keygen::serial_t serial, keygen::buffer &out_key, keygen::buffer &out_value, keygen::serial_t data_age) { keyvalue_maker.pair(serial, out_key, out_value, data_age, false); } void generate_pair(const keygen::serial_t serial) { keyvalue_maker.pair(serial, key, data, 0, true); } bool mode_readonly() const { return (config.params.mode_flags & MDBX_RDONLY) ? true : false; } public: testcase(const actor_config &config, const mdbx_pid_t pid) : config(config), pid(pid) { start_timestamp.reset(); memset(&last, 0, sizeof(last)); } virtual bool setup(); virtual bool run() { return true; } virtual bool teardown(); virtual ~testcase() {} }; class testcase_hill : public testcase { public: testcase_hill(const actor_config &config, const mdbx_pid_t pid) : testcase(config, pid) {} bool run() override; }; class testcase_append : public testcase { public: testcase_append(const actor_config &config, const mdbx_pid_t pid) : testcase(config, pid) {} bool run() override; }; class testcase_deadread : public testcase { public: testcase_deadread(const actor_config &config, const mdbx_pid_t pid) : testcase(config, pid) {} bool run() override; }; class testcase_deadwrite : public testcase { public: testcase_deadwrite(const actor_config &config, const mdbx_pid_t pid) : testcase(config, pid) {} bool run() override; }; class testcase_jitter : public testcase { public: testcase_jitter(const actor_config &config, const mdbx_pid_t pid) : testcase(config, pid) {} bool run() override; }; class testcase_try : public testcase { public: testcase_try(const actor_config &config, const mdbx_pid_t pid) : testcase(config, pid) {} bool run() override; }; class testcase_copy : public testcase { const std::string copy_pathname; void copy_db(const bool with_compaction); public: testcase_copy(const actor_config &config, const mdbx_pid_t pid) : testcase(config, pid), copy_pathname(config.params.pathname_db + "-copy") {} bool run() override; }; class testcase_ttl : public testcase { using inherited = testcase; protected: struct { unsigned max_window_size{0}; unsigned max_step_size{0}; } sliding; unsigned edge2window(uint64_t edge); unsigned edge2count(uint64_t edge); public: testcase_ttl(const actor_config &config, const mdbx_pid_t pid) : inherited(config, pid) {} bool setup() override; bool run() override; }; class testcase_nested : public testcase_ttl { using inherited = testcase_ttl; using FIFO = std::deque>; uint64_t serial{0}; unsigned clear_wholetable_passed{0}; unsigned clear_stepbystep_passed{0}; unsigned dbfull_passed{0}; bool keyspace_overflow{false}; FIFO fifo; std::stack> stack; bool trim_tail(unsigned window_width); bool grow_head(unsigned head_count); bool pop_txn(bool abort); bool pop_txn() { return pop_txn(inherited::is_nested_txn_available() ? flipcoin_x3() : flipcoin_x2()); } void push_txn(); bool stochastic_breakable_restart_with_nested(bool force_restart = false); public: testcase_nested(const actor_config &config, const mdbx_pid_t pid) : inherited(config, pid) {} bool setup() override; bool run() override; bool teardown() override; };