summaryrefslogtreecommitdiff
path: root/libs/libmdbx/src/test
diff options
context:
space:
mode:
Diffstat (limited to 'libs/libmdbx/src/test')
-rw-r--r--libs/libmdbx/src/test/config.cc22
-rw-r--r--libs/libmdbx/src/test/config.h118
-rw-r--r--libs/libmdbx/src/test/dump-load.sh40
-rw-r--r--libs/libmdbx/src/test/hill.cc41
-rw-r--r--libs/libmdbx/src/test/keygen.cc32
-rw-r--r--libs/libmdbx/src/test/keygen.h21
-rw-r--r--libs/libmdbx/src/test/long_stochastic.sh321
-rw-r--r--libs/libmdbx/src/test/main.cc21
-rw-r--r--libs/libmdbx/src/test/nested.cc130
-rw-r--r--libs/libmdbx/src/test/osal-unix.cc4
-rw-r--r--libs/libmdbx/src/test/test.cc38
-rw-r--r--libs/libmdbx/src/test/test.h59
-rw-r--r--libs/libmdbx/src/test/ttl.cc224
-rw-r--r--libs/libmdbx/src/test/utils.cc14
-rw-r--r--libs/libmdbx/src/test/utils.h4
-rw-r--r--libs/libmdbx/src/test/valgrind_suppress.txt518
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 &params,
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 *>(&params),
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 &params,
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 &params,
void __hot maker::mk_continue(const serial_t serial, const essentials &params,
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 &params,
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 &params,
}
} 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 &params,
@@ -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
+}