summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGeorge Hazan <ghazan@miranda.im>2018-03-07 18:03:25 +0300
committerGeorge Hazan <ghazan@miranda.im>2018-03-07 18:03:31 +0300
commitfe0a3147a8050a987281beb4b5e1481b982c93bc (patch)
tree799fed1bf1567b709086a1cf2d2c1fa3caaa9a4e
parentdfbb53e55b6a537366182a134caccaf5f71569c8 (diff)
merge with libmdbx 0.1
-rw-r--r--plugins/Dbx_mdbx/src/libmdbx/.gitignore32
-rw-r--r--plugins/Dbx_mdbx/src/libmdbx/AUTHORS1
-rw-r--r--plugins/Dbx_mdbx/src/libmdbx/CMakeLists.txt189
-rw-r--r--plugins/Dbx_mdbx/src/libmdbx/COPYRIGHT2
-rw-r--r--plugins/Dbx_mdbx/src/libmdbx/Makefile10
-rw-r--r--plugins/Dbx_mdbx/src/libmdbx/README-RU.md689
-rw-r--r--plugins/Dbx_mdbx/src/libmdbx/README.md813
-rw-r--r--plugins/Dbx_mdbx/src/libmdbx/TODO.md2
-rw-r--r--plugins/Dbx_mdbx/src/libmdbx/build.sh18
-rw-r--r--plugins/Dbx_mdbx/src/libmdbx/libmdbx.files6
-rw-r--r--plugins/Dbx_mdbx/src/libmdbx/libmdbx.includes2
-rw-r--r--plugins/Dbx_mdbx/src/libmdbx/mdbx.h4
-rw-r--r--plugins/Dbx_mdbx/src/libmdbx/package.sh25
-rw-r--r--plugins/Dbx_mdbx/src/libmdbx/src/bits.h32
-rw-r--r--plugins/Dbx_mdbx/src/libmdbx/src/defs.h18
-rw-r--r--plugins/Dbx_mdbx/src/libmdbx/src/lck-posix.c2
-rw-r--r--plugins/Dbx_mdbx/src/libmdbx/src/tools/CMakeLists.txt19
-rw-r--r--plugins/Dbx_mdbx/src/libmdbx/src/tools/mdbx_chk.c4
-rw-r--r--plugins/Dbx_mdbx/src/libmdbx/src/tools/mdbx_copy.12
-rw-r--r--plugins/Dbx_mdbx/src/libmdbx/src/tools/mdbx_copy.c2
-rw-r--r--plugins/Dbx_mdbx/src/libmdbx/src/tools/mdbx_dump.12
-rw-r--r--plugins/Dbx_mdbx/src/libmdbx/src/tools/mdbx_dump.c2
-rw-r--r--plugins/Dbx_mdbx/src/libmdbx/src/tools/mdbx_load.12
-rw-r--r--plugins/Dbx_mdbx/src/libmdbx/src/tools/mdbx_load.c2
-rw-r--r--plugins/Dbx_mdbx/src/libmdbx/src/tools/mdbx_stat.12
-rw-r--r--plugins/Dbx_mdbx/src/libmdbx/src/tools/mdbx_stat.c4
-rw-r--r--plugins/Dbx_mdbx/src/libmdbx/src/version.c2
-rw-r--r--plugins/Dbx_mdbx/src/libmdbx/test/CMakeLists.txt34
-rw-r--r--plugins/Dbx_mdbx/src/libmdbx/test/base.h4
-rw-r--r--plugins/Dbx_mdbx/src/libmdbx/test/cases.cc2
-rw-r--r--plugins/Dbx_mdbx/src/libmdbx/test/chrono.cc2
-rw-r--r--plugins/Dbx_mdbx/src/libmdbx/test/chrono.h2
-rw-r--r--plugins/Dbx_mdbx/src/libmdbx/test/config.cc2
-rw-r--r--plugins/Dbx_mdbx/src/libmdbx/test/config.h2
-rw-r--r--plugins/Dbx_mdbx/src/libmdbx/test/dead.cc4
-rw-r--r--plugins/Dbx_mdbx/src/libmdbx/test/hill.cc4
-rw-r--r--plugins/Dbx_mdbx/src/libmdbx/test/jitter.cc2
-rw-r--r--plugins/Dbx_mdbx/src/libmdbx/test/keygen.cc2
-rw-r--r--plugins/Dbx_mdbx/src/libmdbx/test/keygen.h2
-rw-r--r--plugins/Dbx_mdbx/src/libmdbx/test/log.cc2
-rw-r--r--plugins/Dbx_mdbx/src/libmdbx/test/log.h2
-rw-r--r--plugins/Dbx_mdbx/src/libmdbx/test/main.cc2
-rw-r--r--plugins/Dbx_mdbx/src/libmdbx/test/osal-unix.cc2
-rw-r--r--plugins/Dbx_mdbx/src/libmdbx/test/osal-windows.cc2
-rw-r--r--plugins/Dbx_mdbx/src/libmdbx/test/osal.h2
-rw-r--r--plugins/Dbx_mdbx/src/libmdbx/test/pcrf/CMakeLists.txt7
-rw-r--r--plugins/Dbx_mdbx/src/libmdbx/test/pcrf/README.md2
-rw-r--r--plugins/Dbx_mdbx/src/libmdbx/test/pcrf/pcrf_test.c404
-rw-r--r--plugins/Dbx_mdbx/src/libmdbx/test/test.cc4
-rw-r--r--plugins/Dbx_mdbx/src/libmdbx/test/test.h4
-rw-r--r--plugins/Dbx_mdbx/src/libmdbx/test/utils.cc75
-rw-r--r--plugins/Dbx_mdbx/src/libmdbx/test/utils.h2
-rw-r--r--plugins/Dbx_mdbx/src/libmdbx/tutorial/CMakeLists.txt7
-rw-r--r--plugins/Dbx_mdbx/src/libmdbx/tutorial/sample-bdb.txt2
-rw-r--r--plugins/Dbx_mdbx/src/libmdbx/tutorial/sample-mdbx.c4
55 files changed, 1855 insertions, 618 deletions
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 <philipp.storz@bareos.com>
Quanah Gibson-Mount <quanah@openldap.org>
Salvador Ortiz <sog@msg.com.mx>
Sebastien Launay <sebastien@slaunay.fr>
+Vladimir Romanov <vromanov@gmail.com>
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 <leo@yuriev.ru>.
+Copyright 2015-2018 Leonid Yuriev <leo@yuriev.ru>.
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 <martin@bzero.se> - 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 <martin@bzero.se> - 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 <leo@yuriev.ru>
* 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 <leo@yuriev.ru>
* 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 <leo@yuriev.ru>
* 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 <leo@yuriev.ru>
+ * Copyright 2015-2018 Leonid Yuriev <leo@yuriev.ru>
* 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 <leo@yuriev.ru>.
+.\" Copyright 2015-2018 Leonid Yuriev <leo@yuriev.ru>.
.\" Copyright 2012-2015 Howard Chu, Symas Corp. All Rights Reserved.
.\" Copyright 2015,2016 Peter-Service R&D LLC <http://billing.ru/>.
.\" 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 <leo@yuriev.ru>
+ * Copyright 2015-2018 Leonid Yuriev <leo@yuriev.ru>
* 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 <leo@yuriev.ru>.
+.\" Copyright 2015-2018 Leonid Yuriev <leo@yuriev.ru>.
.\" Copyright 2014-2015 Howard Chu, Symas Corp. All Rights Reserved.
.\" Copyright 2015,2016 Peter-Service R&D LLC <http://billing.ru/>.
.\" 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 <leo@yuriev.ru>
+ * Copyright 2015-2018 Leonid Yuriev <leo@yuriev.ru>
* 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 <leo@yuriev.ru>.
+.\" Copyright 2015-2018 Leonid Yuriev <leo@yuriev.ru>.
.\" Copyright 2014-2015 Howard Chu, Symas Corp. All Rights Reserved.
.\" Copyright 2015,2016 Peter-Service R&D LLC <http://billing.ru/>.
.\" 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 <leo@yuriev.ru>
+ * Copyright 2015-2018 Leonid Yuriev <leo@yuriev.ru>
* 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 <leo@yuriev.ru>.
+.\" Copyright 2015-2018 Leonid Yuriev <leo@yuriev.ru>.
.\" Copyright 2012-2015 Howard Chu, Symas Corp. All Rights Reserved.
.\" Copyright 2015,2016 Peter-Service R&D LLC <http://billing.ru/>.
.\" 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 <leo@yuriev.ru>
+ * Copyright 2015-2018 Leonid Yuriev <leo@yuriev.ru>
* 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 <leo@yuriev.ru>
+/*
+ * Copyright 2017-2018 Leonid Yuriev <leo@yuriev.ru>
* 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 <leo@yuriev.ru>
+ * Copyright 2017-2018 Leonid Yuriev <leo@yuriev.ru>
* 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 <leo@yuriev.ru>
+ * Copyright 2017-2018 Leonid Yuriev <leo@yuriev.ru>
* 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 <leo@yuriev.ru>
+ * Copyright 2017-2018 Leonid Yuriev <leo@yuriev.ru>
* 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 <leo@yuriev.ru>
+ * Copyright 2017-2018 Leonid Yuriev <leo@yuriev.ru>
* 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 <leo@yuriev.ru>
+ * Copyright 2017-2018 Leonid Yuriev <leo@yuriev.ru>
* 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 <leo@yuriev.ru>
+/*
+ * Copyright 2017-2018 Leonid Yuriev <leo@yuriev.ru>
* 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 <leo@yuriev.ru>
+/*
+ * Copyright 2017-2018 Leonid Yuriev <leo@yuriev.ru>
* 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 <leo@yuriev.ru>
+ * Copyright 2017-2018 Leonid Yuriev <leo@yuriev.ru>
* 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 <leo@yuriev.ru>
+ * Copyright 2017-2018 Leonid Yuriev <leo@yuriev.ru>
* 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 <leo@yuriev.ru>
+ * Copyright 2017-2018 Leonid Yuriev <leo@yuriev.ru>
* 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 <leo@yuriev.ru>
+ * Copyright 2017-2018 Leonid Yuriev <leo@yuriev.ru>
* 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 <leo@yuriev.ru>
+ * Copyright 2017-2018 Leonid Yuriev <leo@yuriev.ru>
* 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 <leo@yuriev.ru>
+ * Copyright 2017-2018 Leonid Yuriev <leo@yuriev.ru>
* 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 <leo@yuriev.ru>
+ * Copyright 2017-2018 Leonid Yuriev <leo@yuriev.ru>
* 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 <leo@yuriev.ru>
+ * Copyright 2017-2018 Leonid Yuriev <leo@yuriev.ru>
* 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 <leo@yuriev.ru>
+ * Copyright 2017-2018 Leonid Yuriev <leo@yuriev.ru>
* 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 <leo@yuriev.ru>.
+ * Copyright 2015 Vladimir Romanov
+ * <https://www.linkedin.com/in/vladimirromanov>, 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <sys/stat.h>
+#include <sys/time.h>
+
+#include "mdbx.h"
+#include <assert.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#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 <leo@yuriev.ru>
+/*
+ * Copyright 2017-2018 Leonid Yuriev <leo@yuriev.ru>
* 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 <leo@yuriev.ru>
+/*
+ * Copyright 2017-2018 Leonid Yuriev <leo@yuriev.ru>
* 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 <leo@yuriev.ru>
+ * Copyright 2017-2018 Leonid Yuriev <leo@yuriev.ru>
* and other libmdbx authors: please see AUTHORS file.
* All rights reserved.
*
@@ -18,6 +18,40 @@
#include <ieee754.h>
#endif
+/* Compiler's includes for builtins/intrinsics */
+#if defined(_MSC_VER) || defined(__INTEL_COMPILER)
+#include <intrin.h>
+#elif __GNUC_PREREQ(4, 4) || defined(__clang__)
+#if defined(__ia32__) || defined(__e2k__)
+#include <x86intrin.h>
+#endif /* __ia32__ */
+#if defined(__ia32__)
+#include <cpuid.h>
+#endif /* __ia32__ */
+#elif defined(__SUNPRO_C) || defined(__sun) || defined(sun)
+#include <mbarrier.h>
+#elif (defined(_HPUX_SOURCE) || defined(__hpux) || defined(__HP_aCC)) && \
+ (defined(HP_IA64) || defined(__ia64))
+#include <machine/sys/inline.h>
+#elif defined(__IBMC__) && defined(__powerpc)
+#include <atomic.h>
+#elif defined(_AIX)
+#include <builtins.h>
+#include <sys/atomic_op.h>
+#elif (defined(__osf__) && defined(__DECC)) || defined(__alpha)
+#include <c_asm.h>
+#include <machine/builtins.h>
+#elif defined(__MWERKS__)
+/* CodeWarrior - troubles ? */
+#pragma gcc_extensions
+#elif defined(__SNC__)
+/* Sony PS3 - troubles ? */
+#elif defined(__hppa__) || defined(__hppa)
+#include <machine/inline.h>
+#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 <leo@yuriev.ru>
+ * Copyright 2017-2018 Leonid Yuriev <leo@yuriev.ru>
* 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 <leo@yuriev.ru>.
+ * Copyright 2015-2018 Leonid Yuriev <leo@yuriev.ru>.
* 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 <chipitsine@gmail.com>.
- * Copyright 2015-2017 Leonid Yuriev <leo@yuriev.ru>.
+ * Copyright 2015-2018 Leonid Yuriev <leo@yuriev.ru>.
* Copyright 2012-2015 Howard Chu, Symas Corp.
* All rights reserved.
*