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/append.cc62
-rw-r--r--libs/libmdbx/src/test/base.h4
-rw-r--r--libs/libmdbx/src/test/cases.cc3
-rw-r--r--libs/libmdbx/src/test/config.cc4
-rw-r--r--libs/libmdbx/src/test/config.h20
-rw-r--r--libs/libmdbx/src/test/darwin/LICENSE24
-rw-r--r--libs/libmdbx/src/test/darwin/README.md8
-rw-r--r--libs/libmdbx/src/test/darwin/pthread_barrier.c110
-rw-r--r--libs/libmdbx/src/test/darwin/pthread_barrier.h83
-rw-r--r--libs/libmdbx/src/test/gc.sh66
-rw-r--r--libs/libmdbx/src/test/hill.cc205
-rw-r--r--libs/libmdbx/src/test/keygen.cc3
-rw-r--r--libs/libmdbx/src/test/long_stochastic.sh138
-rw-r--r--libs/libmdbx/src/test/loop.bat15
-rw-r--r--libs/libmdbx/src/test/main.cc26
-rw-r--r--libs/libmdbx/src/test/osal-unix.cc10
-rw-r--r--libs/libmdbx/src/test/osal-windows.cc12
-rw-r--r--libs/libmdbx/src/test/test.cc87
-rw-r--r--libs/libmdbx/src/test/test.h3
-rw-r--r--libs/libmdbx/src/test/ttl.cc100
-rw-r--r--libs/libmdbx/src/test/utils.cc5
-rw-r--r--libs/libmdbx/src/test/utils.h62
22 files changed, 796 insertions, 254 deletions
diff --git a/libs/libmdbx/src/test/append.cc b/libs/libmdbx/src/test/append.cc
index c5e7e91d9e..273f68b810 100644
--- a/libs/libmdbx/src/test/append.cc
+++ b/libs/libmdbx/src/test/append.cc
@@ -15,11 +15,12 @@
#include "test.h"
bool testcase_append::run() {
- db_open();
-
- txn_begin(false);
- MDBX_dbi dbi = db_table_open(true);
- db_table_clear(dbi);
+ MDBX_dbi dbi;
+ int err = db_open__begin__table_create_open_clean(dbi);
+ if (unlikely(err != MDBX_SUCCESS)) {
+ log_notice("append: bailout-prepare due '%s'", mdbx_strerror(err));
+ return true;
+ }
keyvalue_maker.setup(config.params, config.actor_id, 0 /* thread_number */);
/* LY: тест наполнения таблиц в append-режиме,
@@ -41,7 +42,10 @@ bool testcase_append::run() {
simple_checksum inserted_checksum;
uint64_t inserted_number = 0;
uint64_t serial_count = 0;
+
unsigned txn_nops = 0;
+ uint64_t commited_inserted_number = inserted_number;
+ simple_checksum commited_inserted_checksum = inserted_checksum;
while (should_continue()) {
const keygen::serial_t serial = serial_count;
if (!keyvalue_maker.increment(serial_count, 1)) {
@@ -57,10 +61,19 @@ bool testcase_append::run() {
if (cmp == 0 && (config.params.table_flags & MDBX_DUPSORT))
cmp = mdbx_dcmp(txn_guard.get(), dbi, &data->value, &last_data->value);
- int err = mdbx_put(txn_guard.get(), dbi, &key->value, &data->value, flags);
+ err = mdbx_put(txn_guard.get(), dbi, &key->value, &data->value, flags);
+ if (err == MDBX_MAP_FULL && config.params.ignore_dbfull) {
+ log_notice("append: bailout-insert due '%s'", mdbx_strerror(err));
+ txn_end(true);
+ inserted_number = commited_inserted_number;
+ inserted_checksum = commited_inserted_checksum;
+ break;
+ }
+
if (cmp > 0) {
if (unlikely(err != MDBX_SUCCESS))
failure_perror("mdbx_put(appenda-a)", err);
+
memcpy(last_key->value.iov_base, key->value.iov_base,
last_key->value.iov_len = key->value.iov_len);
memcpy(last_data->value.iov_base, data->value.iov_base,
@@ -74,22 +87,40 @@ bool testcase_append::run() {
}
if (++txn_nops >= config.params.batch_write) {
- txn_restart(false, false);
+ err = breakable_restart();
+ if (unlikely(err != MDBX_SUCCESS)) {
+ log_notice("append: bailout-commit due '%s'", mdbx_strerror(err));
+ inserted_number = commited_inserted_number;
+ inserted_checksum = commited_inserted_checksum;
+ break;
+ }
+ commited_inserted_number = inserted_number;
+ commited_inserted_checksum = inserted_checksum;
txn_nops = 0;
}
report(1);
}
- txn_restart(false, true);
+ if (txn_guard) {
+ err = breakable_commit();
+ if (unlikely(err != MDBX_SUCCESS)) {
+ log_notice("append: bailout-commit due '%s'", mdbx_strerror(err));
+ inserted_number = commited_inserted_number;
+ inserted_checksum = commited_inserted_checksum;
+ }
+ }
//----------------------------------------------------------------------------
+ txn_begin(true);
cursor_open(dbi);
MDBX_val check_key, check_data;
- int err =
+ err =
mdbx_cursor_get(cursor_guard.get(), &check_key, &check_data, MDBX_FIRST);
- if (unlikely(err != MDBX_SUCCESS))
- failure_perror("mdbx_cursor_get(MDBX_FIRST)", err);
+ if (likely(inserted_number)) {
+ if (unlikely(err != MDBX_SUCCESS))
+ failure_perror("mdbx_cursor_get(MDBX_FIRST)", err);
+ }
simple_checksum read_checksum;
uint64_t read_count = 0;
@@ -115,15 +146,18 @@ bool testcase_append::run() {
read_checksum.value, inserted_checksum.value);
cursor_close();
+ txn_end(true);
//----------------------------------------------------------------------------
- if (txn_guard)
- txn_end(false);
if (dbi) {
if (config.params.drop_table && !mode_readonly()) {
txn_begin(false);
db_table_drop(dbi);
- txn_end(false);
+ err = breakable_commit();
+ if (unlikely(err != MDBX_SUCCESS)) {
+ log_notice("append: bailout-clean due '%s'", mdbx_strerror(err));
+ return true;
+ }
} else
db_table_close(dbi);
}
diff --git a/libs/libmdbx/src/test/base.h b/libs/libmdbx/src/test/base.h
index 0b4d26e51b..5ca134a81d 100644
--- a/libs/libmdbx/src/test/base.h
+++ b/libs/libmdbx/src/test/base.h
@@ -39,6 +39,10 @@
#include <SDKDDKVer.h>
#endif /* WINDOWS */
+#ifdef __APPLE__
+#define _DARWIN_C_SOURCE
+#endif
+
#include <errno.h>
#include <limits.h>
#include <stdio.h>
diff --git a/libs/libmdbx/src/test/cases.cc b/libs/libmdbx/src/test/cases.cc
index 023a80020c..a98834a457 100644
--- a/libs/libmdbx/src/test/cases.cc
+++ b/libs/libmdbx/src/test/cases.cc
@@ -63,8 +63,7 @@ void testcase_setup(const char *casename, actor_params &params,
log_notice(">>> testcase_setup(%s)", casename);
configure_actor(last_space_id, ac_jitter, nullptr, params);
configure_actor(last_space_id, ac_hill, nullptr, params);
- configure_actor(last_space_id, ac_jitter, nullptr, params);
- configure_actor(last_space_id, ac_hill, nullptr, params);
+ configure_actor(last_space_id, ac_ttl, nullptr, params);
configure_actor(last_space_id, ac_jitter, nullptr, params);
configure_actor(last_space_id, ac_hill, nullptr, params);
configure_actor(last_space_id, ac_ttl, nullptr, params);
diff --git a/libs/libmdbx/src/test/config.cc b/libs/libmdbx/src/test/config.cc
index bfae5c14df..dd150e9a91 100644
--- a/libs/libmdbx/src/test/config.cc
+++ b/libs/libmdbx/src/test/config.cc
@@ -1,4 +1,4 @@
-/*
+/*
* Copyright 2017-2019 Leonid Yuriev <leo@yuriev.ru>
* and other libmdbx authors: please see AUTHORS file.
* All rights reserved.
@@ -412,6 +412,8 @@ void dump(const char *title) {
i->params.max_tables);
log_info("drop table: %s\n", i->params.drop_table ? "Yes" : "No");
+ log_info("ignore MDBX_MAP_FULL error: %s\n",
+ i->params.ignore_dbfull ? "Yes" : "No");
indent.pop();
}
diff --git a/libs/libmdbx/src/test/config.h b/libs/libmdbx/src/test/config.h
index b8a4b6827d..89889d8eb6 100644
--- a/libs/libmdbx/src/test/config.h
+++ b/libs/libmdbx/src/test/config.h
@@ -38,7 +38,8 @@ enum actor_status {
as_running,
as_successful,
as_killed,
- as_failed
+ as_failed,
+ as_coredump,
};
const char *testcase2str(const actor_testcase);
@@ -101,6 +102,22 @@ bool parse_option(int argc, char *const argv[], int &narg, const char *option,
bool parse_option(int argc, char *const argv[], int &narg, const char *option,
int32_t &value, const int32_t minval, const int32_t maxval,
const int32_t default_value = -1);
+
+inline bool parse_option_intptr(int argc, char *const argv[], int &narg,
+ const char *option, intptr_t &value,
+ const intptr_t minval, const intptr_t maxval,
+ const intptr_t default_value = -1) {
+ static_assert(sizeof(intptr_t) == 4 || sizeof(intptr_t) == 8, "WTF?");
+ if (sizeof(intptr_t) == 8)
+ return parse_option(argc, argv, narg, option,
+ *reinterpret_cast<int64_t *>(&value), int64_t(minval),
+ int64_t(maxval), int64_t(default_value));
+ else
+ return parse_option(argc, argv, narg, option,
+ *reinterpret_cast<int32_t *>(&value), int32_t(minval),
+ int32_t(maxval), int32_t(default_value));
+}
+
//-----------------------------------------------------------------------------
#pragma pack(push, 1)
@@ -248,6 +265,7 @@ struct actor_params_pod {
keygen_params_pod keygen;
bool drop_table;
+ bool ignore_dbfull;
};
struct actor_config_pod {
diff --git a/libs/libmdbx/src/test/darwin/LICENSE b/libs/libmdbx/src/test/darwin/LICENSE
new file mode 100644
index 0000000000..6a0dd3066b
--- /dev/null
+++ b/libs/libmdbx/src/test/darwin/LICENSE
@@ -0,0 +1,24 @@
+Copyright (c) 2015, Aleksey Demakov
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
diff --git a/libs/libmdbx/src/test/darwin/README.md b/libs/libmdbx/src/test/darwin/README.md
new file mode 100644
index 0000000000..a6a8fd1a91
--- /dev/null
+++ b/libs/libmdbx/src/test/darwin/README.md
@@ -0,0 +1,8 @@
+# DarwinPthreadBarrier
+
+A pthread_barrier_t implementation for Mac OS/X
+
+There is no pthread_barrier_t in Mac OS/X pthreads. This project fixes
+this omission by providing a simple-minded barrier implementation based
+on a pair of pthread_mutex_t and pthread_cond_t.
+
diff --git a/libs/libmdbx/src/test/darwin/pthread_barrier.c b/libs/libmdbx/src/test/darwin/pthread_barrier.c
new file mode 100644
index 0000000000..054aa00708
--- /dev/null
+++ b/libs/libmdbx/src/test/darwin/pthread_barrier.c
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2015, Aleksey Demakov
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "pthread_barrier.h"
+
+#include <errno.h>
+
+#ifdef __APPLE__
+
+int pthread_barrierattr_init(pthread_barrierattr_t *attr) {
+ memset(attr, 0, sizeof(pthread_barrierattr_t));
+ int m = pthread_mutexattr_init(&attr->mattr);
+ int c = pthread_condattr_init(&attr->cattr);
+ return m ? m : c;
+}
+
+int pthread_barrierattr_destroy(pthread_barrierattr_t *attr) {
+ int c = pthread_condattr_destroy(&attr->cattr);
+ int m = pthread_mutexattr_destroy(&attr->mattr);
+ return m ? m : c;
+}
+
+int pthread_barrierattr_getpshared(const pthread_barrierattr_t *__restrict attr,
+ int *__restrict pshared) {
+ return pthread_condattr_getpshared(&attr->cattr, pshared);
+}
+
+int pthread_barrierattr_setpshared(pthread_barrierattr_t *attr, int pshared) {
+ int m = pthread_mutexattr_setpshared(&attr->mattr, pshared);
+ int c = pthread_condattr_setpshared(&attr->cattr, pshared);
+ return m ? m : c;
+}
+
+int pthread_barrier_init(pthread_barrier_t *__restrict barrier,
+ const pthread_barrierattr_t *__restrict attr,
+ unsigned count) {
+ if (count == 0)
+ return errno = EINVAL;
+
+ int rc = pthread_mutex_init(&barrier->mutex, attr ? &attr->mattr : 0);
+ if (rc)
+ return rc;
+
+ rc = pthread_cond_init(&barrier->cond, attr ? &attr->cattr : 0);
+ if (rc) {
+ int errno_save = errno;
+ pthread_mutex_destroy(&barrier->mutex);
+ errno = errno_save;
+ return rc;
+ }
+
+ barrier->limit = count;
+ barrier->count = 0;
+ barrier->phase = 0;
+ return 0;
+}
+
+int pthread_barrier_destroy(pthread_barrier_t *barrier) {
+ pthread_mutex_destroy(&barrier->mutex);
+ pthread_cond_destroy(&barrier->cond);
+ return 0;
+}
+
+int pthread_barrier_wait(pthread_barrier_t *barrier) {
+ int rc = pthread_mutex_lock(&barrier->mutex);
+ if (rc)
+ return rc;
+
+ barrier->count++;
+ if (barrier->count >= barrier->limit) {
+ barrier->phase++;
+ barrier->count = 0;
+ pthread_cond_broadcast(&barrier->cond);
+ pthread_mutex_unlock(&barrier->mutex);
+ return PTHREAD_BARRIER_SERIAL_THREAD;
+ } else {
+ unsigned phase = barrier->phase;
+ do
+ pthread_cond_wait(&barrier->cond, &barrier->mutex);
+ while (phase == barrier->phase);
+ pthread_mutex_unlock(&barrier->mutex);
+ return 0;
+ }
+}
+
+#endif /* __APPLE__ */
diff --git a/libs/libmdbx/src/test/darwin/pthread_barrier.h b/libs/libmdbx/src/test/darwin/pthread_barrier.h
new file mode 100644
index 0000000000..efa9b9b751
--- /dev/null
+++ b/libs/libmdbx/src/test/darwin/pthread_barrier.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2015, Aleksey Demakov
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef PTHREAD_BARRIER_H
+#define PTHREAD_BARRIER_H
+
+#include <pthread.h>
+
+#ifdef __APPLE__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if !defined(PTHREAD_BARRIER_SERIAL_THREAD)
+#define PTHREAD_BARRIER_SERIAL_THREAD (1)
+#endif
+
+#if !defined(PTHREAD_PROCESS_PRIVATE)
+#define PTHREAD_PROCESS_PRIVATE (42)
+#endif
+#if !defined(PTHREAD_PROCESS_SHARED)
+#define PTHREAD_PROCESS_SHARED (43)
+#endif
+
+typedef struct {
+ pthread_mutexattr_t mattr;
+ pthread_condattr_t cattr;
+} pthread_barrierattr_t;
+
+typedef struct {
+ pthread_mutex_t mutex;
+ pthread_cond_t cond;
+ unsigned int limit;
+ unsigned int count;
+ unsigned int phase;
+} pthread_barrier_t;
+
+int pthread_barrierattr_init(pthread_barrierattr_t *attr);
+int pthread_barrierattr_destroy(pthread_barrierattr_t *attr);
+
+int pthread_barrierattr_getpshared(const pthread_barrierattr_t *__restrict attr,
+ int *__restrict pshared);
+int pthread_barrierattr_setpshared(pthread_barrierattr_t *attr, int pshared);
+
+int pthread_barrier_init(pthread_barrier_t *__restrict barrier,
+ const pthread_barrierattr_t *__restrict attr,
+ unsigned int count);
+int pthread_barrier_destroy(pthread_barrier_t *barrier);
+
+int pthread_barrier_wait(pthread_barrier_t *barrier);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __APPLE__ */
+
+#endif /* PTHREAD_BARRIER_H */
diff --git a/libs/libmdbx/src/test/gc.sh b/libs/libmdbx/src/test/gc.sh
deleted file mode 100644
index 9c1407f53e..0000000000
--- a/libs/libmdbx/src/test/gc.sh
+++ /dev/null
@@ -1,66 +0,0 @@
-#!/bin/bash
-set -euo pipefail
-make check
-TESTDB_PREFIX=${1:-/dev/shm/mdbx-gc-test}.
-
-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)
-
-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[@]}"
-}
-
-function probe {
- echo "=============================================== $(date)"
- echo "${caption}: $*"
- rm -f ${TESTDB_PREFIX}* \
- && ./mdbx_test --repeat=12 --pathname=${TESTDB_PREFIX}db "$@" | lz4 > ${TESTDB_PREFIX}log.lz4 \
- && ./mdbx_chk -nvvv ${TESTDB_PREFIX}db | tee ${TESTDB_PREFIX}chk \
- && ([ ! -e ${TESTDB_PREFIX}db-copy ] || ./mdbx_chk -nvvv ${TESTDB_PREFIX}db-copy | tee ${TESTDB_PREFIX}chk-copy) \
- || (echo "FAILED"; exit 1)
-}
-
-###############################################################################
-
-count=0
-for nops in {2..7}; do
- for ((wbatch=nops-1; wbatch > 0; --wbatch)); do
- loops=$(((333 >> nops) / nops + 3))
- for ((rep=0; rep++ < loops; )); do
- for ((bits=2**${#options[@]}; --bits >= 0; )); do
- seed=$(date +%N)
- caption="Probe #$((++count)) int-key,w/o-dups, repeat ${rep} of ${loops}" probe \
- --pagesize=min --size=6G --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=6G --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=6G --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=6G --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=6G --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
-
-echo "=== ALL DONE ====================== $(date)"
diff --git a/libs/libmdbx/src/test/hill.cc b/libs/libmdbx/src/test/hill.cc
index 5b083e1fcc..1b03ddf0fc 100644
--- a/libs/libmdbx/src/test/hill.cc
+++ b/libs/libmdbx/src/test/hill.cc
@@ -15,11 +15,12 @@
#include "test.h"
bool testcase_hill::run() {
- db_open();
-
- txn_begin(false);
- MDBX_dbi dbi = db_table_open(true);
- txn_end(false);
+ MDBX_dbi dbi;
+ int err = db_open__begin__table_create_open_clean(dbi);
+ if (unlikely(err != MDBX_SUCCESS)) {
+ log_notice("hill: bailout-prepare due '%s'", mdbx_strerror(err));
+ return true;
+ }
/* LY: тест "холмиком":
* - сначала наполняем таблицу циклическими CRUD-манипуляциями,
@@ -59,14 +60,15 @@ bool testcase_hill::run() {
: MDBX_NODUPDATA;
uint64_t serial_count = 0;
+ uint64_t commited_serial = serial_count;
unsigned txn_nops = 0;
- if (!txn_guard)
- txn_begin(false);
while (should_continue()) {
const keygen::serial_t a_serial = serial_count;
- if (unlikely(!keyvalue_maker.increment(serial_count, 1)))
- failure("uphill: unexpected key-space overflow");
+ if (unlikely(!keyvalue_maker.increment(serial_count, 1))) {
+ log_notice("uphill: unexpected key-space overflow");
+ break;
+ }
const keygen::serial_t b_serial = serial_count;
assert(b_serial > a_serial);
@@ -76,26 +78,52 @@ bool testcase_hill::run() {
log_trace("uphill: insert-a (age %" PRIu64 ") %" PRIu64, age_shift,
a_serial);
generate_pair(a_serial, a_key, a_data_1, age_shift);
- int rc = mdbx_put(txn_guard.get(), dbi, &a_key->value, &a_data_1->value,
- insert_flags);
- if (unlikely(rc != MDBX_SUCCESS))
- failure_perror("mdbx_put(insert-a.1)", rc);
+ err = mdbx_put(txn_guard.get(), dbi, &a_key->value, &a_data_1->value,
+ insert_flags);
+ if (unlikely(err != MDBX_SUCCESS)) {
+ if (err == MDBX_MAP_FULL && config.params.ignore_dbfull) {
+ log_notice("uphill: bailout at insert-a due '%s'", mdbx_strerror(err));
+ txn_restart(true, false);
+ serial_count = commited_serial;
+ break;
+ }
+ failure_perror("mdbx_put(insert-a.1)", err);
+ }
if (++txn_nops >= config.params.batch_write) {
- txn_restart(false, false);
+ err = breakable_restart();
+ if (unlikely(err != MDBX_SUCCESS)) {
+ log_notice("uphill: bailout at commit due '%s'", mdbx_strerror(err));
+ serial_count = commited_serial;
+ break;
+ }
+ commited_serial = a_serial;
txn_nops = 0;
}
// создаем вторую запись из пары
log_trace("uphill: insert-b %" PRIu64, b_serial);
generate_pair(b_serial, b_key, b_data, 0);
- rc = mdbx_put(txn_guard.get(), dbi, &b_key->value, &b_data->value,
- insert_flags);
- if (unlikely(rc != MDBX_SUCCESS))
- failure_perror("mdbx_put(insert-b)", rc);
+ err = mdbx_put(txn_guard.get(), dbi, &b_key->value, &b_data->value,
+ insert_flags);
+ if (unlikely(err != MDBX_SUCCESS)) {
+ if (err == MDBX_MAP_FULL && config.params.ignore_dbfull) {
+ log_notice("uphill: bailout at insert-b due '%s'", mdbx_strerror(err));
+ txn_restart(true, false);
+ serial_count = commited_serial;
+ break;
+ }
+ failure_perror("mdbx_put(insert-b)", err);
+ }
if (++txn_nops >= config.params.batch_write) {
- txn_restart(false, false);
+ err = breakable_restart();
+ if (unlikely(err != MDBX_SUCCESS)) {
+ log_notice("uphill: bailout at commit due '%s'", mdbx_strerror(err));
+ serial_count = commited_serial;
+ break;
+ }
+ commited_serial = a_serial;
txn_nops = 0;
}
@@ -104,25 +132,51 @@ bool testcase_hill::run() {
a_serial);
generate_pair(a_serial, a_key, a_data_0, 0);
checkdata("uphill: update-a", dbi, a_key->value, a_data_1->value);
- rc = mdbx_replace(txn_guard.get(), dbi, &a_key->value, &a_data_0->value,
- &a_data_1->value, update_flags);
- if (unlikely(rc != MDBX_SUCCESS))
- failure_perror("mdbx_replace(update-a: 1->0)", rc);
+ err = mdbx_replace(txn_guard.get(), dbi, &a_key->value, &a_data_0->value,
+ &a_data_1->value, update_flags);
+ if (unlikely(err != MDBX_SUCCESS)) {
+ if (err == MDBX_MAP_FULL && config.params.ignore_dbfull) {
+ log_notice("uphill: bailout at update-a due '%s'", mdbx_strerror(err));
+ txn_restart(true, false);
+ serial_count = commited_serial;
+ break;
+ }
+ failure_perror("mdbx_replace(update-a: 1->0)", err);
+ }
if (++txn_nops >= config.params.batch_write) {
- txn_restart(false, false);
+ err = breakable_restart();
+ if (unlikely(err != MDBX_SUCCESS)) {
+ log_notice("uphill: bailout at commit due '%s'", mdbx_strerror(err));
+ serial_count = commited_serial;
+ break;
+ }
+ commited_serial = a_serial;
txn_nops = 0;
}
// удаляем вторую запись
log_trace("uphill: delete-b %" PRIu64, b_serial);
checkdata("uphill: delete-b", dbi, b_key->value, b_data->value);
- rc = mdbx_del(txn_guard.get(), dbi, &b_key->value, &b_data->value);
- if (unlikely(rc != MDBX_SUCCESS))
- failure_perror("mdbx_del(b)", rc);
+ err = mdbx_del(txn_guard.get(), dbi, &b_key->value, &b_data->value);
+ if (unlikely(err != MDBX_SUCCESS)) {
+ if (err == MDBX_MAP_FULL && config.params.ignore_dbfull) {
+ log_notice("uphill: bailout at delete-b due '%s'", mdbx_strerror(err));
+ txn_restart(true, false);
+ serial_count = commited_serial;
+ break;
+ }
+ failure_perror("mdbx_del(b)", err);
+ }
if (++txn_nops >= config.params.batch_write) {
- txn_restart(false, false);
+ err = breakable_restart();
+ if (unlikely(err != MDBX_SUCCESS)) {
+ log_notice("uphill: bailout at commit due '%s'", mdbx_strerror(err));
+ serial_count = commited_serial;
+ break;
+ }
+ commited_serial = a_serial;
txn_nops = 0;
}
@@ -134,7 +188,7 @@ bool testcase_hill::run() {
}
}
- while (serial_count > 0) {
+ while (serial_count > 1) {
if (unlikely(!keyvalue_maker.increment(serial_count, -2)))
failure("downhill: unexpected key-space underflow");
@@ -150,26 +204,48 @@ bool testcase_hill::run() {
generate_pair(a_serial, a_key, a_data_0, 0);
generate_pair(a_serial, a_key, a_data_1, age_shift);
checkdata("downhill: update-a", dbi, a_key->value, a_data_0->value);
- int rc = mdbx_replace(txn_guard.get(), dbi, &a_key->value, &a_data_1->value,
- &a_data_0->value, update_flags);
- if (unlikely(rc != MDBX_SUCCESS))
- failure_perror("mdbx_put(update-a: 0->1)", rc);
+ err = mdbx_replace(txn_guard.get(), dbi, &a_key->value, &a_data_1->value,
+ &a_data_0->value, update_flags);
+ if (unlikely(err != MDBX_SUCCESS)) {
+ if (err == MDBX_MAP_FULL && config.params.ignore_dbfull) {
+ log_notice("downhill: bailout at update-a due '%s'",
+ mdbx_strerror(err));
+ txn_end(true);
+ break;
+ }
+ failure_perror("mdbx_put(update-a: 0->1)", err);
+ }
if (++txn_nops >= config.params.batch_write) {
- txn_restart(false, false);
+ err = breakable_restart();
+ if (unlikely(err != MDBX_SUCCESS)) {
+ log_notice("downhill: bailout at commit due '%s'", mdbx_strerror(err));
+ break;
+ }
txn_nops = 0;
}
// создаем вторую запись из пары
log_trace("downhill: insert-b %" PRIu64, b_serial);
generate_pair(b_serial, b_key, b_data, 0);
- rc = mdbx_put(txn_guard.get(), dbi, &b_key->value, &b_data->value,
- insert_flags);
- if (unlikely(rc != MDBX_SUCCESS))
- failure_perror("mdbx_put(insert-b)", rc);
+ err = mdbx_put(txn_guard.get(), dbi, &b_key->value, &b_data->value,
+ insert_flags);
+ if (unlikely(err != MDBX_SUCCESS)) {
+ if (err == MDBX_MAP_FULL && config.params.ignore_dbfull) {
+ log_notice("downhill: bailout at insert-a due '%s'",
+ mdbx_strerror(err));
+ txn_end(true);
+ break;
+ }
+ failure_perror("mdbx_put(insert-b)", err);
+ }
if (++txn_nops >= config.params.batch_write) {
- txn_restart(false, false);
+ err = breakable_restart();
+ if (unlikely(err != MDBX_SUCCESS)) {
+ log_notice("downhill: bailout at commit due '%s'", mdbx_strerror(err));
+ break;
+ }
txn_nops = 0;
}
@@ -177,38 +253,67 @@ bool testcase_hill::run() {
log_trace("downhill: delete-a (age %" PRIu64 ") %" PRIu64, age_shift,
a_serial);
checkdata("downhill: delete-a", dbi, a_key->value, a_data_1->value);
- rc = mdbx_del(txn_guard.get(), dbi, &a_key->value, &a_data_1->value);
- if (unlikely(rc != MDBX_SUCCESS))
- failure_perror("mdbx_del(a)", rc);
+ err = mdbx_del(txn_guard.get(), dbi, &a_key->value, &a_data_1->value);
+ if (unlikely(err != MDBX_SUCCESS)) {
+ if (err == MDBX_MAP_FULL && config.params.ignore_dbfull) {
+ log_notice("downhill: bailout at delete-a due '%s'",
+ mdbx_strerror(err));
+ txn_end(true);
+ break;
+ }
+ failure_perror("mdbx_del(a)", err);
+ }
if (++txn_nops >= config.params.batch_write) {
- txn_restart(false, false);
+ err = breakable_restart();
+ if (unlikely(err != MDBX_SUCCESS)) {
+ log_notice("downhill: bailout at commit due '%s'", mdbx_strerror(err));
+ break;
+ }
txn_nops = 0;
}
// удаляем вторую запись
log_trace("downhill: delete-b %" PRIu64, b_serial);
checkdata("downhill: delete-b", dbi, b_key->value, b_data->value);
- rc = mdbx_del(txn_guard.get(), dbi, &b_key->value, &b_data->value);
- if (unlikely(rc != MDBX_SUCCESS))
- failure_perror("mdbx_del(b)", rc);
+ err = mdbx_del(txn_guard.get(), dbi, &b_key->value, &b_data->value);
+ if (unlikely(err != MDBX_SUCCESS)) {
+ if (err == MDBX_MAP_FULL && config.params.ignore_dbfull) {
+ log_notice("downhill: bailout at delete-b due '%s'",
+ mdbx_strerror(err));
+ txn_end(true);
+ break;
+ }
+ failure_perror("mdbx_del(b)", err);
+ }
if (++txn_nops >= config.params.batch_write) {
- txn_restart(false, false);
+ err = breakable_restart();
+ if (unlikely(err != MDBX_SUCCESS)) {
+ log_notice("downhill: bailout at commit due '%s'", mdbx_strerror(err));
+ break;
+ }
txn_nops = 0;
}
report(1);
}
- if (txn_guard)
- txn_end(false);
+ if (txn_guard) {
+ err = breakable_commit();
+ if (unlikely(err != MDBX_SUCCESS))
+ log_notice("downhill: bailout at commit due '%s'", mdbx_strerror(err));
+ }
if (dbi) {
if (config.params.drop_table && !mode_readonly()) {
txn_begin(false);
db_table_drop(dbi);
- txn_end(false);
+ err = breakable_commit();
+ if (unlikely(err != MDBX_SUCCESS)) {
+ log_notice("hill: bailout-clean due '%s'", mdbx_strerror(err));
+ return true;
+ }
} else
db_table_close(dbi);
}
diff --git a/libs/libmdbx/src/test/keygen.cc b/libs/libmdbx/src/test/keygen.cc
index 30cdf7a571..0110b049bf 100644
--- a/libs/libmdbx/src/test/keygen.cc
+++ b/libs/libmdbx/src/test/keygen.cc
@@ -184,7 +184,8 @@ bool maker::increment(serial_t &serial, int delta) const {
}
serial_t target = serial + (int64_t)delta;
- if (target > mask(mapping.width)) {
+ if (target > mask(mapping.width) ||
+ ((delta > 0) ? target < serial : target > serial)) {
log_extra("keygen-increment: %" PRIu64 "%-d => %" PRIu64 ", overflow",
serial, delta, target);
return false;
diff --git a/libs/libmdbx/src/test/long_stochastic.sh b/libs/libmdbx/src/test/long_stochastic.sh
new file mode 100644
index 0000000000..954d18268a
--- /dev/null
+++ b/libs/libmdbx/src/test/long_stochastic.sh
@@ -0,0 +1,138 @@
+#!/bin/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
+fi
+
+set -euo pipefail
+
+UNAME="$(uname -s 2>/dev/null || echo Unknown)"
+case ${UNAME} in
+ Linux)
+ MAKE=make
+ if [[ ! -v TESTDB_DIR || -z "$TESTDB_DIR" ]]; then
+ 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 && mount -t tmpfs tmpfs $TESTDB_DIR
+ 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))
+ ;;
+ *)
+ echo "FIXME: ${UNAME} not supported by this script"
+ exit 2
+ ;;
+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
+fi
+
+#
+# В режимах отличных от MDBX_WRITEMAP изменения до записи в файл
+# будут накапливаться в памяти, что может потребовать свободной
+# памяти размером с БД. Кроме этого, в тест входит сценарий
+# создания копия БД на ходу. Поэтому БД не может быть больше 1/3
+# от доступной памяти. Однако, следует учесть что malloc() будет
+# не сразу возвращать выделенную память системе, а также
+# предусмотреть места для логов.
+#
+# In non-MDBX_WRITEMAP modes, updates (dirty pages) will
+# accumulate in memory before writing to the disk, which may
+# require a free memory up to the size of a whole database. In
+# addition, the test includes a script create a copy of the
+# database on the go. Therefore, the database cannot be more 1/3
+# of available memory. Moreover, should be taken into account
+# that malloc() will not return the allocated memory to the
+# 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
+fi
+echo "=== use ${db_size_mb}M for DB"
+
+${MAKE} TESTDB=${TESTDB_DIR}/smoke.db TESTLOG=${TESTDB_DIR}/smoke.log check
+rm -f ${TESTDB_DIR}/*
+
+###############################################################################
+
+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)
+
+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[@]}"
+}
+
+function probe {
+ echo "=============================================== $(date)"
+ echo "${caption}: $*"
+ rm -f ${TESTDB_DIR}/* \
+ && ./mdbx_test --ignore-dbfull --repeat=42 --pathname=${TESTDB_DIR}/long.db "$@" | lz4 > ${TESTDB_DIR}/long.log.lz4 \
+ && ./mdbx_chk -nvvv ${TESTDB_DIR}/long.db | tee ${TESTDB_DIR}/long-chk.log \
+ && ([ ! -e ${TESTDB_DIR}/long.db-copy ] || ./mdbx_chk -nvvv ${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
+
+echo "=== ALL DONE ====================== $(date)"
diff --git a/libs/libmdbx/src/test/loop.bat b/libs/libmdbx/src/test/loop.bat
deleted file mode 100644
index 0e3b6271c9..0000000000
--- a/libs/libmdbx/src/test/loop.bat
+++ /dev/null
@@ -1,15 +0,0 @@
-@echo off
-
-del test.db test.db-lck
-
-:loop
-
-mdbx_test.exe --pathname=test.db --dont-cleanup-after basic > test.log
-if errorlevel 1 goto fail
-
-mdbx_chk.exe -nvvv test.db > chk.log
-if errorlevel 1 goto fail
-goto loop
-
-:fail
-echo FAILED
diff --git a/libs/libmdbx/src/test/main.cc b/libs/libmdbx/src/test/main.cc
index 9dc3eccbae..959359a515 100644
--- a/libs/libmdbx/src/test/main.cc
+++ b/libs/libmdbx/src/test/main.cc
@@ -27,6 +27,8 @@ void actor_params::set_defaults(const std::string &tmpdir) {
loglevel =
#ifdef NDEBUG
logging::info;
+#elif defined(_WIN32) || defined(_WIN64)
+ logging::verbose;
#else
logging::trace;
#endif
@@ -70,6 +72,7 @@ void actor_params::set_defaults(const std::string &tmpdir) {
inject_writefaultn = 0;
drop_table = false;
+ ignore_dbfull = false;
max_readers = 42;
max_tables = 42;
@@ -179,17 +182,19 @@ int main(int argc, char *const argv[]) {
params.datalen_max = datalen_max;
continue;
}
- if (config::parse_option(argc, argv, narg, "size-lower", params.size_lower,
- mdbx_limits_dbsize_min(params.pagesize),
- mdbx_limits_dbsize_max(params.pagesize)))
+ if (config::parse_option_intptr(argc, argv, narg, "size-lower",
+ params.size_lower,
+ mdbx_limits_dbsize_min(params.pagesize),
+ mdbx_limits_dbsize_max(params.pagesize)))
continue;
- if (config::parse_option(argc, argv, narg, "size", params.size_now,
- mdbx_limits_dbsize_min(params.pagesize),
- mdbx_limits_dbsize_max(params.pagesize)))
+ if (config::parse_option_intptr(argc, argv, narg, "size-upper",
+ params.size_upper,
+ mdbx_limits_dbsize_min(params.pagesize),
+ mdbx_limits_dbsize_max(params.pagesize)))
continue;
- if (config::parse_option(argc, argv, narg, "size-upper", params.size_upper,
- mdbx_limits_dbsize_min(params.pagesize),
- mdbx_limits_dbsize_max(params.pagesize)))
+ if (config::parse_option_intptr(argc, argv, narg, "size", params.size_now,
+ mdbx_limits_dbsize_min(params.pagesize),
+ mdbx_limits_dbsize_max(params.pagesize)))
continue;
if (config::parse_option(
argc, argv, narg, "shrink-threshold", params.shrink_threshold, 0,
@@ -288,6 +293,9 @@ int main(int argc, char *const argv[]) {
continue;
if (config::parse_option(argc, argv, narg, "drop", params.drop_table))
continue;
+ if (config::parse_option(argc, argv, narg, "ignore-dbfull",
+ params.ignore_dbfull))
+ continue;
if (config::parse_option(argc, argv, narg, "dump-config",
global::config::dump_config))
continue;
diff --git a/libs/libmdbx/src/test/osal-unix.cc b/libs/libmdbx/src/test/osal-unix.cc
index fd691e354f..0157bace23 100644
--- a/libs/libmdbx/src/test/osal-unix.cc
+++ b/libs/libmdbx/src/test/osal-unix.cc
@@ -21,6 +21,10 @@
#include <sys/wait.h>
#include <unistd.h>
+#ifdef __APPLE__
+#include "darwin/pthread_barrier.c"
+#endif
+
struct shared_t {
pthread_barrier_t barrier;
pthread_mutex_t mutex;
@@ -199,7 +203,9 @@ retry:
if (WIFEXITED(status))
childs[pid] =
(WEXITSTATUS(status) == EXIT_SUCCESS) ? as_successful : as_failed;
- else if (WIFSIGNALED(status) || WCOREDUMP(status))
+ else if (WCOREDUMP(status))
+ childs[pid] = as_coredump;
+ else if (WIFSIGNALED(status))
childs[pid] = as_killed;
else if (WIFSTOPPED(status))
childs[pid] = as_debuging;
@@ -216,7 +222,7 @@ retry:
if (ts.tv_sec == 0 && ts.tv_nsec == 0)
ts.tv_nsec = 1;
if (nanosleep(&ts, &ts) == 0) {
- /* timeout and no signal fomr child */
+ /* timeout and no signal from child */
pid = 0;
return 0;
}
diff --git a/libs/libmdbx/src/test/osal-windows.cc b/libs/libmdbx/src/test/osal-windows.cc
index 5858e89530..975d8268f5 100644
--- a/libs/libmdbx/src/test/osal-windows.cc
+++ b/libs/libmdbx/src/test/osal-windows.cc
@@ -312,14 +312,22 @@ actor_status osal_actor_info(const mdbx_pid_t pid) {
case EXIT_SUCCESS:
status = as_successful;
break;
- // case EXCEPTION_BREAKPOINT:
+ case EXCEPTION_BREAKPOINT:
case EXCEPTION_SINGLE_STEP:
status = as_debuging;
break;
case STATUS_CONTROL_C_EXIT:
- case EXCEPTION_NONCONTINUABLE_EXCEPTION:
status = as_killed;
break;
+ case EXCEPTION_ACCESS_VIOLATION:
+ case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
+ case EXCEPTION_DATATYPE_MISALIGNMENT:
+ case EXCEPTION_STACK_OVERFLOW:
+ case EXCEPTION_INVALID_DISPOSITION:
+ case EXCEPTION_ILLEGAL_INSTRUCTION:
+ case EXCEPTION_NONCONTINUABLE_EXCEPTION:
+ status = as_coredump;
+ break;
default:
status = as_failed;
break;
diff --git a/libs/libmdbx/src/test/test.cc b/libs/libmdbx/src/test/test.cc
index cf61f1eeee..e9e925e9a9 100644
--- a/libs/libmdbx/src/test/test.cc
+++ b/libs/libmdbx/src/test/test.cc
@@ -55,6 +55,8 @@ const char *status2str(actor_status status) {
return "killed";
case as_failed:
return "failed";
+ case as_coredump:
+ return "coredump";
}
}
@@ -137,6 +139,8 @@ void testcase::db_open() {
if (!db_guard)
db_prepare();
+
+ jitter_delay(true);
int rc = mdbx_env_open(db_guard.get(), config.params.pathname_db.c_str(),
(unsigned)config.params.mode_flags, 0640);
if (unlikely(rc != MDBX_SUCCESS))
@@ -170,20 +174,42 @@ void testcase::txn_begin(bool readonly, unsigned flags) {
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))
+ failure_perror("mdbx_txn_abort()", err);
+ } else
+ failure_perror("mdbx_txn_commit()", err);
+ }
+
+ log_trace("<< txn_commit: %s", rc ? "failed" : "Ok");
+ return rc;
+}
+
void testcase::txn_end(bool abort) {
log_trace(">> txn_end(%s)", abort ? "abort" : "commit");
assert(txn_guard);
MDBX_txn *txn = txn_guard.release();
if (abort) {
- int rc = mdbx_txn_abort(txn);
- if (unlikely(rc != MDBX_SUCCESS))
- failure_perror("mdbx_txn_abort()", rc);
+ int err = mdbx_txn_abort(txn);
+ if (unlikely(err != MDBX_SUCCESS && err != MDBX_THREAD_MISMATCH))
+ failure_perror("mdbx_txn_abort()", err);
} else {
txn_inject_writefault(txn);
- int rc = mdbx_txn_commit(txn);
- if (unlikely(rc != MDBX_SUCCESS))
- failure_perror("mdbx_txn_commit()", rc);
+ int err = mdbx_txn_commit(txn);
+ if (unlikely(err != MDBX_SUCCESS))
+ failure_perror("mdbx_txn_commit()", err);
}
log_trace("<< txn_end(%s)", abort ? "abort" : "commit");
@@ -211,6 +237,16 @@ void testcase::cursor_close() {
log_trace("<< cursor_close()");
}
+int testcase::breakable_restart() {
+ int rc = MDBX_SUCCESS;
+ if (txn_guard)
+ rc = breakable_commit();
+ if (cursor_guard)
+ cursor_close();
+ txn_begin(false, 0);
+ return rc;
+}
+
void testcase::txn_restart(bool abort, bool readonly, unsigned flags) {
if (txn_guard)
txn_end(abort);
@@ -394,6 +430,28 @@ void testcase::update_canary(uint64_t increment) {
log_trace("<< update_canary: sequence = %" PRIu64, canary_now.y);
}
+int testcase::db_open__begin__table_create_open_clean(MDBX_dbi &dbi) {
+ db_open();
+
+ int err, retry_left = 42;
+ for (;;) {
+ txn_begin(false);
+ dbi = db_table_open(true);
+ db_table_clear(dbi);
+ err = breakable_commit();
+ if (likely(err == MDBX_SUCCESS)) {
+ txn_begin(false);
+ return MDBX_SUCCESS;
+ }
+ if (--retry_left == 0)
+ break;
+ jitter_delay(true);
+ }
+ log_notice("db_begin_table_create_open_clean: bailout due '%s'",
+ mdbx_strerror(err));
+ return err;
+}
+
MDBX_dbi testcase::db_table_open(bool create) {
log_trace(">> testcase::db_table_create");
@@ -513,22 +571,27 @@ bool test_execute(const actor_config &config_const) {
if (!test->setup()) {
log_notice("test setup failed");
return false;
- } else if (!test->run()) {
+ }
+ if (!test->run()) {
log_notice("test failed");
return false;
- } else if (!test->teardown()) {
+ }
+ if (!test->teardown()) {
log_notice("test teardown failed");
return false;
- } else {
- if (config.params.nrepeat == 1)
- log_info("test successed");
- else if (config.params.nrepeat == 1)
+ }
+
+ if (config.params.nrepeat == 1)
+ log_info("test successed");
+ else {
+ if (config.params.nrepeat)
log_info("test successed (iteration %zi of %zi)", iter,
size_t(config.params.nrepeat));
else
log_info("test successed (iteration %zi)", iter);
config.params.keygen.seed += INT32_C(0xA4F4D37B);
}
+
} while (config.params.nrepeat == 0 || iter < config.params.nrepeat);
return true;
} catch (const std::exception &pipets) {
diff --git a/libs/libmdbx/src/test/test.h b/libs/libmdbx/src/test/test.h
index 117a66f93c..fb5ad4ee83 100644
--- a/libs/libmdbx/src/test/test.h
+++ b/libs/libmdbx/src/test/test.h
@@ -105,7 +105,9 @@ protected:
void db_open();
void db_close();
void txn_begin(bool readonly, unsigned flags = 0);
+ int breakable_commit();
void txn_end(bool abort);
+ int breakable_restart();
void txn_restart(bool abort, bool readonly, unsigned flags = 0);
void cursor_open(unsigned dbi);
void cursor_close();
@@ -121,6 +123,7 @@ protected:
void db_table_drop(MDBX_dbi handle);
void db_table_clear(MDBX_dbi handle);
void db_table_close(MDBX_dbi handle);
+ int db_open__begin__table_create_open_clean(MDBX_dbi &dbi);
bool wait4start();
void report(size_t nops_done);
diff --git a/libs/libmdbx/src/test/ttl.cc b/libs/libmdbx/src/test/ttl.cc
index 1ecfd0c7e5..b2650f0c13 100644
--- a/libs/libmdbx/src/test/ttl.cc
+++ b/libs/libmdbx/src/test/ttl.cc
@@ -29,12 +29,12 @@ static unsigned edge2count(uint64_t edge, unsigned count_max) {
}
bool testcase_ttl::run() {
- db_open();
-
- txn_begin(false);
- MDBX_dbi dbi = db_table_open(true);
- db_table_clear(dbi);
- txn_end(false);
+ MDBX_dbi dbi;
+ int err = db_open__begin__table_create_open_clean(dbi);
+ if (unlikely(err != MDBX_SUCCESS)) {
+ log_notice("ttl: bailout-prepare due '%s'", mdbx_strerror(err));
+ return true;
+ }
/* LY: тест "эмуляцией time-to-live":
* - организуется "скользящее окно", которое двигается вперед вдоль
@@ -53,11 +53,26 @@ bool testcase_ttl::run() {
*/
/* LY: для параметризации используем подходящие параметры, которые не имеют
- * здесь смысла в первоначальном значении */
- const unsigned window_max =
- (config.params.batch_read > 999) ? config.params.batch_read : 1000;
- const unsigned count_max =
- (config.params.batch_write > 999) ? config.params.batch_write : 1000;
+ * здесь смысла в первоначальном значении. */
+ const unsigned window_max_lower =
+#ifdef __APPLE__
+ 333;
+#else
+ 999;
+#endif
+ const unsigned count_max_lower =
+#ifdef __APPLE__
+ 333;
+#else
+ 999;
+#endif
+
+ 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_info("ttl: using `batch_read` value %u for window_max", window_max);
log_info("ttl: using `batch_write` value %u for count_max", count_max);
@@ -73,15 +88,13 @@ bool testcase_ttl::run() {
std::deque<std::pair<uint64_t, unsigned>> fifo;
uint64_t serial = 0;
while (should_continue()) {
- if (!txn_guard)
- txn_begin(false);
const uint64_t salt = prng64_white(seed) /* mdbx_txn_id(txn_guard.get()) */;
const unsigned window_width = edge2window(salt, window_max);
- const unsigned head_count = edge2count(salt, count_max);
- log_info("ttl: step #%zu (serial %" PRIu64
- ", window %u, count %u) salt %" PRIu64,
- nops_completed, serial, window_width, head_count, salt);
+ unsigned head_count = edge2count(salt, count_max);
+ log_verbose("ttl: step #%zu (serial %" PRIu64
+ ", window %u, count %u) salt %" PRIu64,
+ nops_completed, serial, window_width, head_count, salt);
if (window_width) {
while (fifo.size() > window_width) {
@@ -93,9 +106,14 @@ bool testcase_ttl::run() {
for (unsigned n = 0; n < tail_count; ++n) {
log_trace("ttl: remove-tail %" PRIu64, serial);
generate_pair(tail_serial);
- int err = mdbx_del(txn_guard.get(), dbi, &key->value, &data->value);
- if (unlikely(err != MDBX_SUCCESS))
+ err = mdbx_del(txn_guard.get(), dbi, &key->value, &data->value);
+ if (unlikely(err != MDBX_SUCCESS)) {
+ if (err == MDBX_MAP_FULL && config.params.ignore_dbfull) {
+ log_notice("ttl: tail-bailout due '%s'", mdbx_strerror(err));
+ goto bailout;
+ }
failure_perror("mdbx_del(tail)", err);
+ }
if (unlikely(!keyvalue_maker.increment(tail_serial, 1)))
failure("ttl: unexpected key-space overflow on the tail");
}
@@ -106,30 +124,54 @@ bool testcase_ttl::run() {
fifo.clear();
}
- txn_restart(false, false);
+ err = breakable_restart();
+ if (unlikely(err != MDBX_SUCCESS)) {
+ 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);
- int err = mdbx_put(txn_guard.get(), dbi, &key->value, &data->value,
- insert_flags);
- if (unlikely(err != MDBX_SUCCESS))
+ 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));
+ 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)))
- failure("uphill: unexpected key-space overflow");
+ 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();
}
-
- txn_end(false);
report(1);
}
+bailout:
+ txn_end(true);
if (dbi) {
if (config.params.drop_table && !mode_readonly()) {
txn_begin(false);
db_table_drop(dbi);
- txn_end(false);
+ err = breakable_commit();
+ if (unlikely(err != MDBX_SUCCESS)) {
+ log_notice("ttl: bailout-clean due '%s'", mdbx_strerror(err));
+ return true;
+ }
} else
db_table_close(dbi);
}
diff --git a/libs/libmdbx/src/test/utils.cc b/libs/libmdbx/src/test/utils.cc
index ddf47a4cd9..d9b3538b99 100644
--- a/libs/libmdbx/src/test/utils.cc
+++ b/libs/libmdbx/src/test/utils.cc
@@ -17,6 +17,9 @@
#if defined(HAVE_IEEE754_H) || __has_include(<ieee754.h>)
#include <ieee754.h>
#endif
+#if defined(__APPLE__) || defined(__MACH__)
+#include <mach/mach_time.h>
+#endif /* defined(__APPLE__) || defined(__MACH__) */
std::string format(const char *fmt, ...) {
va_list ap, ones;
@@ -353,7 +356,7 @@ void jitter_delay(bool extra) {
cpu_relax();
if (dice > 2) {
unsigned us = entropy_white() &
- (extra ? 0xfffff /* 1.05 s */ : 0x3ff /* 1 ms */);
+ (extra ? 0xffff /* 656 ms */ : 0x3ff /* 1 ms */);
log_trace("== jitter.delay: %0.6f", us / 1000000.0);
osal_udelay(us);
}
diff --git a/libs/libmdbx/src/test/utils.h b/libs/libmdbx/src/test/utils.h
index efda8394fc..d1b859acd4 100644
--- a/libs/libmdbx/src/test/utils.h
+++ b/libs/libmdbx/src/test/utils.h
@@ -26,9 +26,14 @@
#endif
#if __GNUC_PREREQ(4, 4) || defined(__clang__)
+#ifndef bswap64
#define bswap64(v) __builtin_bswap64(v)
+#endif
+#ifndef bswap32
#define bswap32(v) __builtin_bswap32(v)
-#if __GNUC_PREREQ(4, 8) || __has_builtin(__builtin_bswap16)
+#endif
+#if (__GNUC_PREREQ(4, 8) || __has_builtin(__builtin_bswap16)) && \
+ !defined(bswap16)
#define bswap16(v) __builtin_bswap16(v)
#endif
@@ -184,52 +189,9 @@ static __inline uint64_t rot64(uint64_t v, unsigned s) {
}
#endif /* rot64 */
-#ifndef mul_32x32_64
-static __inline uint64_t mul_32x32_64(uint32_t a, uint32_t b) {
- return a * (uint64_t)b;
-}
-#endif /* mul_32x32_64 */
-
-#ifndef mul_64x64_128
-
-static __inline unsigned add_with_carry(uint64_t *sum, uint64_t addend) {
- *sum += addend;
- return (*sum < addend) ? 1u : 0u;
-}
-
-static __inline uint64_t mul_64x64_128(uint64_t a, uint64_t b, uint64_t *h) {
-#if defined(__SIZEOF_INT128__) || \
- (defined(_INTEGRAL_MAX_BITS) && _INTEGRAL_MAX_BITS >= 128)
- __uint128_t r = (__uint128_t)a * (__uint128_t)b;
- /* modern GCC could nicely optimize this */
- *h = r >> 64;
- return r;
-#elif defined(mul_64x64_high)
- *h = mul_64x64_high(a, b);
- return a * b;
-#else
- /* performs 64x64 to 128 bit multiplication */
- uint64_t ll = mul_32x32_64((uint32_t)a, (uint32_t)b);
- uint64_t lh = mul_32x32_64(a >> 32, (uint32_t)b);
- uint64_t hl = mul_32x32_64((uint32_t)a, b >> 32);
- *h = mul_32x32_64(a >> 32, b >> 32) + (lh >> 32) + (hl >> 32) +
- add_with_carry(&ll, lh << 32) + add_with_carry(&ll, hl << 32);
- return ll;
-#endif
-}
-
-#endif /* mul_64x64_128() */
-
-#ifndef mul_64x64_high
-static __inline uint64_t mul_64x64_high(uint64_t a, uint64_t b) {
- uint64_t h;
- mul_64x64_128(a, b, &h);
- return h;
-}
-#endif /* mul_64x64_high */
-
static __inline bool is_power2(size_t x) { return (x & (x - 1)) == 0; }
+#undef roundup2
static __inline size_t roundup2(size_t value, size_t granularity) {
assert(is_power2(granularity));
return (value + granularity - 1) & ~(granularity - 1);
@@ -285,18 +247,20 @@ struct simple_checksum {
simple_checksum() : value(0) {}
- void push(uint32_t data) {
+ void push(const uint32_t &data) {
value += data * UINT64_C(9386433910765580089) + 1;
value ^= value >> 41;
value *= UINT64_C(0xBD9CACC22C6E9571);
}
- void push(uint64_t data) {
+ void push(const uint64_t &data) {
push((uint32_t)data);
push((uint32_t)(data >> 32));
}
- void push(bool data) { push(data ? UINT32_C(0x780E) : UINT32_C(0xFA18E)); }
+ void push(const bool data) {
+ push(data ? UINT32_C(0x780E) : UINT32_C(0xFA18E));
+ }
void push(const void *ptr, size_t bytes) {
const uint8_t *data = (const uint8_t *)ptr;
@@ -309,7 +273,7 @@ struct simple_checksum {
void push(const std::string &str) { push(str.data(), str.size()); }
void push(unsigned salt, const MDBX_val &val) {
- push(val.iov_len);
+ push(unsigned(val.iov_len));
push(salt);
push(val.iov_base, val.iov_len);
}