From fe0a3147a8050a987281beb4b5e1481b982c93bc Mon Sep 17 00:00:00 2001 From: George Hazan Date: Wed, 7 Mar 2018 18:03:25 +0300 Subject: merge with libmdbx 0.1 --- plugins/Dbx_mdbx/src/libmdbx/.gitignore | 32 +- plugins/Dbx_mdbx/src/libmdbx/AUTHORS | 1 + plugins/Dbx_mdbx/src/libmdbx/CMakeLists.txt | 189 +++++ plugins/Dbx_mdbx/src/libmdbx/COPYRIGHT | 2 +- plugins/Dbx_mdbx/src/libmdbx/Makefile | 10 +- plugins/Dbx_mdbx/src/libmdbx/README-RU.md | 689 +++++++++++++++++ plugins/Dbx_mdbx/src/libmdbx/README.md | 813 ++++++++------------- plugins/Dbx_mdbx/src/libmdbx/TODO.md | 2 +- plugins/Dbx_mdbx/src/libmdbx/build.sh | 18 + plugins/Dbx_mdbx/src/libmdbx/libmdbx.files | 6 + plugins/Dbx_mdbx/src/libmdbx/libmdbx.includes | 2 + plugins/Dbx_mdbx/src/libmdbx/mdbx.h | 4 +- plugins/Dbx_mdbx/src/libmdbx/package.sh | 25 + plugins/Dbx_mdbx/src/libmdbx/src/bits.h | 32 +- plugins/Dbx_mdbx/src/libmdbx/src/defs.h | 18 +- plugins/Dbx_mdbx/src/libmdbx/src/lck-posix.c | 2 +- .../Dbx_mdbx/src/libmdbx/src/tools/CMakeLists.txt | 19 + plugins/Dbx_mdbx/src/libmdbx/src/tools/mdbx_chk.c | 4 +- plugins/Dbx_mdbx/src/libmdbx/src/tools/mdbx_copy.1 | 2 +- plugins/Dbx_mdbx/src/libmdbx/src/tools/mdbx_copy.c | 2 +- plugins/Dbx_mdbx/src/libmdbx/src/tools/mdbx_dump.1 | 2 +- plugins/Dbx_mdbx/src/libmdbx/src/tools/mdbx_dump.c | 2 +- plugins/Dbx_mdbx/src/libmdbx/src/tools/mdbx_load.1 | 2 +- plugins/Dbx_mdbx/src/libmdbx/src/tools/mdbx_load.c | 2 +- plugins/Dbx_mdbx/src/libmdbx/src/tools/mdbx_stat.1 | 2 +- plugins/Dbx_mdbx/src/libmdbx/src/tools/mdbx_stat.c | 4 +- plugins/Dbx_mdbx/src/libmdbx/src/version.c | 2 +- plugins/Dbx_mdbx/src/libmdbx/test/CMakeLists.txt | 34 + plugins/Dbx_mdbx/src/libmdbx/test/base.h | 4 +- plugins/Dbx_mdbx/src/libmdbx/test/cases.cc | 2 +- plugins/Dbx_mdbx/src/libmdbx/test/chrono.cc | 2 +- plugins/Dbx_mdbx/src/libmdbx/test/chrono.h | 2 +- plugins/Dbx_mdbx/src/libmdbx/test/config.cc | 2 +- plugins/Dbx_mdbx/src/libmdbx/test/config.h | 2 +- plugins/Dbx_mdbx/src/libmdbx/test/dead.cc | 4 +- plugins/Dbx_mdbx/src/libmdbx/test/hill.cc | 4 +- plugins/Dbx_mdbx/src/libmdbx/test/jitter.cc | 2 +- plugins/Dbx_mdbx/src/libmdbx/test/keygen.cc | 2 +- plugins/Dbx_mdbx/src/libmdbx/test/keygen.h | 2 +- plugins/Dbx_mdbx/src/libmdbx/test/log.cc | 2 +- plugins/Dbx_mdbx/src/libmdbx/test/log.h | 2 +- plugins/Dbx_mdbx/src/libmdbx/test/main.cc | 2 +- plugins/Dbx_mdbx/src/libmdbx/test/osal-unix.cc | 2 +- plugins/Dbx_mdbx/src/libmdbx/test/osal-windows.cc | 2 +- plugins/Dbx_mdbx/src/libmdbx/test/osal.h | 2 +- .../Dbx_mdbx/src/libmdbx/test/pcrf/CMakeLists.txt | 7 + plugins/Dbx_mdbx/src/libmdbx/test/pcrf/README.md | 2 + plugins/Dbx_mdbx/src/libmdbx/test/pcrf/pcrf_test.c | 404 ++++++++++ plugins/Dbx_mdbx/src/libmdbx/test/test.cc | 4 +- plugins/Dbx_mdbx/src/libmdbx/test/test.h | 4 +- plugins/Dbx_mdbx/src/libmdbx/test/utils.cc | 75 +- plugins/Dbx_mdbx/src/libmdbx/test/utils.h | 2 +- .../Dbx_mdbx/src/libmdbx/tutorial/CMakeLists.txt | 7 + .../Dbx_mdbx/src/libmdbx/tutorial/sample-bdb.txt | 2 +- .../Dbx_mdbx/src/libmdbx/tutorial/sample-mdbx.c | 4 +- 55 files changed, 1855 insertions(+), 618 deletions(-) create mode 100644 plugins/Dbx_mdbx/src/libmdbx/CMakeLists.txt create mode 100644 plugins/Dbx_mdbx/src/libmdbx/README-RU.md create mode 100644 plugins/Dbx_mdbx/src/libmdbx/build.sh create mode 100644 plugins/Dbx_mdbx/src/libmdbx/package.sh create mode 100644 plugins/Dbx_mdbx/src/libmdbx/src/tools/CMakeLists.txt create mode 100644 plugins/Dbx_mdbx/src/libmdbx/test/CMakeLists.txt create mode 100644 plugins/Dbx_mdbx/src/libmdbx/test/pcrf/CMakeLists.txt create mode 100644 plugins/Dbx_mdbx/src/libmdbx/test/pcrf/README.md create mode 100644 plugins/Dbx_mdbx/src/libmdbx/test/pcrf/pcrf_test.c create mode 100644 plugins/Dbx_mdbx/src/libmdbx/tutorial/CMakeLists.txt (limited to 'plugins/Dbx_mdbx') diff --git a/plugins/Dbx_mdbx/src/libmdbx/.gitignore b/plugins/Dbx_mdbx/src/libmdbx/.gitignore index 117141cb06..981df32c76 100644 --- a/plugins/Dbx_mdbx/src/libmdbx/.gitignore +++ b/plugins/Dbx_mdbx/src/libmdbx/.gitignore @@ -1,35 +1,37 @@ -*[~#] *.[ao] *.bak -.le.ini -core -core.* *.exe *.gcda *.gcno *.gcov -libmdbx.creator.user *.lo -mdbx_chk -mdbx_copy -mdbx-dll.VC.db +*.orig +*.rej +*.so +*[~#] +.idea +.le.ini +.vs/ +Win32/ +build-* +cmake-build-* +core +libmdbx.creator.user mdbx-dll.VC.VC.opendb +mdbx-dll.VC.db mdbx-dll.vcxproj.filters +mdbx_chk +mdbx_copy mdbx_dump mdbx_load mdbx_stat -*.orig -*.rej -*.so -/test/test +ootest/test +test.log test/test.vcxproj.user test/tmp.db test/tmp.db-lck tmp.db tmp.db-lck valgrind.* -.vs/ -Win32/ x64/ x86/ -test.log diff --git a/plugins/Dbx_mdbx/src/libmdbx/AUTHORS b/plugins/Dbx_mdbx/src/libmdbx/AUTHORS index 3321baad04..0a1a199682 100644 --- a/plugins/Dbx_mdbx/src/libmdbx/AUTHORS +++ b/plugins/Dbx_mdbx/src/libmdbx/AUTHORS @@ -28,3 +28,4 @@ Philipp Storz Quanah Gibson-Mount Salvador Ortiz Sebastien Launay +Vladimir Romanov diff --git a/plugins/Dbx_mdbx/src/libmdbx/CMakeLists.txt b/plugins/Dbx_mdbx/src/libmdbx/CMakeLists.txt new file mode 100644 index 0000000000..5546488cd1 --- /dev/null +++ b/plugins/Dbx_mdbx/src/libmdbx/CMakeLists.txt @@ -0,0 +1,189 @@ +cmake_minimum_required(VERSION 2.8.7) + +set(TARGET mdbx) +project(${TARGET}) + +# FIXME/TODO: Same as https://github.com/leo-yuriev/libfpta +set(MDBX_VERSION_MAJOR 0) +set(MDBX_VERSION_MINOR 0) +set(MDBX_VERSION_PATCH 0) +set(MDBX_VERSION_STRING ${MDBX_VERSION_MAJOR}.${MDBX_VERSION_MINOR}.${MDBX_VERSION_PATCH}) + +add_definitions(-DMDBX_VERSION_MAJOR=${MDBX_VERSION_MAJOR}) +add_definitions(-DMDBX_VERSION_MINOR=${MDBX_VERSION_MINOR}) +add_definitions(-DMDBX_VERSION_PATCH=${MDBX_VERSION_PATCH}) + +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} -Wno-constant-logical-operand") + 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/plugins/Dbx_mdbx/src/libmdbx/COPYRIGHT b/plugins/Dbx_mdbx/src/libmdbx/COPYRIGHT index 4c5862156f..7c2fd24c78 100644 --- a/plugins/Dbx_mdbx/src/libmdbx/COPYRIGHT +++ b/plugins/Dbx_mdbx/src/libmdbx/COPYRIGHT @@ -1,4 +1,4 @@ -Copyright 2015-2017 Leonid Yuriev . +Copyright 2015-2018 Leonid Yuriev . Copyright 2011-2015 Howard Chu, Symas Corp. Copyright 2015,2016 Peter-Service R&D LLC. All rights reserved. diff --git a/plugins/Dbx_mdbx/src/libmdbx/Makefile b/plugins/Dbx_mdbx/src/libmdbx/Makefile index 56fa996fc9..c434dbdd56 100644 --- a/plugins/Dbx_mdbx/src/libmdbx/Makefile +++ b/plugins/Dbx_mdbx/src/libmdbx/Makefile @@ -24,8 +24,16 @@ suffix ?= CC ?= gcc CXX ?= g++ XCFLAGS ?= -DNDEBUG=1 -DMDBX_DEBUG=0 -DLIBMDBX_EXPORTS=1 -CFLAGS ?= -O2 -g3 -Wall -Wno-constant-logical-operand -Werror -Wextra -ffunction-sections -fPIC -fvisibility=hidden +CFLAGS ?= -O2 -g3 -Wall -Werror -Wextra -ffunction-sections -fPIC -fvisibility=hidden CFLAGS += -D_GNU_SOURCE=1 -std=gnu11 -pthread $(XCFLAGS) + +# temporary workaround for lcc's bug +TARGET_ARCH_e2k = $(shell (export LC_ALL=C; ($(CC) --version 2>&1; $(CC) -v 2>&1) | grep -q -i 'e2k' && echo yes || echo no)) +ifeq ($(TARGET_ARCH_e2k),yes) +TARGET_ARCH := e2k +CFLAGS += -mtune=native -Wno-alignment-reduction-ignored +endif + CXXFLAGS = -std=c++11 $(filter-out -std=gnu11,$(CFLAGS)) TESTDB ?= $(shell [ -d /dev/shm ] && echo /dev/shm || echo /tmp)/mdbx-check.db TESTLOG ?= $(shell [ -d /dev/shm ] && echo /dev/shm || echo /tmp)/mdbx-check.log diff --git a/plugins/Dbx_mdbx/src/libmdbx/README-RU.md b/plugins/Dbx_mdbx/src/libmdbx/README-RU.md new file mode 100644 index 0000000000..679146fd81 --- /dev/null +++ b/plugins/Dbx_mdbx/src/libmdbx/README-RU.md @@ -0,0 +1,689 @@ +libmdbx +====================================== +**The revised and extended descendant of [Symas LMDB](https://symas.com/lmdb/).** + +*The Future will Positive. Всё будет хорошо.* +[![Build Status](https://travis-ci.org/leo-yuriev/libmdbx.svg?branch=master)](https://travis-ci.org/leo-yuriev/libmdbx) +[![Build status](https://ci.appveyor.com/api/projects/status/ue94mlopn50dqiqg/branch/master?svg=true)](https://ci.appveyor.com/project/leo-yuriev/libmdbx/branch/master) +[![Coverity Scan Status](https://scan.coverity.com/projects/12915/badge.svg)](https://scan.coverity.com/projects/reopen-libmdbx) + +English version [by Google](https://translate.googleusercontent.com/translate_c?act=url&ie=UTF8&sl=ru&tl=en&u=https://github.com/leo-yuriev/libmdbx/tree/master) +and [by Yandex](https://translate.yandex.ru/translate?url=https%3A%2F%2Fgithub.com%2FReOpen%2Flibmdbx%2Ftree%2Fmaster&lang=ru-en). + +### Project Status + +**Now MDBX is under _active development_** and until 2018Q2 is expected a big +change both of API and database format. Unfortunately those update will lead to +loss of compatibility with previous versions. + +The aim of this revolution in providing a clearer robust API and adding new +features, including the database properties. + + +## Содержание + +- [Обзор](#Обзор) + - [Сравнение с другими СУБД](#Сравнение-с-другими-СУБД) + - [История & Acknowledgements](#История) +- [Основные свойства](#Основные-свойства) +- [Сравнение производительности](#Сравнение-производительности) + - [Интегральная производительность](#Интегральная-производительность) + - [Масштабируемость чтения](#Масштабируемость-чтения) + - [Синхронная фиксация](#Синхронная-фиксация) + - [Отложенная фиксация](#Отложенная-фиксация) + - [Асинхронная фиксация](#Асинхронная-фиксация) + - [Потребление ресурсов](#Потребление-ресурсов) +- [Недостатки и Компромиссы](#Недостатки-и-Компромиссы) + - [Проблема долгих чтений](#Проблема-долгих-чтений) + - [Сохранность данных в режиме асинхронной фиксации](#Сохранность-данных-в-режиме-асинхронной-фиксации) +- [Доработки и усовершенствования относительно LMDB](#Доработки-и-усовершенствования-относительно-lmdb) + + +## Обзор + +_libmdbx_ - это встраиваемый key-value движок хранения со специфическим +набором свойств и возможностей, ориентированный на создание уникальных +легковесных решений с предельной производительностью. + +_libmdbx_ позволяет множеству процессов совместно читать и обновлять +несколько key-value таблиц с соблюдением [ACID](https://ru.wikipedia.org/wiki/ACID), +при минимальных накладных расходах и амортизационной стоимости любых операций Olog(N). + +_libmdbx_ обеспечивает +[serializability](https://en.wikipedia.org/wiki/Serializability) +изменений и согласованность данных после аварий. При этом транзакции +изменяющие данные никак не мешают операциям чтения и выполняются строго +последовательно с использованием единственного +[мьютекса](https://en.wikipedia.org/wiki/Mutual_exclusion). + +_libmdbx_ позволяет выполнять операции чтения с гарантиями +[wait-free](https://en.wikipedia.org/wiki/Non-blocking_algorithm#Wait-freedom), +параллельно на каждом ядре CPU, без использования атомарных операций +и/или примитивов синхронизации. + +_libmdbx_ не использует [LSM](https://en.wikipedia.org/wiki/Log-structured_merge-tree), а основан на [B+Tree](https://en.wikipedia.org/wiki/B%2B_tree) с [отображением](https://en.wikipedia.org/wiki/Memory-mapped_file) всех данных в память, +при этом текущая версия не использует [WAL](https://en.wikipedia.org/wiki/Write-ahead_logging). +Это предопределяет многие свойства, в том числе удачные и противопоказанные сценарии использования. + +### Сравнение с другими СУБД + +Ввиду того, что в _libmdbx_ сейчас происходит революция, я посчитал лучшим решением +ограничится здесь ссылкой на [главу Comparison with other databases](https://github.com/coreos/bbolt#comparison-with-other-databases) в описании _BoltDB_. + + +### История + +_libmdbx_ является результатом переработки и развития "Lightning Memory-Mapped Database", +известной под аббревиатурой +[LMDB](https://en.wikipedia.org/wiki/Lightning_Memory-Mapped_Database). +Изначально доработка производилась в составе проекта +[ReOpenLDAP](https://github.com/leo-yuriev/ReOpenLDAP). Примерно за год +работы внесенные изменения приобрели самостоятельную ценность. Осенью +2015 доработанный движок был выделен в отдельный проект, который был +[представлен на конференции Highload++ +2015](http://www.highload.ru/2015/abstracts/1831.html). + +В начале 2017 года движок _libmdbx_ получил новый импульс развития, +благодаря использованию в [Fast Positive +Tables](https://github.com/leo-yuriev/libfpta), aka ["Позитивные +Таблицы"](https://github.com/leo-yuriev/libfpta) by [Positive +Technologies](https://www.ptsecurity.ru). + + +#### Acknowledgements + +Howard Chu (Symas Corporation) - the author of LMDB, +from which originated the MDBX in 2015. + +Martin Hedenfalk - the author of `btree.c` code, +which was used for begin development of LMDB. + + +Основные свойства +================= + +_libmdbx_ наследует все ключевые возможности и особенности от +своего прародителя [LMDB](https://en.wikipedia.org/wiki/Lightning_Memory-Mapped_Database), +но с устранением ряда описываемых далее проблем и архитектурных недочетов. + +1. Данные хранятся в упорядоченном отображении (ordered map), ключи всегда + отсортированы, поддерживается выборка диапазонов (range lookups). + +2. Данные отображается в память каждого работающего с БД процесса. + К данным и ключам обеспечивается прямой доступ в памяти без необходимости их + копирования. + +3. Транзакции согласно + [ACID](https://ru.wikipedia.org/wiki/ACID), посредством + [MVCC](https://ru.wikipedia.org/wiki/MVCC) и + [COW](https://ru.wikipedia.org/wiki/%D0%9A%D0%BE%D0%BF%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%BF%D1%80%D0%B8_%D0%B7%D0%B0%D0%BF%D0%B8%D1%81%D0%B8). + Изменения строго последовательны и не блокируются чтением, + конфликты между транзакциями не возможны. + При этом гарантируется чтение только зафиксированных данных, см [relaxing serializability](https://en.wikipedia.org/wiki/Serializability). + +4. Чтение и поиск [без блокировок](https://ru.wikipedia.org/wiki/%D0%9D%D0%B5%D0%B1%D0%BB%D0%BE%D0%BA%D0%B8%D1%80%D1%83%D1%8E%D1%89%D0%B0%D1%8F_%D1%81%D0%B8%D0%BD%D1%85%D1%80%D0%BE%D0%BD%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D1%8F), + без [атомарных операций](https://ru.wikipedia.org/wiki/%D0%90%D1%82%D0%BE%D0%BC%D0%B0%D1%80%D0%BD%D0%B0%D1%8F_%D0%BE%D0%BF%D0%B5%D1%80%D0%B0%D1%86%D0%B8%D1%8F). + Читатели не блокируются операциями записи и не конкурируют + между собой, чтение масштабируется линейно по ядрам CPU. + > Для точности следует отметить, что "подключение к БД" (старт первой + > читающей транзакции в потоке) и "отключение от БД" (закрытие БД или + > завершение потока) требуют краткосрочного захвата блокировки для + > регистрации/дерегистрации текущего потока в "таблице читателей". + +5. Эффективное хранение дубликатов (ключей с несколькими + значениями), без дублирования ключей, с сортировкой значений, в + том числе целочисленных (для вторичных индексов). + +6. Эффективная поддержка коротких ключей фиксированной длины, в том числе целочисленных. + +7. Амортизационная стоимость любой операции Olog(N), + [WAF](https://en.wikipedia.org/wiki/Write_amplification) (Write + Amplification Factor) и RAF (Read Amplification Factor) также Olog(N). + +8. Нет [WAL](https://en.wikipedia.org/wiki/Write-ahead_logging) и журнала + транзакций, после сбоев не требуется восстановление. Не требуется компактификация + или какое-либо периодическое обслуживание. Поддерживается резервное копирование + "по горячему", на работающей БД без приостановки изменения данных. + +9. Отсутствует какое-либо внутреннее управление памятью или кэшированием. Всё + необходимое штатно выполняет ядро ОС! + + +Сравнение производительности +============================ + +Все представленные ниже данные получены многократным прогоном тестов на +ноутбуке Lenovo Carbon-2, i7-4600U 2.1 ГГц, 8 Гб ОЗУ, с SSD-диском +SAMSUNG MZNTD512HAGL-000L1 (DXT23L0Q) 512 Гб. + +Исходный код бенчмарка [_IOArena_](https://github.com/pmwkaa/ioarena) и +сценарии тестирования [доступны на +github](https://github.com/pmwkaa/ioarena/tree/HL%2B%2B2015). + +-------------------------------------------------------------------------------- + +### Интегральная производительность + +Показана соотнесенная сумма ключевых показателей производительности в трёх +бенчмарках: + + - Чтение/Поиск на машине с 4-мя процессорами; + + - Транзакции с [CRUD](https://ru.wikipedia.org/wiki/CRUD)-операциями + (вставка, чтение, обновление, удаление) в режиме **синхронной фиксации** + данных (fdatasync при завершении каждой транзакции или аналог); + + - Транзакции с [CRUD](https://ru.wikipedia.org/wiki/CRUD)-операциями + (вставка, чтение, обновление, удаление) в режиме **отложенной фиксации** + данных (отложенная запись посредством файловой систем или аналог); + +*Бенчмарк в режиме асинхронной записи не включен по двум причинам:* + + 1. Такое сравнение не совсем правомочно, его следует делать с движками + ориентированными на хранение данных в памяти ([Tarantool](https://tarantool.io/), [Redis](https://redis.io/)). + + 2. Превосходство libmdbx становится еще более подавляющем, что мешает + восприятию информации. + +![Comparison #1: Integral Performance](https://raw.githubusercontent.com/wiki/leo-yuriev/libmdbx/img/perf-slide-1.png) + +-------------------------------------------------------------------------------- + +### Масштабируемость чтения + +Для каждого движка показана суммарная производительность при +одновременном выполнении запросов чтения/поиска в 1-2-4-8 потоков на +машине с 4-мя физическими процессорами. + +![Comparison #2: Read Scalability](https://raw.githubusercontent.com/wiki/leo-yuriev/libmdbx/img/perf-slide-2.png) + +-------------------------------------------------------------------------------- + +### Синхронная фиксация + + - Линейная шкала слева и темные прямоугольники соответствуют количеству + транзакций в секунду, усредненному за всё время теста. + + - Логарифмическая шкала справа и желтые интервальные отрезки + соответствуют времени выполнения транзакций. При этом каждый отрезок + показывает минимальное и максимальное время затраченное на выполнение + транзакций, а крестиком отмечено среднеквадратичное значение. + +Выполняется **10.000 транзакций в режиме синхронной фиксации данных** на +диске. При этом требуется гарантия, что при аварийном выключении питания +(или другом подобном сбое) все данные будут консистентны и полностью +соответствовать последней завершенной транзакции. В _libmdbx_ в этом +режиме при фиксации каждой транзакции выполняется системный вызов +[fdatasync](https://linux.die.net/man/2/fdatasync). + +В каждой транзакции выполняется комбинированная CRUD-операция (две +вставки, одно чтение, одно обновление, одно удаление). Бенчмарк стартует +на пустой базе, а при завершении, в результате выполняемых действий, в +базе насчитывается 10.000 небольших key-value записей. + +![Comparison #3: Sync-write mode](https://raw.githubusercontent.com/wiki/leo-yuriev/libmdbx/img/perf-slide-3.png) + +-------------------------------------------------------------------------------- + +### Отложенная фиксация + + - Линейная шкала слева и темные прямоугольники соответствуют количеству + транзакций в секунду, усредненному за всё время теста. + + - Логарифмическая шкала справа и желтые интервальные отрезки + соответствуют времени выполнения транзакций. При этом каждый отрезок + показывает минимальное и максимальное время затраченное на выполнение + транзакций, а крестиком отмечено среднеквадратичное значение. + +Выполняется **100.000 транзакций в режиме отложенной фиксации данных** +на диске. При этом требуется гарантия, что при аварийном выключении +питания (или другом подобном сбое) все данные будут консистентны на +момент завершения одной из транзакций, но допускается потеря изменений +из некоторого количества последних транзакций, что для многих движков +предполагает включение +[WAL](https://en.wikipedia.org/wiki/Write-ahead_logging) (write-ahead +logging) либо журнала транзакций, который в свою очередь опирается на +гарантию упорядоченности данных в журналируемой файловой системе. +_libmdbx_ при этом не ведет WAL, а передает весь контроль файловой +системе и ядру ОС. + +В каждой транзакции выполняется комбинированная CRUD-операция (две +вставки, одно чтение, одно обновление, одно удаление). Бенчмарк стартует +на пустой базе, а при завершении, в результате выполняемых действий, в +базе насчитывается 100.000 небольших key-value записей. + +![Comparison #4: Lazy-write mode](https://raw.githubusercontent.com/wiki/leo-yuriev/libmdbx/img/perf-slide-4.png) + +-------------------------------------------------------------------------------- + +### Асинхронная фиксация + + - Линейная шкала слева и темные прямоугольники соответствуют количеству + транзакций в секунду, усредненному за всё время теста. + + - Логарифмическая шкала справа и желтые интервальные отрезки + соответствуют времени выполнения транзакций. При этом каждый отрезок + показывает минимальное и максимальное время затраченное на выполнение + транзакций, а крестиком отмечено среднеквадратичное значение. + +Выполняется **1.000.000 транзакций в режиме асинхронной фиксации +данных** на диске. При этом требуется гарантия, что при аварийном +выключении питания (или другом подобном сбое) все данные будут +консистентны на момент завершения одной из транзакций, но допускается +потеря изменений из значительного количества последних транзакций. Во +всех движках при этом включался режим предполагающий минимальную +нагрузку на диск по-записи, и соответственно минимальную гарантию +сохранности данных. В _libmdbx_ при этом используется режим асинхронной +записи измененных страниц на диск посредством ядра ОС и системного +вызова [msync(MS_ASYNC)](https://linux.die.net/man/2/msync). + +В каждой транзакции выполняется комбинированная CRUD-операция (две +вставки, одно чтение, одно обновление, одно удаление). Бенчмарк стартует +на пустой базе, а при завершении, в результате выполняемых действий, в +базе насчитывается 10.000 небольших key-value записей. + +![Comparison #5: Async-write mode](https://raw.githubusercontent.com/wiki/leo-yuriev/libmdbx/img/perf-slide-5.png) + +-------------------------------------------------------------------------------- + +### Потребление ресурсов + +Показана соотнесенная сумма использованных ресурсов в ходе бенчмарка в +режиме отложенной фиксации: + + - суммарное количество операций ввода-вывода (IOPS), как записи, так и + чтения. + + - суммарное затраченное время процессора, как в режиме пользовательских процессов, + так и в режиме ядра ОС. + + - использованное место на диске при завершении теста, после закрытия БД из тестирующего процесса, + но без ожидания всех внутренних операций обслуживания (компактификации LSM и т.п.). + +Движок _ForestDB_ был исключен при оформлении результатов, так как +относительно конкурентов многократно превысил потребление каждого из +ресурсов (потратил процессорное время на генерацию IOPS для заполнения +диска), что не позволяло наглядно сравнить показатели остальных движков +на одной диаграмме. + +Все данные собирались посредством системного вызова +[getrusage()](http://man7.org/linux/man-pages/man2/getrusage.2.html) и +сканированием директорий с данными. + +![Comparison #6: Cost comparison](https://raw.githubusercontent.com/wiki/leo-yuriev/libmdbx/img/perf-slide-6.png) + +-------------------------------------------------------------------------------- + +## Недостатки и Компромиссы + +1. Единовременно может выполняться не более одной транзакция изменения данных + (один писатель). Зато все изменения всегда последовательны, не может быть + конфликтов или логических ошибок при откате транзакций. + +2. Отсутствие [WAL](https://en.wikipedia.org/wiki/Write-ahead_logging) + обуславливает относительно большой + [WAF](https://en.wikipedia.org/wiki/Write_amplification) (Write + Amplification Factor). Поэтому фиксация изменений на диске может быть + достаточно дорогой и являться главным ограничением производительности + при интенсивном изменении данных. + > В качестве компромисса _libmdbx_ предлагает несколько режимов ленивой + > и/или периодической фиксации. В том числе режим `MAPASYNC`, при котором + > изменения происходят только в памяти и асинхронно фиксируются на диске + > ядром ОС. + > + > Однако, следует воспринимать это свойство аккуратно и взвешенно. + > Например, полная фиксация транзакции в БД с журналом потребует минимум 2 + > IOPS (скорее всего 3-4) из-за накладных расходов в файловой системе. В + > _libmdbx_ фиксация транзакции также требует от 2 IOPS. Однако, в БД с + > журналом кол-во IOPS будет меняться в зависимости от файловой системы, + > но не от кол-ва записей или их объема. Тогда как в _libmdbx_ кол-во + > будет расти логарифмически от кол-во записей/строк в БД (по высоте + > b+tree). + +3. [COW](https://ru.wikipedia.org/wiki/%D0%9A%D0%BE%D0%BF%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%BF%D1%80%D0%B8_%D0%B7%D0%B0%D0%BF%D0%B8%D1%81%D0%B8) + для реализации [MVCC](https://ru.wikipedia.org/wiki/MVCC) выполняется на + уровне страниц в [B+ + дереве](https://ru.wikipedia.org/wiki/B-%D0%B4%D0%B5%D1%80%D0%B5%D0%B2%D0%BE). + Поэтому изменение данных амортизационно требует копирования Olog(N) + страниц, что расходует [пропускную способность оперативной + памяти](https://en.wikipedia.org/wiki/Memory_bandwidth) и является + основным ограничителем производительности в режиме `MAPASYNC`. + > Этот недостаток неустраним, тем не менее следует дать некоторые пояснения. + > Дело в том, что фиксация изменений на диске потребует гораздо более + > значительного копирования данных в памяти и массы других затратных операций. + > Поэтому обусловленное этим недостатком падение производительности становится + > заметным только при отказе от фиксации изменений на диске. + > Соответственно, корректнее сказать что _libmdbx_ позволяет + > получить персистентность ценой минимального падения производительности. + > Если же нет необходимости оперативно сохранять данные, то логичнее + > использовать `std::map`. + +4. В _LMDB_ существует проблема долгих чтений (приостановленных читателей), + которая приводит к деградации производительности и переполнению БД. + > В _libmdbx_ предложены средства для предотвращения, быстрого выхода из + > некомфортной ситуации и устранения её последствий. Подробности ниже. + +5. В _LMDB_ есть вероятность разрушения БД в режиме `WRITEMAP+MAPASYNC`. + В _libmdbx_ для `WRITEMAP+MAPASYNC` гарантируется как сохранность базы, + так и согласованность данных. + > Дополнительно, в качестве альтернативы, предложен режим `UTTERLY_NOSYNC`. + > Подробности ниже. + + +#### Проблема долгих чтений + +*Следует отметить*, что проблема "сборки мусора" так или иначе +существует во всех СУБД (Vacuum в PostgreSQL). Однако в случае _libmdbx_ +и LMDB она проявляется более остро, прежде всего из-за высокой +производительности, а также из-за намеренного упрощения внутренних +механизмов ради производительности. + +Понимание проблемы требует некоторых пояснений, которые +изложены ниже, но могут быть сложны для быстрого восприятия. +Поэтому, тезисно: + +* Изменение данных на фоне долгой операции чтения может + приводить к исчерпанию места в БД. + +* После чего любая попытка обновить данные будет приводить к + ошибке `MAP_FULL` до завершения долгой операции чтения. + +* Характерными примерами долгих чтений являются горячее + резервное копирования и отладка клиентского приложения при + активной транзакции чтения. + +* В оригинальной _LMDB_ после этого будет наблюдаться + устойчивая деградация производительности всех механизмов + обратной записи на диск (в I/O контроллере, в гипервизоре, + в ядре ОС). + +* В _libmdbx_ предусмотрен механизм аварийного прерывания таких + операций, а также режим `LIFO RECLAIM` устраняющий последующую + деградацию производительности. + +Операции чтения выполняются в контексте снимка данных (версии +БД), который был актуальным на момент старта транзакции чтения. Такой +читаемый снимок поддерживается неизменным до завершения операции. В свою +очередь, это не позволяет повторно использовать страницы БД в +последующих версиях (снимках БД). + +Другими словами, если обновление данных выполняется на фоне долгой +операции чтения, то вместо повторного использования "старых" ненужных +страниц будут выделяться новые, так как "старые" страницы составляют +снимок БД, который еще используется долгой операцией чтения. + +В результате, при интенсивном изменении данных и достаточно длительной +операции чтения, в БД могут быть исчерпаны свободные страницы, что не +позволит создавать новые снимки/версии БД. Такая ситуация будет +сохраняться до завершения операции чтения, которая использует старый +снимок данных и препятствует повторному использованию страниц БД. + +Однако, на этом проблемы не заканчиваются. После описанной ситуации, все +дополнительные страницы, которые были выделены пока переработка старых +была невозможна, будут участвовать в цикле выделения/освобождения до +конца жизни экземпляра БД. В оригинальной _LMDB_ этот цикл использования +страниц работает по принципу [FIFO](https://ru.wikipedia.org/wiki/FIFO). +Поэтому увеличение количества циркулирующий страниц, с точки зрения +механизмов кэширования и/или обратной записи, выглядит как увеличение +рабочего набор данных. Проще говоря, однократное попадание в ситуацию +"уснувшего читателя" приводит к устойчивому эффекту вымывания I/O кэша +при всех последующих изменениях данных. + +Для устранения описанных проблемы в _libmdbx_ сделаны существенные +доработки, подробности ниже. Иллюстрации к проблеме "долгих чтений" +можно найти в [слайдах презентации](http://www.slideshare.net/leoyuriev/lmdb). + +Там же приведен пример количественной оценки прироста производительности +за счет эффективной работы [BBWC](https://en.wikipedia.org/wiki/BBWC) +при включении `LIFO RECLAIM` в _libmdbx_. + + +#### Сохранность данных в режиме асинхронной фиксации + +При работе в режиме `WRITEMAP+MAPSYNC` запись измененных страниц +выполняется ядром ОС, что имеет ряд преимуществ. Так например, при крахе +приложения, ядро ОС сохранит все изменения. + +Однако, при аварийном отключении питания или сбое в ядре ОС, на диске +может быть сохранена только часть измененных страниц БД. При этом с большой +вероятностью может оказаться так, что будут сохранены мета-страницы со +ссылками на страницы с новыми версиями данных, но не сами новые данные. +В этом случае БД будет безвозвратна разрушена, даже если до аварии +производилась полная синхронизация данных (посредством +`mdbx_env_sync()`). + +В _libmdbx_ эта проблема устранена путем полной переработки +пути записи данных: + +* В режиме `WRITEMAP+MAPSYNC` _libmdbx_ не обновляет + мета-страницы непосредственно, а поддерживает их теневые копии + с переносом изменений после фиксации данных. + +* При завершении транзакций, в зависимости от состояния + синхронности данных между диском и оперативной память, + _libmdbx_ помечает точки фиксации либо как сильные (strong), + либо как слабые (weak). Так например, в режиме + `WRITEMAP+MAPSYNC` завершаемые транзакции помечаются как + слабые, а при явной синхронизации данных как сильные. + +* В _libmdbx_ поддерживается не две, а три отдельные мета-страницы. + Это позволяет выполнять фиксацию транзакций с формированием как + сильной, так и слабой точки фиксации, без потери двух предыдущих + точек фиксации (из которых одна может быть сильной, а вторая слабой). + В результате, _libmdbx_ позволяет в произвольном порядке чередовать + сильные и слабые точки фиксации без нарушения соответствующих + гарантий в случае неожиданной системной аварии во время фиксации. + +* При открытии БД выполняется автоматический откат к последней + сильной фиксации. Этим обеспечивается гарантия сохранности БД. + +Такая гарантия надежности не дается бесплатно. Для +сохранности данных, страницы формирующие крайний снимок с +сильной фиксацией, не должны повторно использоваться +(перезаписываться) до формирования следующей сильной точки +фиксации. Таким образом, крайняя точка фиксации создает +описанный выше эффект "долгого чтения". Разница же здесь в том, +что при исчерпании свободных страниц ситуация будет +автоматически исправлена, посредством записи изменений на диск +и формированием новой сильной точки фиксации. + +Таким образом, в режиме безопасной асинхронной фиксации _libmdbx_ будет +всегда использовать новые страницы до исчерпания места в БД или до явного +формирования сильной точки фиксации посредством `mdbx_env_sync()`. +При этом суммарный трафик записи на диск будет примерно такой-же, +как если бы отдельно фиксировалась каждая транзакций. + +В текущей версии _libmdbx_ вам предоставляется выбор между безопасным +режимом (по умолчанию) асинхронной фиксации, и режимом `UTTERLY_NOSYNC` когда +при системной аварии есть шанс полного разрушения БД как в LMDB. + +В последующих версиях _libmdbx_ будут предусмотрены средства +для асинхронной записи данных на диск с автоматическим +формированием сильных точек фиксации. + +-------------------------------------------------------------------------------- + +Доработки и усовершенствования относительно LMDB +================================================ + +1. Режим `LIFO RECLAIM`. + + Для повторного использования выбираются не самые старые, а + самые новые страницы из доступных. За счет этого цикл + использования страниц всегда имеет минимальную длину и не + зависит от общего числа выделенных страниц. + + В результате механизмы кэширования и обратной записи работают с + максимально возможной эффективностью. В случае использования + контроллера дисков или системы хранения с + [BBWC](https://en.wikipedia.org/wiki/BBWC) возможно + многократное увеличение производительности по записи + (обновлению данных). + +2. Обработчик `OOM-KICK`. + + Посредством `mdbx_env_set_oomfunc()` может быть установлен + внешний обработчик (callback), который будет вызван при + исчерпания свободных страниц из-за долгой операцией чтения. + Обработчику будет передан PID и pthread_id виновника. + В свою очередь обработчик может предпринять одно из действий: + + * нейтрализовать виновника (отправить сигнал kill #9), если + долгое чтение выполняется сторонним процессом; + + * отменить или перезапустить проблемную операцию чтения, если + операция выполняется одним из потоков текущего процесса; + + * подождать некоторое время, в расчете что проблемная операция + чтения будет штатно завершена; + + * прервать текущую операцию изменения данных с возвратом кода + ошибки. + +3. Гарантия сохранности БД в режиме `WRITEMAP+MAPSYNC`. + > В текущей версии _libmdbx_ вам предоставляется выбор между безопасным + > режимом (по умолчанию) асинхронной фиксации, и режимом `UTTERLY_NOSYNC` + > когда при системной аварии есть шанс полного разрушения БД как в LMDB. + > Для подробностей смотрите раздел + > [Сохранность данных в режиме асинхронной фиксации](#Сохранность-данных-в-режиме-асинхронной-фиксации). + +4. Возможность автоматического формирования контрольных точек +(сброса данных на диск) при накоплении заданного объёма изменений, +устанавливаемого функцией `mdbx_env_set_syncbytes()`. + +5. Возможность получить отставание текущей транзакции чтения от +последней версии данных в БД посредством `mdbx_txn_straggler()`. + +6. Утилита mdbx_chk для проверки БД и функция `mdbx_env_pgwalk()` для +обхода всех страниц БД. + +7. Управление отладкой и получение отладочных сообщений посредством +`mdbx_setup_debug()`. + +8. Возможность связать с каждой завершаемой транзакцией до 3 +дополнительных маркеров посредством `mdbx_canary_put()`, и прочитать их +в транзакции чтения посредством `mdbx_canary_get()`. + +9. Возможность узнать есть ли за текущей позицией курсора строка данных +посредством `mdbx_cursor_eof()`. + +10. Возможность явно запросить обновление существующей записи, без +создания новой посредством флажка `MDBX_CURRENT` для `mdbx_put()`. + +11. Возможность посредством `mdbx_replace()` обновить или удалить запись +с получением предыдущего значения данных, а также адресно изменить +конкретное multi-значение. + +12. Поддержка ключей и значений нулевой длины, включая сортированные +дубликаты. + +13. Исправленный вариант `mdbx_cursor_count()`, возвращающий корректное +количество дубликатов для всех типов таблиц и любого положения курсора. + +14. Возможность открыть БД в эксклюзивном режиме посредством +`mdbx_env_open_ex()`, например в целях её проверки. + +15. Возможность закрыть БД в "грязном" состоянии (без сброса данных и +формирования сильной точки фиксации) посредством `mdbx_env_close_ex()`. + +16. Возможность получить посредством `mdbx_env_info()` дополнительную +информацию, включая номер самой старой версии БД (снимка данных), +который используется одним из читателей. + +17. Функция `mdbx_del()` не игнорирует дополнительный (уточняющий) +аргумент `data` для таблиц без дубликатов (без флажка `MDBX_DUPSORT`), а +при его ненулевом значении всегда использует его для сверки с удаляемой +записью. + +18. Возможность открыть dbi-таблицу, одновременно с установкой +компараторов для ключей и данных, посредством `mdbx_dbi_open_ex()`. + +19. Возможность посредством `mdbx_is_dirty()` определить находятся ли +некоторый ключ или данные в "грязной" странице БД. Таким образом, +избегая лишнего копирования данных перед выполнением модифицирующих +операций (значения в размещенные "грязных" страницах могут быть +перезаписаны при изменениях, иначе они будут неизменны). + +20. Корректное обновление текущей записи, в том числе сортированного +дубликата, при использовании режима `MDBX_CURRENT` в +`mdbx_cursor_put()`. + +21. Все курсоры, как в транзакциях только для чтения, так и в пишущих, +могут быть переиспользованы посредством `mdbx_cursor_renew()` и ДОЛЖНЫ +ОСВОБОЖДАТЬСЯ ЯВНО. + > + > ## _ВАЖНО_, Обратите внимание! + > + > Это единственное изменение в API, которое значимо меняет + > семантику управления курсорами и может приводить к утечкам + > памяти. Следует отметить, что это изменение вынужденно. + > Так устраняется неоднозначность с массой тяжких последствий: + > + > - обращение к уже освобожденной памяти; + > - попытки повторного освобождения памяти; + > - memory corruption and segfaults. + +22. Дополнительный код ошибки `MDBX_EMULTIVAL`, который возвращается из +`mdbx_put()` и `mdbx_replace()` при попытке выполнить неоднозначное +обновление или удаления одного из нескольких значений с одним ключом. + +23. Возможность посредством `mdbx_get_ex()` получить значение по +заданному ключу, одновременно с количеством дубликатов. + +24. Наличие функций `mdbx_cursor_on_first()` и `mdbx_cursor_on_last()`, +которые позволяют быстро выяснить стоит ли курсор на первой/последней +позиции. + +25. При завершении читающих транзакций, открытые в них DBI-хендлы не +закрываются и не теряются при завершении таких транзакций посредством +`mdbx_txn_abort()` или `mdbx_txn_reset()`. Что позволяет избавится от ряда +сложно обнаруживаемых ошибок. + +26. Генерация последовательностей посредством `mdbx_dbi_sequence()`. + +27. Расширенное динамическое управление размером БД, включая выбор +размера страницы посредством `mdbx_env_set_geometry()`, +в том числе в **Windows** + +28. Три мета-страницы вместо двух, что позволяет гарантированно +консистентно обновлять слабые контрольные точки фиксации без риска +повредить крайнюю сильную точку фиксации. + +29. В _libmdbx_ реализован автоматический возврат освобождающихся +страниц в область нераспределенного резерва в конце файла данных. При +этом уменьшается количество страниц загруженных в память и участвующих в +цикле обновления данных и записи на диск. Фактически _libmdbx_ выполняет +постоянную компактификацию данных, но не затрачивая на это +дополнительных ресурсов, а только освобождая их. При освобождении места +в БД и установки соответствующих параметров геометрии базы данных, также будет +уменьшаться размер файла на диске, в том числе в **Windows**. + +-------------------------------------------------------------------------------- + +``` +$ objdump -f -h -j .text libmdbx.so + +libmdbx.so: file format elf64-x86-64 +architecture: i386:x86-64, flags 0x00000150: +HAS_SYMS, DYNAMIC, D_PAGED +start address 0x000030e0 + +Sections: +Idx Name Size VMA LMA File off Algn + 11 .text 00014d84 00000000000030e0 00000000000030e0 000030e0 2**4 + CONTENTS, ALLOC, LOAD, READONLY, CODE + +``` + +``` +$ gcc -v +Using built-in specs. +COLLECT_GCC=gcc +COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/7/lto-wrapper +OFFLOAD_TARGET_NAMES=nvptx-none +OFFLOAD_TARGET_DEFAULT=1 +Target: x86_64-linux-gnu +Configured with: ../src/configure -v --with-pkgversion='Ubuntu 7.2.0-8ubuntu3' --with-bugurl=file:///usr/share/doc/gcc-7/README.Bugs --enable-languages=c,ada,c++,go,brig,d,fortran,objc,obj-c++ --prefix=/usr --with-gcc-major-version-only --program-suffix=-7 --program-prefix=x86_64-linux-gnu- --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-gnu-unique-object --disable-vtable-verify --enable-libmpx --enable-plugin --enable-default-pie --with-system-zlib --with-target-system-zlib --enable-objc-gc=auto --enable-multiarch --disable-werror --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-offload-targets=nvptx-none --without-cuda-driver --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu +Thread model: posix +gcc version 7.2.0 (Ubuntu 7.2.0-8ubuntu3) +``` diff --git a/plugins/Dbx_mdbx/src/libmdbx/README.md b/plugins/Dbx_mdbx/src/libmdbx/README.md index 96811b9f93..fb5e2e7538 100644 --- a/plugins/Dbx_mdbx/src/libmdbx/README.md +++ b/plugins/Dbx_mdbx/src/libmdbx/README.md @@ -1,94 +1,69 @@ libmdbx ====================================== -**The revised and extended descendant of [Symas LMDB](https://symas.com/lmdb/).** +**Revised and extended descendant of [Symas LMDB](https://symas.com/lmdb/).** -*The Future will Positive. Всё будет хорошо.* +*The Future will be positive.* [![Build Status](https://travis-ci.org/leo-yuriev/libmdbx.svg?branch=master)](https://travis-ci.org/leo-yuriev/libmdbx) [![Build status](https://ci.appveyor.com/api/projects/status/ue94mlopn50dqiqg/branch/master?svg=true)](https://ci.appveyor.com/project/leo-yuriev/libmdbx/branch/master) [![Coverity Scan Status](https://scan.coverity.com/projects/12915/badge.svg)](https://scan.coverity.com/projects/reopen-libmdbx) -English version [by Google](https://translate.googleusercontent.com/translate_c?act=url&ie=UTF8&sl=ru&tl=en&u=https://github.com/leo-yuriev/libmdbx/tree/master) -and [by Yandex](https://translate.yandex.ru/translate?url=https%3A%2F%2Fgithub.com%2FReOpen%2Flibmdbx%2Ftree%2Fmaster&lang=ru-en). - ### Project Status -**Now MDBX is under _active development_** and until 2018Q2 is expected a big -change both of API and database format. Unfortunately those update will lead to -loss of compatibility with previous versions. - -The aim of this revolution in providing a clearer robust API and adding new -features, including the database properties. - +**MDBX is under _active development_**, database format and API aren't stable +at least until 2018Q2. New version won't be backwards compatible. +Main focus of the rework is to provide clear and robust API and new features. -## Содержание +## Contents -- [Обзор](#Обзор) - - [Сравнение с другими СУБД](#Сравнение-с-другими-СУБД) - - [История & Acknowledgements](#История) -- [Основные свойства](#Основные-свойства) -- [Сравнение производительности](#Сравнение-производительности) - - [Интегральная производительность](#Интегральная-производительность) - - [Масштабируемость чтения](#Масштабируемость-чтения) - - [Синхронная фиксация](#Синхронная-фиксация) - - [Отложенная фиксация](#Отложенная-фиксация) - - [Асинхронная фиксация](#Асинхронная-фиксация) - - [Потребление ресурсов](#Потребление-ресурсов) -- [Недостатки и Компромиссы](#Недостатки-и-Компромиссы) - - [Проблема долгих чтений](#Проблема-долгих-чтений) - - [Сохранность данных в режиме асинхронной фиксации](#Сохранность-данных-в-режиме-асинхронной-фиксации) -- [Доработки и усовершенствования относительно LMDB](#Доработки-и-усовершенствования-относительно-lmdb) +- [Overview](#overview) + - [Comparison with other DBs](#comparison-with-other-dbs) + - [History & Acknowledgements](#history) +- [Main features](#main-features) +- [Performance comparison](#performance-comparison) + - [Integral performance](#integral-performance) + - [Read scalability](#read-scalability) + - [Sync-write mode](#sync-write-mode) + - [Lazy-write mode](#lazy-write-mode) + - [Async-write mode](#async-write-mode) + - [Cost comparison](#cost-comparison) +- [Gotchas](#gotchas) + - [Long-time read transactions problem](#long-time-read-transactions-problem) + - [Data safety in async-write-mode](#data-safety-in-async-write-mode) +- [Improvements over LMDB](#improvements-over-lmdb) -## Обзор +## Overview -_libmdbx_ - это встраиваемый key-value движок хранения со специфическим -набором свойств и возможностей, ориентированный на создание уникальных -легковесных решений с предельной производительностью. +_libmdbx_ is an embedded lightweight key-value database engine oriented for performance. -_libmdbx_ позволяет множеству процессов совместно читать и обновлять -несколько key-value таблиц с соблюдением [ACID](https://ru.wikipedia.org/wiki/ACID), -при минимальных накладных расходах и амортизационной стоимости любых операций Olog(N). +_libmdbx_ allows multiple processes to read and update several key-value tables concurrently, +while being [ACID](https://en.wikipedia.org/wiki/ACID)-compliant, with minimal overhead and operation cost of Olog(N). -_libmdbx_ обеспечивает -[serializability](https://en.wikipedia.org/wiki/Serializability) -изменений и согласованность данных после аварий. При этом транзакции -изменяющие данные никак не мешают операциям чтения и выполняются строго -последовательно с использованием единственного -[мьютекса](https://en.wikipedia.org/wiki/Mutual_exclusion). +_libmdbx_ provides +[serializability](https://en.wikipedia.org/wiki/Serializability) and consistency of data after crash. +Read-write transactions don't block read-only transactions and are +[serialized](https://en.wikipedia.org/wiki/Serializability) by [mutex](https://en.wikipedia.org/wiki/Mutual_exclusion). -_libmdbx_ позволяет выполнять операции чтения с гарантиями -[wait-free](https://en.wikipedia.org/wiki/Non-blocking_algorithm#Wait-freedom), -параллельно на каждом ядре CPU, без использования атомарных операций -и/или примитивов синхронизации. +_libmdbx_ [wait-free](https://en.wikipedia.org/wiki/Non-blocking_algorithm#Wait-freedom) provides parallel read transactions +without atomic operations or synchronization primitives. -_libmdbx_ не использует [LSM](https://en.wikipedia.org/wiki/Log-structured_merge-tree), а основан на [B+Tree](https://en.wikipedia.org/wiki/B%2B_tree) с [отображением](https://en.wikipedia.org/wiki/Memory-mapped_file) всех данных в память, -при этом текущая версия не использует [WAL](https://en.wikipedia.org/wiki/Write-ahead_logging). -Это предопределяет многие свойства, в том числе удачные и противопоказанные сценарии использования. +_libmdbx_ uses [B+Trees](https://en.wikipedia.org/wiki/B%2B_tree) and [mmap](https://en.wikipedia.org/wiki/Memory-mapped_file), +doesn't use [WAL](https://en.wikipedia.org/wiki/Write-ahead_logging). This might have caveats for some workloads. -### Сравнение с другими СУБД +### Comparison with other DBs -Ввиду того, что в _libmdbx_ сейчас происходит революция, я посчитал лучшим решением -ограничится здесь ссылкой на [главу Comparison with other databases](https://github.com/coreos/bbolt#comparison-with-other-databases) в описании _BoltDB_. +Because _libmdbx_ is currently overhauled, I think it's better to just link +[chapter of Comparison with other databases](https://github.com/coreos/bbolt#comparison-with-other-databases) here. +### History -### История - -_libmdbx_ является результатом переработки и развития "Lightning Memory-Mapped Database", -известной под аббревиатурой -[LMDB](https://en.wikipedia.org/wiki/Lightning_Memory-Mapped_Database). -Изначально доработка производилась в составе проекта -[ReOpenLDAP](https://github.com/leo-yuriev/ReOpenLDAP). Примерно за год -работы внесенные изменения приобрели самостоятельную ценность. Осенью -2015 доработанный движок был выделен в отдельный проект, который был -[представлен на конференции Highload++ -2015](http://www.highload.ru/2015/abstracts/1831.html). - -В начале 2017 года движок _libmdbx_ получил новый импульс развития, -благодаря использованию в [Fast Positive -Tables](https://github.com/leo-yuriev/libfpta), aka ["Позитивные -Таблицы"](https://github.com/leo-yuriev/libfpta) by [Positive -Technologies](https://www.ptsecurity.ru). +_libmdbx_ design is based on [Lightning Memory-Mapped Database](https://en.wikipedia.org/wiki/Lightning_Memory-Mapped_Database). +Initial development was going in [ReOpenLDAP](https://github.com/leo-yuriev/ReOpenLDAP) project, about a year later it +received separate development effort and in autumn 2015 was isolated to separate project, which was +[presented at Highload++ 2015 conference](http://www.highload.ru/2015/abstracts/1831.html). +Since early 2017 _libmdbx_ is used in [Fast Positive Tables](https://github.com/leo-yuriev/libfpta), +by [Positive Technologies](https://www.ptsecurity.com). #### Acknowledgements @@ -99,565 +74,371 @@ Martin Hedenfalk - the author of `btree.c` code, which was used for begin development of LMDB. -Основные свойства -================= - -_libmdbx_ наследует все ключевые возможности и особенности от -своего прародителя [LMDB](https://en.wikipedia.org/wiki/Lightning_Memory-Mapped_Database), -но с устранением ряда описываемых далее проблем и архитектурных недочетов. +Main features +============= -1. Данные хранятся в упорядоченном отображении (ordered map), ключи всегда - отсортированы, поддерживается выборка диапазонов (range lookups). +_libmdbx_ inherits all keys features and characteristics from +[LMDB](https://en.wikipedia.org/wiki/Lightning_Memory-Mapped_Database): -2. Данные отображается в память каждого работающего с БД процесса. - К данным и ключам обеспечивается прямой доступ в памяти без необходимости их - копирования. +1. Data is stored in ordered map, keys are always sorted, range lookups are supported. -3. Транзакции согласно - [ACID](https://ru.wikipedia.org/wiki/ACID), посредством - [MVCC](https://ru.wikipedia.org/wiki/MVCC) и - [COW](https://ru.wikipedia.org/wiki/%D0%9A%D0%BE%D0%BF%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%BF%D1%80%D0%B8_%D0%B7%D0%B0%D0%BF%D0%B8%D1%81%D0%B8). - Изменения строго последовательны и не блокируются чтением, - конфликты между транзакциями не возможны. - При этом гарантируется чтение только зафиксированных данных, см [relaxing serializability](https://en.wikipedia.org/wiki/Serializability). +2. Data is [mmaped](https://en.wikipedia.org/wiki/Memory-mapped_file) to memory of each worker DB process, read transactions are zero-copy. -4. Чтение и поиск [без блокировок](https://ru.wikipedia.org/wiki/%D0%9D%D0%B5%D0%B1%D0%BB%D0%BE%D0%BA%D0%B8%D1%80%D1%83%D1%8E%D1%89%D0%B0%D1%8F_%D1%81%D0%B8%D0%BD%D1%85%D1%80%D0%BE%D0%BD%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D1%8F), - без [атомарных операций](https://ru.wikipedia.org/wiki/%D0%90%D1%82%D0%BE%D0%BC%D0%B0%D1%80%D0%BD%D0%B0%D1%8F_%D0%BE%D0%BF%D0%B5%D1%80%D0%B0%D1%86%D0%B8%D1%8F). - Читатели не блокируются операциями записи и не конкурируют - между собой, чтение масштабируется линейно по ядрам CPU. - > Для точности следует отметить, что "подключение к БД" (старт первой - > читающей транзакции в потоке) и "отключение от БД" (закрытие БД или - > завершение потока) требуют краткосрочного захвата блокировки для - > регистрации/дерегистрации текущего потока в "таблице читателей". +3. Transactions are [ACID](https://en.wikipedia.org/wiki/ACID)-compliant, thanks to + [MVCC](https://en.wikipedia.org/wiki/Multiversion_concurrency_control) and [CoW](https://en.wikipedia.org/wiki/Copy-on-write). + Writes are strongly serialized and aren't blocked by reads, transactions can't conflict with each other. + Reads are guaranteed to get only commited data + ([relaxing serializability](https://en.wikipedia.org/wiki/Serializability#Relaxing_serializability)). -5. Эффективное хранение дубликатов (ключей с несколькими - значениями), без дублирования ключей, с сортировкой значений, в - том числе целочисленных (для вторичных индексов). +4. Reads and queries are [non-blocking](https://en.wikipedia.org/wiki/Non-blocking_algorithm), + don't use [atomic operations](https://en.wikipedia.org/wiki/Linearizability#High-level_atomic_operations). + Readers don't block each other and aren't blocked by writers. Read performance scales linearly with CPU core count. + > Though "connect to DB" (start of first read transaction in thread) and "disconnect from DB" (shutdown or thread + > termination) requires to acquire a lock to register/unregister current thread from "readers table" -6. Эффективная поддержка коротких ключей фиксированной длины, в том числе целочисленных. +5. Keys with multiple values are stored efficiently without key duplication, sorted by value, including integers + (reasonable for secondary indexes). -7. Амортизационная стоимость любой операции Olog(N), - [WAF](https://en.wikipedia.org/wiki/Write_amplification) (Write - Amplification Factor) и RAF (Read Amplification Factor) также Olog(N). +6. Efficient operation on short fixed length keys, including integer ones. -8. Нет [WAL](https://en.wikipedia.org/wiki/Write-ahead_logging) и журнала - транзакций, после сбоев не требуется восстановление. Не требуется компактификация - или какое-либо периодическое обслуживание. Поддерживается резервное копирование - "по горячему", на работающей БД без приостановки изменения данных. +7. [WAF](https://en.wikipedia.org/wiki/Write_amplification) (Write Amplification Factor) и RAF (Read Amplification Factor) + are Olog(N). -9. Отсутствует какое-либо внутреннее управление памятью или кэшированием. Всё - необходимое штатно выполняет ядро ОС! +8. No [WAL](https://en.wikipedia.org/wiki/Write-ahead_logging) and transaction journal. + In case of a crash no recovery needed. No need for regular maintenance. Backups can be made on the fly on working DB + without freezing writers. +9. No custom memory management, all done with standard OS syscalls. -Сравнение производительности -============================ -Все представленные ниже данные получены многократным прогоном тестов на -ноутбуке Lenovo Carbon-2, i7-4600U 2.1 ГГц, 8 Гб ОЗУ, с SSD-диском -SAMSUNG MZNTD512HAGL-000L1 (DXT23L0Q) 512 Гб. +Performance comparison +===================== -Исходный код бенчмарка [_IOArena_](https://github.com/pmwkaa/ioarena) и -сценарии тестирования [доступны на -github](https://github.com/pmwkaa/ioarena/tree/HL%2B%2B2015). +All benchmarks were done by [IOArena](https://github.com/pmwkaa/ioarena) +and multiple [scripts](https://github.com/pmwkaa/ioarena/tree/HL%2B%2B2015) +runs on Lenovo Carbon-2 laptop, i7-4600U 2.1 GHz, 8 Gb RAM, +SSD SAMSUNG MZNTD512HAGL-000L1 (DXT23L0Q) 512 Gb. -------------------------------------------------------------------------------- -### Интегральная производительность +### Integral performance -Показана соотнесенная сумма ключевых показателей производительности в трёх -бенчмарках: +Here showed sum of performance metrics in 3 benchmarks: - - Чтение/Поиск на машине с 4-мя процессорами; + - Read/Search on 4 CPU cores machine; - - Транзакции с [CRUD](https://ru.wikipedia.org/wiki/CRUD)-операциями - (вставка, чтение, обновление, удаление) в режиме **синхронной фиксации** - данных (fdatasync при завершении каждой транзакции или аналог); + - Transactions with [CRUD](https://en.wikipedia.org/wiki/CRUD) operations + in sync-write mode (fdatasync is called after each transaction); - - Транзакции с [CRUD](https://ru.wikipedia.org/wiki/CRUD)-операциями - (вставка, чтение, обновление, удаление) в режиме **отложенной фиксации** - данных (отложенная запись посредством файловой систем или аналог); + - Transactions with [CRUD](https://en.wikipedia.org/wiki/CRUD) operations + in lazy-write mode (moment to sync data to persistent storage is decided by OS). -*Бенчмарк в режиме асинхронной записи не включен по двум причинам:* +*Reasons why asynchronous mode isn't benchmarked here:* - 1. Такое сравнение не совсем правомочно, его следует делать с движками - ориентированными на хранение данных в памяти ([Tarantool](https://tarantool.io/), [Redis](https://redis.io/)). + 1. It doesn't make sense as it has to be done with DB engines, oriented for keeping data in memory e.g. + [Tarantool](https://tarantool.io/), [Redis](https://redis.io/)), etc. - 2. Превосходство libmdbx становится еще более подавляющем, что мешает - восприятию информации. + 2. Performance gap is too high to compare in any meaningful way. ![Comparison #1: Integral Performance](https://raw.githubusercontent.com/wiki/leo-yuriev/libmdbx/img/perf-slide-1.png) -------------------------------------------------------------------------------- -### Масштабируемость чтения +### Read Scalability -Для каждого движка показана суммарная производительность при -одновременном выполнении запросов чтения/поиска в 1-2-4-8 потоков на -машине с 4-мя физическими процессорами. +Summary performance with concurrent read/search queries in 1-2-4-8 threads on 4 CPU cores machine. ![Comparison #2: Read Scalability](https://raw.githubusercontent.com/wiki/leo-yuriev/libmdbx/img/perf-slide-2.png) -------------------------------------------------------------------------------- -### Синхронная фиксация +### Sync-write mode - - Линейная шкала слева и темные прямоугольники соответствуют количеству - транзакций в секунду, усредненному за всё время теста. + - Linear scale on left and dark rectangles mean arithmetic mean transactions per second; - - Логарифмическая шкала справа и желтые интервальные отрезки - соответствуют времени выполнения транзакций. При этом каждый отрезок - показывает минимальное и максимальное время затраченное на выполнение - транзакций, а крестиком отмечено среднеквадратичное значение. + - Logarithmic scale on right is in seconds and yellow intervals mean execution time of transactions. + Each interval shows minimal and maximum execution time, cross marks standard deviation. -Выполняется **10.000 транзакций в режиме синхронной фиксации данных** на -диске. При этом требуется гарантия, что при аварийном выключении питания -(или другом подобном сбое) все данные будут консистентны и полностью -соответствовать последней завершенной транзакции. В _libmdbx_ в этом -режиме при фиксации каждой транзакции выполняется системный вызов -[fdatasync](https://linux.die.net/man/2/fdatasync). +**10,000 transactions in sync-write mode**. In case of a crash all data is consistent and state is right after last successful transaction. [fdatasync](https://linux.die.net/man/2/fdatasync) syscall is used after each write transaction in this mode. -В каждой транзакции выполняется комбинированная CRUD-операция (две -вставки, одно чтение, одно обновление, одно удаление). Бенчмарк стартует -на пустой базе, а при завершении, в результате выполняемых действий, в -базе насчитывается 10.000 небольших key-value записей. +In the benchmark each transaction contains combined CRUD operations (2 inserts, 1 read, 1 update, 1 delete). +Benchmark starts on empty database and after full run the database contains 10,000 small key-value records. ![Comparison #3: Sync-write mode](https://raw.githubusercontent.com/wiki/leo-yuriev/libmdbx/img/perf-slide-3.png) -------------------------------------------------------------------------------- -### Отложенная фиксация - - - Линейная шкала слева и темные прямоугольники соответствуют количеству - транзакций в секунду, усредненному за всё время теста. - - - Логарифмическая шкала справа и желтые интервальные отрезки - соответствуют времени выполнения транзакций. При этом каждый отрезок - показывает минимальное и максимальное время затраченное на выполнение - транзакций, а крестиком отмечено среднеквадратичное значение. - -Выполняется **100.000 транзакций в режиме отложенной фиксации данных** -на диске. При этом требуется гарантия, что при аварийном выключении -питания (или другом подобном сбое) все данные будут консистентны на -момент завершения одной из транзакций, но допускается потеря изменений -из некоторого количества последних транзакций, что для многих движков -предполагает включение -[WAL](https://en.wikipedia.org/wiki/Write-ahead_logging) (write-ahead -logging) либо журнала транзакций, который в свою очередь опирается на -гарантию упорядоченности данных в журналируемой файловой системе. -_libmdbx_ при этом не ведет WAL, а передает весь контроль файловой -системе и ядру ОС. - -В каждой транзакции выполняется комбинированная CRUD-операция (две -вставки, одно чтение, одно обновление, одно удаление). Бенчмарк стартует -на пустой базе, а при завершении, в результате выполняемых действий, в -базе насчитывается 100.000 небольших key-value записей. +### Lazy-write mode + + - Linear scale on left and dark rectangles mean arithmetic mean of thousands transactions per second; + + - Logarithmic scale on right in seconds and yellow intervals mean execution time of transactions. Each interval shows minimal and maximum execution time, cross marks standard deviation. + +**100,000 transactions in lazy-write mode**. +In case of a crash all data is consistent and state is right after one of last transactions, but transactions after it +will be lost. Other DB engines use [WAL](https://en.wikipedia.org/wiki/Write-ahead_logging) or transaction journal for that, +which in turn depends on order of operations in journaled filesystem. _libmdbx_ doesn't use WAL and hands I/O operations +to filesystem and OS kernel (mmap). + +In the benchmark each transaction contains combined CRUD operations (2 inserts, 1 read, 1 update, 1 delete). +Benchmark starts on empty database and after full run the database contains 100,000 small key-value records. + ![Comparison #4: Lazy-write mode](https://raw.githubusercontent.com/wiki/leo-yuriev/libmdbx/img/perf-slide-4.png) -------------------------------------------------------------------------------- -### Асинхронная фиксация +### Async-write mode - - Линейная шкала слева и темные прямоугольники соответствуют количеству - транзакций в секунду, усредненному за всё время теста. + - Linear scale on left and dark rectangles mean arithmetic mean of thousands transactions per second; - - Логарифмическая шкала справа и желтые интервальные отрезки - соответствуют времени выполнения транзакций. При этом каждый отрезок - показывает минимальное и максимальное время затраченное на выполнение - транзакций, а крестиком отмечено среднеквадратичное значение. + - Logarithmic scale on right in seconds and yellow intervals mean execution time of transactions. Each interval shows minimal and maximum execution time, cross marks standard deviation. -Выполняется **1.000.000 транзакций в режиме асинхронной фиксации -данных** на диске. При этом требуется гарантия, что при аварийном -выключении питания (или другом подобном сбое) все данные будут -консистентны на момент завершения одной из транзакций, но допускается -потеря изменений из значительного количества последних транзакций. Во -всех движках при этом включался режим предполагающий минимальную -нагрузку на диск по-записи, и соответственно минимальную гарантию -сохранности данных. В _libmdbx_ при этом используется режим асинхронной -записи измененных страниц на диск посредством ядра ОС и системного -вызова [msync(MS_ASYNC)](https://linux.die.net/man/2/msync). +**1,000,000 transactions in async-write mode**. In case of a crash all data will be consistent and state will be right after one of last transactions, but lost transaction count is much higher than in lazy-write mode. All DB engines in this mode do as little writes as possible on persistent storage. _libmdbx_ uses [msync(MS_ASYNC)](https://linux.die.net/man/2/msync) in this mode. -В каждой транзакции выполняется комбинированная CRUD-операция (две -вставки, одно чтение, одно обновление, одно удаление). Бенчмарк стартует -на пустой базе, а при завершении, в результате выполняемых действий, в -базе насчитывается 10.000 небольших key-value записей. +In the benchmark each transaction contains combined CRUD operations (2 inserts, 1 read, 1 update, 1 delete). +Benchmark starts on empty database and after full run the database contains 10,000 small key-value records. ![Comparison #5: Async-write mode](https://raw.githubusercontent.com/wiki/leo-yuriev/libmdbx/img/perf-slide-5.png) -------------------------------------------------------------------------------- -### Потребление ресурсов +### Cost comparison -Показана соотнесенная сумма использованных ресурсов в ходе бенчмарка в -режиме отложенной фиксации: +Summary of used resources during lazy-write mode benchmarks: - - суммарное количество операций ввода-вывода (IOPS), как записи, так и - чтения. + - Read and write IOPS; - - суммарное затраченное время процессора, как в режиме пользовательских процессов, - так и в режиме ядра ОС. + - Sum of user CPU time and sys CPU time; - - использованное место на диске при завершении теста, после закрытия БД из тестирующего процесса, - но без ожидания всех внутренних операций обслуживания (компактификации LSM и т.п.). + - Used space on persistent storage after the test and closed DB, but not waiting for the end of all internal + housekeeping operations (LSM compactification, etc). -Движок _ForestDB_ был исключен при оформлении результатов, так как -относительно конкурентов многократно превысил потребление каждого из -ресурсов (потратил процессорное время на генерацию IOPS для заполнения -диска), что не позволяло наглядно сравнить показатели остальных движков -на одной диаграмме. +_ForestDB_ is excluded because benchmark showed it's resource consumption for each resource (CPU, IOPS) much higher than other engines which prevents to meaningfully compare it with them. -Все данные собирались посредством системного вызова -[getrusage()](http://man7.org/linux/man-pages/man2/getrusage.2.html) и -сканированием директорий с данными. +All benchmark data is gathered by [getrusage()](http://man7.org/linux/man-pages/man2/getrusage.2.html) syscall and by +scanning data directory. ![Comparison #6: Cost comparison](https://raw.githubusercontent.com/wiki/leo-yuriev/libmdbx/img/perf-slide-6.png) -------------------------------------------------------------------------------- -## Недостатки и Компромиссы - -1. Единовременно может выполняться не более одной транзакция изменения данных - (один писатель). Зато все изменения всегда последовательны, не может быть - конфликтов или логических ошибок при откате транзакций. - -2. Отсутствие [WAL](https://en.wikipedia.org/wiki/Write-ahead_logging) - обуславливает относительно большой - [WAF](https://en.wikipedia.org/wiki/Write_amplification) (Write - Amplification Factor). Поэтому фиксация изменений на диске может быть - достаточно дорогой и являться главным ограничением производительности - при интенсивном изменении данных. - > В качестве компромисса _libmdbx_ предлагает несколько режимов ленивой - > и/или периодической фиксации. В том числе режим `MAPASYNC`, при котором - > изменения происходят только в памяти и асинхронно фиксируются на диске - > ядром ОС. +## Gotchas + +1. At one moment there can be only one writer. But this allows to serialize writes and eliminate any possibility + of conflict or logical errors during transaction rollback. + +2. No [WAL](https://en.wikipedia.org/wiki/Write-ahead_logging) means relatively + big [WAF](https://en.wikipedia.org/wiki/Write_amplification) (Write Amplification Factor). + Because of this syncing data to disk might be quite resource intensive and be main performance bottleneck + during intensive write workload. + > As compromise _libmdbx_ allows several modes of lazy and/or periodic syncing, including `MAPASYNC` mode, which modificate + > data in memory and asynchronously syncs data to disc, moment to sync is picked by OS. > - > Однако, следует воспринимать это свойство аккуратно и взвешенно. - > Например, полная фиксация транзакции в БД с журналом потребует минимум 2 - > IOPS (скорее всего 3-4) из-за накладных расходов в файловой системе. В - > _libmdbx_ фиксация транзакции также требует от 2 IOPS. Однако, в БД с - > журналом кол-во IOPS будет меняться в зависимости от файловой системы, - > но не от кол-ва записей или их объема. Тогда как в _libmdbx_ кол-во - > будет расти логарифмически от кол-во записей/строк в БД (по высоте - > b+tree). - -3. [COW](https://ru.wikipedia.org/wiki/%D0%9A%D0%BE%D0%BF%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%BF%D1%80%D0%B8_%D0%B7%D0%B0%D0%BF%D0%B8%D1%81%D0%B8) - для реализации [MVCC](https://ru.wikipedia.org/wiki/MVCC) выполняется на - уровне страниц в [B+ - дереве](https://ru.wikipedia.org/wiki/B-%D0%B4%D0%B5%D1%80%D0%B5%D0%B2%D0%BE). - Поэтому изменение данных амортизационно требует копирования Olog(N) - страниц, что расходует [пропускную способность оперативной - памяти](https://en.wikipedia.org/wiki/Memory_bandwidth) и является - основным ограничителем производительности в режиме `MAPASYNC`. - > Этот недостаток неустраним, тем не менее следует дать некоторые пояснения. - > Дело в том, что фиксация изменений на диске потребует гораздо более - > значительного копирования данных в памяти и массы других затратных операций. - > Поэтому обусловленное этим недостатком падение производительности становится - > заметным только при отказе от фиксации изменений на диске. - > Соответственно, корректнее сказать что _libmdbx_ позволяет - > получить персистентность ценой минимального падения производительности. - > Если же нет необходимости оперативно сохранять данные, то логичнее - > использовать `std::map`. - -4. В _LMDB_ существует проблема долгих чтений (приостановленных читателей), - которая приводит к деградации производительности и переполнению БД. - > В _libmdbx_ предложены средства для предотвращения, быстрого выхода из - > некомфортной ситуации и устранения её последствий. Подробности ниже. - -5. В _LMDB_ есть вероятность разрушения БД в режиме `WRITEMAP+MAPASYNC`. - В _libmdbx_ для `WRITEMAP+MAPASYNC` гарантируется как сохранность базы, - так и согласованность данных. - > Дополнительно, в качестве альтернативы, предложен режим `UTTERLY_NOSYNC`. - > Подробности ниже. - - -#### Проблема долгих чтений - -*Следует отметить*, что проблема "сборки мусора" так или иначе -существует во всех СУБД (Vacuum в PostgreSQL). Однако в случае _libmdbx_ -и LMDB она проявляется более остро, прежде всего из-за высокой -производительности, а также из-за намеренного упрощения внутренних -механизмов ради производительности. - -Понимание проблемы требует некоторых пояснений, которые -изложены ниже, но могут быть сложны для быстрого восприятия. -Поэтому, тезисно: - -* Изменение данных на фоне долгой операции чтения может - приводить к исчерпанию места в БД. - -* После чего любая попытка обновить данные будет приводить к - ошибке `MAP_FULL` до завершения долгой операции чтения. - -* Характерными примерами долгих чтений являются горячее - резервное копирования и отладка клиентского приложения при - активной транзакции чтения. - -* В оригинальной _LMDB_ после этого будет наблюдаться - устойчивая деградация производительности всех механизмов - обратной записи на диск (в I/O контроллере, в гипервизоре, - в ядре ОС). - -* В _libmdbx_ предусмотрен механизм аварийного прерывания таких - операций, а также режим `LIFO RECLAIM` устраняющий последующую - деградацию производительности. - -Операции чтения выполняются в контексте снимка данных (версии -БД), который был актуальным на момент старта транзакции чтения. Такой -читаемый снимок поддерживается неизменным до завершения операции. В свою -очередь, это не позволяет повторно использовать страницы БД в -последующих версиях (снимках БД). - -Другими словами, если обновление данных выполняется на фоне долгой -операции чтения, то вместо повторного использования "старых" ненужных -страниц будут выделяться новые, так как "старые" страницы составляют -снимок БД, который еще используется долгой операцией чтения. - -В результате, при интенсивном изменении данных и достаточно длительной -операции чтения, в БД могут быть исчерпаны свободные страницы, что не -позволит создавать новые снимки/версии БД. Такая ситуация будет -сохраняться до завершения операции чтения, которая использует старый -снимок данных и препятствует повторному использованию страниц БД. - -Однако, на этом проблемы не заканчиваются. После описанной ситуации, все -дополнительные страницы, которые были выделены пока переработка старых -была невозможна, будут участвовать в цикле выделения/освобождения до -конца жизни экземпляра БД. В оригинальной _LMDB_ этот цикл использования -страниц работает по принципу [FIFO](https://ru.wikipedia.org/wiki/FIFO). -Поэтому увеличение количества циркулирующий страниц, с точки зрения -механизмов кэширования и/или обратной записи, выглядит как увеличение -рабочего набор данных. Проще говоря, однократное попадание в ситуацию -"уснувшего читателя" приводит к устойчивому эффекту вымывания I/O кэша -при всех последующих изменениях данных. - -Для устранения описанных проблемы в _libmdbx_ сделаны существенные -доработки, подробности ниже. Иллюстрации к проблеме "долгих чтений" -можно найти в [слайдах презентации](http://www.slideshare.net/leoyuriev/lmdb). - -Там же приведен пример количественной оценки прироста производительности -за счет эффективной работы [BBWC](https://en.wikipedia.org/wiki/BBWC) -при включении `LIFO RECLAIM` в _libmdbx_. - - -#### Сохранность данных в режиме асинхронной фиксации - -При работе в режиме `WRITEMAP+MAPSYNC` запись измененных страниц -выполняется ядром ОС, что имеет ряд преимуществ. Так например, при крахе -приложения, ядро ОС сохранит все изменения. - -Однако, при аварийном отключении питания или сбое в ядре ОС, на диске -может быть сохранена только часть измененных страниц БД. При этом с большой -вероятностью может оказаться так, что будут сохранены мета-страницы со -ссылками на страницы с новыми версиями данных, но не сами новые данные. -В этом случае БД будет безвозвратна разрушена, даже если до аварии -производилась полная синхронизация данных (посредством -`mdbx_env_sync()`). - -В _libmdbx_ эта проблема устранена путем полной переработки -пути записи данных: - -* В режиме `WRITEMAP+MAPSYNC` _libmdbx_ не обновляет - мета-страницы непосредственно, а поддерживает их теневые копии - с переносом изменений после фиксации данных. - -* При завершении транзакций, в зависимости от состояния - синхронности данных между диском и оперативной память, - _libmdbx_ помечает точки фиксации либо как сильные (strong), - либо как слабые (weak). Так например, в режиме - `WRITEMAP+MAPSYNC` завершаемые транзакции помечаются как - слабые, а при явной синхронизации данных как сильные. - -* В _libmdbx_ поддерживается не две, а три отдельные мета-страницы. - Это позволяет выполнять фиксацию транзакций с формированием как - сильной, так и слабой точки фиксации, без потери двух предыдущих - точек фиксации (из которых одна может быть сильной, а вторая слабой). - В результате, _libmdbx_ позволяет в произвольном порядке чередовать - сильные и слабые точки фиксации без нарушения соответствующих - гарантий в случае неожиданной системной аварии во время фиксации. - -* При открытии БД выполняется автоматический откат к последней - сильной фиксации. Этим обеспечивается гарантия сохранности БД. - -Такая гарантия надежности не дается бесплатно. Для -сохранности данных, страницы формирующие крайний снимок с -сильной фиксацией, не должны повторно использоваться -(перезаписываться) до формирования следующей сильной точки -фиксации. Таким образом, крайняя точка фиксации создает -описанный выше эффект "долгого чтения". Разница же здесь в том, -что при исчерпании свободных страниц ситуация будет -автоматически исправлена, посредством записи изменений на диск -и формированием новой сильной точки фиксации. - -Таким образом, в режиме безопасной асинхронной фиксации _libmdbx_ будет -всегда использовать новые страницы до исчерпания места в БД или до явного -формирования сильной точки фиксации посредством `mdbx_env_sync()`. -При этом суммарный трафик записи на диск будет примерно такой-же, -как если бы отдельно фиксировалась каждая транзакций. - -В текущей версии _libmdbx_ вам предоставляется выбор между безопасным -режимом (по умолчанию) асинхронной фиксации, и режимом `UTTERLY_NOSYNC` когда -при системной аварии есть шанс полного разрушения БД как в LMDB. - -В последующих версиях _libmdbx_ будут предусмотрены средства -для асинхронной записи данных на диск с автоматическим -формированием сильных точек фиксации. + > Although this should be used with care, synchronous transactions in a DB with transaction journal will require 2 IOPS + > minimum (probably 3-4 in practice) because of filesystem overhead, overhead depends on filesystem, not on record + > count or record size. In _libmdbx_ IOPS count will grow logarithmically depending on record count in DB (height of B+ tree) + > and will require at least 2 IOPS per transaction too. + +3. [CoW](https://en.wikipedia.org/wiki/Copy-on-write) + for [MVCC](https://en.wikipedia.org/wiki/Multiversion_concurrency_control) is done on memory page level with [B+ + trees](https://ru.wikipedia.org/wiki/B-%D0%B4%D0%B5%D1%80%D0%B5%D0%B2%D0%BE). + Therefore altering data requires to copy about Olog(N) memory pages, which uses [memory bandwidth](https://en.wikipedia.org/wiki/Memory_bandwidth) and is main performance bottleneck in `MAPASYNC` mode. + > This is unavoidable, but isn't that bad. Syncing data to disk requires much more similar operations which will + > be done by OS, therefore this is noticeable only if data sync to persistent storage is fully disabled. + > _libmdbx_ allows to safely save data to persistent storage with minimal performance overhead. If there is no need + > to save data to persistent storage then it's much more preferable to use `std::map`. + + +4. LMDB has a problem of long-time readers which degrades performance and bloats DB + > _libmdbx_ addresses that, details below. + +5. _LMDB_ is susceptible to DB corruption in `WRITEMAP+MAPASYNC` mode. + _libmdbx_ in `WRITEMAP+MAPASYNC` guarantees DB integrity and consistency of data. + > Additionally there is an alternative: `UTTERLY_NOSYNC` mode. Details below. + + +#### Long-time read transactions problem + +Garbage collection problem exists in all databases one way or another (e.g. VACUUM in PostgreSQL). +But in _libmbdx_ and LMDB it's even more important because of high performance and deliberate +simplification of internals with emphasis on performance. + +* Altering data during long read operation may exhaust available space on persistent storage. + +* If available space is exhausted then any attempt to update data + results in `MAP_FULL` error until long read operation ends. + +* Main examples of long readers is hot backup + and debugging of client application which actively uses read transactions. + +* In _LMDB_ this results in degraded performance of all operations + of syncing data to persistent storage. + +* _libmdbx_ has a mechanism which aborts such operations and `LIFO RECLAIM` + mode which addresses performance degradation. + +Read operations operate only over snapshot of DB which is consistent on the moment when read transaction started. +This snapshot doesn't change throughout the transaction but this leads to inability to reclaim the pages until +read transaction ends. + +In _LMDB_ this leads to a problem that memory pages, allocated for operations during long read, will be used for operations +and won't be reclaimed until DB process terminates. In _LMDB_ they are used in +[FIFO](https://en.wikipedia.org/wiki/FIFO_(computing_and_electronics)) manner, which causes increased page count +and less chance of cache hit during I/O. In other words: one long-time reader can impact performance of all database +until it'll be reopened. + +_libmdbx_ addresses the problem, details below. Illustrations to this problem can be found in the +[presentation](http://www.slideshare.net/leoyuriev/lmdb). There is also example of performance increase thanks to +[BBWC](https://en.wikipedia.org/wiki/Disk_buffer#Write_acceleration) when `LIFO RECLAIM` enabled in _libmdbx_. + +#### Data safety in async-write mode + +In `WRITEMAP+MAPSYNC` mode dirty pages are written to persistent storage by kernel. This means that in case of application +crash OS kernel will write all dirty data to disk and nothing will be lost. But in case of hardware malfunction or OS kernel +fatal error only some dirty data might be synced to disk, and there is high probability that pages with metadata saved, +will point to non-saved, hence non-existent, data pages. In such situation DB is completely corrupted and can't be +repaired even if there was full sync before the crash via `mdbx_env_sync(). + +_libmdbx_ addresses this by fully reimplementing write path of data: + +* In `WRITEMAP+MAPSYNC` mode meta-data pages aren't updated in place, instead their shadow copies are used and their updates + are synced after data is flushed to disk. + +* During transaction commit _libmdbx_ marks synchronization points as steady or weak depending on how much synchronization + needed between RAM and persistent storage, e.g. in `WRITEMAP+MAPSYNC` commited transactions are marked as weak, + but during explicit data synchronization - as steady. + +* _libmdbx_ maintains three separate meta-pages instead of two. This allows to commit transaction with steady or +weak synchronization point without losing two previous synchronization points (one of them can be steady, and second - weak). +This allows to order weak and steady synchronization points in any order without losing consistency in case of system crash. + +* During DB open _libmdbx_ rollbacks to the last steady synchronization point, this guarantees database integrity. + +For data safety pages which form database snapshot with steady synchronization point must not be updated until next steady +synchronization point. So last steady synchronization point creates "long-time read" effect. The only difference that in case +of memory exhaustion the problem will be immediately addressed by flushing changes to persistent storage and forming new steady +synchronization point. + +So in async-write mode _libmdbx_ will always use new pages until memory is exhausted or `mdbx_env_sync()`is invoked. Total +disk usage will be almost the same as in sync-write mode. + +Current _libmdbx_ gives a choice of safe async-write mode (default) and `UTTERLY_NOSYNC` mode which may result in full DB +corruption during system crash as with LMDB. + +Next version of _libmdbx_ will create steady synchronization points automatically in async-write mode. -------------------------------------------------------------------------------- -Доработки и усовершенствования относительно LMDB +Improvements over LMDB ================================================ -1. Режим `LIFO RECLAIM`. - - Для повторного использования выбираются не самые старые, а - самые новые страницы из доступных. За счет этого цикл - использования страниц всегда имеет минимальную длину и не - зависит от общего числа выделенных страниц. +1. `LIFO RECLAIM` mode: - В результате механизмы кэширования и обратной записи работают с - максимально возможной эффективностью. В случае использования - контроллера дисков или системы хранения с - [BBWC](https://en.wikipedia.org/wiki/BBWC) возможно - многократное увеличение производительности по записи - (обновлению данных). + The newest pages are picked for reuse instead of the oldest. + This allows to minimize reclaim loop and make it execution time independent from total page count. -2. Обработчик `OOM-KICK`. + This results in OS kernel cache mechanisms working with maximum efficiency. + In case of using disc controllers or storages with + [BBWC](https://en.wikipedia.org/wiki/Disk_buffer#Write_acceleration) this may greatly improve + write performance. - Посредством `mdbx_env_set_oomfunc()` может быть установлен - внешний обработчик (callback), который будет вызван при - исчерпания свободных страниц из-за долгой операцией чтения. - Обработчику будет передан PID и pthread_id виновника. - В свою очередь обработчик может предпринять одно из действий: +2. `OOM-KICK` callback. - * нейтрализовать виновника (отправить сигнал kill #9), если - долгое чтение выполняется сторонним процессом; + `mdbx_env_set_oomfunc()` allows to set a callback, which will be called + in the event of memory exhausting during long-time read transaction. + Callback will be invoked with PID and pthread_id of offending thread as parameters. + Callback can do any of this things to remedy the problem: - * отменить или перезапустить проблемную операцию чтения, если - операция выполняется одним из потоков текущего процесса; + * wait for read transaction to finish normally; - * подождать некоторое время, в расчете что проблемная операция - чтения будет штатно завершена; + * kill the offending process (signal 9), if separate process is doing long-time read; - * прервать текущую операцию изменения данных с возвратом кода - ошибки. + * abort or restart offending read transaction if it's running in sibling thread; -3. Гарантия сохранности БД в режиме `WRITEMAP+MAPSYNC`. + * abort current write transaction with returning error code. -В текущей версии _libmdbx_ вам предоставляется выбор между безопасным -режимом (по умолчанию) асинхронной фиксации, и режимом `UTTERLY_NOSYNC` -когда при системной аварии есть шанс полного разрушения БД как в LMDB. -Для подробностей смотрите раздел -[Сохранность данных в режиме асинхронной фиксации](#Сохранность-данных-в-режиме-асинхронной-фиксации). +3. Guarantee of DB integrity in `WRITEMAP+MAPSYNC` mode: + > Current _libmdbx_ gives a choice of safe async-write mode (default) + > and `UTTERLY_NOSYNC` mode which may result in full + > DB corruption during system crash as with LMDB. For details see + > [Data safety in async-write mode](#data-safety-in-async-write-mode). -4. Возможность автоматического формирования контрольных точек -(сброса данных на диск) при накоплении заданного объёма изменений, -устанавливаемого функцией `mdbx_env_set_syncbytes()`. +4. Automatic creation of synchronization points (flush changes to persistent storage) + when changes reach set threshold (threshold can be set by `mdbx_env_set_syncbytes()`). -5. Возможность получить отставание текущей транзакции чтения от -последней версии данных в БД посредством `mdbx_txn_straggler()`. +5. Ability to get how far current read-only snapshot is from latest version of the DB by `mdbx_txn_straggler()`. -6. Утилита mdbx_chk для проверки БД и функция `mdbx_env_pgwalk()` для -обхода всех страниц БД. +6. `mdbx_chk` tool for DB checking and `mdbx_env_pgwalk()` for page-walking all pages in DB. -7. Управление отладкой и получение отладочных сообщений посредством -`mdbx_setup_debug()`. +7. Control over debugging and receiving of debugging messages via `mdbx_setup_debug()`. -8. Возможность связать с каждой завершаемой транзакцией до 3 -дополнительных маркеров посредством `mdbx_canary_put()`, и прочитать их -в транзакции чтения посредством `mdbx_canary_get()`. +8. Ability to assign up to 3 markers to commiting transaction with `mdbx_canary_put()` and then get them in read transaction + by `mdbx_canary_get()`. -9. Возможность узнать есть ли за текущей позицией курсора строка данных -посредством `mdbx_cursor_eof()`. +9. Check if there is a row with data after current cursor position via `mdbx_cursor_eof()`. -10. Возможность явно запросить обновление существующей записи, без -создания новой посредством флажка `MDBX_CURRENT` для `mdbx_put()`. +10. Ability to explicitly request update of current record without creating new record. Implemented as `MDBX_CURRENT` flag + for `mdbx_put()`. -11. Возможность посредством `mdbx_replace()` обновить или удалить запись -с получением предыдущего значения данных, а также адресно изменить -конкретное multi-значение. +11. Ability to update or delete record and get previous value via `mdbx_replace()` Also can update specific multi-value. -12. Поддержка ключей и значений нулевой длины, включая сортированные -дубликаты. +12. Support for keys and values of zero length, including sorted duplicates. -13. Исправленный вариант `mdbx_cursor_count()`, возвращающий корректное -количество дубликатов для всех типов таблиц и любого положения курсора. +13. Fixed `mdbx_cursor_count()`, which returns correct count of duplicated for all table types and any cursor position. -14. Возможность открыть БД в эксклюзивном режиме посредством -`mdbx_env_open_ex()`, например в целях её проверки. +14. Ability to open DB in exclusive mode via `mdbx_env_open_ex()`, e.g. for integrity check. -15. Возможность закрыть БД в "грязном" состоянии (без сброса данных и -формирования сильной точки фиксации) посредством `mdbx_env_close_ex()`. +15. Ability to close DB in "dirty" state (without data flush and creation of steady synchronization point) + via `mdbx_env_close_ex()`. -16. Возможность получить посредством `mdbx_env_info()` дополнительную -информацию, включая номер самой старой версии БД (снимка данных), -который используется одним из читателей. +16. Ability to get addition info, including number of the oldest snapshot of DB, which is used by one of the readers. + Implemented via `mdbx_env_info()`. -17. Функция `mdbx_del()` не игнорирует дополнительный (уточняющий) -аргумент `data` для таблиц без дубликатов (без флажка `MDBX_DUPSORT`), а -при его ненулевом значении всегда использует его для сверки с удаляемой -записью. +17. `mdbx_del()` doesn't ignore additional argument (specifier) `data` + for tables without duplicates (without flag `MDBX_DUPSORT`), if `data` is not zero then always uses it to verify + record, which is being deleted. -18. Возможность открыть dbi-таблицу, одновременно с установкой -компараторов для ключей и данных, посредством `mdbx_dbi_open_ex()`. +18. Ability to open dbi-table with simultaneous setup of comparators for keys and values, via `mdbx_dbi_open_ex()`. -19. Возможность посредством `mdbx_is_dirty()` определить находятся ли -некоторый ключ или данные в "грязной" странице БД. Таким образом, -избегая лишнего копирования данных перед выполнением модифицирующих -операций (значения в размещенные "грязных" страницах могут быть -перезаписаны при изменениях, иначе они будут неизменны). +19. Ability to find out if key or value are in dirty page. This may be useful to make a decision to avoid + excessive CoW before updates. Implemented via `mdbx_is_dirty()`. -20. Корректное обновление текущей записи, в том числе сортированного -дубликата, при использовании режима `MDBX_CURRENT` в -`mdbx_cursor_put()`. +20. Correct update of current record in `MDBX_CURRENT` mode of `mdbx_cursor_put()`, including sorted duplicated. -21. Все курсоры, как в транзакциях только для чтения, так и в пишущих, -могут быть переиспользованы посредством `mdbx_cursor_renew()` и ДОЛЖНЫ -ОСВОБОЖДАТЬСЯ ЯВНО. - > - > ## _ВАЖНО_, Обратите внимание! - > - > Это единственное изменение в API, которое значимо меняет - > семантику управления курсорами и может приводить к утечкам - > памяти. Следует отметить, что это изменение вынужденно. - > Так устраняется неоднозначность с массой тяжких последствий: +21. All cursors in all read and write transactions can be reused by `mdbx_cursor_renew()` and MUST be freed explicitly. + > ## Caution, please pay attention! > - > - обращение к уже освобожденной памяти; - > - попытки повторного освобождения памяти; + > This is the only change of API, which changes semantics of cursor management + > and can lead to memory leaks on misuse. This is a needed change as it eliminates ambiguity + > which helps to avoid such errors as: + > - use-after-free; + > - double-free; > - memory corruption and segfaults. -22. Дополнительный код ошибки `MDBX_EMULTIVAL`, который возвращается из -`mdbx_put()` и `mdbx_replace()` при попытке выполнить неоднозначное -обновление или удаления одного из нескольких значений с одним ключом. +22. Additional error code `MDBX_EMULTIVAL`, which is returned by `mdbx_put()` and + `mdbx_replace()` in case is ambiguous update or delete. -23. Возможность посредством `mdbx_get_ex()` получить значение по -заданному ключу, одновременно с количеством дубликатов. +23. Ability to get value by key and duplicates count by `mdbx_get_ex()` -24. Наличие функций `mdbx_cursor_on_first()` и `mdbx_cursor_on_last()`, -которые позволяют быстро выяснить стоит ли курсор на первой/последней -позиции. +24. Functions `mdbx_cursor_on_first() and mdbx_cursor_on_last(), which allows to know if cursor is currently on first or + last position respectively. -25. При завершении читающих транзакций, открытые в них DBI-хендлы не -закрываются и не теряются при завершении таких транзакций посредством -`mdbx_txn_abort()` или `mdbx_txn_reset()`. Что позволяет избавится от ряда -сложно обнаруживаемых ошибок. +25. If read transaction is aborted via `mdbx_txn_abort()` or `mdbx_txn_reset()` then DBI-handles, which were opened in it, + aren't closed or deleted. This allows to avoid several types of hard-to-debug errors. -26. Генерация последовательностей посредством `mdbx_dbi_sequence()`. +26. Sequence generation via `mdbx_dbi_sequence()`. -27. Расширенное динамическое управление размером БД, включая выбор -размера страницы посредством `mdbx_env_set_geometry()`, -в том числе в **Windows** +27. Advanced dynamic control over DB size, including ability to choose page size via `mdbx_env_set_geometry()`, + including on Windows. -28. Три мета-страницы вместо двух, что позволяет гарантированно -консистентно обновлять слабые контрольные точки фиксации без риска -повредить крайнюю сильную точку фиксации. +28. Three meta-pages instead two, this allows to guarantee consistently update weak sync-points without risking to + corrupt last steady sync-point. -29. В _libmdbx_ реализован автоматический возврат освобождающихся -страниц в область нераспределенного резерва в конце файла данных. При -этом уменьшается количество страниц загруженных в память и участвующих в -цикле обновления данных и записи на диск. Фактически _libmdbx_ выполняет -постоянную компактификацию данных, но не затрачивая на это -дополнительных ресурсов, а только освобождая их. При освобождении места -в БД и установки соответствующих параметров геометрии базы данных, также будет -уменьшаться размер файла на диске, в том числе в **Windows**. +29. Automatic reclaim of freed pages to specific reserved space in the end of database file. This lowers amount of pages, + loaded to memory, used in update/flush loop. In fact _libmdbx_ constantly performs compactification of data, + but doesn't use addition resources for that. Space reclaim of DB and setup of database geometry parameters also decreases + size of the database on disk, including on Windows. -------------------------------------------------------------------------------- diff --git a/plugins/Dbx_mdbx/src/libmdbx/TODO.md b/plugins/Dbx_mdbx/src/libmdbx/TODO.md index 8ff0b3f268..1af7e004bb 100644 --- a/plugins/Dbx_mdbx/src/libmdbx/TODO.md +++ b/plugins/Dbx_mdbx/src/libmdbx/TODO.md @@ -1,4 +1,4 @@ -Допеределки +Допеределки =========== - [ ] Перевод mdbx-tools на С++ и сборка для Windows. - [ ] Переход на CMake, замена заглушек mdbx_version и mdbx_build. diff --git a/plugins/Dbx_mdbx/src/libmdbx/build.sh b/plugins/Dbx_mdbx/src/libmdbx/build.sh new file mode 100644 index 0000000000..5170882265 --- /dev/null +++ b/plugins/Dbx_mdbx/src/libmdbx/build.sh @@ -0,0 +1,18 @@ +#!/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/plugins/Dbx_mdbx/src/libmdbx/libmdbx.files b/plugins/Dbx_mdbx/src/libmdbx/libmdbx.files index 2679802998..38125146e4 100644 --- a/plugins/Dbx_mdbx/src/libmdbx/libmdbx.files +++ b/plugins/Dbx_mdbx/src/libmdbx/libmdbx.files @@ -1,3 +1,9 @@ +CMakeLists.txt +README-RU.md +pcrf_test/CMakeLists.txt +src/tools/CMakeLists.txt +test/CMakeLists.txt +tutorial/CMakeLists.txt tutorial/sample-mdbx.c AUTHORS LICENSE diff --git a/plugins/Dbx_mdbx/src/libmdbx/libmdbx.includes b/plugins/Dbx_mdbx/src/libmdbx/libmdbx.includes index eb512a01ce..0b5ade5690 100644 --- a/plugins/Dbx_mdbx/src/libmdbx/libmdbx.includes +++ b/plugins/Dbx_mdbx/src/libmdbx/libmdbx.includes @@ -2,3 +2,5 @@ src src/tools test +pcrf_test +tutorial diff --git a/plugins/Dbx_mdbx/src/libmdbx/mdbx.h b/plugins/Dbx_mdbx/src/libmdbx/mdbx.h index 4daf525f6a..2cf3273b06 100644 --- a/plugins/Dbx_mdbx/src/libmdbx/mdbx.h +++ b/plugins/Dbx_mdbx/src/libmdbx/mdbx.h @@ -1,4 +1,4 @@ -/* LICENSE AND COPYRUSTING ***************************************************** +/* LICENSE AND COPYRUSTING ***************************************************** * * Copyright 2015-2018 Leonid Yuriev * and other libmdbx authors: please see AUTHORS file. @@ -165,7 +165,7 @@ typedef pthread_t mdbx_tid_t; /*--------------------------------------------------------------------------*/ #define MDBX_VERSION_MAJOR 0 -#define MDBX_VERSION_MINOR 0 +#define MDBX_VERSION_MINOR 1 #if defined(LIBMDBX_EXPORTS) #define LIBMDBX_API __dll_export diff --git a/plugins/Dbx_mdbx/src/libmdbx/package.sh b/plugins/Dbx_mdbx/src/libmdbx/package.sh new file mode 100644 index 0000000000..d7f9ab297a --- /dev/null +++ b/plugins/Dbx_mdbx/src/libmdbx/package.sh @@ -0,0 +1,25 @@ +#!/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/plugins/Dbx_mdbx/src/libmdbx/src/bits.h b/plugins/Dbx_mdbx/src/libmdbx/src/bits.h index a714ae733c..67def2ed90 100644 --- a/plugins/Dbx_mdbx/src/libmdbx/src/bits.h +++ b/plugins/Dbx_mdbx/src/libmdbx/src/bits.h @@ -65,7 +65,15 @@ * But you could remove this #error and try to continue at your own risk. * In such case please don't rise up an issues related ONLY to old compilers. */ -# warning "libmdbx required at least GCC 4.2 compatible C/C++ compiler." +# warning "libmdbx required GCC >= 4.2" +#endif + +#if defined(__clang__) && !__CLANG_PREREQ(3,8) + /* Actualy libmdbx was not tested with CLANG older than 3.8. + * But you could remove this #error and try to continue at your own risk. + * In such case please don't rise up an issues related ONLY to old compilers. + */ +# warning "libmdbx required CLANG >= 3.8" #endif #if defined(__GLIBC__) && !__GLIBC_PREREQ(2,12) @@ -80,6 +88,28 @@ # warning "libmdbx don't compatible with ThreadSanitizer, you will get a lot of false-positive issues." #endif /* __SANITIZE_THREAD__ */ +#if __has_warning("-Wconstant-logical-operand") +#if defined(__clang__) +#pragma clang diagnostic ignored "-Wconstant-logical-operand" +#elif defined(__GNUC__) +#pragma GCC diagnostic ignored "-Wconstant-logical-operand" +#else +#pragma warning disable "constant-logical-operand" +#endif +#endif /* -Wconstant-logical-operand */ + +#if __has_warning("-Walignment-reduction-ignored") || defined(__e2k__) || defined(__ICC) +#if defined(__ICC) +#pragma warning(disable: 3453 1366) +#elif defined(__clang__) +#pragma clang diagnostic ignored "-Walignment-reduction-ignored" +#elif defined(__GNUC__) +#pragma GCC diagnostic ignored "-Walignment-reduction-ignored" +#else +#pragma warning disable "alignment-reduction-ignored" +#endif +#endif /* -Wno-constant-logical-operand */ + #include "./osal.h" /* *INDENT-ON* */ diff --git a/plugins/Dbx_mdbx/src/libmdbx/src/defs.h b/plugins/Dbx_mdbx/src/libmdbx/src/defs.h index 5e6110f8c5..7c3bc1d540 100644 --- a/plugins/Dbx_mdbx/src/libmdbx/src/defs.h +++ b/plugins/Dbx_mdbx/src/libmdbx/src/defs.h @@ -1,4 +1,4 @@ -/* +/* * Copyright 2015-2018 Leonid Yuriev * and other libmdbx authors: please see AUTHORS file. * All rights reserved. @@ -59,6 +59,14 @@ # define __has_builtin(x) (0) #endif +#ifndef __has_warning +# define __has_warning(x) (0) +#endif + +#ifndef __has_include +# define __has_include(x) (0) +#endif + #if __has_feature(thread_sanitizer) # define __SANITIZE_THREAD__ 1 #endif @@ -268,7 +276,9 @@ #ifndef __hot # if defined(__OPTIMIZE__) -# if defined(__clang__) && !__has_attribute(hot) +# if defined(__e2k__) +# define __hot __attribute__((hot)) __optimize(3) +# elif defined(__clang__) && !__has_attribute(hot) /* just put frequently used functions in separate section */ # define __hot __attribute__((section("text.hot"))) __optimize("O3") # elif defined(__GNUC__) || __has_attribute(hot) @@ -283,7 +293,9 @@ #ifndef __cold # if defined(__OPTIMIZE__) -# if defined(__clang__) && !__has_attribute(cold) +# if defined(__e2k__) +# define __cold __attribute__((cold)) __optimize(1) +# elif defined(__clang__) && !__has_attribute(cold) /* just put infrequently used functions in separate section */ # define __cold __attribute__((section("text.unlikely"))) __optimize("Os") # elif defined(__GNUC__) || __has_attribute(cold) diff --git a/plugins/Dbx_mdbx/src/libmdbx/src/lck-posix.c b/plugins/Dbx_mdbx/src/libmdbx/src/lck-posix.c index 5916c40e80..64f493cb15 100644 --- a/plugins/Dbx_mdbx/src/libmdbx/src/lck-posix.c +++ b/plugins/Dbx_mdbx/src/libmdbx/src/lck-posix.c @@ -1,4 +1,4 @@ -/* +/* * Copyright 2015-2018 Leonid Yuriev * and other libmdbx authors: please see AUTHORS file. * All rights reserved. diff --git a/plugins/Dbx_mdbx/src/libmdbx/src/tools/CMakeLists.txt b/plugins/Dbx_mdbx/src/libmdbx/src/tools/CMakeLists.txt new file mode 100644 index 0000000000..3e200098f8 --- /dev/null +++ b/plugins/Dbx_mdbx/src/libmdbx/src/tools/CMakeLists.txt @@ -0,0 +1,19 @@ +project(mdbx_tools) + +set(MDBX_TOOLS + mdbx_chk + mdbx_copy + mdbx_dump + mdbx_load + mdbx_stat + ) + +foreach (TOOL ${MDBX_TOOLS}) + add_executable(${TOOL} ${TOOL}.c) + + target_link_libraries(${TOOL} mdbx) + install(TARGETS ${TOOL} DESTINATION ${CMAKE_INSTALL_PREFIX}/bin COMPONENT mdbx) + if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${TOOL}.1) + install(FILES ${TOOL}.1 DESTINATION ${CMAKE_INSTALL_PREFIX}/man/man1 COMPONENT mdbx) + endif() +endforeach() diff --git a/plugins/Dbx_mdbx/src/libmdbx/src/tools/mdbx_chk.c b/plugins/Dbx_mdbx/src/libmdbx/src/tools/mdbx_chk.c index ef2184ffe9..054e6e979e 100644 --- a/plugins/Dbx_mdbx/src/libmdbx/src/tools/mdbx_chk.c +++ b/plugins/Dbx_mdbx/src/libmdbx/src/tools/mdbx_chk.c @@ -1,7 +1,7 @@ -/* mdbx_chk.c - memory-mapped database check tool */ +/* mdbx_chk.c - memory-mapped database check tool */ /* - * Copyright 2015-2017 Leonid Yuriev + * Copyright 2015-2018 Leonid Yuriev * and other libmdbx authors: please see AUTHORS file. * All rights reserved. * diff --git a/plugins/Dbx_mdbx/src/libmdbx/src/tools/mdbx_copy.1 b/plugins/Dbx_mdbx/src/libmdbx/src/tools/mdbx_copy.1 index 825fb269e5..db6c453abd 100644 --- a/plugins/Dbx_mdbx/src/libmdbx/src/tools/mdbx_copy.1 +++ b/plugins/Dbx_mdbx/src/libmdbx/src/tools/mdbx_copy.1 @@ -1,4 +1,4 @@ -.\" Copyright 2015-2017 Leonid Yuriev . +.\" Copyright 2015-2018 Leonid Yuriev . .\" Copyright 2012-2015 Howard Chu, Symas Corp. All Rights Reserved. .\" Copyright 2015,2016 Peter-Service R&D LLC . .\" Copying restrictions apply. See COPYRIGHT/LICENSE. diff --git a/plugins/Dbx_mdbx/src/libmdbx/src/tools/mdbx_copy.c b/plugins/Dbx_mdbx/src/libmdbx/src/tools/mdbx_copy.c index 6d57113418..ee3f739d4d 100644 --- a/plugins/Dbx_mdbx/src/libmdbx/src/tools/mdbx_copy.c +++ b/plugins/Dbx_mdbx/src/libmdbx/src/tools/mdbx_copy.c @@ -1,7 +1,7 @@ /* mdbx_copy.c - memory-mapped database backup tool */ /* - * Copyright 2015-2017 Leonid Yuriev + * Copyright 2015-2018 Leonid Yuriev * and other libmdbx authors: please see AUTHORS file. * All rights reserved. * diff --git a/plugins/Dbx_mdbx/src/libmdbx/src/tools/mdbx_dump.1 b/plugins/Dbx_mdbx/src/libmdbx/src/tools/mdbx_dump.1 index f8dd62a1ce..ccfcc0c9da 100644 --- a/plugins/Dbx_mdbx/src/libmdbx/src/tools/mdbx_dump.1 +++ b/plugins/Dbx_mdbx/src/libmdbx/src/tools/mdbx_dump.1 @@ -1,4 +1,4 @@ -.\" Copyright 2015-2017 Leonid Yuriev . +.\" Copyright 2015-2018 Leonid Yuriev . .\" Copyright 2014-2015 Howard Chu, Symas Corp. All Rights Reserved. .\" Copyright 2015,2016 Peter-Service R&D LLC . .\" Copying restrictions apply. See COPYRIGHT/LICENSE. diff --git a/plugins/Dbx_mdbx/src/libmdbx/src/tools/mdbx_dump.c b/plugins/Dbx_mdbx/src/libmdbx/src/tools/mdbx_dump.c index d0f9a91121..c854e0ad15 100644 --- a/plugins/Dbx_mdbx/src/libmdbx/src/tools/mdbx_dump.c +++ b/plugins/Dbx_mdbx/src/libmdbx/src/tools/mdbx_dump.c @@ -1,7 +1,7 @@ /* mdbx_dump.c - memory-mapped database dump tool */ /* - * Copyright 2015-2017 Leonid Yuriev + * Copyright 2015-2018 Leonid Yuriev * and other libmdbx authors: please see AUTHORS file. * All rights reserved. * diff --git a/plugins/Dbx_mdbx/src/libmdbx/src/tools/mdbx_load.1 b/plugins/Dbx_mdbx/src/libmdbx/src/tools/mdbx_load.1 index ac04101c89..7a18a6c018 100644 --- a/plugins/Dbx_mdbx/src/libmdbx/src/tools/mdbx_load.1 +++ b/plugins/Dbx_mdbx/src/libmdbx/src/tools/mdbx_load.1 @@ -1,4 +1,4 @@ -.\" Copyright 2015-2017 Leonid Yuriev . +.\" Copyright 2015-2018 Leonid Yuriev . .\" Copyright 2014-2015 Howard Chu, Symas Corp. All Rights Reserved. .\" Copyright 2015,2016 Peter-Service R&D LLC . .\" Copying restrictions apply. See COPYRIGHT/LICENSE. diff --git a/plugins/Dbx_mdbx/src/libmdbx/src/tools/mdbx_load.c b/plugins/Dbx_mdbx/src/libmdbx/src/tools/mdbx_load.c index 19a0a3dfb5..51f2993b28 100644 --- a/plugins/Dbx_mdbx/src/libmdbx/src/tools/mdbx_load.c +++ b/plugins/Dbx_mdbx/src/libmdbx/src/tools/mdbx_load.c @@ -1,7 +1,7 @@ /* mdbx_load.c - memory-mapped database load tool */ /* - * Copyright 2015-2017 Leonid Yuriev + * Copyright 2015-2018 Leonid Yuriev * and other libmdbx authors: please see AUTHORS file. * All rights reserved. * diff --git a/plugins/Dbx_mdbx/src/libmdbx/src/tools/mdbx_stat.1 b/plugins/Dbx_mdbx/src/libmdbx/src/tools/mdbx_stat.1 index 2056decb84..ca427f7a66 100644 --- a/plugins/Dbx_mdbx/src/libmdbx/src/tools/mdbx_stat.1 +++ b/plugins/Dbx_mdbx/src/libmdbx/src/tools/mdbx_stat.1 @@ -1,4 +1,4 @@ -.\" Copyright 2015-2017 Leonid Yuriev . +.\" Copyright 2015-2018 Leonid Yuriev . .\" Copyright 2012-2015 Howard Chu, Symas Corp. All Rights Reserved. .\" Copyright 2015,2016 Peter-Service R&D LLC . .\" Copying restrictions apply. See COPYRIGHT/LICENSE. diff --git a/plugins/Dbx_mdbx/src/libmdbx/src/tools/mdbx_stat.c b/plugins/Dbx_mdbx/src/libmdbx/src/tools/mdbx_stat.c index 249837c653..b8733eacd9 100644 --- a/plugins/Dbx_mdbx/src/libmdbx/src/tools/mdbx_stat.c +++ b/plugins/Dbx_mdbx/src/libmdbx/src/tools/mdbx_stat.c @@ -1,7 +1,7 @@ -/* mdbx_stat.c - memory-mapped database status tool */ +/* mdbx_stat.c - memory-mapped database status tool */ /* - * Copyright 2015-2017 Leonid Yuriev + * Copyright 2015-2018 Leonid Yuriev * and other libmdbx authors: please see AUTHORS file. * All rights reserved. * diff --git a/plugins/Dbx_mdbx/src/libmdbx/src/version.c b/plugins/Dbx_mdbx/src/libmdbx/src/version.c index aed4fab508..3b2ffdba76 100644 --- a/plugins/Dbx_mdbx/src/libmdbx/src/version.c +++ b/plugins/Dbx_mdbx/src/libmdbx/src/version.c @@ -14,7 +14,7 @@ #include "./bits.h" -#if MDBX_VERSION_MAJOR != 0 || MDBX_VERSION_MINOR != 0 +#if MDBX_VERSION_MAJOR != 0 || MDBX_VERSION_MINOR != 1 #error "API version mismatch!" #endif diff --git a/plugins/Dbx_mdbx/src/libmdbx/test/CMakeLists.txt b/plugins/Dbx_mdbx/src/libmdbx/test/CMakeLists.txt new file mode 100644 index 0000000000..3ed01bddf9 --- /dev/null +++ b/plugins/Dbx_mdbx/src/libmdbx/test/CMakeLists.txt @@ -0,0 +1,34 @@ +set(TARGET mdbx_test) +project(${TARGET}) + +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-missing-declarations") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-cast-qual") + +add_executable(${TARGET} + base.h + cases.cc + chrono.cc + chrono.h + config.cc + config.h + dead.cc + hill.cc + jitter.cc + keygen.cc + keygen.h + log.cc + log.h + main.cc + osal.h + osal-unix.cc + test.cc + test.h + try.cc + utils.cc + utils.h +) + +target_link_libraries(${TARGET} + mdbx + ) + diff --git a/plugins/Dbx_mdbx/src/libmdbx/test/base.h b/plugins/Dbx_mdbx/src/libmdbx/test/base.h index ffcf61adf3..63554e730d 100644 --- a/plugins/Dbx_mdbx/src/libmdbx/test/base.h +++ b/plugins/Dbx_mdbx/src/libmdbx/test/base.h @@ -1,5 +1,5 @@ -/* - * Copyright 2017 Leonid Yuriev +/* + * Copyright 2017-2018 Leonid Yuriev * and other libmdbx authors: please see AUTHORS file. * All rights reserved. * diff --git a/plugins/Dbx_mdbx/src/libmdbx/test/cases.cc b/plugins/Dbx_mdbx/src/libmdbx/test/cases.cc index a1953f53c1..4f4306d58e 100644 --- a/plugins/Dbx_mdbx/src/libmdbx/test/cases.cc +++ b/plugins/Dbx_mdbx/src/libmdbx/test/cases.cc @@ -1,5 +1,5 @@ /* - * Copyright 2017 Leonid Yuriev + * Copyright 2017-2018 Leonid Yuriev * and other libmdbx authors: please see AUTHORS file. * All rights reserved. * diff --git a/plugins/Dbx_mdbx/src/libmdbx/test/chrono.cc b/plugins/Dbx_mdbx/src/libmdbx/test/chrono.cc index b6245295e7..f734668628 100644 --- a/plugins/Dbx_mdbx/src/libmdbx/test/chrono.cc +++ b/plugins/Dbx_mdbx/src/libmdbx/test/chrono.cc @@ -1,5 +1,5 @@ /* - * Copyright 2017 Leonid Yuriev + * Copyright 2017-2018 Leonid Yuriev * and other libmdbx authors: please see AUTHORS file. * All rights reserved. * diff --git a/plugins/Dbx_mdbx/src/libmdbx/test/chrono.h b/plugins/Dbx_mdbx/src/libmdbx/test/chrono.h index d5e9f658d1..c2bd5627a6 100644 --- a/plugins/Dbx_mdbx/src/libmdbx/test/chrono.h +++ b/plugins/Dbx_mdbx/src/libmdbx/test/chrono.h @@ -1,5 +1,5 @@ /* - * Copyright 2017 Leonid Yuriev + * Copyright 2017-2018 Leonid Yuriev * and other libmdbx authors: please see AUTHORS file. * All rights reserved. * diff --git a/plugins/Dbx_mdbx/src/libmdbx/test/config.cc b/plugins/Dbx_mdbx/src/libmdbx/test/config.cc index 81b26b8383..2b9c6e5a0c 100644 --- a/plugins/Dbx_mdbx/src/libmdbx/test/config.cc +++ b/plugins/Dbx_mdbx/src/libmdbx/test/config.cc @@ -1,5 +1,5 @@ /* - * Copyright 2017 Leonid Yuriev + * Copyright 2017-2018 Leonid Yuriev * and other libmdbx authors: please see AUTHORS file. * All rights reserved. * diff --git a/plugins/Dbx_mdbx/src/libmdbx/test/config.h b/plugins/Dbx_mdbx/src/libmdbx/test/config.h index 4c9aa2142b..5efd31100a 100644 --- a/plugins/Dbx_mdbx/src/libmdbx/test/config.h +++ b/plugins/Dbx_mdbx/src/libmdbx/test/config.h @@ -1,5 +1,5 @@ /* - * Copyright 2017 Leonid Yuriev + * Copyright 2017-2018 Leonid Yuriev * and other libmdbx authors: please see AUTHORS file. * All rights reserved. * diff --git a/plugins/Dbx_mdbx/src/libmdbx/test/dead.cc b/plugins/Dbx_mdbx/src/libmdbx/test/dead.cc index c7b338ff7a..ee13fbd0ae 100644 --- a/plugins/Dbx_mdbx/src/libmdbx/test/dead.cc +++ b/plugins/Dbx_mdbx/src/libmdbx/test/dead.cc @@ -1,5 +1,5 @@ -/* - * Copyright 2017 Leonid Yuriev +/* + * Copyright 2017-2018 Leonid Yuriev * and other libmdbx authors: please see AUTHORS file. * All rights reserved. * diff --git a/plugins/Dbx_mdbx/src/libmdbx/test/hill.cc b/plugins/Dbx_mdbx/src/libmdbx/test/hill.cc index 72a6e95b35..c9115784d4 100644 --- a/plugins/Dbx_mdbx/src/libmdbx/test/hill.cc +++ b/plugins/Dbx_mdbx/src/libmdbx/test/hill.cc @@ -1,5 +1,5 @@ -/* - * Copyright 2017 Leonid Yuriev +/* + * Copyright 2017-2018 Leonid Yuriev * and other libmdbx authors: please see AUTHORS file. * All rights reserved. * diff --git a/plugins/Dbx_mdbx/src/libmdbx/test/jitter.cc b/plugins/Dbx_mdbx/src/libmdbx/test/jitter.cc index 92d272e1e5..e7faf2a3f9 100644 --- a/plugins/Dbx_mdbx/src/libmdbx/test/jitter.cc +++ b/plugins/Dbx_mdbx/src/libmdbx/test/jitter.cc @@ -1,5 +1,5 @@ /* - * Copyright 2017 Leonid Yuriev + * Copyright 2017-2018 Leonid Yuriev * and other libmdbx authors: please see AUTHORS file. * All rights reserved. * diff --git a/plugins/Dbx_mdbx/src/libmdbx/test/keygen.cc b/plugins/Dbx_mdbx/src/libmdbx/test/keygen.cc index 806d4ba80d..99b46f2976 100644 --- a/plugins/Dbx_mdbx/src/libmdbx/test/keygen.cc +++ b/plugins/Dbx_mdbx/src/libmdbx/test/keygen.cc @@ -1,5 +1,5 @@ /* - * Copyright 2017 Leonid Yuriev + * Copyright 2017-2018 Leonid Yuriev * and other libmdbx authors: please see AUTHORS file. * All rights reserved. * diff --git a/plugins/Dbx_mdbx/src/libmdbx/test/keygen.h b/plugins/Dbx_mdbx/src/libmdbx/test/keygen.h index 911ea6d89b..c1e907bc0b 100644 --- a/plugins/Dbx_mdbx/src/libmdbx/test/keygen.h +++ b/plugins/Dbx_mdbx/src/libmdbx/test/keygen.h @@ -1,5 +1,5 @@ /* - * Copyright 2017 Leonid Yuriev + * Copyright 2017-2018 Leonid Yuriev * and other libmdbx authors: please see AUTHORS file. * All rights reserved. * diff --git a/plugins/Dbx_mdbx/src/libmdbx/test/log.cc b/plugins/Dbx_mdbx/src/libmdbx/test/log.cc index 5ab9951338..8697effd8f 100644 --- a/plugins/Dbx_mdbx/src/libmdbx/test/log.cc +++ b/plugins/Dbx_mdbx/src/libmdbx/test/log.cc @@ -1,5 +1,5 @@ /* - * Copyright 2017 Leonid Yuriev + * Copyright 2017-2018 Leonid Yuriev * and other libmdbx authors: please see AUTHORS file. * All rights reserved. * diff --git a/plugins/Dbx_mdbx/src/libmdbx/test/log.h b/plugins/Dbx_mdbx/src/libmdbx/test/log.h index 3ffc21d2e8..c321f3ce91 100644 --- a/plugins/Dbx_mdbx/src/libmdbx/test/log.h +++ b/plugins/Dbx_mdbx/src/libmdbx/test/log.h @@ -1,5 +1,5 @@ /* - * Copyright 2017 Leonid Yuriev + * Copyright 2017-2018 Leonid Yuriev * and other libmdbx authors: please see AUTHORS file. * All rights reserved. * diff --git a/plugins/Dbx_mdbx/src/libmdbx/test/main.cc b/plugins/Dbx_mdbx/src/libmdbx/test/main.cc index 2c4f9b09fd..adf60aae62 100644 --- a/plugins/Dbx_mdbx/src/libmdbx/test/main.cc +++ b/plugins/Dbx_mdbx/src/libmdbx/test/main.cc @@ -1,5 +1,5 @@ /* - * Copyright 2017 Leonid Yuriev + * Copyright 2017-2018 Leonid Yuriev * and other libmdbx authors: please see AUTHORS file. * All rights reserved. * diff --git a/plugins/Dbx_mdbx/src/libmdbx/test/osal-unix.cc b/plugins/Dbx_mdbx/src/libmdbx/test/osal-unix.cc index 88a10f1175..7625e0f419 100644 --- a/plugins/Dbx_mdbx/src/libmdbx/test/osal-unix.cc +++ b/plugins/Dbx_mdbx/src/libmdbx/test/osal-unix.cc @@ -1,5 +1,5 @@ /* - * Copyright 2017 Leonid Yuriev + * Copyright 2017-2018 Leonid Yuriev * and other libmdbx authors: please see AUTHORS file. * All rights reserved. * diff --git a/plugins/Dbx_mdbx/src/libmdbx/test/osal-windows.cc b/plugins/Dbx_mdbx/src/libmdbx/test/osal-windows.cc index 57f7f547f2..b1a8928cbb 100644 --- a/plugins/Dbx_mdbx/src/libmdbx/test/osal-windows.cc +++ b/plugins/Dbx_mdbx/src/libmdbx/test/osal-windows.cc @@ -1,5 +1,5 @@ /* - * Copyright 2017 Leonid Yuriev + * Copyright 2017-2018 Leonid Yuriev * and other libmdbx authors: please see AUTHORS file. * All rights reserved. * diff --git a/plugins/Dbx_mdbx/src/libmdbx/test/osal.h b/plugins/Dbx_mdbx/src/libmdbx/test/osal.h index 8f1626350c..f75aae3831 100644 --- a/plugins/Dbx_mdbx/src/libmdbx/test/osal.h +++ b/plugins/Dbx_mdbx/src/libmdbx/test/osal.h @@ -1,5 +1,5 @@ /* - * Copyright 2017 Leonid Yuriev + * Copyright 2017-2018 Leonid Yuriev * and other libmdbx authors: please see AUTHORS file. * All rights reserved. * diff --git a/plugins/Dbx_mdbx/src/libmdbx/test/pcrf/CMakeLists.txt b/plugins/Dbx_mdbx/src/libmdbx/test/pcrf/CMakeLists.txt new file mode 100644 index 0000000000..399c33f88f --- /dev/null +++ b/plugins/Dbx_mdbx/src/libmdbx/test/pcrf/CMakeLists.txt @@ -0,0 +1,7 @@ +set(TARGET pcrf_test) +project(${TARGET}) + +add_executable(${TARGET} pcrf_test.c) + +target_link_libraries(${TARGET} mdbx) + diff --git a/plugins/Dbx_mdbx/src/libmdbx/test/pcrf/README.md b/plugins/Dbx_mdbx/src/libmdbx/test/pcrf/README.md new file mode 100644 index 0000000000..b2c9b5ce95 --- /dev/null +++ b/plugins/Dbx_mdbx/src/libmdbx/test/pcrf/README.md @@ -0,0 +1,2 @@ +PCRF Session DB emulation test + diff --git a/plugins/Dbx_mdbx/src/libmdbx/test/pcrf/pcrf_test.c b/plugins/Dbx_mdbx/src/libmdbx/test/pcrf/pcrf_test.c new file mode 100644 index 0000000000..213c8b1d80 --- /dev/null +++ b/plugins/Dbx_mdbx/src/libmdbx/test/pcrf/pcrf_test.c @@ -0,0 +1,404 @@ +/* + * Copyright 2016-2017 Leonid Yuriev . + * Copyright 2015 Vladimir Romanov + * , Yota Lab. + * + * This file is part of libmdbx. + * + * ReOpenMDBX is free software; you can redistribute it and/or modify it under + * the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * ReOpenMDBX is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#include +#include + +#include "mdbx.h" +#include +#include +#include +#include +#include +#include +#include +#include + +#define IP_PRINTF_ARG_HOST(addr) \ + (int)((addr) >> 24), (int)((addr) >> 16 & 0xff), (int)((addr) >> 8 & 0xff), \ + (int)((addr)&0xff) + +char opt_db_path[PATH_MAX] = "/root/lmdbx_bench2"; +static MDBX_env *env; +#define REC_COUNT 10240000 +int64_t ids[REC_COUNT * 10]; +int32_t ids_count = 0; + +int64_t mdbx_add_count = 0; +int64_t mdbx_del_count = 0; +uint64_t mdbx_add_time = 0; +uint64_t mdbx_del_time = 0; +int64_t obj_id = 0; +int64_t mdbx_data_size = 0; +int64_t mdbx_key_size = 0; + +typedef struct { + char session_id1[100]; + char session_id2[100]; + char ip[20]; + uint8_t fill[100]; +} session_data_t; + +typedef struct { + int64_t obj_id; + int8_t event_type; +} __attribute__((__packed__)) event_data_t; + +static void add_id_to_pool(int64_t id) { + ids[ids_count] = id; + ids_count++; +} + +static inline int64_t getClockUs(void) { + struct timespec val; +#ifdef CYGWIN + clock_gettime(CLOCK_REALTIME, &val); +#else + clock_gettime(CLOCK_MONOTONIC, &val); +#endif + return val.tv_sec * ((int64_t)1000000) + val.tv_nsec / 1000; +} + +static int64_t get_id_from_pool() { + if (ids_count == 0) { + return -1; + } + int32_t index = rand() % ids_count; + int64_t id = ids[index]; + ids[index] = ids[ids_count - 1]; + ids_count--; + return id; +} + +#define MDBX_CHECK(x) \ + do { \ + const int rc = (x); \ + if (rc != MDBX_SUCCESS) { \ + printf("Error [%d] %s in %s at %s:%d\n", rc, mdbx_strerror(rc), #x, \ + __FILE__, __LINE__); \ + exit(EXIT_FAILURE); \ + } \ + } while (0) + +static void db_connect() { + MDBX_dbi dbi_session; + MDBX_dbi dbi_session_id; + MDBX_dbi dbi_event; + MDBX_dbi dbi_ip; + + MDBX_CHECK(mdbx_env_create(&env)); + MDBX_CHECK( + mdbx_env_set_mapsize(env, REC_COUNT * sizeof(session_data_t) * 10)); + MDBX_CHECK(mdbx_env_set_maxdbs(env, 30)); + MDBX_CHECK(mdbx_env_open(env, opt_db_path, + MDBX_CREATE | MDBX_WRITEMAP | MDBX_MAPASYNC | + MDBX_NOSYNC | MDBX_LIFORECLAIM, + 0664)); + MDBX_txn *txn; + + // transaction init + MDBX_CHECK(mdbx_txn_begin(env, NULL, 0, &txn)); + // open database in read-write mode + MDBX_CHECK(mdbx_dbi_open(txn, "session", MDBX_CREATE, &dbi_session)); + MDBX_CHECK(mdbx_dbi_open(txn, "session_id", MDBX_CREATE, &dbi_session_id)); + MDBX_CHECK(mdbx_dbi_open(txn, "event", MDBX_CREATE, &dbi_event)); + MDBX_CHECK(mdbx_dbi_open(txn, "ip", MDBX_CREATE, &dbi_ip)); + // transaction commit + MDBX_CHECK(mdbx_txn_commit(txn)); + printf("Connection open\n"); +} + +static void create_record(int64_t record_id) { + MDBX_dbi dbi_session; + MDBX_dbi dbi_session_id; + MDBX_dbi dbi_event; + MDBX_dbi dbi_ip; + event_data_t event; + MDBX_txn *txn; + session_data_t data; + // transaction init + snprintf(data.session_id1, sizeof(data.session_id1), + "prefix%02ld_%02ld.fill.fill.fill.fill.fill.fill;%ld", + record_id % 3 + 1, record_id % 9 + 1, record_id); + snprintf(data.session_id2, sizeof(data.session_id2), + "dprefix%ld;%ld.fill.fill.;suffix", record_id, + record_id % 1000000000 + 99999); + snprintf(data.ip, sizeof(data.ip), "%d.%d.%d.%d", + IP_PRINTF_ARG_HOST(record_id & 0xFFFFFFFF)); + event.obj_id = record_id; + event.event_type = 1; + + MDBX_val _session_id1_rec = {data.session_id1, strlen(data.session_id1)}; + MDBX_val _session_id2_rec = {data.session_id2, strlen(data.session_id2)}; + MDBX_val _ip_rec = {data.ip, strlen(data.ip)}; + MDBX_val _obj_id_rec = {&record_id, sizeof(record_id)}; + MDBX_val _data_rec = {&data, offsetof(session_data_t, fill) + + (rand() % sizeof(data.fill))}; + MDBX_val _event_rec = {&event, sizeof(event)}; + + uint64_t start = getClockUs(); + MDBX_CHECK(mdbx_txn_begin(env, NULL, 0, &txn)); + MDBX_CHECK(mdbx_dbi_open(txn, "session", MDBX_CREATE, &dbi_session)); + MDBX_CHECK(mdbx_dbi_open(txn, "session_id", MDBX_CREATE, &dbi_session_id)); + MDBX_CHECK(mdbx_dbi_open(txn, "event", MDBX_CREATE, &dbi_event)); + MDBX_CHECK(mdbx_dbi_open(txn, "ip", MDBX_CREATE, &dbi_ip)); + MDBX_CHECK(mdbx_put(txn, dbi_session, &_obj_id_rec, &_data_rec, + MDBX_NOOVERWRITE | MDBX_NODUPDATA)); + MDBX_CHECK(mdbx_put(txn, dbi_session_id, &_session_id1_rec, &_obj_id_rec, + MDBX_NOOVERWRITE | MDBX_NODUPDATA)); + MDBX_CHECK(mdbx_put(txn, dbi_session_id, &_session_id2_rec, &_obj_id_rec, + MDBX_NOOVERWRITE | MDBX_NODUPDATA)); + MDBX_CHECK(mdbx_put(txn, dbi_ip, &_ip_rec, &_obj_id_rec, 0)); + MDBX_CHECK(mdbx_put(txn, dbi_event, &_event_rec, &_obj_id_rec, 0)); + MDBX_CHECK(mdbx_txn_commit(txn)); + + mdbx_data_size += (_data_rec.iov_len + _obj_id_rec.iov_len * 4); + mdbx_key_size += + (_obj_id_rec.iov_len + _session_id1_rec.iov_len + + _session_id2_rec.iov_len + _ip_rec.iov_len + _event_rec.iov_len); + + // transaction commit + mdbx_add_count++; + mdbx_add_time += (getClockUs() - start); +} + +static void delete_record(int64_t record_id) { + MDBX_dbi dbi_session; + MDBX_dbi dbi_session_id; + MDBX_dbi dbi_event; + MDBX_dbi dbi_ip; + event_data_t event; + MDBX_txn *txn; + + // transaction init + uint64_t start = getClockUs(); + MDBX_CHECK(mdbx_txn_begin(env, NULL, 0, &txn)); + // open database in read-write mode + MDBX_CHECK(mdbx_dbi_open(txn, "session", MDBX_CREATE, &dbi_session)); + MDBX_CHECK(mdbx_dbi_open(txn, "session_id", MDBX_CREATE, &dbi_session_id)); + MDBX_CHECK(mdbx_dbi_open(txn, "event", MDBX_CREATE, &dbi_event)); + MDBX_CHECK(mdbx_dbi_open(txn, "ip", MDBX_CREATE, &dbi_ip)); + // put data + MDBX_val _obj_id_rec = {&record_id, sizeof(record_id)}; + MDBX_val _data_rec; + // get data + MDBX_CHECK(mdbx_get(txn, dbi_session, &_obj_id_rec, &_data_rec)); + session_data_t *data = (session_data_t *)_data_rec.iov_base; + + MDBX_val _session_id1_rec = {data->session_id1, strlen(data->session_id1)}; + MDBX_val _session_id2_rec = {data->session_id2, strlen(data->session_id2)}; + MDBX_val _ip_rec = {data->ip, strlen(data->ip)}; + MDBX_CHECK(mdbx_del(txn, dbi_session_id, &_session_id1_rec, NULL)); + MDBX_CHECK(mdbx_del(txn, dbi_session_id, &_session_id2_rec, NULL)); + MDBX_CHECK(mdbx_del(txn, dbi_ip, &_ip_rec, NULL)); + event.obj_id = record_id; + event.event_type = 1; + MDBX_val _event_rec = {&event, sizeof(event)}; + MDBX_CHECK(mdbx_del(txn, dbi_event, &_event_rec, NULL)); + MDBX_CHECK(mdbx_del(txn, dbi_session, &_obj_id_rec, NULL)); + + mdbx_data_size -= (_data_rec.iov_len + _obj_id_rec.iov_len * 4); + mdbx_key_size -= + (_obj_id_rec.iov_len + _session_id1_rec.iov_len + + _session_id2_rec.iov_len + _ip_rec.iov_len + _event_rec.iov_len); + + // transaction commit + MDBX_CHECK(mdbx_txn_commit(txn)); + mdbx_del_count++; + mdbx_del_time += (getClockUs() - start); +} + +static void db_disconnect() { + mdbx_env_close(env); + printf("Connection closed\n"); +} + +static void get_db_stat(const char *db, int64_t *ms_branch_pages, + int64_t *ms_leaf_pages) { + MDBX_txn *txn; + MDBX_stat stat; + MDBX_dbi dbi; + + MDBX_CHECK(mdbx_txn_begin(env, NULL, MDBX_RDONLY, &txn)); + MDBX_CHECK(mdbx_dbi_open(txn, db, MDBX_CREATE, &dbi)); + MDBX_CHECK(mdbx_dbi_stat(txn, dbi, &stat, sizeof(stat))); + mdbx_txn_abort(txn); + printf("%15s | %15ld | %5u | %10ld | %10ld | %11ld |\n", db, + stat.ms_branch_pages, stat.ms_depth, stat.ms_entries, + stat.ms_leaf_pages, stat.ms_overflow_pages); + (*ms_branch_pages) += stat.ms_branch_pages; + (*ms_leaf_pages) += stat.ms_leaf_pages; +} + +static void periodic_stat(void) { + int64_t ms_branch_pages = 0; + int64_t ms_leaf_pages = 0; + MDBX_stat mst; + MDBX_envinfo mei; + MDBX_CHECK(mdbx_env_stat(env, &mst, sizeof(mst))); + MDBX_CHECK(mdbx_env_info(env, &mei, sizeof(mei))); + printf("Environment Info\n"); + printf(" Pagesize: %u\n", mst.ms_psize); + if (mei.mi_geo.lower != mei.mi_geo.upper) { + printf(" Dynamic datafile: %" PRIu64 "..%" PRIu64 " bytes (+%" PRIu64 + "/-%" PRIu64 "), %" PRIu64 "..%" PRIu64 " pages (+%" PRIu64 + "/-%" PRIu64 ")\n", + mei.mi_geo.lower, mei.mi_geo.upper, mei.mi_geo.grow, + mei.mi_geo.shrink, mei.mi_geo.lower / mst.ms_psize, + mei.mi_geo.upper / mst.ms_psize, mei.mi_geo.grow / mst.ms_psize, + mei.mi_geo.shrink / mst.ms_psize); + printf(" Current datafile: %" PRIu64 " bytes, %" PRIu64 " pages\n", + mei.mi_geo.current, mei.mi_geo.current / mst.ms_psize); + } else { + printf(" Fixed datafile: %" PRIu64 " bytes, %" PRIu64 " pages\n", + mei.mi_geo.current, mei.mi_geo.current / mst.ms_psize); + } + printf(" Current mapsize: %" PRIu64 " bytes, %" PRIu64 " pages \n", + mei.mi_mapsize, mei.mi_mapsize / mst.ms_psize); + printf(" Number of pages used: %" PRIu64 "\n", mei.mi_last_pgno + 1); + printf(" Last transaction ID: %" PRIu64 "\n", mei.mi_recent_txnid); + printf(" Tail transaction ID: %" PRIu64 " (%" PRIi64 ")\n", + mei.mi_latter_reader_txnid, + mei.mi_latter_reader_txnid - mei.mi_recent_txnid); + printf(" Max readers: %u\n", mei.mi_maxreaders); + printf(" Number of readers used: %u\n", mei.mi_numreaders); + + printf(" Name | ms_branch_pages | depth | entries | leaf_pages " + "| overf_pages |\n"); + get_db_stat("session", &ms_branch_pages, &ms_leaf_pages); + get_db_stat("session_id", &ms_branch_pages, &ms_leaf_pages); + get_db_stat("event", &ms_branch_pages, &ms_leaf_pages); + get_db_stat("ip", &ms_branch_pages, &ms_leaf_pages); + printf("%15s | %15ld | %5s | %10s | %10ld | %11s |\n", "", ms_branch_pages, + "", "", ms_leaf_pages, ""); + + static int64_t prev_add_count; + static int64_t prev_del_count; + static uint64_t prev_add_time; + static uint64_t prev_del_time; + static int64_t t = -1; + if (t > 0) { + int64_t delta = (getClockUs() - t); + printf( + "CPS: add %ld, delete %ld, items processed - %ldK data=%ldK key=%ldK\n", + (mdbx_add_count - prev_add_count) * 1000000 / delta, + (mdbx_del_count - prev_del_count) * 1000000 / delta, obj_id / 1024, + mdbx_data_size / 1024, mdbx_key_size / 1024); + printf("usage data=%ld%%", ((mdbx_data_size + mdbx_key_size) * 100) / + ((ms_leaf_pages + ms_branch_pages) * 4096)); + if (prev_add_time != mdbx_add_time) { + printf(" Add : %ld c/s", (mdbx_add_count - prev_add_count) * 1000000 / + (mdbx_add_time - prev_add_time)); + } + if (prev_del_time != mdbx_del_time) { + printf(" Del : %ld c/s", (mdbx_del_count - prev_del_count) * 1000000 / + (mdbx_del_time - prev_del_time)); + } + if (mdbx_add_time) { + printf(" tAdd : %ld c/s", mdbx_add_count * 1000000 / mdbx_add_time); + } + if (mdbx_del_time) { + printf(" tDel : %ld c/s", mdbx_del_count * 1000000 / mdbx_del_time); + } + puts(""); + } + t = getClockUs(); + prev_add_count = mdbx_add_count; + prev_del_count = mdbx_del_count; + prev_add_time = mdbx_add_time; + prev_del_time = mdbx_del_time; +} + +// static void periodic_add_rec() { +// for (int i = 0; i < 10240; i++) { +// if (ids_count <= REC_COUNT) { +// int64_t id = obj_id++; +// create_record(id); +// add_id_to_pool(id); +// } +// if (ids_count > REC_COUNT) { +// int64_t id = get_id_from_pool(); +// delete_record(id); +// } +// } +// periodic_stat(); +//} + +int main(int argc, char **argv) { + (void)argc; + (void)argv; + + char filename[PATH_MAX]; + int i; + + mkdir(opt_db_path, 0775); + + strcpy(filename, opt_db_path); + strcat(filename, "/mdbx.dat"); + remove(filename); + + strcpy(filename, opt_db_path); + strcat(filename, "/mdbx.lck"); + remove(filename); + + puts("Open DB..."); + db_connect(); + puts("Create data..."); + int64_t t = getClockUs(); + for (i = 0; i < REC_COUNT; i++) { + int64_t id = obj_id++; + create_record(id); + add_id_to_pool(id); + if (i % 1000 == 0) { + int64_t now = getClockUs(); + if ((now - t) > 1000000L) { + periodic_stat(); + t = now; + } + } + } + periodic_stat(); + while (1) { + int i; + for (i = 0; i < 1000; i++) { + int64_t id = obj_id++; + create_record(id); + add_id_to_pool(id); + id = get_id_from_pool(); + delete_record(id); + } + // for (i = 0; i < 50; i++) { + // int64_t id = obj_id++; + // create_record(id); + // add_id_to_pool(id); + // } + // int64_t id = obj_id++; + // create_record(id); + // add_id_to_pool(id); + int64_t now = getClockUs(); + if ((now - t) > 10000000L) { + periodic_stat(); + t = now; + } + } + db_disconnect(); + return 0; +} diff --git a/plugins/Dbx_mdbx/src/libmdbx/test/test.cc b/plugins/Dbx_mdbx/src/libmdbx/test/test.cc index f1dc226465..8700d74add 100644 --- a/plugins/Dbx_mdbx/src/libmdbx/test/test.cc +++ b/plugins/Dbx_mdbx/src/libmdbx/test/test.cc @@ -1,5 +1,5 @@ -/* - * Copyright 2017 Leonid Yuriev +/* + * Copyright 2017-2018 Leonid Yuriev * and other libmdbx authors: please see AUTHORS file. * All rights reserved. * diff --git a/plugins/Dbx_mdbx/src/libmdbx/test/test.h b/plugins/Dbx_mdbx/src/libmdbx/test/test.h index 67fadebf07..4158aba457 100644 --- a/plugins/Dbx_mdbx/src/libmdbx/test/test.h +++ b/plugins/Dbx_mdbx/src/libmdbx/test/test.h @@ -1,5 +1,5 @@ -/* - * Copyright 2017 Leonid Yuriev +/* + * Copyright 2017-2018 Leonid Yuriev * and other libmdbx authors: please see AUTHORS file. * All rights reserved. * diff --git a/plugins/Dbx_mdbx/src/libmdbx/test/utils.cc b/plugins/Dbx_mdbx/src/libmdbx/test/utils.cc index 9a6338cc31..96866f61b7 100644 --- a/plugins/Dbx_mdbx/src/libmdbx/test/utils.cc +++ b/plugins/Dbx_mdbx/src/libmdbx/test/utils.cc @@ -1,5 +1,5 @@ /* - * Copyright 2017 Leonid Yuriev + * Copyright 2017-2018 Leonid Yuriev * and other libmdbx authors: please see AUTHORS file. * All rights reserved. * @@ -18,6 +18,40 @@ #include #endif +/* Compiler's includes for builtins/intrinsics */ +#if defined(_MSC_VER) || defined(__INTEL_COMPILER) +#include +#elif __GNUC_PREREQ(4, 4) || defined(__clang__) +#if defined(__ia32__) || defined(__e2k__) +#include +#endif /* __ia32__ */ +#if defined(__ia32__) +#include +#endif /* __ia32__ */ +#elif defined(__SUNPRO_C) || defined(__sun) || defined(sun) +#include +#elif (defined(_HPUX_SOURCE) || defined(__hpux) || defined(__HP_aCC)) && \ + (defined(HP_IA64) || defined(__ia64)) +#include +#elif defined(__IBMC__) && defined(__powerpc) +#include +#elif defined(_AIX) +#include +#include +#elif (defined(__osf__) && defined(__DECC)) || defined(__alpha) +#include +#include +#elif defined(__MWERKS__) +/* CodeWarrior - troubles ? */ +#pragma gcc_extensions +#elif defined(__SNC__) +/* Sony PS3 - troubles ? */ +#elif defined(__hppa__) || defined(__hppa) +#include +#else +#error Unsupported C compiler, please use GNU C 4.4 or newer +#endif /* Compiler */ + std::string format(const char *fmt, ...) { va_list ap, ones; va_start(ap, fmt); @@ -93,19 +127,6 @@ bool hex2data(const char *hex_begin, const char *hex_end, void *ptr, //----------------------------------------------------------------------------- -#ifdef __mips__ -static uint64_t *mips_tsc_addr; - -__cold static void mips_rdtsc_init() { - int mem_fd = open("/dev/mem", O_RDONLY | O_SYNC, 0); - HIPPEUS_ENSURE(mem_fd >= 0); - - mips_tsc_addr = mmap(nullptr, pagesize, PROT_READ, MAP_SHARED, mem_fd, - 0x10030000 /* MIPS_ZBUS_TIMER */); - close(mem_fd); -} -#endif /* __mips__ */ - uint64_t entropy_ticks(void) { #if defined(__GNUC__) || defined(__clang__) #if defined(__ia64__) @@ -141,31 +162,11 @@ uint64_t entropy_ticks(void) { __asm __volatile("mftbu %0" : "=r"(tbu)); return (((uin64_t)tbu0) << 32) | tbl; -#elif defined(__mips__) - if (mips_tsc_addr != MAP_FAILED) { - if (unlikely(!mips_tsc_addr)) { - static pthread_once_t is_initialized = PTHREAD_ONCE_INIT; - int rc = pthread_once(&is_initialized, mips_rdtsc_init); - if (unlikely(rc)) - failure_perror("pthread_once()", rc); - } - if (mips_tsc_addr != MAP_FAILED) - return *mips_tsc_addr; - } -#elif defined(__x86_64__) || defined(__i386__) -#if __GNUC_PREREQ(4, 7) || __has_builtin(__builtin_ia32_rdtsc) - return __builtin_ia32_rdtsc(); -#else - unsigned lo, hi; - - /* LY: Using the "a" and "d" constraints is important for correct code. */ - __asm __volatile("rdtsc" : "=a"(lo), "=d"(hi)); - - return (((uint64_t)hi) << 32) + lo; -#endif #endif /* arch selector */ +#endif /* __GNUC__ || __clang__ */ -#elif defined(_M_IX86) || defined(_M_X64) +#if defined(__e2k__) || defined(__elbrus__) || defined(_M_IX86) || \ + defined(_M_X64) || defined(__x86_64__) || defined(__i386__) return __rdtsc(); #elif defined(_WIN32) || defined(_WIN64) || defined(_WINDOWS) LARGE_INTEGER PerformanceCount; diff --git a/plugins/Dbx_mdbx/src/libmdbx/test/utils.h b/plugins/Dbx_mdbx/src/libmdbx/test/utils.h index b7fd62eb09..176823c60b 100644 --- a/plugins/Dbx_mdbx/src/libmdbx/test/utils.h +++ b/plugins/Dbx_mdbx/src/libmdbx/test/utils.h @@ -1,5 +1,5 @@ /* - * Copyright 2017 Leonid Yuriev + * Copyright 2017-2018 Leonid Yuriev * and other libmdbx authors: please see AUTHORS file. * All rights reserved. * diff --git a/plugins/Dbx_mdbx/src/libmdbx/tutorial/CMakeLists.txt b/plugins/Dbx_mdbx/src/libmdbx/tutorial/CMakeLists.txt new file mode 100644 index 0000000000..196f1f48d7 --- /dev/null +++ b/plugins/Dbx_mdbx/src/libmdbx/tutorial/CMakeLists.txt @@ -0,0 +1,7 @@ +set(TARGET mdbx_tutorial) +project(${TARGET}) + +add_executable(${TARGET} sample-mdbx.c) + +target_link_libraries(${TARGET} mdbx) + diff --git a/plugins/Dbx_mdbx/src/libmdbx/tutorial/sample-bdb.txt b/plugins/Dbx_mdbx/src/libmdbx/tutorial/sample-bdb.txt index 6e3a739d62..1015d06460 100644 --- a/plugins/Dbx_mdbx/src/libmdbx/tutorial/sample-bdb.txt +++ b/plugins/Dbx_mdbx/src/libmdbx/tutorial/sample-bdb.txt @@ -4,7 +4,7 @@ */ /* - * Copyright 2015-2017 Leonid Yuriev . + * Copyright 2015-2018 Leonid Yuriev . * Copyright 2012-2015 Howard Chu, Symas Corp. * Copyright 2015,2016 Peter-Service R&D LLC. * All rights reserved. diff --git a/plugins/Dbx_mdbx/src/libmdbx/tutorial/sample-mdbx.c b/plugins/Dbx_mdbx/src/libmdbx/tutorial/sample-mdbx.c index f5188cf423..aaafbc31cf 100644 --- a/plugins/Dbx_mdbx/src/libmdbx/tutorial/sample-mdbx.c +++ b/plugins/Dbx_mdbx/src/libmdbx/tutorial/sample-mdbx.c @@ -1,11 +1,11 @@ -/* sample-mdb.txt - MDB toy/sample +/* sample-mdb.txt - MDB toy/sample * * Do a line-by-line comparison of this and sample-bdb.txt */ /* * Copyright 2017 Ilya Shipitsin . - * Copyright 2015-2017 Leonid Yuriev . + * Copyright 2015-2018 Leonid Yuriev . * Copyright 2012-2015 Howard Chu, Symas Corp. * All rights reserved. * -- cgit v1.2.3