mirror of
https://github.com/isar/libmdbx.git
synced 2024-10-29 23:19:20 +08:00
mdbx-testing: добавление `extra/crunched_delete'.
This commit is contained in:
parent
25efb58790
commit
3517db6178
@ -117,6 +117,13 @@ if(UNIX AND NOT SUBPROJECT)
|
|||||||
set_target_properties(test_extra_doubtless_positioning PROPERTIES
|
set_target_properties(test_extra_doubtless_positioning PROPERTIES
|
||||||
CXX_STANDARD ${MDBX_CXX_STANDARD} CXX_STANDARD_REQUIRED ON)
|
CXX_STANDARD ${MDBX_CXX_STANDARD} CXX_STANDARD_REQUIRED ON)
|
||||||
endif()
|
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()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
@ -207,6 +214,7 @@ else()
|
|||||||
if (ENABLE_MEMCHECK)
|
if (ENABLE_MEMCHECK)
|
||||||
set_tests_properties(extra_doubtless_positioning PROPERTIES TIMEOUT 10800)
|
set_tests_properties(extra_doubtless_positioning PROPERTIES TIMEOUT 10800)
|
||||||
endif()
|
endif()
|
||||||
|
add_test(NAME extra_crunched_delete COMMAND test_extra_crunched_delete)
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
414
test/extra/crunched_delete.c++
Normal file
414
test/extra/crunched_delete.c++
Normal file
@ -0,0 +1,414 @@
|
|||||||
|
#include "mdbx.h++"
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <iostream>
|
||||||
|
#include <random>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#if MDBX_DEBUG || !defined(NDEBUG)
|
||||||
|
#define NN 1024
|
||||||
|
#else
|
||||||
|
#define NN 16384
|
||||||
|
#endif
|
||||||
|
|
||||||
|
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<char *>(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;
|
||||||
|
|
||||||
|
acase(unsigned klen_min, unsigned klen_max, unsigned vlen_min,
|
||||||
|
unsigned vlen_max, unsigned dupmax_log2)
|
||||||
|
: klen_min(klen_min), klen_max(klen_max), vlen_min(vlen_min),
|
||||||
|
vlen_max(vlen_max), dupmax_log2(dupmax_log2) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
// std::random_device rd;
|
||||||
|
std::mt19937_64 rnd;
|
||||||
|
|
||||||
|
static unsigned prng_fast(uint32_t &seed) {
|
||||||
|
seed = seed * 1103515245 + 12345;
|
||||||
|
return seed >> 17;
|
||||||
|
}
|
||||||
|
|
||||||
|
static mdbx::slice mk(mdbx::default_buffer &buf, unsigned min, unsigned max) {
|
||||||
|
uint32_t seed = rnd() % (NN + NN);
|
||||||
|
unsigned len = (min < max) ? min + prng_fast(seed) % (max - min) : min;
|
||||||
|
buf.clear_and_reserve(len);
|
||||||
|
for (unsigned i = 0; i < len; ++i)
|
||||||
|
buf.append_byte(prng_fast(seed));
|
||||||
|
return buf.slice();
|
||||||
|
}
|
||||||
|
|
||||||
|
static mdbx::slice mk_key(mdbx::default_buffer &buf, const acase &thecase) {
|
||||||
|
return mk(buf, thecase.klen_min, thecase.klen_max);
|
||||||
|
}
|
||||||
|
|
||||||
|
static mdbx::slice mk_val(mdbx::default_buffer &buf, const acase &thecase) {
|
||||||
|
return mk(buf, thecase.vlen_min, thecase.vlen_max);
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
if (txn.get_map_stat(map).ms_entries < NN) {
|
||||||
|
mdbx::buffer k, v;
|
||||||
|
for (auto i = 0u; i < NN; i++) {
|
||||||
|
mk_key(k, thecase);
|
||||||
|
for (auto ii = thecase.dupmax_log2
|
||||||
|
? 1u + (rnd() & ((2u << thecase.dupmax_log2) - 1u))
|
||||||
|
: 1u;
|
||||||
|
ii > 0; --ii)
|
||||||
|
txn.upsert(map, k, mk_val(v, thecase));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void chunched_delete(mdbx::txn txn, const acase &thecase,
|
||||||
|
const unsigned n) {
|
||||||
|
// printf(">> %s, case #%i\n", __FUNCTION__, n);
|
||||||
|
mdbx::buffer k, v;
|
||||||
|
auto map = txn.open_map_accede(name(n));
|
||||||
|
|
||||||
|
{
|
||||||
|
auto cursor = txn.open_cursor(map);
|
||||||
|
while (true) {
|
||||||
|
const unsigned all = cursor.txn().get_map_stat(cursor.map()).ms_entries;
|
||||||
|
// printf("== seek random of %u\n", all);
|
||||||
|
|
||||||
|
const char *last_op;
|
||||||
|
bool last_r;
|
||||||
|
|
||||||
|
if ((last_op = "MDBX_GET_BOTH",
|
||||||
|
last_r = cursor.find_multivalue(mk_key(k, thecase),
|
||||||
|
mk_val(v, thecase), false)) ||
|
||||||
|
rnd() % 3 == 0 ||
|
||||||
|
(last_op = "MDBX_SET_RANGE",
|
||||||
|
last_r = cursor.lower_bound(mk_key(k, thecase), false))) {
|
||||||
|
int i = int(rnd() % 7) - 3;
|
||||||
|
// if (i)
|
||||||
|
// printf(" %s -> %s\n", last_op, last_r ? "true" : "false");
|
||||||
|
// printf("== shift multi %i\n", i);
|
||||||
|
try {
|
||||||
|
while (i < 0 && (last_op = "MDBX_PREV_DUP",
|
||||||
|
last_r = cursor.to_current_prev_multi(false)))
|
||||||
|
++i;
|
||||||
|
while (i > 0 && (last_op = "MDBX_NEXT_DUP",
|
||||||
|
last_r = cursor.to_current_next_multi(false)))
|
||||||
|
--i;
|
||||||
|
} catch (const mdbx::no_data &) {
|
||||||
|
printf("cursor_del() -> exception, last %s %s\n", last_op,
|
||||||
|
last_r ? "true" : "false");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// printf(" %s -> %s\n", last_op, last_r ? "true" : "false");
|
||||||
|
|
||||||
|
if (all < 42) {
|
||||||
|
// printf("== erase-tail\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
auto i = all % 17 + 1;
|
||||||
|
try {
|
||||||
|
last_r = cursor.erase();
|
||||||
|
do {
|
||||||
|
// printf("== erase-chunk: %u\n", i);
|
||||||
|
// printf(" cursor_del() -> %s\n", last_r ? "true" : "false");
|
||||||
|
} while (cursor.to_next(false) && --i > 0);
|
||||||
|
} catch (const mdbx::no_data &) {
|
||||||
|
printf("cursor_del() -> exception, last %s %s\n", last_op,
|
||||||
|
last_r ? "true" : "false");
|
||||||
|
}
|
||||||
|
|
||||||
|
// (void) last_op;
|
||||||
|
// (void) last_r;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cursor.to_first(false))
|
||||||
|
do
|
||||||
|
cursor.erase();
|
||||||
|
while (cursor.to_next(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
// printf("<< %s, case #%i\n", __FUNCTION__, n);
|
||||||
|
}
|
||||||
|
|
||||||
|
static char log_buffer[1024];
|
||||||
|
|
||||||
|
static void logger_nofmt(MDBX_log_level_t loglevel, const char *function,
|
||||||
|
int line, const char *msg, unsigned length) noexcept {
|
||||||
|
(void)length;
|
||||||
|
(void)loglevel;
|
||||||
|
fprintf(stdout, "%s:%u %s", function, line, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool outofrange_prev(mdbx::env env) {
|
||||||
|
mdbx::cursor_managed cursor;
|
||||||
|
const std::array<mdbx::pair, 4> items = {
|
||||||
|
{{"k1", "v1"}, {"k1", "v2"}, {"k2", "v1"}, {"k2", "v2"}}};
|
||||||
|
|
||||||
|
auto txn = env.start_write();
|
||||||
|
auto multi =
|
||||||
|
txn.create_map("multi", mdbx::key_mode::usual, mdbx::value_mode::multi);
|
||||||
|
auto simple = txn.create_map("simple");
|
||||||
|
txn.clear_map(multi);
|
||||||
|
txn.clear_map(simple);
|
||||||
|
|
||||||
|
txn.insert(simple, items[0]);
|
||||||
|
txn.insert(simple, items[3]);
|
||||||
|
cursor.bind(txn, simple);
|
||||||
|
const auto simple_oor = cursor.lower_bound("k3");
|
||||||
|
if (simple_oor) {
|
||||||
|
std::cerr << "simple-outofrange " << simple_oor << "\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const auto simple_oor_prevdup = cursor.to_current_prev_multi(false);
|
||||||
|
if (simple_oor_prevdup) {
|
||||||
|
std::cerr << "simple-outofrange-prevdup " << simple_oor_prevdup << "\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const auto simple_oor_prev = cursor.to_previous(false);
|
||||||
|
if (!simple_oor_prev || simple_oor_prev != items[3]) {
|
||||||
|
std::cerr << "simple-outofrange-prev " << simple_oor_prev << "\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
txn.append(multi, items[0]);
|
||||||
|
txn.append(multi, items[1]);
|
||||||
|
txn.append(multi, items[2]);
|
||||||
|
txn.append(multi, items[3]);
|
||||||
|
cursor.bind(txn, multi);
|
||||||
|
const auto multi_oor = cursor.lower_bound("k3");
|
||||||
|
if (multi_oor) {
|
||||||
|
std::cerr << "multi-outofrange " << multi_oor << "\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const auto multi_oor_prevdup = cursor.to_current_prev_multi(false);
|
||||||
|
if (multi_oor_prevdup) {
|
||||||
|
std::cerr << "multi-outofrange-prevdup " << multi_oor_prevdup << "\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const auto multi_oor_prev = cursor.to_previous(false);
|
||||||
|
if (!multi_oor_prev || multi_oor_prev != items[3]) {
|
||||||
|
std::cerr << "multi-outofrange-prev " << multi_oor_prev << "\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
txn.commit();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool next_prev_current(mdbx::env env) {
|
||||||
|
const std::array<mdbx::pair, 4> items = {
|
||||||
|
{{"k1", "v1"}, {"k1", "v2"}, {"k2", "v1"}, {"k2", "v2"}}};
|
||||||
|
|
||||||
|
auto txn = env.start_write();
|
||||||
|
auto map =
|
||||||
|
txn.create_map("multi", mdbx::key_mode::usual, mdbx::value_mode::multi);
|
||||||
|
txn.clear_map(map);
|
||||||
|
for (const auto &i : items)
|
||||||
|
txn.upsert(map, i);
|
||||||
|
|
||||||
|
auto cursor = txn.open_cursor(map);
|
||||||
|
const auto first = cursor.to_first(false);
|
||||||
|
if (!first || first != items[0]) {
|
||||||
|
std::cerr << "bad-first " << first << "\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const auto next1 = cursor.to_next(false);
|
||||||
|
if (!next1 || next1 != items[1]) {
|
||||||
|
std::cerr << "bad-next-1 " << next1 << "\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const auto next2 = cursor.to_next(false);
|
||||||
|
if (!next2 || next2 != items[2]) {
|
||||||
|
std::cerr << "bad-next-2 " << next2 << "\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const auto prev1 = cursor.to_previous(false);
|
||||||
|
if (!prev1 || prev1 != items[1]) {
|
||||||
|
std::cerr << "bad-prev-1 " << prev1 << "\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const auto prev2 = cursor.to_previous(false);
|
||||||
|
if (!prev2 || prev2 != items[0]) {
|
||||||
|
std::cerr << "bad-prev-2 " << prev2 << "\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!cursor.erase(false)) {
|
||||||
|
std::cerr << "bad-erase\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto after_del = cursor.current(false);
|
||||||
|
if (!after_del || after_del != items[1]) {
|
||||||
|
std::cerr << "bad-after-del, current " << after_del << "\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const auto next_after_del1 = cursor.to_next(false);
|
||||||
|
if (!next_after_del1 || next_after_del1 != items[2]) {
|
||||||
|
std::cerr << "bad-next_after_del1 " << next_after_del1;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const auto next_after_del2 = cursor.to_next(false);
|
||||||
|
if (!next_after_del2 || next_after_del2 != items[3]) {
|
||||||
|
std::cerr << "bad-next_after_del2 " << next_after_del2;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const auto next_after_del3 = cursor.to_next(false);
|
||||||
|
if (next_after_del3) {
|
||||||
|
std::cerr << "bad-next_after_del3 " << next_after_del3;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
txn.commit();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool simple(mdbx::env env) {
|
||||||
|
const std::array<mdbx::pair, 3> items = {
|
||||||
|
{{"k0", "v0"}, {"k1", "v1"}, {"k2", "v2"}}};
|
||||||
|
|
||||||
|
auto txn = env.start_write();
|
||||||
|
auto map = txn.create_map("simple");
|
||||||
|
txn.clear_map(map);
|
||||||
|
for (const auto &i : items)
|
||||||
|
txn.insert(map, i);
|
||||||
|
|
||||||
|
auto cursor = txn.open_cursor(map);
|
||||||
|
cursor.seek(items[1].key);
|
||||||
|
|
||||||
|
const auto seek = cursor.current(false);
|
||||||
|
if (seek != items[1]) {
|
||||||
|
std::cerr << "bad-seek, current " << seek << "\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!cursor.erase()) {
|
||||||
|
std::cerr << "bad-erase\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto next = cursor.to_next(false);
|
||||||
|
if (!next || next != items[2]) {
|
||||||
|
std::cerr << "bad-next " << next;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto after_del = cursor.current(false);
|
||||||
|
if (!after_del || after_del != items[2]) {
|
||||||
|
std::cerr << "bad-after-del, current " << after_del << "\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
txn.commit();
|
||||||
|
|
||||||
|
txn = env.start_read();
|
||||||
|
cursor.bind(txn, map);
|
||||||
|
|
||||||
|
#define BAD_CODE 1
|
||||||
|
#if BAD_CODE
|
||||||
|
const auto first = cursor.to_next(false);
|
||||||
|
#else
|
||||||
|
const auto first = cursor.to_first(false);
|
||||||
|
#endif
|
||||||
|
const auto second = cursor.to_next(false);
|
||||||
|
const auto eof = cursor.to_next(false);
|
||||||
|
|
||||||
|
if (!first || first != items[0]) {
|
||||||
|
std::cerr << "bad-first " << first << "\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!second || second != items[2]) {
|
||||||
|
std::cerr << "bad-second " << second << "\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (eof) {
|
||||||
|
std::cerr << "bad-eof " << eof << "\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, const char *argv[]) {
|
||||||
|
(void)argc;
|
||||||
|
(void)argv;
|
||||||
|
|
||||||
|
mdbx_setup_debug_nofmt(MDBX_LOG_NOTICE, MDBX_DBG_ASSERT, logger_nofmt,
|
||||||
|
log_buffer, sizeof(log_buffer));
|
||||||
|
|
||||||
|
const char *filename = "test-crunched-del";
|
||||||
|
mdbx::env::remove(filename);
|
||||||
|
|
||||||
|
std::vector<acase> testset;
|
||||||
|
// Там ключи разной длины - от 1 до 64 байт.
|
||||||
|
// Значения разной длины от 100 до 1000 байт.
|
||||||
|
testset.emplace_back(/* keylen_min */ 1, /* keylen_max */ 64,
|
||||||
|
/* datalen_min */ 100, /* datalen_max */ 4000,
|
||||||
|
/* dups_log2 */ 6);
|
||||||
|
// В одной таблице DupSort: path -> version_u64+data
|
||||||
|
// path - это префикс в дереве. Самые частые длины: 1-5 байт и 32-36 байт.
|
||||||
|
testset.emplace_back(1, 5, 100, 1000, 8);
|
||||||
|
testset.emplace_back(32, 36, 100, 1000, 7);
|
||||||
|
// В другой DupSort: timestamp_u64 -> path
|
||||||
|
testset.emplace_back(8, 8, 1, 5, 10);
|
||||||
|
testset.emplace_back(8, 8, 32, 36, 9);
|
||||||
|
|
||||||
|
mdbx::env_managed env(filename, mdbx::env_managed::create_parameters(),
|
||||||
|
mdbx::env::operate_parameters(42));
|
||||||
|
if (!simple(env) || !next_prev_current(env) || !outofrange_prev(env))
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
|
||||||
|
auto txn = env.start_write();
|
||||||
|
for (unsigned i = 0; i < testset.size(); ++i)
|
||||||
|
create_and_fill(txn, testset[i], i);
|
||||||
|
txn.commit();
|
||||||
|
|
||||||
|
// mdbx_setup_debug_nofmt(MDBX_LOG_TRACE, MDBX_DBG_AUDIT | MDBX_DBG_ASSERT,
|
||||||
|
// logger_nofmt, log_buffer, sizeof(log_buffer));
|
||||||
|
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;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user