summaryrefslogtreecommitdiff
path: root/libs/libmdbx/src
diff options
context:
space:
mode:
authorGeorge Hazan <ghazan@miranda.im>2018-09-17 21:21:41 +0300
committerGeorge Hazan <ghazan@miranda.im>2018-09-17 21:21:49 +0300
commitd6edbc9108a3ea183774c84993f679e824ad8a6f (patch)
tree8c00b848a3e72b502493648d7eaaa05f68addd87 /libs/libmdbx/src
parentdcc57d5f94f77ed22127cbe3ac2babcf2dd6d4e3 (diff)
libmdbx: another important bugfix
Diffstat (limited to 'libs/libmdbx/src')
-rw-r--r--libs/libmdbx/src/CMakeLists.txt193
-rw-r--r--libs/libmdbx/src/TODO.md89
-rw-r--r--libs/libmdbx/src/build.sh18
-rw-r--r--libs/libmdbx/src/mdbx.h2
-rw-r--r--libs/libmdbx/src/package.sh25
-rw-r--r--libs/libmdbx/src/src/mdbx.c225
-rw-r--r--libs/libmdbx/src/test/gc.sh30
-rw-r--r--libs/libmdbx/src/test/hill.cc7
-rw-r--r--libs/libmdbx/src/test/jitter.cc6
-rw-r--r--libs/libmdbx/src/test/keygen.cc4
-rw-r--r--libs/libmdbx/src/test/keygen.h3
-rw-r--r--libs/libmdbx/src/test/main.cc8
-rw-r--r--libs/libmdbx/src/test/osal-unix.cc14
-rw-r--r--libs/libmdbx/src/test/test.cc10
-rw-r--r--libs/libmdbx/src/test/test.h2
-rw-r--r--libs/libmdbx/src/test/utils.cc5
-rw-r--r--libs/libmdbx/src/test/utils.h2
17 files changed, 162 insertions, 481 deletions
diff --git a/libs/libmdbx/src/CMakeLists.txt b/libs/libmdbx/src/CMakeLists.txt
deleted file mode 100644
index b664075556..0000000000
--- a/libs/libmdbx/src/CMakeLists.txt
+++ /dev/null
@@ -1,193 +0,0 @@
-cmake_minimum_required(VERSION 2.8.7)
-set(TARGET mdbx)
-project(${TARGET})
-
-message(WARNING "
-***************************************************************
- MDBX is under active development, database format and API
- aren't stable at least until 2018Q3. New version won't be
- backwards compatible. Main focus of the rework is to provide
- clear and robust API and new features.
-***************************************************************
-")
-
-set(MDBX_VERSION_MAJOR 0)
-set(MDBX_VERSION_MINOR 1)
-set(MDBX_VERSION_RELEASE 3)
-set(MDBX_VERSION_REVISION 1)
-
-set(MDBX_VERSION_STRING ${MDBX_VERSION_MAJOR}.${MDBX_VERSION_MINOR}.${MDBX_VERSION_RELEASE})
-
-enable_language(C)
-enable_language(CXX)
-
-set(CMAKE_CXX_STANDARD 11)
-set(CMAKE_CXX_STANDARD_REQUIRED on)
-
-add_definitions(-DNDEBUG=1 -DMDBX_DEBUG=0 -DLIBMDBX_EXPORTS=1 -D_GNU_SOURCE=1)
-
-find_package(Threads REQUIRED)
-
-get_directory_property(hasParent PARENT_DIRECTORY)
-if(hasParent)
- set(STANDALONE_BUILD 0)
-else()
- set(STANDALONE_BUILD 1)
- enable_testing()
-
- if (CMAKE_C_COMPILER_ID MATCHES GNU)
- set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O2")
- set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g3")
- set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall")
- set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Werror")
- set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wextra")
- set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -ffunction-sections")
- set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC")
- set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fvisibility=hidden")
- set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu11")
- set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pthread")
- endif()
-
- if (CMAKE_CXX_COMPILER_ID MATCHES GNU)
- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -W")
- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall")
- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror")
- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wpointer-arith")
- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-sign-compare")
- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wformat-security")
- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Woverloaded-virtual")
- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wwrite-strings")
- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fmax-errors=20")
- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=hidden")
- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-parameter -Wunused-function -Wunused-variable -Wunused-value -Wmissing-declarations")
- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-missing-field-initializers")
- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wcast-qual")
- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ggdb")
- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-omit-frame-pointer")
- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-strict-aliasing")
- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -finline-functions-called-once")
- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-packed-bitfield-compat")
-
- set(CMAKE_CXX_FLAGS_DEBUG "-O0 -g3")
- set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O3 -g3")
- endif()
-
- if (COVERAGE)
- if (NOT "${CMAKE_BUILD_TYPE}" STREQUAL "Debug")
- message(FATAL_ERROR "Coverage requires -DCMAKE_BUILD_TYPE=Debug Current value=${CMAKE_BUILD_TYPE}")
- endif()
-
- message(STATUS "Setting coverage compiler flags")
- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -ggdb3 -O0 --coverage -fprofile-arcs -ftest-coverage")
- set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -ggdb3 -O0 --coverage -fprofile-arcs -ftest-coverage")
- add_definitions(-DCOVERAGE_TEST)
- endif()
-
- if (NOT TRAVIS)
- set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fsanitize=address -fsanitize=leak -fstack-protector-strong -static-libasan")
- endif()
-endif()
-
-set(${TARGET}_SRC
- mdbx.h
- src/bits.h
- src/defs.h
- src/lck-posix.c
- src/mdbx.c
- src/osal.c
- src/osal.h
- src/version.c
- )
-
-add_library(${TARGET}_STATIC STATIC
- ${${TARGET}_SRC}
- )
-
-add_library(${TARGET} ALIAS ${TARGET}_STATIC)
-
-add_library(${TARGET}_SHARED SHARED
- ${${TARGET}_SRC}
- )
-
-set_target_properties(${TARGET}_SHARED PROPERTIES
- VERSION ${MDBX_VERSION_STRING}
- SOVERSION ${MDBX_VERSION_MAJOR}
- OUTPUT_NAME ${TARGET}
- CLEAN_DIRECT_OUTPUT 1
- )
-
-set_target_properties(${TARGET}_STATIC PROPERTIES
- VERSION ${MDBX_VERSION_STRING}
- SOVERSION ${MDBX_VERSION_MAJOR}
- OUTPUT_NAME ${TARGET}
- CLEAN_DIRECT_OUTPUT 1
- )
-
-target_include_directories(${TARGET}_STATIC PUBLIC
- ${CMAKE_CURRENT_SOURCE_DIR})
-target_include_directories(${TARGET}_SHARED PUBLIC
- ${CMAKE_CURRENT_SOURCE_DIR})
-
-target_link_libraries(${TARGET}_STATIC ${CMAKE_THREAD_LIBS_INIT})
-target_link_libraries(${TARGET}_SHARED ${CMAKE_THREAD_LIBS_INIT})
-if(UNIX AND NOT APPLE)
- target_link_libraries(${TARGET}_STATIC rt)
- target_link_libraries(${TARGET}_SHARED rt)
-endif()
-
-install(TARGETS ${TARGET}_STATIC DESTINATION ${CMAKE_INSTALL_PREFIX}/lib64 COMPONENT mdbx)
-install(TARGETS ${TARGET}_SHARED DESTINATION ${CMAKE_INSTALL_PREFIX}/lib64 COMPONENT mdbx)
-install(FILES mdbx.h DESTINATION ${CMAKE_INSTALL_PREFIX}/include COMPONENT mdbx-devel)
-
-add_subdirectory(src/tools)
-add_subdirectory(test)
-add_subdirectory(test/pcrf)
-add_subdirectory(tutorial)
-
-##############################################################################
-
-set(CPACK_GENERATOR "RPM")
-set(CPACK_RPM_COMPONENT_INSTALL ON)
-
-# Version
-if (NOT "$ENV{BUILD_NUMBER}" STREQUAL "")
- set(CPACK_PACKAGE_RELEASE $ENV{BUILD_NUMBER})
-else()
- if (NOT "$ENV{CI_PIPELINE_ID}" STREQUAL "")
- set(CPACK_PACKAGE_RELEASE $ENV{CI_PIPELINE_ID})
- else()
- set(CPACK_PACKAGE_RELEASE 1)
- endif()
-endif()
-set(CPACK_RPM_PACKAGE_RELEASE ${CPACK_PACKAGE_RELEASE})
-
-set(CPACK_PACKAGE_VERSION ${MDBX_VERSION_STRING})
-set(CPACK_PACKAGE_VERSION_FULL ${CPACK_PACKAGE_VERSION}-${CPACK_PACKAGE_RELEASE})
-
-set(CPACK_RPM_mdbx-devel_PACKAGE_REQUIRES "mdbx = ${CPACK_PACKAGE_VERSION}")
-
-set(CPACK_RPM_SPEC_INSTALL_POST "/bin/true")
-set(CPACK_RPM_mdbx_PACKAGE_NAME mdbx)
-set(CPACK_RPM_mdbx-devel_PACKAGE_NAME mdbx-devel)
-set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "The revised and extended descendant of Symas LMDB")
-
-set(CPACK_PACKAGE_VENDOR "???")
-set(CPACK_PACKAGE_CONTACT "Vladimir Romanov")
-set(CPACK_PACKAGE_RELOCATABLE false)
-set(CPACK_RPM_PACKAGE_ARCHITECTURE "x86_64")
-set(CPACK_RPM_PACKAGE_REQUIRES "")
-set(CPACK_RPM_PACKAGE_GROUP "Applications/Database")
-
-set(CPACK_RPM_mdbx_FILE_NAME "${CPACK_RPM_mdbx_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION_FULL}.${CPACK_RPM_PACKAGE_ARCHITECTURE}.rpm")
-set(CPACK_RPM_mdbx-devel_FILE_NAME "${CPACK_RPM_mdbx-devel_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION_FULL}.${CPACK_RPM_PACKAGE_ARCHITECTURE}.rpm")
-
-set(CPACK_RPM_EXCLUDE_FROM_AUTO_FILELIST_ADDITION
- /usr/local
- /usr/local/bin
- /usr/local/lib64
- /usr/local/include
- /usr/local/man
- /usr/local/man/man1
- )
-
-include(CPack)
diff --git a/libs/libmdbx/src/TODO.md b/libs/libmdbx/src/TODO.md
deleted file mode 100644
index 810b18dc4a..0000000000
--- a/libs/libmdbx/src/TODO.md
+++ /dev/null
@@ -1,89 +0,0 @@
-Допеределки
-===========
-- [ ] Перевод mdbx-tools на С++ и сборка для Windows.
-- [ ] Переход на CMake, замена заглушек mdbx_version и mdbx_build.
-- [ ] Актуализация README.md
-- [ ] Переход на C++11, добавление #pramga detect_mismatch().
-- [ ] Убрать MDB_DEBUG (всегда: логирование важный ситуаций и ошибок, опционально: включение ассертов и трассировка).
-- [ ] Заменить mdbx_debug на mdbx_trace, и почистить...
-- [ ] Заметить максимум assert() на mdbx_assert(env, ...).
-
-Качество и CI
-=============
-- [ ] Добавить в CI linux сборки для 32-битных таргетов.
-
-Доработки API
-=============
-- [ ] Поправить/Добавить описание нового API.
-- [ ] Добавить возможность "подбора" режима для mdbx_env_open().
-- [ ] Переименовать в API: env->db, db->tbl.
-
-Тесты
-=====
-- [ ] Тестирование поддержки lockless-режима.
-- [x] Додумать имя и размещение тестовой БД по-умолчанию.
-- [ ] Реализовать cleanup в тесте.
-- [ ] usage для теста.
-- [ ] Логирование в файл, плюс более полный progress bar.
-- [ ] Опция игнорирования (пропуска части теста) при переполнении БД.
-- [ ] Базовый бенчмарк.
-
-Развитие
-========
-- [ ] Отслеживание времени жизни DBI-хендлов.
-- [ ] Отрефакторить mdbx_freelist_save().
-- [ ] Хранить "свободный хвост" не связанный с freeDB в META.
-- [x] Возврат выделенных страниц в unallocated tail-pool.
-- [ ] Валидатор страниц БД по номеру транзакции:
- ~0 при переработке и номер транзакции при выделении,
- проверять что этот номер больше головы реклайминга и не-больше текущей транзакции.
-- [ ] Размещение overflow-pages в отдельном mmap/файле с собственной геометрией.
-- [ ] Зафиксировать формат БД.
-- [ ] Валидатор страниц по CRC32, плюс контроль номер транзакии под модулю 2^32.
-- [ ] Валидатор страниц по t1ha c контролем снимков/версий БД на основе Merkle Tree.
-- [ ] Возможность хранения ключей внутри data (libfptu).
-- [ ] Асинхронная фиксация (https://github.com/leo-yuriev/libmdbx/issues/5).
-- [ ] (Пере)Выделять память под IDL-списки с учетом реального кол-ва страниц, т.е. max(MDB_IDL_UM_MAX/MDB_IDL_UM_MAX, npages).
-
------------------------------------------------------------------------
-
-Сделано
-=======
-- [x] разделение errno и GetLastError().
-- [x] CI посредством AppVeyor.
-- [x] тест конкурентного доступа.
-- [x] тест основного функционала (заменить текущий треш).
-- [x] uint32/uint64 в структурах.
-- [x] Завершить переименование.
-- [x] Макросы версионности, сделать как в fpta (cmake?).
-- [x] Попробовать убрать yield (или что там с местом?).
-- [x] trinity для copy/compaction.
-- [x] trinity для mdbx_chk и mdbx_stat.
-- [x] проверки с mdbx_meta_eq.
-- [x] Не проверять режим при открытии в readonly.
-- [x] Поправить выбор tail в mdbx_chk.
-- [x] Там-же проверять позицию реклайминга.
-- [x] поправить проблему открытия после READ-ONLY.
-- [x] static-assertы на размер/выравнивание lck, meta и т.п.
-- [x] Зачистить size_t.
-- [x] Добавить локи вокруг dbi.
-- [x] Привести в порядок volatile.
-- [x] контроль meta.mapsize.
-- [x] переработка формата: заголовки страниц, meta, clk...
-- [x] зачистка Doxygen и бесполезных комментариев.
-- [x] Добавить поле типа контрольной суммы.
-- [x] Добавить поле/флаг размера pgno_t.
-- [x] Поменять сигнатуры.
-- [x] Добавить мета-страницы в coredump, проверить lck.
-- [x] Сделать список для txnid_t, кода sizeof(txnid_t) > sizeof(pgno_t) и вернуть размер pgno_t.
-- [x] Избавиться от умножения на размер страницы (заменить на сдвиг).
-- [x] Устранение всех предупреждений (в том числе под Windows).
-- [x] Добавить 'mti_reader_finished_flag'.
-- [x] Погасить все level4-warnings от MSVC, включить /WX.
-- [x] Проверка посредством Coverity с гашением всех дефектов.
-- [x] Полная матрица Windows-сборок (2013/2015/2017).
-- [x] Дать возможность задавать размер страницы при создании БД.
-- [x] Изменение mapsize через API с блокировкой и увеличением txn.
-- [x] Контроль размера страницы полного размера и кол-ва страниц при создании и обновлении.
-- [x] Инкрементальный mmap.
-- [x] Инкрементальное приращение размера (колбэк стратегии?).
diff --git a/libs/libmdbx/src/build.sh b/libs/libmdbx/src/build.sh
deleted file mode 100644
index 5170882265..0000000000
--- a/libs/libmdbx/src/build.sh
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/bash
-set -e
-CONFIG=$1
-
-if [[ -z "${CONFIG}" ]]; then
- CONFIG=Debug
-fi
-if [[ -r /opt/rh/devtoolset-6/enable ]]; then
- source /opt/rh/devtoolset-6/enable
-fi
-#rm -f -r build || true
-mkdir -p cmake-build-${CONFIG}
-pushd cmake-build-${CONFIG} &> /dev/null
-if [[ ! -r Makefile ]]; then
- cmake .. -DCMAKE_BUILD_TYPE=${CONFIG}
-fi
-make -j8 || exit 1
-popd &> /dev/null
diff --git a/libs/libmdbx/src/mdbx.h b/libs/libmdbx/src/mdbx.h
index 2d0eeba949..8720f34ae0 100644
--- a/libs/libmdbx/src/mdbx.h
+++ b/libs/libmdbx/src/mdbx.h
@@ -1251,6 +1251,8 @@ LIBMDBX_API int mdbx_drop(MDBX_txn *txn, MDBX_dbi dbi, int del);
* - MDBX_EINVAL - an invalid parameter was specified. */
LIBMDBX_API int mdbx_get(MDBX_txn *txn, MDBX_dbi dbi, MDBX_val *key,
MDBX_val *data);
+LIBMDBX_API int mdbx_get2(MDBX_txn *txn, MDBX_dbi dbi, MDBX_val *key,
+ MDBX_val *data);
/* Store items into a database.
*
diff --git a/libs/libmdbx/src/package.sh b/libs/libmdbx/src/package.sh
deleted file mode 100644
index d7f9ab297a..0000000000
--- a/libs/libmdbx/src/package.sh
+++ /dev/null
@@ -1,25 +0,0 @@
-#!/bin/bash
-set -e
-
-CONFIG=$1
-
-if [[ -z "${CONFIG}" ]]; then
- CONFIG=Debug
-fi
-
-DIRNAME=`dirname ${BASH_SOURCE[0]}`
-DIRNAME=`readlink --canonicalize ${DIRNAME}`
-
-if [[ -r /opt/rh/devtoolset-6/enable ]]; then
- source /opt/rh/devtoolset-6/enable
-fi
-
-mkdir -p cmake-build-${CONFIG}
-pushd cmake-build-${CONFIG} &> /dev/null
-if [[ ! -r Makefile ]]; then
- cmake .. -DCMAKE_BUILD_TYPE=${CONFIG}
-fi
-rm -f *.rpm
-make -j8 package || exit 1
-rm -f *-Unspecified.rpm
-popd &> /dev/null
diff --git a/libs/libmdbx/src/src/mdbx.c b/libs/libmdbx/src/src/mdbx.c
index 82e0959134..9cfeba8809 100644
--- a/libs/libmdbx/src/src/mdbx.c
+++ b/libs/libmdbx/src/src/mdbx.c
@@ -3680,6 +3680,25 @@ static int mdbx_update_gc(MDBX_txn *txn) {
if (unlikely(rc != MDBX_SUCCESS))
goto bailout;
+ txnid_t reclaiming_head_id = env->me_last_reclaimed;
+ if (unlikely(reclaiming_head_id == 0)) {
+ reclaiming_head_id = mdbx_find_oldest(txn) - 1;
+ MDBX_val key;
+ rc = mdbx_cursor_get(&mc, &key, NULL, MDBX_FIRST);
+ if (unlikely(rc != MDBX_SUCCESS)) {
+ if (rc != MDBX_NOTFOUND)
+ goto bailout;
+ } else if (unlikely(key.iov_len != sizeof(txnid_t))) {
+ rc = MDBX_CORRUPTED;
+ goto bailout;
+ } else {
+ txnid_t first_pg;
+ memcpy(&first_pg, key.iov_base, sizeof(txnid_t));
+ if (reclaiming_head_id >= first_pg)
+ reclaiming_head_id = first_pg - 1;
+ }
+ }
+
retry:
mdbx_trace(" >> restart");
mdbx_tassert(txn, mdbx_pnl_check(env->me_reclaimed_pglist, true));
@@ -3687,7 +3706,7 @@ retry:
filled_gc_slot = ~0u;
txnid_t cleaned_gc_id = 0, head_gc_id = env->me_last_reclaimed
? env->me_last_reclaimed
- : ~(txnid_t)0;
+ : reclaiming_head_id;
if (unlikely(/* paranoia */ ++loop > 42)) {
mdbx_error("too more loops %u, bailout", loop);
@@ -3963,7 +3982,7 @@ retry:
if (0 >= (int)left)
break;
- const unsigned max_spread = 10;
+ const unsigned prefer_max_scatter = 257;
txnid_t reservation_gc_id;
if (lifo) {
assert(txn->mt_lifo_reclaimed != NULL);
@@ -3976,7 +3995,7 @@ retry:
}
if (head_gc_id > 1 &&
- MDBX_PNL_SIZE(txn->mt_lifo_reclaimed) < max_spread &&
+ MDBX_PNL_SIZE(txn->mt_lifo_reclaimed) < prefer_max_scatter &&
left > ((unsigned)MDBX_PNL_SIZE(txn->mt_lifo_reclaimed) -
reused_gc_slots) *
env->me_maxgc_ov1page) {
@@ -3995,7 +4014,7 @@ retry:
/* LY: freedb is empty, will look any free txn-id in high2low order. */
do {
--head_gc_id;
- assert(MDBX_PNL_LAST(txn->mt_lifo_reclaimed) > head_gc_id);
+ mdbx_assert(env, MDBX_PNL_LAST(txn->mt_lifo_reclaimed) > head_gc_id);
rc = mdbx_txl_append(&txn->mt_lifo_reclaimed, head_gc_id);
if (unlikely(rc != MDBX_SUCCESS))
goto bailout;
@@ -4005,7 +4024,7 @@ retry:
" to lifo-reclaimed, cleaned-gc-slot = %u",
dbg_prefix_mode, head_gc_id, cleaned_gc_slot);
} while (head_gc_id > 1 &&
- MDBX_PNL_SIZE(txn->mt_lifo_reclaimed) < max_spread &&
+ MDBX_PNL_SIZE(txn->mt_lifo_reclaimed) < prefer_max_scatter &&
left > ((unsigned)MDBX_PNL_SIZE(txn->mt_lifo_reclaimed) -
reused_gc_slots) *
env->me_maxgc_ov1page);
@@ -4045,7 +4064,10 @@ retry:
if (chunk < env->me_maxgc_ov1page * 2)
chunk /= 2;
else {
- const unsigned threshold = env->me_maxgc_ov1page * avail_gs_slots;
+ const unsigned threshold =
+ env->me_maxgc_ov1page * ((avail_gs_slots < prefer_max_scatter)
+ ? avail_gs_slots
+ : prefer_max_scatter);
if (left < threshold)
chunk = env->me_maxgc_ov1page;
else {
@@ -4071,7 +4093,7 @@ retry:
chunk = (avail >= tail) ? tail - span
: (avail_gs_slots > 3 &&
- reused_gc_slots < max_spread - 3)
+ reused_gc_slots < prefer_max_scatter - 3)
? avail - span
: tail;
}
@@ -4112,6 +4134,14 @@ retry:
settled += chunk;
mdbx_trace("%s.settled %u (+%u), continue", dbg_prefix_mode, settled,
chunk);
+
+ if (txn->mt_lifo_reclaimed &&
+ unlikely(amount < MDBX_PNL_SIZE(env->me_reclaimed_pglist))) {
+ mdbx_notice("** restart: reclaimed-list growth %u -> %u", amount,
+ (unsigned)MDBX_PNL_SIZE(env->me_reclaimed_pglist));
+ goto retry;
+ }
+
continue;
}
@@ -7142,6 +7172,41 @@ int mdbx_get(MDBX_txn *txn, MDBX_dbi dbi, MDBX_val *key, MDBX_val *data) {
return mdbx_cursor_set(&cx.outer, key, data, MDBX_SET, &exact);
}
+int mdbx_get2(MDBX_txn *txn, MDBX_dbi dbi, MDBX_val *key, MDBX_val *data) {
+ int exact = 0;
+ DKBUF;
+
+ mdbx_debug("===> get db %u key [%s]", dbi, DKEY(key));
+
+ if (unlikely(!key || !data || !txn))
+ return MDBX_EINVAL;
+
+ if (unlikely(txn->mt_signature != MDBX_MT_SIGNATURE))
+ return MDBX_EBADSIGN;
+
+ if (unlikely(txn->mt_owner != mdbx_thread_self()))
+ return MDBX_THREAD_MISMATCH;
+
+ if (unlikely(!TXN_DBI_EXIST(txn, dbi, DB_USRVALID)))
+ return MDBX_EINVAL;
+
+ if (unlikely(txn->mt_flags & MDBX_TXN_BLOCKED))
+ return MDBX_BAD_TXN;
+
+ MDBX_cursor_couple cx;
+ int rc = mdbx_cursor_init(&cx.outer, txn, dbi);
+ if (unlikely(rc != MDBX_SUCCESS))
+ return rc;
+
+ const int op =
+ (txn->mt_dbs[dbi].md_flags & MDBX_DUPSORT) ? MDBX_GET_BOTH : MDBX_SET_KEY;
+ rc = mdbx_cursor_set(&cx.outer, key, data, op, &exact);
+ if (unlikely(rc != MDBX_SUCCESS))
+ return rc;
+
+ return exact ? MDBX_SUCCESS : MDBX_RESULT_TRUE;
+}
+
/* Find a sibling for a page.
* Replaces the page at the top of the cursor's stack with the specified
* sibling, if one exists.
@@ -7739,11 +7804,6 @@ int mdbx_cursor_get(MDBX_cursor *mc, MDBX_val *key, MDBX_val *data,
return MDBX_INCOMPATIBLE;
/* FALLTHRU */
case MDBX_SET:
-#ifndef SLAPD_LMDB_LEGACY
- if (op == MDBX_SET && unlikely(data != NULL))
- return MDBX_EINVAL;
-#endif /* SLAPD_LMDB_LEGACY */
- /* FALLTHRU */
case MDBX_SET_KEY:
case MDBX_SET_RANGE:
if (unlikely(key == NULL))
@@ -8931,131 +8991,6 @@ static int __must_check_result mdbx_node_add_leaf(MDBX_cursor *mc,
return MDBX_SUCCESS;
}
-#if 0
-/* Add a node to the page pointed to by the cursor.
- * Set MDBX_TXN_ERROR on failure.
- *
- * [in] mc The cursor for this operation.
- * [in] indx The index on the page where the new node should be added.
- * [in] key The key for the new node.
- * [in] data The data for the new node, if any.
- * [in] pgno The page number, if adding a branch node.
- * [in] flags Flags for the node.
- *
- * Returns 0 on success, non-zero on failure. Possible errors are:
- *
- * MDBX_ENOMEM - failed to allocate overflow pages for the node.
- * MDBX_PAGE_FULL - there is insufficient room in the page. This error
- * should never happen since all callers already calculate
- * the page's free space before calling this function. */
-static int mdbx_node_add(MDBX_cursor *mc, unsigned indx, const MDBX_val *key,
- MDBX_val *data, pgno_t pgno, unsigned flags) {
- unsigned i;
- size_t node_size = NODESIZE;
- intptr_t room;
- MDBX_node *node;
- MDBX_page *mp = mc->mc_pg[mc->mc_top];
- MDBX_page *ofp = NULL; /* overflow page */
- void *ndata;
-
- mdbx_cassert(mc, mp->mp_upper >= mp->mp_lower);
-
- DKBUF;
- mdbx_debug("add to %s %spage %" PRIaPGNO " index %i, data size %" PRIuPTR
- " key size %" PRIuPTR " [%s]",
- IS_LEAF(mp) ? "leaf" : "branch", IS_SUBP(mp) ? "sub-" : "",
- mp->mp_pgno, indx, data ? data->iov_len : 0,
- key ? key->iov_len : 0, DKEY(key));
-
- room = (intptr_t)SIZELEFT(mp) - (intptr_t)sizeof(indx_t);
- if (key != NULL)
- node_size += key->iov_len;
- if (IS_LEAF(mp)) {
- mdbx_cassert(mc, key && data);
- if (unlikely(F_ISSET(flags, F_BIGDATA))) {
- /* Data already on overflow page. */
- node_size += sizeof(pgno_t);
- } else if (unlikely(node_size + data->iov_len >
- mc->mc_txn->mt_env->me_nodemax)) {
- pgno_t ovpages = OVPAGES(mc->mc_txn->mt_env, data->iov_len);
- int rc;
- /* Put data on overflow page. */
- mdbx_debug("data size is %" PRIuPTR ", node would be %" PRIuPTR
- ", put data on overflow page",
- data->iov_len, node_size + data->iov_len);
- node_size = EVEN(node_size + sizeof(pgno_t));
- if ((intptr_t)node_size > room)
- goto full;
- if ((rc = mdbx_page_new(mc, P_OVERFLOW, ovpages, &ofp)))
- return rc;
- mdbx_debug("allocated overflow page %" PRIaPGNO, ofp->mp_pgno);
- flags |= F_BIGDATA;
- goto update;
- } else {
- node_size += data->iov_len;
- }
- }
- node_size = EVEN(node_size);
- if (unlikely((intptr_t)node_size > room))
- goto full;
-
-update:
- /* Move higher pointers up one slot. */
- for (i = NUMKEYS(mp); i > indx; i--)
- mp->mp_ptrs[i] = mp->mp_ptrs[i - 1];
-
- /* Adjust free space offsets. */
- size_t ofs = mp->mp_upper - node_size;
- mdbx_cassert(mc, ofs >= mp->mp_lower + sizeof(indx_t));
- mdbx_cassert(mc, ofs <= UINT16_MAX);
- mp->mp_ptrs[indx] = (uint16_t)ofs;
- mp->mp_upper = (uint16_t)ofs;
- mp->mp_lower += sizeof(indx_t);
-
- /* Write the node data. */
- node = NODEPTR(mp, indx);
- node->mn_ksize = (key == NULL) ? 0 : (uint16_t)key->iov_len;
- node->mn_flags = (uint16_t)flags;
- if (IS_LEAF(mp))
- SETDSZ(node, data->iov_len);
- else
- SETPGNO(node, pgno);
-
- if (key)
- memcpy(NODEKEY(node), key->iov_base, key->iov_len);
-
- if (IS_LEAF(mp)) {
- ndata = NODEDATA(node);
- if (likely(ofp == NULL)) {
- if (unlikely(F_ISSET(flags, F_BIGDATA)))
- memcpy(ndata, data->iov_base, sizeof(pgno_t));
- else if (F_ISSET(flags, MDBX_RESERVE))
- data->iov_base = ndata;
- else if (likely(ndata != data->iov_base))
- memcpy(ndata, data->iov_base, data->iov_len);
- } else {
- memcpy(ndata, &ofp->mp_pgno, sizeof(pgno_t));
- ndata = PAGEDATA(ofp);
- if (F_ISSET(flags, MDBX_RESERVE))
- data->iov_base = ndata;
- else if (likely(ndata != data->iov_base))
- memcpy(ndata, data->iov_base, data->iov_len);
- }
- }
-
- return MDBX_SUCCESS;
-
-full:
- mdbx_debug("not enough room in page %" PRIaPGNO ", got %u ptrs", mp->mp_pgno,
- NUMKEYS(mp));
- mdbx_debug("upper-lower = %u - %u = %" PRIiPTR, mp->mp_upper, mp->mp_lower,
- room);
- mdbx_debug("node size = %" PRIuPTR, node_size);
- mc->mc_txn->mt_flags |= MDBX_TXN_ERROR;
- return MDBX_PAGE_FULL;
-}
-#endif
-
/* Delete the specified node from a page.
* [in] mc Cursor pointing to the node to delete.
* [in] ksize The size of a node. Only used if the page is
@@ -11751,6 +11686,20 @@ int mdbx_dbi_open_ex(MDBX_txn *txn, const char *table_name, unsigned user_flags,
if (unlikely(!txn || !dbi || (user_flags & ~VALID_FLAGS) != 0))
return MDBX_EINVAL;
+ switch (user_flags &
+ (MDBX_INTEGERDUP | MDBX_DUPFIXED | MDBX_DUPSORT | MDBX_REVERSEDUP)) {
+ default:
+ return MDBX_EINVAL;
+ case MDBX_DUPSORT:
+ case MDBX_DUPSORT | MDBX_REVERSEDUP:
+ case MDBX_DUPSORT | MDBX_DUPFIXED:
+ case MDBX_DUPSORT | MDBX_DUPFIXED | MDBX_REVERSEDUP:
+ case MDBX_DUPSORT | MDBX_DUPFIXED | MDBX_INTEGERDUP:
+ case MDBX_DUPSORT | MDBX_DUPFIXED | MDBX_INTEGERDUP | MDBX_REVERSEDUP:
+ case 0:
+ break;
+ }
+
if (unlikely(txn->mt_signature != MDBX_MT_SIGNATURE))
return MDBX_EBADSIGN;
@@ -12983,7 +12932,7 @@ int mdbx_replace(MDBX_txn *txn, MDBX_dbi dbi, MDBX_val *key, MDBX_val *new_data,
rc = mdbx_cursor_get(&cx.outer, &present_key, &present_data, MDBX_SET_KEY);
if (unlikely(rc != MDBX_SUCCESS)) {
old_data->iov_base = NULL;
- old_data->iov_len = rc;
+ old_data->iov_len = 0;
if (rc != MDBX_NOTFOUND || (flags & MDBX_CURRENT))
goto bailout;
} else if (flags & MDBX_NOOVERWRITE) {
diff --git a/libs/libmdbx/src/test/gc.sh b/libs/libmdbx/src/test/gc.sh
index bddd92af24..0625518a4f 100644
--- a/libs/libmdbx/src/test/gc.sh
+++ b/libs/libmdbx/src/test/gc.sh
@@ -32,31 +32,31 @@ function probe {
caption="Failfast #1" probe \
--pagesize=min --size=6G --table=+data.dups --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=max \
- --nops=99999 --batch.write=9 --mode=-writemap,-coalesce,+lifo --keygen.seed=248240655 --hill
+ --nops=99999 --batch.write=9 --mode=-writemap,-coalesce,+lifo --keygen.seed=248240655 basic
caption="Failfast #2" probe \
--pagesize=min --size=6G --table=-data.dups --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=1111 \
- --nops=999999 --batch.write=999 --mode=+writemap,+coalesce,+lifo --keygen.seed=259083046 --hill
+ --nops=999999 --batch.write=999 --mode=+writemap,+coalesce,+lifo --keygen.seed=259083046 basic
caption="Failfast #3" probe \
--pagesize=min --size=6G --table=-data.dups --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=1111 \
- --nops=999999 --batch.write=999 --mode=+writemap,+coalesce,+lifo --keygen.seed=522365681 --hill
+ --nops=999999 --batch.write=999 --mode=+writemap,+coalesce,+lifo --keygen.seed=522365681 basic
caption="Failfast #4" probe \
--pagesize=min --size=6G --table=-data.dups --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=1111 \
- --nops=999999 --batch.write=9999 --mode=-writemap,+coalesce,+lifo --keygen.seed=866083781 --hill
+ --nops=999999 --batch.write=9999 --mode=-writemap,+coalesce,+lifo --keygen.seed=866083781 basic
caption="Failfast #5" probe \
--pagesize=min --size=6G --table=-data.dups --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=1111 \
- --nops=999999 --batch.write=999 --mode=+writemap,-coalesce,+lifo --keygen.seed=246539192 --hill
+ --nops=999999 --batch.write=999 --mode=+writemap,-coalesce,+lifo --keygen.seed=246539192 basic
caption="Failfast #6" probe \
--pagesize=min --size=6G --table=-data.dups --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=1111 \
- --nops=999999 --batch.write=999 --mode=+writemap,+coalesce,+lifo --keygen.seed=540406278 --hill
+ --nops=999999 --batch.write=999 --mode=+writemap,+coalesce,+lifo --keygen.seed=540406278 basic
caption="Failfast #7" probe \
--pagesize=min --size=6G --table=+data.dups --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=max \
- --nops=999999 --batch.write=999 --mode=-writemap,+coalesce,+lifo --keygen.seed=619798690 --hill
+ --nops=999999 --batch.write=999 --mode=-writemap,+coalesce,+lifo --keygen.seed=619798690 basic
count=0
for nops in {2..7}; do
@@ -65,14 +65,26 @@ for nops in {2..7}; do
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} --hill
+ --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} --hill
+ --keygen.seed=${seed} basic
done
done
done
diff --git a/libs/libmdbx/src/test/hill.cc b/libs/libmdbx/src/test/hill.cc
index 0193c4f2f5..10cb308942 100644
--- a/libs/libmdbx/src/test/hill.cc
+++ b/libs/libmdbx/src/test/hill.cc
@@ -53,7 +53,7 @@ bool testcase_hill::run() {
*/
/* TODO: работа в несколько потоков */
- keyvalue_maker.setup(config.params, 0 /* thread_number */);
+ keyvalue_maker.setup(config.params, config.actor_id, 0 /* thread_number */);
keygen::buffer a_key = keygen::alloc(config.params.keylen_max);
keygen::buffer a_data_0 = keygen::alloc(config.params.datalen_max);
@@ -114,6 +114,7 @@ bool testcase_hill::run() {
log_trace("uphill: update-a (age %" PRIu64 "->0) %" PRIu64, age_shift,
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))
@@ -126,6 +127,7 @@ bool testcase_hill::run() {
// удаляем вторую запись
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);
@@ -158,6 +160,7 @@ bool testcase_hill::run() {
a_serial);
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))
@@ -184,6 +187,7 @@ 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);
@@ -195,6 +199,7 @@ bool testcase_hill::run() {
// удаляем вторую запись
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);
diff --git a/libs/libmdbx/src/test/jitter.cc b/libs/libmdbx/src/test/jitter.cc
index e7faf2a3f9..2551400443 100644
--- a/libs/libmdbx/src/test/jitter.cc
+++ b/libs/libmdbx/src/test/jitter.cc
@@ -58,7 +58,11 @@ bool testcase_jitter::run() {
jitter_delay();
db_close();
- report(1);
+
+ /* just 'align' nops with other tests with batching */
+ const auto batching =
+ std::max(config.params.batch_read, config.params.batch_write);
+ report(std::max(1u, batching / 2));
}
return true;
}
diff --git a/libs/libmdbx/src/test/keygen.cc b/libs/libmdbx/src/test/keygen.cc
index 0b68194dc1..c7a706065f 100644
--- a/libs/libmdbx/src/test/keygen.cc
+++ b/libs/libmdbx/src/test/keygen.cc
@@ -142,7 +142,7 @@ void __hot maker::pair(serial_t serial, const buffer &key, buffer &value,
}
}
-void maker::setup(const config::actor_params_pod &actor,
+void maker::setup(const config::actor_params_pod &actor, unsigned actor_id,
unsigned thread_number) {
key_essentials.flags =
actor.table_flags & (MDBX_INTEGERKEY | MDBX_REVERSEKEY | MDBX_DUPSORT);
@@ -161,7 +161,7 @@ void maker::setup(const config::actor_params_pod &actor,
assert(thread_number < 2);
(void)thread_number;
mapping = actor.keygen;
- salt = actor.keygen.seed * UINT64_C(14653293970879851569);
+ salt = (actor.keygen.seed + actor_id) * UINT64_C(14653293970879851569);
// FIXME: TODO
base = 0;
diff --git a/libs/libmdbx/src/test/keygen.h b/libs/libmdbx/src/test/keygen.h
index 449165ae9a..bbd97b29d1 100644
--- a/libs/libmdbx/src/test/keygen.h
+++ b/libs/libmdbx/src/test/keygen.h
@@ -119,7 +119,8 @@ public:
void pair(serial_t serial, const buffer &key, buffer &value,
serial_t value_age);
- void setup(const config::actor_params_pod &actor, unsigned thread_number);
+ void setup(const config::actor_params_pod &actor, unsigned actor_id,
+ unsigned thread_number);
bool increment(serial_t &serial, int delta);
};
diff --git a/libs/libmdbx/src/test/main.cc b/libs/libmdbx/src/test/main.cc
index 7493ab75c3..3384311b3b 100644
--- a/libs/libmdbx/src/test/main.cc
+++ b/libs/libmdbx/src/test/main.cc
@@ -154,8 +154,14 @@ int main(int argc, char *const argv[]) {
config::mode_bits))
continue;
if (config::parse_option(argc, argv, narg, "table", params.table_flags,
- config::table_bits))
+ config::table_bits)) {
+ if ((params.table_flags & MDBX_DUPFIXED) == 0)
+ params.table_flags &= ~MDBX_INTEGERDUP;
+ if ((params.table_flags & MDBX_DUPSORT) == 0)
+ params.table_flags &=
+ ~(MDBX_DUPFIXED | MDBX_REVERSEDUP | MDBX_INTEGERDUP);
continue;
+ }
if (config::parse_option(argc, argv, narg, "pagesize", params.pagesize,
mdbx_limits_pgsize_min(),
diff --git a/libs/libmdbx/src/test/osal-unix.cc b/libs/libmdbx/src/test/osal-unix.cc
index 8132e267ef..6661ae42c8 100644
--- a/libs/libmdbx/src/test/osal-unix.cc
+++ b/libs/libmdbx/src/test/osal-unix.cc
@@ -182,6 +182,9 @@ void osal_killall_actors(void) {
}
int osal_actor_poll(mdbx_pid_t &pid, unsigned timeout) {
+ struct timespec ts;
+ ts.tv_nsec = 0;
+ ts.tv_sec = timeout;
retry:
int status, options = WNOHANG;
#ifdef WUNTRACED
@@ -209,9 +212,16 @@ retry:
}
if (pid == 0) {
- if (timeout && sleep(timeout))
+ /* child still running */
+ if (ts.tv_sec == 0 && ts.tv_nsec == 0)
+ ts.tv_nsec = 1;
+ if (nanosleep(&ts, &ts) == 0) {
+ /* timeout and no signal fomr child */
+ pid = 0;
+ return 0;
+ }
+ if (errno == EINTR)
goto retry;
- return 0;
}
switch (errno) {
diff --git a/libs/libmdbx/src/test/test.cc b/libs/libmdbx/src/test/test.cc
index c28bbd221e..b9663c2a09 100644
--- a/libs/libmdbx/src/test/test.cc
+++ b/libs/libmdbx/src/test/test.cc
@@ -410,6 +410,16 @@ void testcase::db_table_close(MDBX_dbi handle) {
log_trace("<< testcase::db_table_close");
}
+void testcase::checkdata(const char *step, MDBX_dbi handle, MDBX_val key2check,
+ MDBX_val expected_valued) {
+ MDBX_val actual_value = expected_valued;
+ int rc = mdbx_get2(txn_guard.get(), handle, &key2check, &actual_value);
+ if (unlikely(rc != MDBX_SUCCESS))
+ failure_perror(step, rc);
+ if (!is_samedata(&actual_value, &expected_valued))
+ failure("%s data mismatch", step);
+}
+
//-----------------------------------------------------------------------------
bool test_execute(const actor_config &config) {
diff --git a/libs/libmdbx/src/test/test.h b/libs/libmdbx/src/test/test.h
index ef1c4caa47..4b10a40fef 100644
--- a/libs/libmdbx/src/test/test.h
+++ b/libs/libmdbx/src/test/test.h
@@ -112,6 +112,8 @@ protected:
void fetch_canary();
void update_canary(uint64_t increment);
void kick_progress(bool active) const;
+ void checkdata(const char *step, MDBX_dbi handle, MDBX_val key2check,
+ MDBX_val expected_valued);
MDBX_dbi db_table_open(bool create);
void db_table_drop(MDBX_dbi handle);
diff --git a/libs/libmdbx/src/test/utils.cc b/libs/libmdbx/src/test/utils.cc
index 53a750e314..5a5290f057 100644
--- a/libs/libmdbx/src/test/utils.cc
+++ b/libs/libmdbx/src/test/utils.cc
@@ -91,6 +91,11 @@ bool hex2data(const char *hex_begin, const char *hex_end, void *ptr,
return true;
}
+bool is_samedata(const MDBX_val *a, const MDBX_val *b) {
+ return a->iov_len == b->iov_len &&
+ memcmp(a->iov_base, b->iov_base, a->iov_len) == 0;
+}
+
//-----------------------------------------------------------------------------
/* TODO: replace my 'libmera' from t1ha. */
diff --git a/libs/libmdbx/src/test/utils.h b/libs/libmdbx/src/test/utils.h
index 5d62909fd5..42d497e86e 100644
--- a/libs/libmdbx/src/test/utils.h
+++ b/libs/libmdbx/src/test/utils.h
@@ -317,7 +317,7 @@ struct simple_checksum {
std::string data2hex(const void *ptr, size_t bytes, simple_checksum &checksum);
bool hex2data(const char *hex_begin, const char *hex_end, void *ptr,
size_t bytes, simple_checksum &checksum);
-
+bool is_samedata(const MDBX_val *a, const MDBX_val *b);
std::string format(const char *fmt, ...);
uint64_t entropy_ticks(void);