Достаточно запутанно:
- Для полноценного контроля при использовании Valgrind или ASAN
требуется закрашивать/отравлять отображение файла БД выше границы
распределенных страниц.
- Производить такое подкрашивание/отравление необходимо в синхронизации
с пишущими транзакциями и запросами на изменение геометрии, в том числе
при изменении размера БД и/или геометрии другим процессом.
- Для такой синхронизации логично и проще всего использовать основной
мьютекс/механизм блокировки пишущих транзакций, что и происходит внутри
txn_valgrind().
- Однако, в этой схеме может возникать ошибка EDEADLK, когда
txn_valgrind() вызывается при завершении читающей транзакции
выполняющейся с дополнительной блокировкой пишущих транзакций.
- Как таковая ошибка EDEADLK при этом проблем не создаёт и поэтому
просто игнорируется. Но утилита mdbx_chk при работе в кооперативном
(не эксклюзивном) режиме чтения-записи использует именно такой сценарий,
а возникающую при этом ошибку EDEADLK засчитывает как проблему при
проверке.
= В результате, при использовании Valgrind или ASAN утилита mdbx_chk
запущенная с опциями `-wc` всегда завершается неудачей из-за как минимум
одной проблемы в ходе проверки. Что внешне выглядит как
недочет/ошибка/регресс и создает проблемы при автоматизированном
тестировании.
Добавленный костыль использует atomic-счетчик, который инкремируется до
и декремируется после попытки захвата блокировки изнутри txn_valgrind().
В свою очередь, код обрабатывающий ошибку захвата блокировки, игнорирует
EDEADLK при ненулевом значении счетчика. Активируется костыль только при
сборке с поддержкой Valgrind или включенном ASAN, и не оказывает
никакого влияния в остальных случаях.
Существует проблема https://libmdbx.dqdkfa.ru/dead-github/issues/269,
которая проявляется только при специфической неупорядоченности внутри
ядра ОС, когда страницы, записанные в файл отображенный в память,
становятся видны в памяти посредством работы unified page cache:
- если записанная последней мета-страница "обгоняет" ранее записанные,
т.е. когда записанное в файл позже становится видимым в отображении
раньше, чем записанное ранее.
Теперь, вместо постоянной полной сверки записываемых страниц,
выполняется легковесная проверка при старте транзакций, с переключением
в режим "как раньше" при обнаружении проблемы.
В результате, в некоторых сценариях возвращается 5-10%
производительности, а в отдельных синтетических тестах до 30%.
Это вынужденный читинг для "починки" сравнительных бенчмарков при
размещении БД в /dev/shm.
Проблема в том, что актуальные ядра Linux для файлов размещенных в tmpfs
возвращают mincore=false. В результате, в простейших бенчмарках видно
двукратное снижение производительности, просто из-за вызовов write()
выполняемых для prefault.
Из-за этого, в таких синтетических тестах, новая libmdbx становится
существенно медленнее предыдущих версий, в том числе LMDB.
Изменение формата LCK-файла означает что версии libmdbx использующие
разный формат не смогут работать с одной БД одновременно, а только
поочередно (LCK-файл переписывается при открытии первым открывающим БД
процессом).
1. Поле mti_unsynced_pages теперь 64-битное (чтобы не контролировать
переполнение) и перемещено для соблюдения выравнивания.
2. Поле mti_sync_timestamp переименовано в mti_eoos_timestamp
одновременно со сменой семантики. Теперь время отсчитывается не от
момента сброса данных на диск, а с момента входа в «грязное» состояние.
Скорее всего, текущая версия формата LCK не окончательная
и изменится до релиза.
В режиме MDBX_WRITEMAP с опцией сборки MDBX_AVOID_MSYNC=0 отслеживание грязных страниц не требуется.
Эта доработка устраняет еще одну из недоделок (пункт в TODO).