From 1da92cb0f72baf3b1671cecf68609145d960979c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9B=D0=B5=D0=BE=D0=BD=D0=B8=D0=B4=20=D0=AE=D1=80=D1=8C?= =?UTF-8?q?=D0=B5=D0=B2=20=28Leonid=20Yuriev=29?= Date: Fri, 7 Nov 2025 22:38:02 +0300 Subject: [PATCH] mdbx-test: initial/incomplete/draft test for `get-cached` feature. --- test/CMakeLists.txt | 1 + test/extra/cursor_closing.c++ | 4 +- test/extra/get_cached.c++ | 191 ++++++++++++++++++++++++++++++++++ 3 files changed, 194 insertions(+), 2 deletions(-) create mode 100644 test/extra/get_cached.c++ diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index df9c7281..7e9bf673 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -292,6 +292,7 @@ else() add_extra_test(dbi) add_extra_test(open) add_extra_test(txn) + add_extra_test(get_cached) endif() add_extra_test(hex_base64_base58) endif() diff --git a/test/extra/cursor_closing.c++ b/test/extra/cursor_closing.c++ index fe234d7a..0cbed259 100644 --- a/test/extra/cursor_closing.c++ +++ b/test/extra/cursor_closing.c++ @@ -42,7 +42,7 @@ static char log_buffer[1024]; //-------------------------------------------------------------------------------------------- -bool case0(mdbx::env env) { +bool case0_trivia(mdbx::env env) { auto txn = env.start_write(); auto table = txn.create_map("case0", mdbx::key_mode::usual, mdbx::value_mode::single); auto cursor_1 = txn.open_cursor(table); @@ -395,7 +395,7 @@ int doit() { mdbx::env_managed env(db_filename, mdbx::env_managed::create_parameters(), mdbx::env::operate_parameters(N + 2, 0, mdbx::env::nested_transactions)); - bool ok = case0(env); + bool ok = case0_trivia(env); ok = case1(env) && ok; ok = case2(env) && ok; diff --git a/test/extra/get_cached.c++ b/test/extra/get_cached.c++ new file mode 100644 index 00000000..ea555176 --- /dev/null +++ b/test/extra/get_cached.c++ @@ -0,0 +1,191 @@ +#include "mdbx.h++" + +#include +#include +#include +#include +#if defined(__cpp_lib_latch) && __cpp_lib_latch >= 201907L +#include +#include +#endif + +#if defined(ENABLE_MEMCHECK) || defined(MDBX_CI) +#if MDBX_DEBUG || !defined(NDEBUG) +#define RELIEF_FACTOR 16 +#else +#define RELIEF_FACTOR 8 +#endif +#elif MDBX_DEBUG || !defined(NDEBUG) || defined(__APPLE__) || defined(_WIN32) +#define RELIEF_FACTOR 4 +#elif UINTPTR_MAX > 0xffffFFFFul || ULONG_MAX > 0xffffFFFFul +#define RELIEF_FACTOR 2 +#else +#define RELIEF_FACTOR 1 +#endif + +// static const auto NN = 1000u / RELIEF_FACTOR; + +#if defined(__cpp_lib_latch) && __cpp_lib_latch >= 201907L +static const auto N = std::min(17u, std::thread::hardware_concurrency()); +#else +static const auto N = 3u; +#endif + +static void logger_nofmt(MDBX_log_level_t loglevel, const char *function, int line, const char *msg, + unsigned length) noexcept { + (void)length; + (void)loglevel; + fflush(nullptr); + std::cout << function << ":" << line << " " << msg; + std::cout.flush(); +} + +static char log_buffer[1024]; + +//-------------------------------------------------------------------------------------------- + +typedef MDBX_cache_result_t (*get_cached_t)(const MDBX_txn *txn, MDBX_dbi dbi, const MDBX_val *key, MDBX_val *data, + MDBX_cache_entry_t *entry); + +static bool check_state(const MDBX_cache_result_t &r, const MDBX_error_t wanna_errcode, + const MDBX_cache_status_t wanna_status, unsigned line) { + if (r.errcode == wanna_errcode && r.status == wanna_status) + return true; + std::cerr << "unecpected (at " << line + << "): " + "err " + << r.errcode << " (wanna " << wanna_errcode + << "), " + "status " + << r.status << " (wanna " << wanna_status << ")" << std::endl; + return false; +} + +static bool check_state_and_value(const MDBX_cache_result_t &r, const mdbx::slice &value, + const MDBX_error_t wanna_errcode, const MDBX_cache_status_t wanna_status, + const mdbx::slice &wanna_value, unsigned line) { + + bool ok = check_state(r, wanna_errcode, wanna_status, line); + if (value != wanna_value) { + std::cerr << "mismatch value (at " << line << "): " << value << " (wanna " << wanna_value << ")" << std::endl; + ok = false; + } + return ok; +} + +bool case0_trivia(mdbx::env env) { + get_cached_t get_cached = mdbx_cache_get_SingleThreaded; + auto txn = env.start_write(); + auto table = txn.create_map("case0", mdbx::key_mode::usual, mdbx::value_mode::single); + + MDBX_cache_entry_t entry; + mdbx_cache_init(&entry); + MDBX_val data; + MDBX_cache_result_t r; + + bool ok = true; + r = get_cached(txn, table, mdbx::slice("key"), &data, &entry); + ok = check_state(r, MDBX_NOTFOUND, MDBX_CACHE_DIRTY, __LINE__) && ok; + + txn.commit_embark_read(); + r = get_cached(txn, table, mdbx::slice("key"), &data, &entry); + ok = check_state(r, MDBX_NOTFOUND, MDBX_CACHE_REFRESHED, __LINE__) && ok; + + // drops the table as if it were done by another process + { + auto params = mdbx::env::operate_parameters(42); + params.options.no_sticky_threads = true; + mdbx::env_managed env2(env.get_path(), params); + auto txn2 = env2.start_write(); + txn2.drop_map("case0"); + txn2.commit(); + } + txn.renew_reading(); + r = get_cached(txn, table, mdbx::slice("key"), &data, &entry); + ok = check_state(r, MDBX_NOTFOUND, MDBX_CACHE_CONFIRMED, __LINE__) && ok; + + txn.abort(); + txn = env.start_write(); + table = txn.create_map("case0", mdbx::key_mode::usual, mdbx::value_mode::single); + r = get_cached(txn, table, mdbx::slice("key"), &data, &entry); + ok = check_state(r, MDBX_NOTFOUND, MDBX_CACHE_DIRTY, __LINE__) && ok; + + txn.commit_embark_read(); + r = get_cached(txn, table, mdbx::slice("key"), &data, &entry); + ok = check_state(r, MDBX_NOTFOUND, MDBX_CACHE_CONFIRMED, __LINE__) && ok; + + txn.abort(); + txn = env.start_write(); + txn.insert(table, "key", "value"); + r = get_cached(txn, table, mdbx::slice("key"), &data, &entry); + ok = check_state_and_value(r, data, MDBX_SUCCESS, MDBX_CACHE_DIRTY, "value", __LINE__) && ok; + + txn.commit_embark_read(); + r = get_cached(txn, table, mdbx::slice("key"), &data, &entry); + ok = check_state_and_value(r, data, MDBX_SUCCESS, MDBX_CACHE_REFRESHED, "value", __LINE__) && ok; + + txn.abort(); + txn = env.start_write(); + r = get_cached(txn, table, mdbx::slice("key"), &data, &entry); + ok = check_state_and_value(r, data, MDBX_SUCCESS, MDBX_CACHE_HIT, "value", __LINE__) && ok; + txn.update(table, "key", "42"); + r = get_cached(txn, table, mdbx::slice("key"), &data, &entry); + ok = check_state_and_value(r, data, MDBX_SUCCESS, MDBX_CACHE_DIRTY, "42", __LINE__) && ok; + + txn.commit_embark_read(); + r = get_cached(txn, table, mdbx::slice("key"), &data, &entry); + ok = check_state_and_value(r, data, MDBX_SUCCESS, MDBX_CACHE_REFRESHED, "42", __LINE__) && ok; + + MDBX_cache_entry_t entry2; + mdbx_cache_init(&entry2); + txn.abort(); + txn = env.start_write(); + txn.insert(table, "key2", "value2"); + r = get_cached(txn, table, mdbx::slice("key"), &data, &entry); + ok = check_state_and_value(r, data, MDBX_SUCCESS, MDBX_CACHE_DIRTY, "42", __LINE__) && ok; + r = get_cached(txn, table, mdbx::slice("key2"), &data, &entry2); + ok = check_state_and_value(r, data, MDBX_SUCCESS, MDBX_CACHE_DIRTY, "value2", __LINE__) && ok; + + txn.commit_embark_read(); + r = get_cached(txn, table, mdbx::slice("key"), &data, &entry); + ok = check_state_and_value(r, data, MDBX_SUCCESS, MDBX_CACHE_REFRESHED, "42", __LINE__) && ok; + r = get_cached(txn, table, mdbx::slice("key2"), &data, &entry2); + ok = check_state_and_value(r, data, MDBX_SUCCESS, MDBX_CACHE_REFRESHED, "value2", __LINE__) && ok; + + return ok; +} + +//-------------------------------------------------------------------------------------------- + +int doit() { + mdbx::path db_filename = "test-get-cached"; + mdbx::env::remove(db_filename); + + mdbx::env_managed env(db_filename, mdbx::env_managed::create_parameters(), + mdbx::env::operate_parameters(N + 2, 0, mdbx::env::nested_transactions)); + + bool ok = case0_trivia(env); + // ok = case1(env) && ok; + // ok = case2(env) && ok; + + if (ok) { + std::cout << "OK\n"; + return EXIT_SUCCESS; + } else { + std::cout << "FAIL!\n"; + return EXIT_FAILURE; + } +} + +int main(int argc, char *argv[]) { + (void)argc; + (void)argv; + mdbx_setup_debug_nofmt(MDBX_LOG_NOTICE, MDBX_DBG_ASSERT | MDBX_DBG_LEGACY_MULTIOPEN, logger_nofmt, log_buffer, + sizeof(log_buffer)); + try { + return doit(); + } catch (const std::exception &ex) { + std::cerr << "Exception: " << ex.what() << "\n"; + return EXIT_FAILURE; + } +}