diff options
Diffstat (limited to 'libs/libmdbx/src/test')
-rw-r--r-- | libs/libmdbx/src/test/config.cc | 22 | ||||
-rw-r--r-- | libs/libmdbx/src/test/config.h | 118 | ||||
-rw-r--r-- | libs/libmdbx/src/test/dump-load.sh | 40 | ||||
-rw-r--r-- | libs/libmdbx/src/test/hill.cc | 41 | ||||
-rw-r--r-- | libs/libmdbx/src/test/keygen.cc | 32 | ||||
-rw-r--r-- | libs/libmdbx/src/test/keygen.h | 21 | ||||
-rw-r--r-- | libs/libmdbx/src/test/long_stochastic.sh | 321 | ||||
-rw-r--r-- | libs/libmdbx/src/test/main.cc | 21 | ||||
-rw-r--r-- | libs/libmdbx/src/test/nested.cc | 130 | ||||
-rw-r--r-- | libs/libmdbx/src/test/osal-unix.cc | 4 | ||||
-rw-r--r-- | libs/libmdbx/src/test/test.cc | 38 | ||||
-rw-r--r-- | libs/libmdbx/src/test/test.h | 59 | ||||
-rw-r--r-- | libs/libmdbx/src/test/ttl.cc | 224 | ||||
-rw-r--r-- | libs/libmdbx/src/test/utils.cc | 14 | ||||
-rw-r--r-- | libs/libmdbx/src/test/utils.h | 4 | ||||
-rw-r--r-- | libs/libmdbx/src/test/valgrind_suppress.txt | 518 |
16 files changed, 1202 insertions, 405 deletions
diff --git a/libs/libmdbx/src/test/config.cc b/libs/libmdbx/src/test/config.cc index f7f960c579..5e1979718a 100644 --- a/libs/libmdbx/src/test/config.cc +++ b/libs/libmdbx/src/test/config.cc @@ -395,6 +395,8 @@ void dump(const char *title) { i->params.keygen.split, i->params.keygen.width - i->params.keygen.split); log_verbose("keygen.seed: %u\n", i->params.keygen.seed); + log_verbose("keygen.zerofill: %s\n", + i->params.keygen.zero_fill ? "Yes" : "No"); log_verbose("key: minlen %u, maxlen %u\n", i->params.keylen_min, i->params.keylen_max); log_verbose("data: minlen %u, maxlen %u\n", i->params.datalen_min, @@ -450,18 +452,14 @@ using namespace config; actor_config::actor_config(actor_testcase testcase, const actor_params ¶ms, unsigned space_id, unsigned wait4id) - : params(params) { - this->space_id = space_id; - this->actor_id = 1 + (unsigned)global::actors.size(); - this->testcase = testcase; - this->wait4id = wait4id; - signal_nops = 0; -} + : actor_config_pod(1 + unsigned(global::actors.size()), testcase, space_id, + wait4id), + params(params) {} const std::string actor_config::serialize(const char *prefix) const { simple_checksum checksum; - std::string result; + if (prefix) result.append(prefix); @@ -473,13 +471,13 @@ const std::string actor_config::serialize(const char *prefix) const { result.append(params.pathname_log); result.push_back('|'); - static_assert(std::is_pod<actor_params_pod>::value, + static_assert(std::is_trivially_copyable<actor_params_pod>::value, "actor_params_pod should by POD"); result.append(data2hex(static_cast<const actor_params_pod *>(¶ms), sizeof(actor_params_pod), checksum)); result.push_back('|'); - static_assert(std::is_pod<actor_config_pod>::value, + static_assert(std::is_trivially_copyable<actor_config_pod>::value, "actor_config_pod should by POD"); result.append(data2hex(static_cast<const actor_config_pod *>(this), sizeof(actor_config_pod), checksum)); @@ -525,7 +523,7 @@ bool actor_config::deserialize(const char *str, actor_config &config) { TRACE("<< actor_config::deserialize: slash-3\n"); return false; } - static_assert(std::is_pod<actor_params_pod>::value, + static_assert(std::is_trivially_copyable<actor_params_pod>::value, "actor_params_pod should by POD"); if (!hex2data(str, slash, static_cast<actor_params_pod *>(&config.params), sizeof(actor_params_pod), checksum)) { @@ -540,7 +538,7 @@ bool actor_config::deserialize(const char *str, actor_config &config) { TRACE("<< actor_config::deserialize: slash-4\n"); return false; } - static_assert(std::is_pod<actor_config_pod>::value, + static_assert(std::is_trivially_copyable<actor_config_pod>::value, "actor_config_pod should by POD"); if (!hex2data(str, slash, static_cast<actor_config_pod *>(&config), sizeof(actor_config_pod), checksum)) { diff --git a/libs/libmdbx/src/test/config.h b/libs/libmdbx/src/test/config.h index 7efd09dd54..2ab4742a2e 100644 --- a/libs/libmdbx/src/test/config.h +++ b/libs/libmdbx/src/test/config.h @@ -124,7 +124,8 @@ inline bool parse_option_intptr(int argc, char *const argv[], int &narg, #pragma pack(push, 1) struct keygen_params_pod { - /* Параметры генератора пар key-value. + /* Параметры генератора пар key-value. Также может быть полезным описание + * алгоритма генерации в keygen.h * * Ключи и значения генерируются по задаваемым параметрам на основе "плоской" * исходной координаты. При этом, в общем случае, в процессе тестов исходная @@ -141,20 +142,20 @@ struct keygen_params_pod { * - libmdbx поддерживает два существенно различающихся вида таблиц, * "уникальные" (без дубликатов и без multi-value), и так называемые * "с дубликатами" (c multi-value). - * - Для таблиц "без дубликатов" только размер связанных к ключами значений + * - Для таблиц "без дубликатов" только размер связанных с ключами значений * (данных) оказывает влияния на работу движка, непосредственно содержимое * данных не анализируется движком и не оказывает влияния на его работу. * - Для таблиц "с дубликатами", при наличии более одного значения для * некоторого ключа, формируется дочернее btree-поддерево. Это дерево - * формируется в отдельном "кусте" страниц и обслуживается независимо - * от окружения родительского ключа. + * формируется во вложенной странице или отдельном "кусте" страниц, + * и обслуживается независимо от окружения родительского ключа. * - Таким образом, паттерн генерации значений имеет смысл только для * таблиц "с дубликатами" и только в контексте одного значения ключа. - * Иначе говоря, нет смысла в со-координации генерации паттернов для - * ключей и значений. Более того, генерацию значений всегда необходимо - * рассматривать в контексте связки с одним значением ключа. - * - Тем не менее, во всех случаях достаточно важным является равномерная - * всех возможных сочетаний длин ключей и данных. + * Иначе говоря, не имеет смысла взаимная координация при генерации + * значений для разных ключей. Поэтому генерацию значений следует + * рассматривать только в контексте связки с одним значением ключа. + * - Тем не менее, во всех случаях достаточно важным является равновероятное + * распределение всех возможных сочетаний длин ключей и данных. * * width: * Большинство тестов предполагают создание или итерирование некоторого @@ -166,7 +167,7 @@ struct keygen_params_pod { * степени двойки. Это ограничение можно снять, но ценой увеличения * вычислительной сложности, включая потерю простоты и прозрачности. * - * С другой стороны, не-битовый width может быть полезен: + * С другой стороны, не-n-битовый width может быть полезен: * - Позволит генерировать ключи/значения в точно задаваемом диапазоне. * Например, перебрать в псевдо-случайном порядке 10001 значение. * - Позволит поровну разделять заданное пространство (диапазон) @@ -203,7 +204,7 @@ struct keygen_params_pod { * rotate и offset: * Для проверки слияния и разделения страниц внутри движка требуются * генерация ключей/значений в виде не-смежных последовательностей, как-бы - * в виде "пунктира", который постепенно заполняет весь заданных диапазон. + * в виде "пунктира", который постепенно заполняет весь заданный диапазон. * * Параметры позволяют генерировать такой "пунктир". Соответственно rotate * задает циклический сдвиг вправо, а offset задает смещение, точнее говоря @@ -224,55 +225,62 @@ struct keygen_params_pod { * номера будет отрезано для генерации значения. */ - uint8_t width; - uint8_t mesh; - uint8_t rotate; - uint8_t split; - uint32_t seed; - uint64_t offset; - keygen_case keycase; + uint8_t width{0}; + uint8_t mesh{0}; + uint8_t rotate{0}; + uint8_t split{0}; + uint32_t seed{0}; + uint64_t offset{0}; + keygen_case keycase{kc_random}; + bool zero_fill{false}; }; struct actor_params_pod { - unsigned mode_flags; - unsigned table_flags; - intptr_t size_lower; - intptr_t size_now; - intptr_t size_upper; - int shrink_threshold; - int growth_step; - int pagesize; - - unsigned test_duration; - unsigned test_nops; - unsigned nrepeat; - unsigned nthreads; - - unsigned keylen_min, keylen_max; - unsigned datalen_min, datalen_max; - - unsigned batch_read; - unsigned batch_write; - - unsigned delaystart; - unsigned waitfor_nops; - unsigned inject_writefaultn; - - unsigned max_readers; - unsigned max_tables; + unsigned mode_flags{0}; + unsigned table_flags{0}; + intptr_t size_lower{0}; + intptr_t size_now{0}; + intptr_t size_upper{0}; + int shrink_threshold{0}; + int growth_step{0}; + int pagesize{0}; + + unsigned test_duration{0}; + unsigned test_nops{0}; + unsigned nrepeat{0}; + unsigned nthreads{0}; + + unsigned keylen_min{0}, keylen_max{0}; + unsigned datalen_min{0}, datalen_max{0}; + + unsigned batch_read{0}; + unsigned batch_write{0}; + + unsigned delaystart{0}; + unsigned waitfor_nops{0}; + unsigned inject_writefaultn{0}; + + unsigned max_readers{0}; + unsigned max_tables{0}; keygen_params_pod keygen; - uint8_t loglevel; - bool drop_table; - bool ignore_dbfull; - bool speculum; + uint8_t loglevel{0}; + bool drop_table{0}; + bool ignore_dbfull{0}; + bool speculum{0}; }; struct actor_config_pod { - unsigned actor_id, space_id; - actor_testcase testcase; - unsigned wait4id; - unsigned signal_nops; + unsigned actor_id{0}, space_id{0}; + actor_testcase testcase{ac_none}; + unsigned wait4id{0}; + unsigned signal_nops{0}; + + actor_config_pod() = default; + actor_config_pod(unsigned actor_id, actor_testcase testcase, + unsigned space_id, unsigned wait4id) + : actor_id(actor_id), space_id(space_id), testcase(testcase), + wait4id(wait4id) {} }; #pragma pack(pop) @@ -286,8 +294,9 @@ void dump(const char *title = "config-dump: "); struct actor_params : public config::actor_params_pod { std::string pathname_log; std::string pathname_db; - void set_defaults(const std::string &tmpdir); + actor_params() = default; + void set_defaults(const std::string &tmpdir); unsigned mdbx_keylen_min() const; unsigned mdbx_keylen_max() const; unsigned mdbx_datalen_min() const; @@ -299,10 +308,11 @@ struct actor_config : public config::actor_config_pod { bool wanna_event4signalling() const { return true /* TODO ? */; } + actor_config() = default; actor_config(actor_testcase testcase, const actor_params ¶ms, unsigned space_id, unsigned wait4id); - actor_config(const char *str) { + actor_config(const char *str) : actor_config() { if (!deserialize(str, *this)) failure("Invalid internal parameter '%s'\n", str); } diff --git a/libs/libmdbx/src/test/dump-load.sh b/libs/libmdbx/src/test/dump-load.sh new file mode 100644 index 0000000000..55fa5c7f33 --- /dev/null +++ b/libs/libmdbx/src/test/dump-load.sh @@ -0,0 +1,40 @@ +#!/usr/bin/env bash + +echo "------------------------------------------------------------------------------" + +if [ -z "$1" ]; then + echo "No mdbx-db pathname given"; + exit 2 +elif [ ! -e "$1" ]; then + echo "The mdbx-db '$1' don't exists"; + exit 2 +else + echo ">>>>>>>>>> $1" + RECO="$1.recovered" + rm -f dump1.txt dump2.txt "$RECO" + if ./mdbx_chk "$1"; then + echo ">>>>>>>>>> SOURCE VALID" + (./mdbx_dump -a "$1" > dump1.txt && \ + ./mdbx_load -nf dump1.txt "$RECO" && \ + ./mdbx_chk "$RECO" && \ + echo ">>>>>>>>>> DUMP/LOAD/CHK OK") || (echo ">>>>>>>>>> DUMP/LOAD/CHK FAILED"; exit 1) + REMOVE_RECO=1 + elif ./mdbx_chk -i "$1"; then + echo ">>>>>>>>>> SOURCE HAS WRONG-ORDER, TRY RECOVERY" + (./mdbx_dump -a "$1" > dump1.txt && \ + ./mdbx_load -anf dump1.txt "$RECO" && \ + ./mdbx_chk -i "$RECO" && \ + echo ">>>>>>>>>> DUMP/LOAD/CHK OK") || (echo ">>>>>>>>>> DUMP/LOAD/CHK FAILED"; exit 1) + REMOVE_RECO=0 + else + echo ">>>>>>>>>> SOURCE CORRUPTED, TRY RECOVERY" + (./mdbx_dump -ar "$1" > dump1.txt && \ + ./mdbx_load -ranf dump1.txt "$RECO" && \ + ./mdbx_chk -i "$RECO" && \ + echo ">>>>>>>>>> DUMP/LOAD/CHK OK") || (echo ">>>>>>>>>> DUMP/LOAD/CHK FAILED"; exit 1) + REMOVE_RECO=0 + fi + ./mdbx_dump -a "$RECO" > dump2.txt && diff -u dump1.txt dump2.txt && \ + rm -f dump1.txt dump2.txt && [ $REMOVE_RECO -ne 0 ] && rm -f "$RECO" + exit 0 +fi diff --git a/libs/libmdbx/src/test/hill.cc b/libs/libmdbx/src/test/hill.cc index 37f748c9f7..efc43abe2a 100644 --- a/libs/libmdbx/src/test/hill.cc +++ b/libs/libmdbx/src/test/hill.cc @@ -232,6 +232,47 @@ bool testcase_hill::run() { } } + if (txn_guard) { + MDBX_stat stat; + err = mdbx_dbi_stat(txn_guard.get(), dbi, &stat, sizeof(stat)); + if (unlikely(err != MDBX_SUCCESS)) + failure_perror("mdbx_dbi_stat()", err); + + uint32_t nested_deepmask; + err = mdbx_dbi_dupsort_depthmask(txn_guard.get(), dbi, &nested_deepmask); + if (unlikely(err != MDBX_SUCCESS && err != MDBX_RESULT_TRUE)) + failure_perror("mdbx_dbi_stat_nested_deepmask()", err); + + if (err != MDBX_SUCCESS) { + log_notice("hill: reached %d tree depth", stat.ms_depth); + } else { + std::string str; + int prev = -2, i = 0; + do { + while (!(nested_deepmask & 1)) + ++i, nested_deepmask >>= 1; + if (prev + 1 == i) { + if (str.back() != '-') + str.push_back('-'); + prev = i; + continue; + } + if (!str.empty()) { + if (str.back() == '-') + str.append(std::to_string(prev)); + str.push_back(','); + } + str.append(std::to_string(i)); + prev = i; + } while (++i, nested_deepmask >>= 1); + if (str.back() == '-') + str.append(std::to_string(prev)); + + log_notice("hill: reached %d tree depth & %s sub-tree depth(s)", + stat.ms_depth, str.c_str()); + } + } + while (serial_count > 1) { if (unlikely(!keyvalue_maker.increment(serial_count, -2))) failure("downhill: unexpected key-space underflow"); diff --git a/libs/libmdbx/src/test/keygen.cc b/libs/libmdbx/src/test/keygen.cc index 374537dd6e..05070afe02 100644 --- a/libs/libmdbx/src/test/keygen.cc +++ b/libs/libmdbx/src/test/keygen.cc @@ -78,9 +78,11 @@ void __hot maker::pair(serial_t serial, const buffer &key, buffer &value, assert(mapping.mesh <= mapping.width); assert(mapping.rotate <= mapping.width); assert(mapping.offset <= mask(mapping.width)); - assert(!(key_essentials.flags & - ~(MDBX_INTEGERKEY | MDBX_REVERSEKEY | MDBX_DUPSORT))); - assert(!(value_essentials.flags & ~(MDBX_INTEGERDUP | MDBX_REVERSEDUP))); + assert( + !(key_essentials.flags & ~(essentials::prng_fill_flag | MDBX_INTEGERKEY | + MDBX_REVERSEKEY | MDBX_DUPSORT))); + assert(!(value_essentials.flags & + ~(essentials::prng_fill_flag | MDBX_INTEGERDUP | MDBX_REVERSEDUP))); log_trace("keygen-pair: serial %" PRIu64 ", data-age %" PRIu64, serial, value_age); @@ -213,6 +215,11 @@ void maker::setup(const config::actor_params_pod &actor, unsigned actor_id, (uint32_t)actor.datalen_max, (uint32_t)mdbx_limits_valsize_max(actor.pagesize, key_essentials.flags)); + if (!actor.keygen.zero_fill) { + key_essentials.flags |= essentials::prng_fill_flag; + value_essentials.flags |= essentials::prng_fill_flag; + } + (void)thread_number; mapping = actor.keygen; salt = (actor.keygen.seed + actor_id) * UINT64_C(14653293970879851569); @@ -298,6 +305,10 @@ void __hot maker::mk_begin(const serial_t serial, const essentials ¶ms, void __hot maker::mk_continue(const serial_t serial, const essentials ¶ms, result &out) { + static_assert((essentials::prng_fill_flag & + (MDBX_DUPSORT | MDBX_DUPFIXED | MDBX_INTEGERKEY | + MDBX_INTEGERDUP | MDBX_REVERSEKEY | MDBX_REVERSEDUP)) == 0, + "WTF?"); out.value.iov_base = out.bytes; if (params.flags & (MDBX_INTEGERKEY | MDBX_INTEGERDUP)) { assert(params.maxlen == params.minlen); @@ -308,7 +319,11 @@ void __hot maker::mk_continue(const serial_t serial, const essentials ¶ms, out.u32 = (uint32_t)serial; } else if (params.flags & (MDBX_REVERSEKEY | MDBX_REVERSEDUP)) { if (out.value.iov_len > 8) { - memset(out.bytes, '\0', out.value.iov_len - 8); + if (params.flags & essentials::prng_fill_flag) { + uint64_t state = serial ^ UINT64_C(0x41803711c9b75f19); + prng_fill(state, out.bytes, out.value.iov_len - 8); + } else + memset(out.bytes, '\0', out.value.iov_len - 8); unaligned::store(out.bytes + out.value.iov_len - 8, htobe64(serial)); } else { out.u64 = htobe64(serial); @@ -317,8 +332,13 @@ void __hot maker::mk_continue(const serial_t serial, const essentials ¶ms, } } else { out.u64 = htole64(serial); - if (out.value.iov_len > 8) - memset(out.bytes + 8, '\0', out.value.iov_len - 8); + if (out.value.iov_len > 8) { + if (params.flags & essentials::prng_fill_flag) { + uint64_t state = serial ^ UINT64_C(0x923ab47b7ee6f6e4); + prng_fill(state, out.bytes + 8, out.value.iov_len - 8); + } else + memset(out.bytes + 8, '\0', out.value.iov_len - 8); + } } assert(out.value.iov_len >= params.minlen); diff --git a/libs/libmdbx/src/test/keygen.h b/libs/libmdbx/src/test/keygen.h index b5674f1ca6..c36cc1a2e7 100644 --- a/libs/libmdbx/src/test/keygen.h +++ b/libs/libmdbx/src/test/keygen.h @@ -23,9 +23,8 @@ namespace keygen { /* Под "генерацией ключей" здесь понимается генерация обоих значений для * пар key-value, т.е. не только ключей, но и ассоциированных с ними данных. - */ - -/* Генерацию ключей нельзя отнести к простым задачам, так как требования + * + * Генерацию ключей нельзя отнести к простым задачам, так как требования * примерно следующие: * - генерация разного количества уникальных ключей различной длины * в задаваемом диапазоне; @@ -67,7 +66,8 @@ namespace keygen { * 1) смещение (сложение) по модулю; * 2) циклический сдвиг; * 3) добавление абсолютного смещения (базы); - */ + * + * Также см. описание параметров генератора ключей и значений в config.h */ typedef uint64_t serial_t; @@ -103,13 +103,14 @@ buffer alloc(size_t limit); class maker { config::keygen_params_pod mapping; - serial_t base; - serial_t salt; + serial_t base{0}; + serial_t salt{0}; struct essentials { - uint16_t minlen; - uint16_t flags; - uint32_t maxlen; + uint16_t minlen{0}; + enum { prng_fill_flag = 1 }; + uint16_t flags{0}; + uint32_t maxlen{0}; } key_essentials, value_essentials; static void mk_begin(const serial_t serial, const essentials ¶ms, @@ -122,8 +123,6 @@ class maker { } public: - maker() { memset(this, 0, sizeof(*this)); } - void pair(serial_t serial, const buffer &key, buffer &value, serial_t value_age, const bool keylen_changeable); void setup(const config::actor_params_pod &actor, unsigned actor_id, diff --git a/libs/libmdbx/src/test/long_stochastic.sh b/libs/libmdbx/src/test/long_stochastic.sh index 58ec6d5c6e..7141ee62c5 100644 --- a/libs/libmdbx/src/test/long_stochastic.sh +++ b/libs/libmdbx/src/test/long_stochastic.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash -if ! which make cc c++ tee lz4 >/dev/null; then - echo "Please install the following prerequisites: make cc c++ tee lz4" >&2 - exit 1 +if ! which make cc c++ tee lz4 banner >/dev/null; then + echo "Please install the following prerequisites: make cc c++ tee lz4 banner" >&2 + exit 1 fi set -euo pipefail @@ -19,72 +19,72 @@ UNAME="$(uname -s 2>/dev/null || echo Unknown)" # 1. clean data from prev runs and examine available RAM if [[ -v VALGRIND && ! -z "$VALGRIND" ]]; then - rm -f valgrind-*.log + rm -f valgrind-*.log else - VALGRIND=time + VALGRIND=time fi WANNA_MOUNT=0 case ${UNAME} in - Linux) - MAKE=make - if [[ ! -v TESTDB_DIR || -z "$TESTDB_DIR" ]]; then - for old_test_dir in $(ls -d /dev/shm/mdbx-test.[0-9]*); do - rm -rf $old_test_dir - done - TESTDB_DIR="/dev/shm/mdbx-test.$$" - fi - mkdir -p $TESTDB_DIR && rm -f $TESTDB_DIR/* - - if LC_ALL=C free | grep -q -i available; then - ram_avail_mb=$(($(LC_ALL=C free | grep -i Mem: | tr -s [:blank:] ' ' | cut -d ' ' -f 7) / 1024)) - else - ram_avail_mb=$(($(LC_ALL=C free | grep -i Mem: | tr -s [:blank:] ' ' | cut -d ' ' -f 4) / 1024)) - fi - ;; - - FreeBSD) - MAKE=gmake - if [[ ! -v TESTDB_DIR || -z "$TESTDB_DIR" ]]; then - for old_test_dir in $(ls -d /tmp/mdbx-test.[0-9]*); do - umount $old_test_dir && rm -r $old_test_dir - done - TESTDB_DIR="/tmp/mdbx-test.$$" - rm -rf $TESTDB_DIR && mkdir -p $TESTDB_DIR - WANNA_MOUNT=1 - else - mkdir -p $TESTDB_DIR && rm -f $TESTDB_DIR/* - fi - - ram_avail_mb=$(($(LC_ALL=C vmstat -s | grep -ie '[0-9] pages free$' | cut -d p -f 1) * ($(LC_ALL=C vmstat -s | grep -ie '[0-9] bytes per page$' | cut -d b -f 1) / 1024) / 1024)) - ;; - - Darwin) - MAKE=make - if [[ ! -v TESTDB_DIR || -z "$TESTDB_DIR" ]]; then - for vol in $(ls -d /Volumes/mdx[0-9]*[0-9]tst); do - disk=$(mount | grep $vol | cut -d ' ' -f 1) - echo "umount: volume $vol disk $disk" - hdiutil unmount $vol -force - hdiutil detach $disk - done - TESTDB_DIR="/Volumes/mdx$$tst" - WANNA_MOUNT=1 - else - mkdir -p $TESTDB_DIR && rm -f $TESTDB_DIR/* - fi - - pagesize=$(($(LC_ALL=C vm_stat | grep -o 'page size of [0-9]\+ bytes' | cut -d' ' -f 4) / 1024)) - freepages=$(LC_ALL=C vm_stat | grep '^Pages free:' | grep -o '[0-9]\+\.$' | cut -d'.' -f 1) - ram_avail_mb=$((pagesize * freepages / 1024)) - echo "pagesize ${pagesize}K, freepages ${freepages}, ram_avail_mb ${ram_avail_mb}" - - ;; - - *) - echo "FIXME: ${UNAME} not supported by this script" - exit 2 - ;; + Linux) + MAKE=make + if [[ ! -v TESTDB_DIR || -z "$TESTDB_DIR" ]]; then + for old_test_dir in $(ls -d /dev/shm/mdbx-test.[0-9]*); do + rm -rf $old_test_dir + done + TESTDB_DIR="/dev/shm/mdbx-test.$$" + fi + mkdir -p $TESTDB_DIR && rm -f $TESTDB_DIR/* + + if LC_ALL=C free | grep -q -i available; then + ram_avail_mb=$(($(LC_ALL=C free | grep -i Mem: | tr -s [:blank:] ' ' | cut -d ' ' -f 7) / 1024)) + else + ram_avail_mb=$(($(LC_ALL=C free | grep -i Mem: | tr -s [:blank:] ' ' | cut -d ' ' -f 4) / 1024)) + fi + ;; + + FreeBSD) + MAKE=gmake + if [[ ! -v TESTDB_DIR || -z "$TESTDB_DIR" ]]; then + for old_test_dir in $(ls -d /tmp/mdbx-test.[0-9]*); do + umount $old_test_dir && rm -r $old_test_dir + done + TESTDB_DIR="/tmp/mdbx-test.$$" + rm -rf $TESTDB_DIR && mkdir -p $TESTDB_DIR + WANNA_MOUNT=1 + else + mkdir -p $TESTDB_DIR && rm -f $TESTDB_DIR/* + fi + + ram_avail_mb=$(($(LC_ALL=C vmstat -s | grep -ie '[0-9] pages free$' | cut -d p -f 1) * ($(LC_ALL=C vmstat -s | grep -ie '[0-9] bytes per page$' | cut -d b -f 1) / 1024) / 1024)) + ;; + + Darwin) + MAKE=make + if [[ ! -v TESTDB_DIR || -z "$TESTDB_DIR" ]]; then + for vol in $(ls -d /Volumes/mdx[0-9]*[0-9]tst); do + disk=$(mount | grep $vol | cut -d ' ' -f 1) + echo "umount: volume $vol disk $disk" + hdiutil unmount $vol -force + hdiutil detach $disk + done + TESTDB_DIR="/Volumes/mdx$$tst" + WANNA_MOUNT=1 + else + mkdir -p $TESTDB_DIR && rm -f $TESTDB_DIR/* + fi + + pagesize=$(($(LC_ALL=C vm_stat | grep -o 'page size of [0-9]\+ bytes' | cut -d' ' -f 4) / 1024)) + freepages=$(LC_ALL=C vm_stat | grep '^Pages free:' | grep -o '[0-9]\+\.$' | cut -d'.' -f 1) + ram_avail_mb=$((pagesize * freepages / 1024)) + echo "pagesize ${pagesize}K, freepages ${freepages}, ram_avail_mb ${ram_avail_mb}" + + ;; + + *) + echo "FIXME: ${UNAME} not supported by this script" + exit 2 + ;; esac ############################################################################### @@ -93,8 +93,8 @@ esac echo "=== ${ram_avail_mb}M RAM available" ram_reserve4logs_mb=1234 if [ $ram_avail_mb -lt $ram_reserve4logs_mb ]; then - echo "=== At least ${ram_reserve4logs_mb}Mb RAM required" - exit 3 + echo "=== At least ${ram_reserve4logs_mb}Mb RAM required" + exit 3 fi # @@ -116,36 +116,36 @@ fi # system immediately, as well some space is required for logs. # db_size_mb=$(((ram_avail_mb - ram_reserve4logs_mb) / 4)) -if [ $db_size_mb -gt 3072 ]; then - db_size_mb=3072 +if [ $db_size_mb -gt 17408 ]; then + db_size_mb=17408 fi echo "=== use ${db_size_mb}M for DB" ############################################################################### # 3. Create test-directory in ramfs/tmpfs, i.e. create/format/mount if required case ${UNAME} in - Linux) - ;; - - FreeBSD) - if [[ WANNA_MOUNT ]]; then - mount -t tmpfs tmpfs $TESTDB_DIR - fi - ;; - - Darwin) - if [[ WANNA_MOUNT ]]; then - ramdisk_size_mb=$((42 + db_size_mb * 2 + ram_reserve4logs_mb)) - number_of_sectors=$((ramdisk_size_mb * 2048)) - ramdev=$(hdiutil attach -nomount ram://${number_of_sectors}) - diskutil erasevolume ExFAT "mdx$$tst" ${ramdev} - fi - ;; - - *) - echo "FIXME: ${UNAME} not supported by this script" - exit 2 - ;; + Linux) + ;; + + FreeBSD) + if [[ WANNA_MOUNT ]]; then + mount -t tmpfs tmpfs $TESTDB_DIR + fi + ;; + + Darwin) + if [[ WANNA_MOUNT ]]; then + ramdisk_size_mb=$((42 + db_size_mb * 2 + ram_reserve4logs_mb)) + number_of_sectors=$((ramdisk_size_mb * 2048)) + ramdev=$(hdiutil attach -nomount ram://${number_of_sectors}) + diskutil erasevolume ExFAT "mdx$$tst" ${ramdev} + fi + ;; + + *) + echo "FIXME: ${UNAME} not supported by this script" + exit 2 + ;; esac ############################################################################### @@ -157,64 +157,115 @@ rm -f ${TESTDB_DIR}/* ############################################################################### # 5. run stochastic iterations -function rep9 { printf "%*s" $1 '' | tr ' ' '9'; } function join { local IFS="$1"; shift; echo "$*"; } function bit2option { local -n arr=$1; (( ($2&(1<<$3)) != 0 )) && echo -n '+' || echo -n '-'; echo "${arr[$3]}"; } options=(writemap coalesce lifo notls) function bits2list { - local -n arr=$1 - local i - local list=() - for ((i=0; i<${#arr[@]}; ++i)) do - list[$i]=$(bit2option $1 $2 $i) - done - join , "${list[@]}" + local -n arr=$1 + local i + local list=() + for ((i=0; i<${#arr[@]}; ++i)) do + list[$i]=$(bit2option $1 $2 $i) + done + join , "${list[@]}" } function probe { - echo "=============================================== $(date)" - echo "${caption}: $*" - rm -f ${TESTDB_DIR}/* \ - && ${VALGRIND} ./mdbx_test --ignore-dbfull --repeat=42 --pathname=${TESTDB_DIR}/long.db "$@" | lz4 > ${TESTDB_DIR}/long.log.lz4 \ - && ${VALGRIND} ./mdbx_chk -nvvv ${TESTDB_DIR}/long.db | tee ${TESTDB_DIR}/long-chk.log \ - && ([ ! -e ${TESTDB_DIR}/long.db-copy ] || ${VALGRIND} ./mdbx_chk -nvvv ${TESTDB_DIR}/long.db-copy | tee ${TESTDB_DIR}/long-chk-copy.log) \ - || (echo "FAILED"; exit 1) + echo "----------------------------------------------- $(date)" + echo "${caption}: $*" + rm -f ${TESTDB_DIR}/* \ + && ${VALGRIND} ./mdbx_test ${speculum} --ignore-dbfull --repeat=3 --pathname=${TESTDB_DIR}/long.db --cleanup-after=no "$@" \ + | tee >(lz4 > ${TESTDB_DIR}/long.log.lz4) | grep -e reach -e achieve \ + && ${VALGRIND} ./mdbx_chk ${TESTDB_DIR}/long.db | tee ${TESTDB_DIR}/long-chk.log \ + && ([ ! -e ${TESTDB_DIR}/long.db-copy ] || ${VALGRIND} ./mdbx_chk ${TESTDB_DIR}/long.db-copy | tee ${TESTDB_DIR}/long-chk-copy.log) \ + || (echo "FAILED"; exit 1) } #------------------------------------------------------------------------------ count=0 -for nops in $(seq 2 6); do - for ((wbatch=nops-1; wbatch > 0; --wbatch)); do - loops=$(((111 >> nops) / nops + 3)) - for ((rep=0; rep++ < loops; )); do - for ((bits=2**${#options[@]}; --bits >= 0; )); do - seed=$(($(date +%s) + RANDOM)) - caption="Probe #$((++count)) int-key,w/o-dups, repeat ${rep} of ${loops}" probe \ - --pagesize=min --size-upper=${db_size_mb}M --table=+key.integer,-data.dups --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=1111 \ - --nops=$( rep9 $nops ) --batch.write=$( rep9 $wbatch ) --mode=$(bits2list options $bits) \ - --keygen.seed=${seed} basic - caption="Probe #$((++count)) int-key,with-dups, repeat ${rep} of ${loops}" probe \ - --pagesize=min --size-upper=${db_size_mb}M --table=+key.integer,+data.dups --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=max \ - --nops=$( rep9 $nops ) --batch.write=$( rep9 $wbatch ) --mode=$(bits2list options $bits) \ - --keygen.seed=${seed} basic - caption="Probe #$((++count)) int-key,int-data, repeat ${rep} of ${loops}" probe \ - --pagesize=min --size-upper=${db_size_mb}M --table=+key.integer,+data.integer --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=max \ - --nops=$( rep9 $nops ) --batch.write=$( rep9 $wbatch ) --mode=$(bits2list options $bits) \ - --keygen.seed=${seed} basic - caption="Probe #$((++count)) w/o-dups, repeat ${rep} of ${loops}" probe \ - --pagesize=min --size-upper=${db_size_mb}M --table=-data.dups --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=1111 \ - --nops=$( rep9 $nops ) --batch.write=$( rep9 $wbatch ) --mode=$(bits2list options $bits) \ - --keygen.seed=${seed} basic - caption="Probe #$((++count)) with-dups, repeat ${rep} of ${loops}" probe \ - --pagesize=min --size-upper=${db_size_mb}M --table=+data.dups --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=max \ - --nops=$( rep9 $nops ) --batch.write=$( rep9 $wbatch ) --mode=$(bits2list options $bits) \ - --keygen.seed=${seed} basic - done - done - done -done +cases='?' +for nops in 10 100 1000 10000 100000 1000000 10000000 100000000 1000000000; do + echo "=======================================================================" + wbatch=$((nops / 10 + 1)) + speculum=$([ $nops -le 1000 ] && echo '--speculum' || true) + while true; do + echo "=======================================================================" + banner "$nops / $wbatch" + subcase=0 + for ((bits=2**${#options[@]}; --bits >= 0; )); do + seed=$(($(date +%s) + RANDOM)) + + split=30 + caption="Probe #$((++count)) int-key,with-dups, split=${split}, case $((++subcase)) of ${cases}" probe \ + --pagesize=min --size-upper=${db_size_mb}M --table=+key.integer,+data.dups --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=max \ + --nops=$nops --batch.write=$wbatch --mode=$(bits2list options $bits) \ + --keygen.seed=${seed} basic + caption="Probe #$((++count)) int-key,int-data, split=${split}, case $((++subcase)) of ${cases}" probe \ + --pagesize=min --size-upper=${db_size_mb}M --table=+key.integer,+data.integer --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=max \ + --nops=$nops --batch.write=$wbatch --mode=$(bits2list options $bits) \ + --keygen.seed=${seed} basic + caption="Probe #$((++count)) with-dups, split=${split}, case $((++subcase)) of ${cases}" probe \ + --pagesize=min --size-upper=${db_size_mb}M --table=+data.dups --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=max \ + --nops=$nops --batch.write=$wbatch --mode=$(bits2list options $bits) \ + --keygen.seed=${seed} basic + + split=24 + caption="Probe #$((++count)) int-key,with-dups, split=${split}, case $((++subcase)) of ${cases}" probe \ + --pagesize=min --size-upper=${db_size_mb}M --table=+key.integer,+data.dups --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=max \ + --nops=$nops --batch.write=$wbatch --mode=$(bits2list options $bits) \ + --keygen.seed=${seed} basic + caption="Probe #$((++count)) int-key,int-data, split=${split}, case $((++subcase)) of ${cases}" probe \ + --pagesize=min --size-upper=${db_size_mb}M --table=+key.integer,+data.integer --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=max \ + --nops=$nops --batch.write=$wbatch --mode=$(bits2list options $bits) \ + --keygen.seed=${seed} basic + caption="Probe #$((++count)) with-dups, split=${split}, case $((++subcase)) of ${cases}" probe \ + --pagesize=min --size-upper=${db_size_mb}M --table=+data.dups --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=max \ + --nops=$nops --batch.write=$wbatch --mode=$(bits2list options $bits) \ + --keygen.seed=${seed} basic + + split=16 + caption="Probe #$((++count)) int-key,w/o-dups, split=${split}, case $((++subcase)) of ${cases}" probe \ + --pagesize=min --size-upper=${db_size_mb}M --table=+key.integer,-data.dups --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=1111 \ + --nops=$nops --batch.write=$wbatch --mode=$(bits2list options $bits) \ + --keygen.seed=${seed} basic + caption="Probe #$((++count)) int-key,with-dups, split=${split}, case $((++subcase)) of ${cases}" probe \ + --pagesize=min --size-upper=${db_size_mb}M --table=+key.integer,+data.dups --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=max \ + --nops=$nops --batch.write=$wbatch --mode=$(bits2list options $bits) \ + --keygen.seed=${seed} basic + caption="Probe #$((++count)) int-key,int-data, split=${split}, case $((++subcase)) of ${cases}" probe \ + --pagesize=min --size-upper=${db_size_mb}M --table=+key.integer,+data.integer --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=max \ + --nops=$nops --batch.write=$wbatch --mode=$(bits2list options $bits) \ + --keygen.seed=${seed} basic + caption="Probe #$((++count)) w/o-dups, split=${split}, case $((++subcase)) of ${cases}" probe \ + --pagesize=min --size-upper=${db_size_mb}M --table=-data.dups --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=1111 \ + --nops=$nops --batch.write=$wbatch --mode=$(bits2list options $bits) \ + --keygen.seed=${seed} basic + caption="Probe #$((++count)) with-dups, split=${split}, case $((++subcase)) of ${cases}" probe \ + --pagesize=min --size-upper=${db_size_mb}M --table=+data.dups --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=max \ + --nops=$nops --batch.write=$wbatch --mode=$(bits2list options $bits) \ + --keygen.seed=${seed} basic + + split=4 + caption="Probe #$((++count)) int-key,w/o-dups, split=${split}, case $((++subcase)) of ${cases}" probe \ + --pagesize=min --size-upper=${db_size_mb}M --table=+key.integer,-data.dups --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=1111 \ + --nops=$nops --batch.write=$wbatch --mode=$(bits2list options $bits) \ + --keygen.seed=${seed} basic + caption="Probe #$((++count)) int-key,int-data, split=${split}, case $((++subcase)) of ${cases}" probe \ + --pagesize=min --size-upper=${db_size_mb}M --table=+key.integer,+data.integer --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=max \ + --nops=$nops --batch.write=$wbatch --mode=$(bits2list options $bits) \ + --keygen.seed=${seed} basic + caption="Probe #$((++count)) w/o-dups, split=${split}, case $((++subcase)) of ${cases}" probe \ + --pagesize=min --size-upper=${db_size_mb}M --table=-data.dups --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=1111 \ + --nops=$nops --batch.write=$wbatch --mode=$(bits2list options $bits) \ + --keygen.seed=${seed} basic + done # options + cases="${subcase}" + wbatch=$(((wbatch > 9) ? wbatch / 10 : 1)) + if [ $wbatch -eq 1 -o $((nops / wbatch)) -gt 1000 ]; then break; fi + done # batch (write-ops per txn) +done # n-ops echo "=== ALL DONE ====================== $(date)" diff --git a/libs/libmdbx/src/test/main.cc b/libs/libmdbx/src/test/main.cc index 656d7c52f9..c6b15ea29b 100644 --- a/libs/libmdbx/src/test/main.cc +++ b/libs/libmdbx/src/test/main.cc @@ -81,6 +81,7 @@ void __noreturn usage(void) { " --keygen.width=N TBD (see the source code)\n" " --keygen.mesh=N TBD (see the source code)\n" " --keygen.seed=N TBD (see the source code)\n" + " --keygen.zerofill=yes|NO TBD (see the source code)\n" " --keygen.split=N TBD (see the source code)\n" " --keygen.rotate=N TBD (see the source code)\n" " --keygen.offset=N TBD (see the source code)\n" @@ -136,6 +137,7 @@ void actor_params::set_defaults(const std::string &tmpdir) { pagesize = -1; keygen.seed = 1; + keygen.zero_fill = false; keygen.keycase = kc_random; keygen.width = (table_flags & MDBX_DUPSORT) ? 32 : 64; keygen.mesh = keygen.width; @@ -209,7 +211,11 @@ std::string thunk_param(const actor_config &config) { void cleanup() { log_trace(">> cleanup"); - /* TODO: remove each database */ + for (const auto &db_path : global::databases) { + int err = osal_removefile(db_path); + if (err != MDBX_SUCCESS && err != MDBX_ENOFILE) + failure_perror(db_path.c_str(), err); + } log_trace("<< cleanup"); } @@ -317,19 +323,22 @@ int main(int argc, char *const argv[]) { continue; if (config::parse_option(argc, argv, narg, "keygen.width", - params.keygen.width, 1, 64)) + params.keygen.width, 8, 64)) continue; if (config::parse_option(argc, argv, narg, "keygen.mesh", - params.keygen.mesh, 1, 64)) + params.keygen.mesh, 0, 64)) continue; if (config::parse_option(argc, argv, narg, "keygen.seed", params.keygen.seed, config::no_scale)) continue; + if (config::parse_option(argc, argv, narg, "keygen.zerofill", + params.keygen.zero_fill)) + continue; if (config::parse_option(argc, argv, narg, "keygen.split", - params.keygen.split, 1, 64)) + params.keygen.split, 0, 63)) continue; if (config::parse_option(argc, argv, narg, "keygen.rotate", - params.keygen.rotate, 1, 64)) + params.keygen.rotate, 0, 63)) continue; if (config::parse_option(argc, argv, narg, "keygen.offset", params.keygen.offset, config::binary)) @@ -587,7 +596,7 @@ int main(int argc, char *const argv[]) { } log_notice("RESULT: %s\n", failed ? "Failed" : "Successful"); - if (global::config::cleanup_before) { + if (global::config::cleanup_after) { if (failed) log_verbose("skip cleanup"); else diff --git a/libs/libmdbx/src/test/nested.cc b/libs/libmdbx/src/test/nested.cc index beebd88071..85df6fa62b 100644 --- a/libs/libmdbx/src/test/nested.cc +++ b/libs/libmdbx/src/test/nested.cc @@ -15,6 +15,25 @@ #include "test.h" #include <cmath> +/* LY: тест "эмуляцией time-to-live" с вложенными транзакциями: + * - организуется "скользящее окно", которое каждую транзакцию сдвигается + * вперед вдоль числовой оси. + * - по переднему краю "скользящего окна" записи добавляются в таблицу, + * а по заднему удаляются. + * - количество добавляемых/удаляемых записей псевдослучайно зависит + * от номера транзакции, но с экспоненциальным распределением. + * - размер "скользящего окна" также псевдослучайно зависит от номера + * транзакции с "отрицательным" экспоненциальным распределением + * MAX_WIDTH - exp(rnd(N)), при уменьшении окна сдвигается задний + * край и удаляются записи позади него. + * - групповое добавление данных в начало окна и групповое удаление в конце, + * преимущественно выполняются во вложенных транзакциях. + * - меньшая часть запускаемых вложенных транзакций отменяется, с последующим + * продолжением итераций с состояния предыдущиего коммита. + * + * Таким образом имитируется поведение таблицы с TTL: записи стохастически + * добавляются и удаляются, и изредка происходят массивные удаления. */ + bool testcase_nested::setup() { if (!inherited::setup()) return false; @@ -56,18 +75,6 @@ bool testcase_nested::teardown() { return inherited::teardown() && ok; } -static unsigned edge2window(uint64_t edge, unsigned window_max) { - const double rnd = u64_to_double1(bleach64(edge)); - const unsigned window = window_max - std::lrint(std::pow(window_max, rnd)); - return window; -} - -static unsigned edge2count(uint64_t edge, unsigned count_max) { - const double rnd = u64_to_double1(prng64_map1_white(edge)); - const unsigned count = std::lrint(std::pow(count_max, rnd)); - return count; -} - void testcase_nested::push_txn() { MDBX_txn *txn; unsigned flags = @@ -75,11 +82,7 @@ void testcase_nested::push_txn() { int err = mdbx_txn_begin(db_guard.get(), txn_guard.get(), flags, &txn); if (unlikely(err != MDBX_SUCCESS)) failure_perror("mdbx_txn_begin(nested)", err); -#if __cplusplus >= 201703L - stack.emplace(txn, serial, fifo, speculum); -#else - stack.push(std::make_tuple(scoped_txn_guard(txn), serial, fifo, speculum)); -#endif + stack.emplace(scoped_txn_guard(txn), serial, fifo, speculum); std::swap(txn_guard, std::get<0>(stack.top())); log_verbose("begin level#%zu txn #%" PRIu64 ", flags 0x%x, serial %" PRIu64, stack.size(), mdbx_txn_id(txn), flags, serial); @@ -150,7 +153,8 @@ bool testcase_nested::stochastic_breakable_restart_with_nested( } bool testcase_nested::trim_tail(unsigned window_width) { - if (window_width) { + if (window_width || flipcoin()) { + clear_stepbystep_passed += window_width == 0; while (fifo.size() > window_width) { uint64_t tail_serial = fifo.back().first; const unsigned tail_count = fifo.back().second; @@ -171,6 +175,7 @@ bool testcase_nested::trim_tail(unsigned window_width) { if (unlikely(!keyvalue_maker.increment(tail_serial, 1))) failure("nested: unexpected key-space overflow on the tail"); } + report(tail_count); } } else if (!fifo.empty()) { log_verbose("nested: purge state %" PRIu64 " - %" PRIu64 ", fifo-items %zu", @@ -178,7 +183,8 @@ bool testcase_nested::trim_tail(unsigned window_width) { fifo.size()); db_table_clear(dbi, txn_guard.get()); fifo.clear(); - speculum.clear(); + clear_wholetable_passed += 1; + report(1); } return true; } @@ -198,6 +204,7 @@ retry: log_notice("nested: head-insert skip due '%s'", mdbx_strerror(err)); head_count = n; stochastic_breakable_restart_with_nested(true); + dbfull_passed += 1; goto retry; } failure_perror("mdbx_put(head)", err); @@ -205,7 +212,10 @@ retry: if (unlikely(!keyvalue_maker.increment(serial, 1))) { log_notice("nested: unexpected key-space overflow"); - return false; + keyspace_overflow = true; + head_count = n; + stochastic_breakable_restart_with_nested(true); + goto retry; } } @@ -213,48 +223,19 @@ retry: } bool testcase_nested::run() { - /* LY: тест "эмуляцией time-to-live" с вложенными транзакциями: - * - организуется "скользящее окно", которое каждую транзакцию сдвигается - * вперед вдоль числовой оси. - * - по переднему краю "скользящего окна" записи добавляются в таблицу, - * а по заднему удаляются. - * - количество добавляемых/удаляемых записей псевдослучайно зависит - * от номера транзакции, но с экспоненциальным распределением. - * - размер "скользящего окна" также псевдослучайно зависит от номера - * транзакции с "отрицательным" экспоненциальным распределением - * MAX_WIDTH - exp(rnd(N)), при уменьшении окна сдвигается задний - * край и удаляются записи позади него. - * - групповое добавление данных в начало окна и групповое уделение в конце, - * в половине случаев выполняются во вложенных транзакциях. - * - половина запускаемых вложенных транзакций отменяется, последуюим - * повтором групповой операции. - * - * Таким образом имитируется поведение таблицы с TTL: записи стохастически - * добавляются и удаляются, но изредка происходят массивные удаления. */ - - /* LY: для параметризации используем подходящие параметры, которые не имеют - * здесь смысла в первоначальном значении. */ - const unsigned window_max_lower = 333; - const unsigned count_max_lower = 333; - - const unsigned window_max = (config.params.batch_read > window_max_lower) - ? config.params.batch_read - : window_max_lower; - const unsigned count_max = (config.params.batch_write > count_max_lower) - ? config.params.batch_write - : count_max_lower; - log_verbose("nested: using `batch_read` value %u for window_max", window_max); - log_verbose("nested: using `batch_write` value %u for count_max", count_max); - uint64_t seed = prng64_map2_white(config.params.keygen.seed) + config.actor_id; - while (should_continue()) { + clear_wholetable_passed = 0; + clear_stepbystep_passed = 0; + dbfull_passed = 0; + unsigned loops = 0; + while (true) { const uint64_t salt = prng64_white(seed) /* mdbx_txn_id(txn_guard.get()) */; const unsigned window_width = - flipcoin_x4() ? 0 : edge2window(salt, window_max); - const unsigned head_count = edge2count(salt, count_max); - log_debug("nested: step #%zu (serial %" PRIu64 + (!should_continue() || flipcoin_x4()) ? 0 : edge2window(salt); + const unsigned head_count = edge2count(salt); + log_debug("nested: step #%" PRIu64 " (serial %" PRIu64 ", window %u, count %u) salt %" PRIu64, nops_completed, serial, window_width, head_count, salt); @@ -269,16 +250,33 @@ bool testcase_nested::run() { return false; } - if (!grow_head(head_count)) - return false; - if (!stochastic_breakable_restart_with_nested()) - log_notice("nested: skip commit/restart after head-grow"); - if (!speculum_verify()) { - log_notice("nested: bailout after head-grow"); - return false; + if (!keyspace_overflow && (should_continue() || !clear_wholetable_passed || + !clear_stepbystep_passed)) { + unsigned underutilization_x256 = + txn_underutilization_x256(txn_guard.get()); + if (dbfull_passed > underutilization_x256) { + log_notice("nested: skip head-grow to avoid one more dbfull (was %u, " + "underutilization %.2f%%)", + dbfull_passed, underutilization_x256 / 2.560); + continue; + } + if (!grow_head(head_count)) + return false; + if (!stochastic_breakable_restart_with_nested()) + log_notice("nested: skip commit/restart after head-grow"); + if (!speculum_verify()) { + log_notice("nested: bailout after head-grow"); + return false; + } + loops += 1; + } else if (fifo.empty()) { + log_notice("nested: done %u whole loops, %" PRIu64 " ops, %" PRIu64 + " items", + loops, nops_completed, serial); + break; + } else { + log_notice("nested: done, wait for empty, skip head-grow"); } - - report(1); } while (!stack.empty()) diff --git a/libs/libmdbx/src/test/osal-unix.cc b/libs/libmdbx/src/test/osal-unix.cc index dc0774063a..757c8583c0 100644 --- a/libs/libmdbx/src/test/osal-unix.cc +++ b/libs/libmdbx/src/test/osal-unix.cc @@ -517,9 +517,9 @@ std::string osal_tempdir(void) { tempdir = getenv("TEMPDIR"); if (!tempdir) tempdir = getenv("TEMP"); - if (tempdir) { + if (tempdir && *tempdir) { std::string dir(tempdir); - if (!dir.empty() && dir.at(dir.length() - 1) != '/') + if (dir.back() != '/') dir.append("/"); return dir; } diff --git a/libs/libmdbx/src/test/test.cc b/libs/libmdbx/src/test/test.cc index 9da2cf2fff..beeba7abab 100644 --- a/libs/libmdbx/src/test/test.cc +++ b/libs/libmdbx/src/test/test.cc @@ -172,28 +172,29 @@ void testcase::txn_begin(bool readonly, unsigned flags) { 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() { - int rc = MDBX_SUCCESS; log_trace(">> txn_commit"); assert(txn_guard); MDBX_txn *txn = txn_guard.release(); txn_inject_writefault(txn); - int err = mdbx_txn_commit(txn); - if (unlikely(err != MDBX_SUCCESS)) { - if (err == MDBX_MAP_FULL && config.params.ignore_dbfull) { - rc = err; - err = mdbx_txn_abort(txn); - if (unlikely(err != MDBX_SUCCESS && err != MDBX_THREAD_MISMATCH && - err != MDBX_BAD_TXN)) - failure_perror("mdbx_txn_abort()", err); - } else - failure_perror("mdbx_txn_commit()", err); + 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"); @@ -221,14 +222,17 @@ void testcase::txn_end(bool abort) { MDBX_txn *txn = txn_guard.release(); if (abort) { int err = mdbx_txn_abort(txn); - if (unlikely(err != MDBX_SUCCESS && err != MDBX_THREAD_MISMATCH && - err != MDBX_BAD_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"); @@ -336,18 +340,18 @@ void testcase::report(size_t nops_done) { return; nops_completed += nops_done; - log_debug("== complete +%" PRIuPTR " iteration, total %" PRIuPTR " 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 %" PRIuPTR ")", 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 %" PRIuPTR ")", nops_completed); + log_trace("<< signal(n-ops %" PRIu64 ")", nops_completed); } } @@ -491,6 +495,7 @@ void testcase::db_table_drop(MDBX_dbi handle) { 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"); @@ -502,6 +507,7 @@ void testcase::db_table_clear(MDBX_dbi handle, MDBX_txn *txn) { 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"); } diff --git a/libs/libmdbx/src/test/test.h b/libs/libmdbx/src/test/test.h index 8837cb598b..8ecf5c46fe 100644 --- a/libs/libmdbx/src/test/test.h +++ b/libs/libmdbx/src/test/test.h @@ -104,7 +104,9 @@ protected: using data_view = std::string; #endif static inline data_view S(const MDBX_val &v) { - return data_view(static_cast<const char *>(v.iov_base), v.iov_len); + return (v.iov_base && v.iov_len) + ? data_view(static_cast<const char *>(v.iov_base), v.iov_len) + : data_view(); } static inline data_view S(const keygen::buffer &b) { return S(b->value); } @@ -131,18 +133,22 @@ protected: return cmp < 0; } }; + + // for simplify the set<pair<key,value>> + // is used instead of multimap<key,value> using SET = std::set<Item, ItemCompare>; const actor_config &config; const mdbx_pid_t pid; - MDBX_dbi dbi; + MDBX_dbi dbi{0}; scoped_db_guard db_guard; scoped_txn_guard txn_guard; scoped_cursor_guard cursor_guard; - bool signalled; + bool signalled{false}; + bool need_speculum_assign{false}; - size_t nops_completed; + uint64_t nops_completed{0}; chrono::time start_timestamp; keygen::buffer key; keygen::buffer data; @@ -152,7 +158,7 @@ protected: mdbx_canary canary; } last; - SET speculum; + SET speculum{ItemCompare(this)}, speculum_commited{ItemCompare(this)}; bool speculum_verify(); int insert(const keygen::buffer &akey, const keygen::buffer &adata, unsigned flags); @@ -211,8 +217,7 @@ protected: public: testcase(const actor_config &config, const mdbx_pid_t pid) - : config(config), pid(pid), signalled(false), nops_completed(0), - speculum(ItemCompare(this)) { + : config(config), pid(pid) { start_timestamp.reset(); memset(&last, 0, sizeof(last)); } @@ -223,20 +228,10 @@ public: virtual ~testcase() {} }; -class testcase_ttl : public testcase { -public: - testcase_ttl(const actor_config &config, const mdbx_pid_t pid) - : testcase(config, pid) {} - bool run() override; -}; - class testcase_hill : public testcase { - using inherited = testcase; - SET speculum_commited; - public: testcase_hill(const actor_config &config, const mdbx_pid_t pid) - : testcase(config, pid), speculum_commited(ItemCompare(this)) {} + : testcase(config, pid) {} bool run() override; }; @@ -286,11 +281,33 @@ public: bool run() override; }; -class testcase_nested : public testcase { +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<std::pair<uint64_t, unsigned>>; - uint64_t serial; + 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<std::tuple<scoped_txn_guard, uint64_t, FIFO, SET>> stack; @@ -306,7 +323,7 @@ class testcase_nested : public testcase { public: testcase_nested(const actor_config &config, const mdbx_pid_t pid) - : testcase(config, pid) {} + : inherited(config, pid) {} bool setup() override; bool run() override; bool teardown() override; diff --git a/libs/libmdbx/src/test/ttl.cc b/libs/libmdbx/src/test/ttl.cc index 92e99b82f6..e3927d9cd4 100644 --- a/libs/libmdbx/src/test/ttl.cc +++ b/libs/libmdbx/src/test/ttl.cc @@ -16,18 +16,99 @@ #include <cmath> #include <deque> -static unsigned edge2window(uint64_t edge, unsigned window_max) { - const double rnd = u64_to_double1(bleach64(edge)); - const unsigned window = window_max - std::lrint(std::pow(window_max, rnd)); - return window; -} +/* LY: тест "эмуляцией time-to-live": + * - организуется "скользящее окно", которое двигается вперед вдоль + * числовой оси каждую транзакцию. + * - по переднему краю "скользящего окна" записи добавляются в таблицу, + * а по заднему удаляются. + * - количество добавляемых/удаляемых записей псевдослучайно зависит + * от номера транзакции, но с экспоненциальным распределением. + * - размер "скользящего окна" также псевдослучайно зависит от номера + * транзакции с "отрицательным" экспоненциальным распределением + * MAX_WIDTH - exp(rnd(N)), при уменьшении окна сдвигается задний + * край и удаляются записи позади него. + * + * Таким образом имитируется поведение таблицы с TTL: записи стохастически + * добавляются и удаляются, но изредка происходит массивное удаление. + */ -static unsigned edge2count(uint64_t edge, unsigned count_max) { +unsigned testcase_ttl::edge2count(uint64_t edge) { const double rnd = u64_to_double1(prng64_map1_white(edge)); - const unsigned count = std::lrint(std::pow(count_max, rnd)); + const unsigned count = std::lrint(std::pow(sliding.max_step_size, rnd)); + // average value: (X - 1) / ln(X), where X = sliding.max_step_size return count; } +unsigned testcase_ttl::edge2window(uint64_t edge) { + const double rnd = u64_to_double1(bleach64(edge)); + const unsigned size = sliding.max_window_size - + std::lrint(std::pow(sliding.max_window_size, rnd)); + // average value: Y - (Y - 1) / ln(Y), where Y = sliding.max_window_size + return size; +} + +static inline double estimate(const double x, const double y) { + /* среднее кол-во операций N = X' * Y', где X' и Y' средние значения + * размера окна и кол-ва добавляемых за один шаг записей: + * X' = (X - 1) / ln(X), где X = sliding.max_step_size + * Y' = Y - (Y - 1) / ln(Y), где Y = sliding.max_window_size */ + return (x - 1) / std::log(x) * (y - (y - 1) / std::log(y)); +} + +bool testcase_ttl::setup() { + const unsigned window_top_lower = + 7 /* нижний предел для верхней границы диапазона, в котором будет + стохастически колебаться размер окна */ + ; + const unsigned count_top_lower = + 7 /* нижний предел для верхней границы диапазона, в котором будет + стохастически колебаться кол-во записей добавляемых на одном шаге */ + ; + + /* для параметризации используем подходящие параметры, + * которые не имеют здесь смысла в первоначальном значении. */ + const double ratio = + double(config.params.batch_read ? config.params.batch_read : 1) / + double(config.params.batch_write ? config.params.batch_write : 1); + + /* проще найти двоичным поиском (вариация метода Ньютона) */ + double hi = config.params.test_nops, lo = 1; + double x = std::sqrt(hi + lo) / ratio; + while (hi > lo) { + const double n = estimate(x, x * ratio); + if (n > config.params.test_nops) + hi = x - 1; + else + lo = x + 1; + x = (hi + lo) / 2; + } + + sliding.max_step_size = std::lrint(x); + if (sliding.max_step_size < count_top_lower) + sliding.max_step_size = count_top_lower; + sliding.max_window_size = std::lrint(x * ratio); + if (sliding.max_window_size < window_top_lower) + sliding.max_window_size = window_top_lower; + + while (estimate(sliding.max_step_size, sliding.max_window_size) > + config.params.test_nops * 2.0) { + if (ratio * sliding.max_step_size > sliding.max_window_size) { + if (sliding.max_step_size < count_top_lower) + break; + sliding.max_step_size = sliding.max_step_size * 7 / 8; + } else { + if (sliding.max_window_size < window_top_lower) + break; + sliding.max_window_size = sliding.max_window_size * 7 / 8; + } + } + + log_verbose("come up window_max %u from `batch_read`", + sliding.max_window_size); + log_verbose("come up step_max %u from `batch_write`", sliding.max_step_size); + return inherited::setup(); +} + bool testcase_ttl::run() { int err = db_open__begin__table_create_open_clean(dbi); if (unlikely(err != MDBX_SUCCESS)) { @@ -35,36 +116,6 @@ bool testcase_ttl::run() { return false; } - /* LY: тест "эмуляцией time-to-live": - * - организуется "скользящее окно", которое двигается вперед вдоль - * числовой оси каждую транзакцию. - * - по переднему краю "скользящего окна" записи добавляются в таблицу, - * а по заднему удаляются. - * - количество добавляемых/удаляемых записей псевдослучайно зависит - * от номера транзакции, но с экспоненциальным распределением. - * - размер "скользящего окна" также псевдослучайно зависит от номера - * транзакции с "отрицательным" экспоненциальным распределением - * MAX_WIDTH - exp(rnd(N)), при уменьшении окна сдвигается задний - * край и удаляются записи позади него. - * - * Таким образом имитируется поведение таблицы с TTL: записи стохастически - * добавляются и удаляются, но изредка происходят массивные удаления. - */ - - /* LY: для параметризации используем подходящие параметры, которые не имеют - * здесь смысла в первоначальном значении. */ - const unsigned window_max_lower = 333; - const unsigned count_max_lower = 333; - - const unsigned window_max = (config.params.batch_read > window_max_lower) - ? config.params.batch_read - : window_max_lower; - const unsigned count_max = (config.params.batch_write > count_max_lower) - ? config.params.batch_write - : count_max_lower; - log_verbose("ttl: using `batch_read` value %u for window_max", window_max); - log_verbose("ttl: using `batch_write` value %u for count_max", count_max); - uint64_t seed = prng64_map2_white(config.params.keygen.seed) + config.actor_id; keyvalue_maker.setup(config.params, config.actor_id, 0 /* thread_number */); @@ -77,17 +128,23 @@ bool testcase_ttl::run() { std::deque<std::pair<uint64_t, unsigned>> fifo; uint64_t serial = 0; bool rc = false; - while (should_continue()) { + unsigned clear_wholetable_passed = 0; + unsigned clear_stepbystep_passed = 0; + unsigned dbfull_passed = 0; + unsigned loops = 0; + bool keyspace_overflow = false; + while (true) { const uint64_t salt = prng64_white(seed) /* mdbx_txn_id(txn_guard.get()) */; const unsigned window_width = - flipcoin_x4() ? 0 : edge2window(salt, window_max); - unsigned head_count = edge2count(salt, count_max); - log_debug("ttl: step #%zu (serial %" PRIu64 + (!should_continue() || flipcoin_x4()) ? 0 : edge2window(salt); + unsigned head_count = edge2count(salt); + log_debug("ttl: step #%" PRIu64 " (serial %" PRIu64 ", window %u, count %u) salt %" PRIu64, nops_completed, serial, window_width, head_count, salt); - if (window_width) { + if (window_width || flipcoin()) { + clear_stepbystep_passed += window_width == 0; while (fifo.size() > window_width) { uint64_t tail_serial = fifo.back().first; const unsigned tail_count = fifo.back().second; @@ -97,7 +154,7 @@ bool testcase_ttl::run() { for (unsigned n = 0; n < tail_count; ++n) { log_trace("ttl: remove-tail %" PRIu64, tail_serial); generate_pair(tail_serial); - err = mdbx_del(txn_guard.get(), dbi, &key->value, &data->value); + err = remove(key, data); if (unlikely(err != MDBX_SUCCESS)) { if (err == MDBX_MAP_FULL && config.params.ignore_dbfull) { log_notice("ttl: tail-bailout due '%s'", mdbx_strerror(err)); @@ -108,11 +165,14 @@ bool testcase_ttl::run() { if (unlikely(!keyvalue_maker.increment(tail_serial, 1))) failure("ttl: unexpected key-space overflow on the tail"); } + report(tail_count); } } else { log_trace("ttl: purge state"); db_table_clear(dbi); fifo.clear(); + clear_wholetable_passed += 1; + report(1); } err = breakable_restart(); @@ -120,38 +180,68 @@ bool testcase_ttl::run() { log_notice("ttl: bailout at commit due '%s'", mdbx_strerror(err)); break; } - fifo.push_front(std::make_pair(serial, head_count)); - retry: - for (unsigned n = 0; n < head_count; ++n) { - log_trace("ttl: insert-head %" PRIu64, serial); - generate_pair(serial); - err = mdbx_put(txn_guard.get(), dbi, &key->value, &data->value, - insert_flags); - if (unlikely(err != MDBX_SUCCESS)) { - if (err == MDBX_MAP_FULL && config.params.ignore_dbfull) { - log_notice("ttl: head-insert skip due '%s'", mdbx_strerror(err)); + if (!speculum_verify()) { + log_notice("ttl: bailout after tail-trim"); + return false; + } + + if (!keyspace_overflow && (should_continue() || !clear_wholetable_passed || + !clear_stepbystep_passed)) { + unsigned underutilization_x256 = + txn_underutilization_x256(txn_guard.get()); + if (dbfull_passed > underutilization_x256) { + log_notice("ttl: skip head-grow to avoid one more dbfull (was %u, " + "underutilization %.2f%%)", + dbfull_passed, underutilization_x256 / 2.560); + continue; + } + fifo.push_front(std::make_pair(serial, head_count)); + retry: + for (unsigned n = 0; n < head_count; ++n) { + log_trace("ttl: insert-head %" PRIu64, serial); + generate_pair(serial); + err = insert(key, data, insert_flags); + if (unlikely(err != MDBX_SUCCESS)) { + if ((err == MDBX_TXN_FULL || err == MDBX_MAP_FULL) && + config.params.ignore_dbfull) { + log_notice("ttl: head-insert skip due '%s'", mdbx_strerror(err)); + txn_restart(true, false); + serial = fifo.front().first; + fifo.front().second = head_count = n; + dbfull_passed += 1; + goto retry; + } + failure_perror("mdbx_put(head)", err); + } + + if (unlikely(!keyvalue_maker.increment(serial, 1))) { + log_notice("ttl: unexpected key-space overflow"); + keyspace_overflow = true; txn_restart(true, false); serial = fifo.front().first; fifo.front().second = head_count = n; goto retry; } - failure_perror("mdbx_put(head)", err); } - - if (unlikely(!keyvalue_maker.increment(serial, 1))) { - log_notice("ttl: unexpected key-space overflow"); - goto bailout; + err = breakable_restart(); + if (unlikely(err != MDBX_SUCCESS)) { + log_notice("ttl: head-commit skip due '%s'", mdbx_strerror(err)); + serial = fifo.front().first; + fifo.pop_front(); } + if (!speculum_verify()) { + log_notice("ttl: bailout after head-grow"); + return false; + } + loops += 1; + } else if (fifo.empty()) { + log_notice("ttl: done %u whole loops, %" PRIu64 " ops, %" PRIu64 " items", + loops, nops_completed, serial); + rc = true; + break; + } else { + log_notice("ttl: done, wait for empty, skip head-grow"); } - err = breakable_restart(); - if (unlikely(err != MDBX_SUCCESS)) { - log_notice("ttl: head-commit skip due '%s'", mdbx_strerror(err)); - serial = fifo.front().first; - fifo.pop_front(); - } - - report(1); - rc = true; } bailout: diff --git a/libs/libmdbx/src/test/utils.cc b/libs/libmdbx/src/test/utils.cc index 596e66e3f5..051671ff87 100644 --- a/libs/libmdbx/src/test/utils.cc +++ b/libs/libmdbx/src/test/utils.cc @@ -265,22 +265,24 @@ uint32_t prng32(uint64_t &state) { } void prng_fill(uint64_t &state, void *ptr, size_t bytes) { + uint32_t u32 = prng32(state); + while (bytes >= 4) { - *((uint32_t *)ptr) = prng32(state); + memcpy(ptr, &u32, 4); ptr = (uint32_t *)ptr + 1; bytes -= 4; + u32 = prng32(state); } switch (bytes & 3) { - case 3: { - uint32_t u32 = prng32(state); + case 3: memcpy(ptr, &u32, 3); - } break; + break; case 2: - *((uint16_t *)ptr) = (uint16_t)prng32(state); + memcpy(ptr, &u32, 2); break; case 1: - *((uint8_t *)ptr) = (uint8_t)prng32(state); + memcpy(ptr, &u32, 1); break; case 0: break; diff --git a/libs/libmdbx/src/test/utils.h b/libs/libmdbx/src/test/utils.h index a5061c1444..9e6d4627aa 100644 --- a/libs/libmdbx/src/test/utils.h +++ b/libs/libmdbx/src/test/utils.h @@ -243,9 +243,9 @@ static __inline void cpu_relax() { //----------------------------------------------------------------------------- struct simple_checksum { - uint64_t value; + uint64_t value{0}; - simple_checksum() : value(0) {} + simple_checksum() = default; void push(const uint32_t &data) { value += data * UINT64_C(9386433910765580089) + 1; diff --git a/libs/libmdbx/src/test/valgrind_suppress.txt b/libs/libmdbx/src/test/valgrind_suppress.txt index 98309ceb4f..e1e152051d 100644 --- a/libs/libmdbx/src/test/valgrind_suppress.txt +++ b/libs/libmdbx/src/test/valgrind_suppress.txt @@ -12,8 +12,12 @@ msync(start) fun:msync ... - fun:mdbx_env_sync_ex + fun:mdbx_env_sync_internal } + +# modern Valgrind don't support the `vector[...]` pattern +# for((i=0;i<64;++i)); do echo -e "{\n pwrite-page-flush-$i\n Memcheck:Param\n pwritev(vector[$i])\n fun:pwritev\n ...\n fun:mdbx_page_flush\n}"; done >> valgrind_suppress.txt + { pwrite-page-flush Memcheck:Param @@ -22,3 +26,515 @@ ... fun:mdbx_page_flush } +{ + pwrite-page-flush-0 + Memcheck:Param + pwritev(vector[0]) + fun:pwritev + ... + fun:mdbx_page_flush +} +{ + pwrite-page-flush-1 + Memcheck:Param + pwritev(vector[1]) + fun:pwritev + ... + fun:mdbx_page_flush +} +{ + pwrite-page-flush-2 + Memcheck:Param + pwritev(vector[2]) + fun:pwritev + ... + fun:mdbx_page_flush +} +{ + pwrite-page-flush-3 + Memcheck:Param + pwritev(vector[3]) + fun:pwritev + ... + fun:mdbx_page_flush +} +{ + pwrite-page-flush-4 + Memcheck:Param + pwritev(vector[4]) + fun:pwritev + ... + fun:mdbx_page_flush +} +{ + pwrite-page-flush-5 + Memcheck:Param + pwritev(vector[5]) + fun:pwritev + ... + fun:mdbx_page_flush +} +{ + pwrite-page-flush-6 + Memcheck:Param + pwritev(vector[6]) + fun:pwritev + ... + fun:mdbx_page_flush +} +{ + pwrite-page-flush-7 + Memcheck:Param + pwritev(vector[7]) + fun:pwritev + ... + fun:mdbx_page_flush +} +{ + pwrite-page-flush-8 + Memcheck:Param + pwritev(vector[8]) + fun:pwritev + ... + fun:mdbx_page_flush +} +{ + pwrite-page-flush-9 + Memcheck:Param + pwritev(vector[9]) + fun:pwritev + ... + fun:mdbx_page_flush +} +{ + pwrite-page-flush-10 + Memcheck:Param + pwritev(vector[10]) + fun:pwritev + ... + fun:mdbx_page_flush +} +{ + pwrite-page-flush-11 + Memcheck:Param + pwritev(vector[11]) + fun:pwritev + ... + fun:mdbx_page_flush +} +{ + pwrite-page-flush-12 + Memcheck:Param + pwritev(vector[12]) + fun:pwritev + ... + fun:mdbx_page_flush +} +{ + pwrite-page-flush-13 + Memcheck:Param + pwritev(vector[13]) + fun:pwritev + ... + fun:mdbx_page_flush +} +{ + pwrite-page-flush-14 + Memcheck:Param + pwritev(vector[14]) + fun:pwritev + ... + fun:mdbx_page_flush +} +{ + pwrite-page-flush-15 + Memcheck:Param + pwritev(vector[15]) + fun:pwritev + ... + fun:mdbx_page_flush +} +{ + pwrite-page-flush-16 + Memcheck:Param + pwritev(vector[16]) + fun:pwritev + ... + fun:mdbx_page_flush +} +{ + pwrite-page-flush-17 + Memcheck:Param + pwritev(vector[17]) + fun:pwritev + ... + fun:mdbx_page_flush +} +{ + pwrite-page-flush-18 + Memcheck:Param + pwritev(vector[18]) + fun:pwritev + ... + fun:mdbx_page_flush +} +{ + pwrite-page-flush-19 + Memcheck:Param + pwritev(vector[19]) + fun:pwritev + ... + fun:mdbx_page_flush +} +{ + pwrite-page-flush-20 + Memcheck:Param + pwritev(vector[20]) + fun:pwritev + ... + fun:mdbx_page_flush +} +{ + pwrite-page-flush-21 + Memcheck:Param + pwritev(vector[21]) + fun:pwritev + ... + fun:mdbx_page_flush +} +{ + pwrite-page-flush-22 + Memcheck:Param + pwritev(vector[22]) + fun:pwritev + ... + fun:mdbx_page_flush +} +{ + pwrite-page-flush-23 + Memcheck:Param + pwritev(vector[23]) + fun:pwritev + ... + fun:mdbx_page_flush +} +{ + pwrite-page-flush-24 + Memcheck:Param + pwritev(vector[24]) + fun:pwritev + ... + fun:mdbx_page_flush +} +{ + pwrite-page-flush-25 + Memcheck:Param + pwritev(vector[25]) + fun:pwritev + ... + fun:mdbx_page_flush +} +{ + pwrite-page-flush-26 + Memcheck:Param + pwritev(vector[26]) + fun:pwritev + ... + fun:mdbx_page_flush +} +{ + pwrite-page-flush-27 + Memcheck:Param + pwritev(vector[27]) + fun:pwritev + ... + fun:mdbx_page_flush +} +{ + pwrite-page-flush-28 + Memcheck:Param + pwritev(vector[28]) + fun:pwritev + ... + fun:mdbx_page_flush +} +{ + pwrite-page-flush-29 + Memcheck:Param + pwritev(vector[29]) + fun:pwritev + ... + fun:mdbx_page_flush +} +{ + pwrite-page-flush-30 + Memcheck:Param + pwritev(vector[30]) + fun:pwritev + ... + fun:mdbx_page_flush +} +{ + pwrite-page-flush-31 + Memcheck:Param + pwritev(vector[31]) + fun:pwritev + ... + fun:mdbx_page_flush +} +{ + pwrite-page-flush-32 + Memcheck:Param + pwritev(vector[32]) + fun:pwritev + ... + fun:mdbx_page_flush +} +{ + pwrite-page-flush-33 + Memcheck:Param + pwritev(vector[33]) + fun:pwritev + ... + fun:mdbx_page_flush +} +{ + pwrite-page-flush-34 + Memcheck:Param + pwritev(vector[34]) + fun:pwritev + ... + fun:mdbx_page_flush +} +{ + pwrite-page-flush-35 + Memcheck:Param + pwritev(vector[35]) + fun:pwritev + ... + fun:mdbx_page_flush +} +{ + pwrite-page-flush-36 + Memcheck:Param + pwritev(vector[36]) + fun:pwritev + ... + fun:mdbx_page_flush +} +{ + pwrite-page-flush-37 + Memcheck:Param + pwritev(vector[37]) + fun:pwritev + ... + fun:mdbx_page_flush +} +{ + pwrite-page-flush-38 + Memcheck:Param + pwritev(vector[38]) + fun:pwritev + ... + fun:mdbx_page_flush +} +{ + pwrite-page-flush-39 + Memcheck:Param + pwritev(vector[39]) + fun:pwritev + ... + fun:mdbx_page_flush +} +{ + pwrite-page-flush-40 + Memcheck:Param + pwritev(vector[40]) + fun:pwritev + ... + fun:mdbx_page_flush +} +{ + pwrite-page-flush-41 + Memcheck:Param + pwritev(vector[41]) + fun:pwritev + ... + fun:mdbx_page_flush +} +{ + pwrite-page-flush-42 + Memcheck:Param + pwritev(vector[42]) + fun:pwritev + ... + fun:mdbx_page_flush +} +{ + pwrite-page-flush-43 + Memcheck:Param + pwritev(vector[43]) + fun:pwritev + ... + fun:mdbx_page_flush +} +{ + pwrite-page-flush-44 + Memcheck:Param + pwritev(vector[44]) + fun:pwritev + ... + fun:mdbx_page_flush +} +{ + pwrite-page-flush-45 + Memcheck:Param + pwritev(vector[45]) + fun:pwritev + ... + fun:mdbx_page_flush +} +{ + pwrite-page-flush-46 + Memcheck:Param + pwritev(vector[46]) + fun:pwritev + ... + fun:mdbx_page_flush +} +{ + pwrite-page-flush-47 + Memcheck:Param + pwritev(vector[47]) + fun:pwritev + ... + fun:mdbx_page_flush +} +{ + pwrite-page-flush-48 + Memcheck:Param + pwritev(vector[48]) + fun:pwritev + ... + fun:mdbx_page_flush +} +{ + pwrite-page-flush-49 + Memcheck:Param + pwritev(vector[49]) + fun:pwritev + ... + fun:mdbx_page_flush +} +{ + pwrite-page-flush-50 + Memcheck:Param + pwritev(vector[50]) + fun:pwritev + ... + fun:mdbx_page_flush +} +{ + pwrite-page-flush-51 + Memcheck:Param + pwritev(vector[51]) + fun:pwritev + ... + fun:mdbx_page_flush +} +{ + pwrite-page-flush-52 + Memcheck:Param + pwritev(vector[52]) + fun:pwritev + ... + fun:mdbx_page_flush +} +{ + pwrite-page-flush-53 + Memcheck:Param + pwritev(vector[53]) + fun:pwritev + ... + fun:mdbx_page_flush +} +{ + pwrite-page-flush-54 + Memcheck:Param + pwritev(vector[54]) + fun:pwritev + ... + fun:mdbx_page_flush +} +{ + pwrite-page-flush-55 + Memcheck:Param + pwritev(vector[55]) + fun:pwritev + ... + fun:mdbx_page_flush +} +{ + pwrite-page-flush-56 + Memcheck:Param + pwritev(vector[56]) + fun:pwritev + ... + fun:mdbx_page_flush +} +{ + pwrite-page-flush-57 + Memcheck:Param + pwritev(vector[57]) + fun:pwritev + ... + fun:mdbx_page_flush +} +{ + pwrite-page-flush-58 + Memcheck:Param + pwritev(vector[58]) + fun:pwritev + ... + fun:mdbx_page_flush +} +{ + pwrite-page-flush-59 + Memcheck:Param + pwritev(vector[59]) + fun:pwritev + ... + fun:mdbx_page_flush +} +{ + pwrite-page-flush-60 + Memcheck:Param + pwritev(vector[60]) + fun:pwritev + ... + fun:mdbx_page_flush +} +{ + pwrite-page-flush-61 + Memcheck:Param + pwritev(vector[61]) + fun:pwritev + ... + fun:mdbx_page_flush +} +{ + pwrite-page-flush-62 + Memcheck:Param + pwritev(vector[62]) + fun:pwritev + ... + fun:mdbx_page_flush +} +{ + pwrite-page-flush-63 + Memcheck:Param + pwritev(vector[63]) + fun:pwritev + ... + fun:mdbx_page_flush +} |