diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 61531a57..6961823c 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -106,6 +106,13 @@ if(UNIX AND NOT SUBPROJECT) set_target_properties(test_extra_doubtless_positioning PROPERTIES CXX_STANDARD ${MDBX_CXX_STANDARD} CXX_STANDARD_REQUIRED ON) endif() + add_executable(test_extra_crunched_delete extra/crunched_delete.c++) + target_include_directories(test_extra_crunched_delete PRIVATE "${PROJECT_SOURCE_DIR}") + target_link_libraries(test_extra_crunched_delete ${TOOL_MDBX_LIB}) + if(MDBX_CXX_STANDARD) + set_target_properties(test_extra_crunched_delete PROPERTIES + CXX_STANDARD ${MDBX_CXX_STANDARD} CXX_STANDARD_REQUIRED ON) + endif() endif() endif() @@ -196,6 +203,7 @@ else() if (ENABLE_MEMCHECK) set_tests_properties(extra_doubtless_positioning PROPERTIES TIMEOUT 10800) endif() + add_test(NAME extra_crunched_delete COMMAND test_extra_crunched_delete) endif() endif() diff --git a/test/extra/crunched_delete.c++ b/test/extra/crunched_delete.c++ new file mode 100644 index 00000000..75be5443 --- /dev/null +++ b/test/extra/crunched_delete.c++ @@ -0,0 +1,136 @@ +#include "mdbx.h++" + +#include +#include +#include + +#define NN 10000 + +std::string format_va(const char *fmt, va_list ap) { + va_list ones; + va_copy(ones, ap); +#ifdef _MSC_VER + int needed = _vscprintf(fmt, ap); +#else + int needed = vsnprintf(nullptr, 0, fmt, ap); +#endif + assert(needed >= 0); + std::string result; + result.reserve(size_t(needed + 1)); + result.resize(size_t(needed), '\0'); + assert(int(result.capacity()) > needed); + int actual = vsnprintf(const_cast(result.data()), result.capacity(), + fmt, ones); + assert(actual == needed); + (void)actual; + va_end(ones); + return result; +} + +std::string format(const char *fmt, ...) { + va_list ap; + va_start(ap, fmt); + std::string result = format_va(fmt, ap); + va_end(ap); + return result; +} + +struct acase { + unsigned klen_min, klen_max; + unsigned vlen_min, vlen_max; + unsigned dupmax_log2; +}; + +// std::random_device rd; +std::mt19937_64 rnd; + +static unsigned rnd_len(unsigned min, unsigned max) { + return (min < max) ? min + rnd() % (max - min) : min; +} + +static mdbx::slice mk(mdbx::default_buffer &buf, unsigned len) { + buf.clear_and_reserve(len); + for (unsigned i = 0; i < len; ++i) + buf.append_byte(rnd()); + return buf.slice(); +} + +static std::string name(unsigned n) { return format("Commitment_%05u", n); } + +static mdbx::map_handle create_and_fill(mdbx::txn txn, const acase &thecase, + const unsigned n) { + auto map = txn.create_map(name(n), + (thecase.klen_min == thecase.klen_max && + (thecase.klen_min == 4 || thecase.klen_max == 8)) + ? mdbx::key_mode::ordinal + : mdbx::key_mode::usual, + (thecase.vlen_min == thecase.vlen_max) + ? mdbx::value_mode::multi_samelength + : mdbx::value_mode::multi); + mdbx::buffer k, v; + for (auto i = 0u; i < NN; i++) { + mk(k, rnd_len(thecase.klen_min, thecase.klen_min)); + for (auto ii = thecase.dupmax_log2 + ? 1u + (rnd() & ((2u << thecase.dupmax_log2) - 1u)) + : 1u; + ii > 0; --ii) + txn.upsert(map, k, mk(v, rnd_len(thecase.vlen_min, thecase.vlen_min))); + } + return map; +} + +static void chunched_delete(mdbx::txn txn, const acase &thecase, + const unsigned n) { + mdbx::buffer r; + auto map = txn.open_map_accede(name(n)); + auto cursor = txn.open_cursor(map); + for (size_t n = 0; n < 10; ++n) { + mk(r, rnd_len(thecase.klen_min, thecase.klen_min)); + if (cursor.lower_bound(r)) + do + cursor.erase(); + while (cursor.to_next(false)); + } + + if (cursor.to_first(false)) + do + cursor.erase(); + while (cursor.to_next(false)); +} + +int main(int argc, const char *argv[]) { + (void)argc; + (void)argv; + + mdbx::env::remove("."); + + std::vector testset; + // Там ключи разной длины - от 1 до 64 байт. Значения разной длины от 100 до + // 1000 байт. + testset.emplace_back(/* keylen_min */ 1, /* keylen_max */ 64, + /* datalen_min */ 100, /* datalen_max */ 4000, + /* dups_log2 */ 10); + // В одной таблице DupSort: path -> version_u64+data + // path - это префикс в дереве. Самые частые длины: 1-5 байт и 32-36 байт. + testset.emplace_back(1, 5, 100, 1000, 12); + testset.emplace_back(32, 36, 100, 1000, 12); + // В другой DupSort: timestamp_u64 -> path + testset.emplace_back(8, 8, 1, 5, 12); + testset.emplace_back(8, 8, 32, 36, 12); + + mdbx::env_managed env(".", mdbx::env_managed::create_parameters(), + mdbx::env::operate_parameters(42)); + + auto txn = env.start_write(); + for (unsigned i = 0; i < testset.size(); ++i) + create_and_fill(txn, testset[i], i); + txn.commit(); + + txn = env.start_write(); + for (unsigned i = 0; i < testset.size(); ++i) + chunched_delete(txn, testset[i], i); + txn.commit(); + + std::cout << "OK\n"; + return EXIT_SUCCESS; +}