diff options
Diffstat (limited to 'libs/libmdbx/src/test')
-rw-r--r-- | libs/libmdbx/src/test/append.cc | 62 | ||||
-rw-r--r-- | libs/libmdbx/src/test/base.h | 4 | ||||
-rw-r--r-- | libs/libmdbx/src/test/cases.cc | 3 | ||||
-rw-r--r-- | libs/libmdbx/src/test/config.cc | 4 | ||||
-rw-r--r-- | libs/libmdbx/src/test/config.h | 20 | ||||
-rw-r--r-- | libs/libmdbx/src/test/darwin/LICENSE | 24 | ||||
-rw-r--r-- | libs/libmdbx/src/test/darwin/README.md | 8 | ||||
-rw-r--r-- | libs/libmdbx/src/test/darwin/pthread_barrier.c | 110 | ||||
-rw-r--r-- | libs/libmdbx/src/test/darwin/pthread_barrier.h | 83 | ||||
-rw-r--r-- | libs/libmdbx/src/test/gc.sh | 66 | ||||
-rw-r--r-- | libs/libmdbx/src/test/hill.cc | 205 | ||||
-rw-r--r-- | libs/libmdbx/src/test/keygen.cc | 3 | ||||
-rw-r--r-- | libs/libmdbx/src/test/long_stochastic.sh | 138 | ||||
-rw-r--r-- | libs/libmdbx/src/test/loop.bat | 15 | ||||
-rw-r--r-- | libs/libmdbx/src/test/main.cc | 26 | ||||
-rw-r--r-- | libs/libmdbx/src/test/osal-unix.cc | 10 | ||||
-rw-r--r-- | libs/libmdbx/src/test/osal-windows.cc | 12 | ||||
-rw-r--r-- | libs/libmdbx/src/test/test.cc | 87 | ||||
-rw-r--r-- | libs/libmdbx/src/test/test.h | 3 | ||||
-rw-r--r-- | libs/libmdbx/src/test/ttl.cc | 100 | ||||
-rw-r--r-- | libs/libmdbx/src/test/utils.cc | 5 | ||||
-rw-r--r-- | libs/libmdbx/src/test/utils.h | 62 |
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 ¶ms, 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); } |