RKL — сортированный набор txnid, использующий внутри комбинацию
непрерывного интервала и списка. Обеспечивает хранение id записей при
переработке, очистку и обновлении GC, включая возврат остатков
переработанных страниц.
Итератор для RKL — обеспечивает изоляцию внутреннего устройства rkl от
остального кода, чем существенно его упрощает. Фактически именно
использованием rkl с итераторами ликвидируется "ребус" исторически
образовавшийся в gc-update.
--
При переработке GC записи преимущественно выбираются последовательно, но
это не гарантируется. В LIFO-режиме переработка и добавление записей в
rkl происходит преимущественно в обратном порядке, но из-за завершения
читающих транзакций могут быть «скачки» в прямом направлении. В
FIFO-режиме записи GC перерабатываются в прямом порядке и при этом
линейно, но не обязательно строго последовательно, при этом
гарантируется что между добавляемыми в rkl идентификаторами в GC нет
записей, т.е. между первой (минимальный id) и последней (максимальный
id) в GC нет записей и весь интервал может быть использован для возврата
остатков страниц в GC.
Таким образом, комбинация линейного интервала и списка (отсортированного
в порядке возрастания элементов) является рациональным решением, близким
к теоретически оптимальному пределу.
Реализация rkl достаточно проста/прозрачная, если не считать неочевидную
«магию» обмена непрерывного интервала и образующихся в списке
последовательностей. Однако, именно этот автоматически выполняемый без
лишних операций обмен оправдывает все накладные расходы.
В результате рефакторинга и ряда оптимизаций для завершения/гашения
курсоров в читающих и пишущих транзакций стал использоваться общий код.
Причем за основу, был взят соответствующий фрагмент относящийся к
пишущим транзакциям, в которых пользователю не позволяется
использоваться курсоры для DBI=0 и поэтому эта итераций пропускалась.
В результате, при завершении читающих транзакциях, курсоры связанные с
DBI=0 не завершались должным образом, а при их повторном использовании
или явном закрытии после завершения читающей транзакции происходило
обращение к уже освобожденной памяти. Если же такие курсоры
отсоединялись или закрывались до завершения читающей транзакции, то
ошибка не имела шансов на проявление.
Спасибо Илье Михееву (https://github.com/JkLondon) и команде Erigon (https://erigon.tech) за сообщения о проблеме.
Пакетная вставка значений посредством операции `MDBX_MULTIPLE` могла
приводить к падениям и повреждению структуры БД. Ошибка оставалось не
замеченной из-за специфических условий проявления, которые не
реализовались в тестах.
Проблема присутствовала во всех выпусках начиная с v0.13.1, но
соответствующая ошибка не связана с конкретным коммита в истории, а
является следствием нескольких доработок (шагов рефакторинга), которые
суммарно привели к регрессу.
Технически ошибка обусловлена не-обнулением переменной, которая не
обнулялась в некотором пути выполнения и исходно не требовала обнуления,
но такое обнуление потребовалось после ряда этапов оптимизации кода и
рефакторинга.
Основным условием проявления является пакетная вставка multi-значений в
dupsort-таблицу с фиксированным размером значений, при котором набор
значений соответствующий обновляемом ключу, перестаёт помещаться на
вложенной странице и преобразуется/выносится во вложенное дерево
страниц. Если такой вынос/преобразование происходило до исчерпания
переданного набора значений, то при следующей итерации повторно
производились действия соответствующие выносу данных в отдельное дерево
страниц. Что могла приводить как к разыменованию неверных указателей
(повреждению содержимого памяти) и/или к повреждению содержимого страниц
образующих структуру БД.
Исправление свелось к добавлению одной строчки кода, но также были
расширены тесты для покрытия соответствующих сценариев.
Проблема была в том, что в случаях фиксированного размера значений
clc.lmin/clc.lmax устанавливались в env->kvs[], а затем корректировались
по актуальному размеру данных в БД. Поэтому при конкурентном вызове из
разных потоков, один поток мог выполнять инициализацию, а второй
прочитать временные/промежуточные значения lmin/lmax.
В результате, при конкурентном старте транзакций в разных потоках при
использовании только-что открытого dbi-хендла, проверка допустимости
длины значения могла заканчиваться ложной ошибкой MDBX_BAD_VALSIZE.
По недосмотру в выпусках остался предварительный/черновой вариант
функции mdbx_txn_release_all_cursors(), который смешивает в возвращаемом
значении информацию об ошибке/успехе и количество обработанных курсоров.
За-за чего невозможно отличить одно от другого, например ошибку EPERM на
Linux от одного успешно закрытого курсора.
Теперь mdbx_txn_release_all_cursors() возвращает только код ошибки,
а для получения кол-ва закрытых курсоров в API добавлена функция mdbx_txn_release_all_cursors_ex().
Некая проблема была в том, что при высоком уровне логирования в логгер
также отправлялись неизбежные MDBX_NOTFOND при достижении конца
интегрируемых данных. В свою очередь, chk-логика формирования отчета
подсчитывала эти сообщения как ошибки при проверке БД...
Теоретически до этого коммита могла быть некоторая неувязка:
- при открытии БД с размером страницы 4K на Windows (где размер секции кратен 64K) в режиме read-only,
- после того как БД использовалась на POSIX (где размер отображения кратен размеру системной страницы).
Ранее ошибка могла возвращаться со стороны системы (например INVALID_PARAMETER) и по ней крайне сложно было понять в чем дело.
Теперь же будет логирование ошибки и возврат MDBX_WANNA_RECOVERY.
Переработка 05cdf9d202b14ac09c801c7893e65271fa27f378. У предыдущего
варианта был недостаток, при необходимости выдачи предупреждения
и открытии БД с изменением геометрии, предупреждение не выдавалось,
что может затруднять анализ/разбор проблемных ситуаций.
Изменение геометрии (увеличение размера) больших БД может быть не
возможно после их открытия вследствие системных ограничений (отсутствия
свободного адресного пространства).
Поэтому API предусматривает возможность запросить изменение
геометрии/размера БД перед её открытием. В этом сценарии ранее могло
выдаваться лишнее/ненужное предупреждение о несоответствии файла БД
новому размеру. Теперь этот недостаток исправлен.
Спасибо Илье Михееву (Erigon) за сообщение об этом недочете.