diff options
author | George Hazan <ghazan@miranda.im> | 2018-09-17 21:21:41 +0300 |
---|---|---|
committer | George Hazan <ghazan@miranda.im> | 2018-09-17 21:21:49 +0300 |
commit | d6edbc9108a3ea183774c84993f679e824ad8a6f (patch) | |
tree | 8c00b848a3e72b502493648d7eaaa05f68addd87 /libs/libmdbx/src | |
parent | dcc57d5f94f77ed22127cbe3ac2babcf2dd6d4e3 (diff) |
libmdbx: another important bugfix
Diffstat (limited to 'libs/libmdbx/src')
-rw-r--r-- | libs/libmdbx/src/CMakeLists.txt | 193 | ||||
-rw-r--r-- | libs/libmdbx/src/TODO.md | 89 | ||||
-rw-r--r-- | libs/libmdbx/src/build.sh | 18 | ||||
-rw-r--r-- | libs/libmdbx/src/mdbx.h | 2 | ||||
-rw-r--r-- | libs/libmdbx/src/package.sh | 25 | ||||
-rw-r--r-- | libs/libmdbx/src/src/mdbx.c | 225 | ||||
-rw-r--r-- | libs/libmdbx/src/test/gc.sh | 30 | ||||
-rw-r--r-- | libs/libmdbx/src/test/hill.cc | 7 | ||||
-rw-r--r-- | libs/libmdbx/src/test/jitter.cc | 6 | ||||
-rw-r--r-- | libs/libmdbx/src/test/keygen.cc | 4 | ||||
-rw-r--r-- | libs/libmdbx/src/test/keygen.h | 3 | ||||
-rw-r--r-- | libs/libmdbx/src/test/main.cc | 8 | ||||
-rw-r--r-- | libs/libmdbx/src/test/osal-unix.cc | 14 | ||||
-rw-r--r-- | libs/libmdbx/src/test/test.cc | 10 | ||||
-rw-r--r-- | libs/libmdbx/src/test/test.h | 2 | ||||
-rw-r--r-- | libs/libmdbx/src/test/utils.cc | 5 | ||||
-rw-r--r-- | libs/libmdbx/src/test/utils.h | 2 |
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); |