Ошибка не была замечена ранее из-за много-ходового сценария воспроизведения:
1. Создаём экземпляр MDBX_env посредством mdbx_env_create();
2. Пытаемся открыть БД посредством mdbx_env_open() в режиме
чтения-записи и эта попытка должны быть неудачной;
3. Не освобождая экземпляр MDBX_env повторно открываем его в режиме
только-чтение;
4. Закрываем среду посредством mdbx_env_close().
Падение происходит на пункте 4, либо на пункте 3, если попытка
повторного открытия будет не успешной.
Причина в том, что внутренний экземпляр osal_ioring_t инициализировался
только для режимов чтения-записи, а разрушался всегда. При этом после
первого разрушения намеренно оставался в состоянии вызывающем падение
при использовании без инициализации.
[Simon Leier](https://t.me/leisim) сообщал об этой проблеме (теперь
понятно что это было), но из-за сложности сценария проблему не удалось
воспроизвести и идентифицировать.
Ранее упущенный не очевидный момент: При работе БД в режимах
не-синхронной/отложенной фиксации на диске, все процессы-писатели должны
иметь одинаковый режим MDBX_WRITEMAP.
В противном случае, сброс на диск следует выполнять дважды: сначала
msync(), затем fdatasync(). При этом msync() не обязан отрабатывать в
процессах без MDBX_WRITEMAP, так как файл в память отображен только для
чтения. Поэтому, в общем случае, различия по MDBX_WRITEMAP не позволяют
выполнить фиксацию данных на диск, после их изменения в другом процессе.
В режиме MDBX_UTTERLY_NOSYNC позволять совместную работу с MDBX_WRITEMAP
также не следует, поскольку никакой процесс (в том числе последний) не
может гарантированно сбросить данные на диск, а следовательно не должен
помечать какую-либо транзакцию как steady.
В результате, требуется либо запретить совместную работу процессам с
разным MDBX_WRITEMAP в режиме отложенной записи, либо отслеживать такое
смешивание и блокировать steady-пометки - что контрпродуктивно.
Исправление регрессии после коммита db72763de0.
После отключения затратой поддержки списка "грязных" страниц логика
page_retire_ex() оказалась не полной и требовала доработки. Из-за этого
страницы добавленные или клонированные-и-измененные в текущей
транзакции, которые становились не нужными, не возвращались к доступным
для немедленного использования, а помещались в retired-список
становящихся доступными в последующих транзакциях.
В результате, в некоторых сценариях, особенно с интенсивным расщеплением
страниц из-за вставки ключей, происходило необоснованно сильное
потребление/выделение страниц БД. В свою очередь, это приводило к
использованию излишнего кол-ва страниц, увеличению GC, росту RSS и
размеру БД.
Выпуск с существенными доработками и новой функциональностью
в память о российском борце [Иване Сергеевиче Ярыгине](https://ru.wikipedia.org/wiki/Ярыгин,_Иван_Сергеевич).
На Олимпийских играх в Мюнхене в 1972 году Иван Ярыгин уложил всех соперников на лопатки,
суммарно затратив менее 9 минут. Этот рекорд никем не побит до сих пор.
Новое:
------
- Поддержка всех основных опций при сборке посредством CMake.
- Требования к CMake понижены до версии 3.0.2 для возможности сборки для устаревших платформ.
- Добавлена возможность профилирования работы GC в сложных и/или нагруженных
сценариях (например Ethereum/Erigon). По-умолчанию соответствующий код отключен,
а для его активации необходимо указать опцию сборки `MDBX_ENABLE_PROFGC=1`.
- Добавлена функция `mdbx_env_warmup()` для "прогрева" БД с возможностью
закрепления страниц в памяти.
В утилиты `mdbx_chk`, `mdbx_copy` и `mdbx_dump` добавлены опции `-u` и `-U`
для активации соответствующего функционала.
- Отключение учета «грязных» страниц в не требующих этого режимах
(`MDBX_WRITEMAP` при `MDBX_AVOID_MSYNC=0`). Доработка позволяет снизить
накладные расходы и была запланирована давно, но откладывалась так как
требовала других изменений.
- Вытеснение из памяти (спиллинг) «грязных» страниц с учетом размера
large/overflow-страниц. Доработка позволяет корректно соблюдать политику
задаваемую опциями `MDBX_opt_txn_dp_limit`,
`MDBX_opt_spill_max_denominator`, `MDBX_opt_spill_min_denominator` и
была запланирована давно, но откладывалась так как требовала других
изменений.
- Для Windows в API добавлены UNICODE-зависимые определения макросов
`MDBX_DATANAME`, `MDBX_LOCKNAME` и `MDBX_LOCK_SUFFIX`.
- Переход на преимущественное использование типа `size_t` для
уменьшения накладных расходов на платформе Эльбрус.
- В API добавлены функции `mdbx_limits_valsize4page_max()` и
`mdbx_env_get_valsize4page_max()` возвращающие максимальный размер в
байтах значения, которое может быть размещена в одной
large/overflow-странице, а не последовательности из двух или более таких
страниц. Для таблиц с поддержкой дубликатов вынос значений на
large/overflow-страницы не поддерживается, поэтому результат совпадает с
`mdbx_limits_valsize_max()`.
- В API добавлены функции `mdbx_limits_pairsize4page_max()`и
`mdbx_env_get_pairsize4page_max()` возвращающие в байтах максимальный
суммарный размер пары ключ-значение для их размещения на одной листовой
страницы, без выноса значения на отдельную large/overflow-страницу. Для
таблиц с поддержкой дубликатов вынос значений на large/overflow-страницы
не поддерживается, поэтому результат определяет максимальный/допустимый
суммарный размер пары ключ-значение.
- Реализовано использование асинхронной (overlapped) записи в Windows,
включая использования небуфферизированного ввода-вывода и `WriteGather()`.
Это позволяет сократить накладные расходы и частично обойти проблемы
Windows с низкой производительностью ввода-вывода, включая большие
задержки `FlushFileBuffers()`. Новый код также обеспечивает консолидацию
записываемых регионов на всех платформах, а на Windows использование
событий (events) сведено к минимум, одновременно с автоматических
использованием `WriteGather()`. Поэтому ожидается существенное снижение
накладных расходов взаимодействия с ОС, а в Windows это ускорение, в
некоторых сценариях, может быть кратным в сравнении с LMDB.
- Добавлена опция сборки `MDBX_AVOID_MSYNC`, которая определяет
поведение libmdbx в режиме `MDBX_WRITE_MAP` (когда данные изменяются
непосредственно в отображенных в ОЗУ страницах БД):
* Если `MDBX_AVOID_MSYNC=0` (по умолчанию на всех системах кроме Windows),
то (как прежде) сохранение данных выполняется посредством `msync()`,
либо `FlushViewOfFile()` на Windows. На платформах с полноценной
подсистемой виртуальной памяти и адекватным файловым вводом-выводом
это обеспечивает минимум накладных расходов (один системный вызов)
и максимальную производительность. Однако, на Windows приводит
к значительной деградации, в том числе из-за того что после
`FlushViewOfFile()` требуется также вызов `FlushFileBuffers()`
с массой проблем и суеты внутри ядра ОС.
* Если `MDBX_AVOID_MSYNC=1` (по умолчанию только на Windows), то
сохранение данных выполняется явной записью в файл каждой измененной
страницы БД. Это требует дополнительных накладных расходов, как
на отслеживание измененных страниц (ведение списков "грязных"
страниц), так и на системные вызовы для их записи.
Кроме этого, с точки зрения подсистемы виртуальной памяти ядра ОС,
страницы БД измененные в ОЗУ и явно записанные в файл, могут либо
оставаться "грязными" и быть повторно записаны ядром ОС позже,
либо требовать дополнительных накладных расходов для отслеживания
PTE (Page Table Entries), их модификации и дополнительного копирования
данных. Тем не менее, по имеющейся информации, на Windows такой путь
записи данных в целом обеспечивает более высокую производительность.
- Улучшение эвристики включения авто-слияния записей GC.
- Изменение формата LCK и семантики некоторых внутренних полей. Версии
libmdbx использующие разный формат не смогут работать с одной БД
одновременно, а только поочередно (LCK-файл переписывается при открытии
первым открывающим БД процессом).
- В `C++` API добавлены методы фиксации транзакции с получением информации
о задержках.
- Added `MDBX_HAVE_BUILT IN_CPU_SUPPORTS` build option to control use GCC's
`__builtin_cpu_supports()` function, which could be unavailable on a fake
OSes (macos, ios, android, etc).
Исправления (без корректировок вышеперечисленных новых функций):
----------------------------------------------------------------
- Устранения ряда предупреждений при сборке посредством MinGW.
- Устранение ложно-положительных сообщений от Valgrind об использовании
не инициализированных данных из-за выравнивающих зазоров в `struct troika`.
- Исправлен возврат неожиданной ошибки `MDBX_BUSY` из функций `mdbx_env_set_option()`,
`mdbx_env_set_syncbytes()` и `mdbx_env_set_syncperiod()`.
- Небольшие исправления для совместимости с CMake 3.8
- Больше контроля и осторожности (паранойи) для страховки от дефектов `mremap()`.
- Костыль для починки сборки со старыми версиями `stdatomic.h` из GNU Lib C,
где макросы `ATOMIC_*_LOCK_FREE` ошибочно переопределяются через функции.
- Использование `fcntl64(F_GETLK64/F_SETLK64/F_SETLKW64)` при наличии.
Это решает проблему срабатывания проверочного утверждения при сборке для
платформ где тип `off_t` шире соответствующих полей `структуры flock`,
используемой для блокировки файлов.
- Доработан сбор информации о задержках при фиксации транзакций:
* Устранено искажение замеров длительности обновления GC
при включении отладочного внутреннего аудита;
* Защита от undeflow-нуля только общей задержки в метриках,
чтобы исключить ситуации, когда сумма отдельных стадий
больше общей длительности.
- Ряд исправлений для устранения срабатываний проверочных утверждения в
отладочных сборках.
- Более осторожное преобразование к типу `mdbx_tid_t` для устранения
предупреждений.
- Исправление лишнего сброса данных на диск в режиме `MDBX_SAFE_NOSYNC`
при обновлении GC.
- Fixed an extra check for `MDBX_APPENDDUP` inside `mdbx_cursor_put()`
which could result in returning `MDBX_EKEYMISMATCH` for valid cases.
- Fixed nasty `clz()` bug (by using `_BitScanReverse()`, only MSVC builds affected).
Мелочи:
-------
- Исторические ссылки cвязанные с удалённым на ~~github~~ проектом перенаправлены на [web.archive.org](https://web.archive.org/web/https://github.com/erthink/libmdbx).
- Синхронизированны конструкции CMake между проектами.
- Добавлено предупреждение о небезопасности RISC-V.
- Добавлено описание параметров `MDBX_debug_func` и `MDBX_debug_func`.
- Добавлено обходное решение для минимизации ложно-положительных
конфликтов при использовании файловых блокировок в Windows.
- Проверка атомарности C11-операций c 32/64-битными данными.
- Уменьшение в 42 раза значения по-умолчанию для `me_options.dp_limit`
в отладочных сборках.
- Добавление платформы `gcc-riscv64-linux-gnu` в список для цели `cross-gcc`.
- Небольшие правки скрипта `long_stochastic.sh` для работы в Windows.
- Удаление ненужного вызова `LockFileEx()` внутри `mdbx_env_copy()`.
- Добавлено описание использования файловых дескрипторов в различных режимах.
- Добавлено использование `_CrtDbgReport()` в отладочных сборках.
- Fixed an extra ensure/assertion check of `oldest_reader` inside `txn_end()`.
- Removed description of deprecated usage of `MDBX_NODUPDATA`.
- Fixed regression ASAN/Valgring-enabled builds.
- Fixed minor MingGW warning.
64 files changed, 5573 insertions(+), 2510 deletions(-)
Signed-off-by: Леонид Юрьев (Leonid Yuriev) <leo@yuriev.ru>