Compare commits

...

1651 Commits

Author SHA1 Message Date
Леонид Юрьев (Leonid Yuriev)
b3329fddf2
mdbx: исправление опечатки MDBX_ENOMEM.
Вместо `MDBX_ENOMEM` был использован идентификатор `ENOMEM`,
что могло ломать сборку на не-POSIX/Windows платформах,
в зависимости от конфигурации и/или версии SDK.
2025-06-01 11:26:17 +03:00
Леонид Юрьев (Leonid Yuriev)
8b4ec09d08 mdbx: исправление имён параметров в прототипе rkl_destructive_move(). 2025-05-29 22:56:34 +03:00
Леонид Юрьев (Leonid Yuriev)
ecbffc65f4
mdbx: дополнение ChangeLog. 2025-05-21 14:13:36 +03:00
Леонид Юрьев (Leonid Yuriev)
e03b8e1227 mdbx: добавление ignore_enosys_and_einval() и её использование для отказа от OFD-блокировок. 2025-05-20 13:37:54 +03:00
Леонид Юрьев (Leonid Yuriev)
c88c51d33c mdbx: разделение ignore_enosys() и ignore_enosys_and_eagain(). 2025-05-20 13:37:54 +03:00
Леонид Юрьев (Leonid Yuriev)
ef82fea032 mdbx: избавление от memset() внутри lck_op(). 2025-05-20 13:37:54 +03:00
Леонид Юрьев (Leonid Yuriev)
f82cf6a4b3 mdbx: перемещение и доработка проверки _FILE_OFFSET_BITS для Android. 2025-05-20 13:37:54 +03:00
Леонид Юрьев (Leonid Yuriev)
60c0483987 mdbx-tests: устранение бестолковых предупреждений MSVC. 2025-05-16 00:50:55 +03:00
Леонид Юрьев (Leonid Yuriev)
9da03deac0 mdbx: исправление пропущенного const (косметика). 2025-05-16 00:08:51 +03:00
Леонид Юрьев (Leonid Yuriev)
34f0f682da mdbx: исправление assert-проверки внутри txn_end().
В случае ошибки запуска транзакции (например из-за невозможности расширения отображения при увеличении БД в другом процессе),
сигнатура транзакции отсутствует, что вызывало срабатывание assert-проверки.
2025-05-15 23:29:18 +03:00
Леонид Юрьев (Leonid Yuriev)
9fb0919468 mdbx: опечатка в ChangLog. 2025-05-06 15:41:31 +03:00
Леонид Юрьев (Leonid Yuriev)
a13147d115
mdbx: выпуск 0.14.1 "Горналь".
Первый выпуск в новом кусте/линейке версий с добавлением функционала, расширением API и внутренними переработками.
За перечнем доработок и изменений обращайтесь к [ChangeLog](https://libmdbx.dqdkfa.ru/md__change_log.html).

git diff' stat: 166 files changed, 9467 insertions(+), 5597 deletions(-).
Signed-off-by: Леонид Юрьев (Leonid Yuriev) <leo@yuriev.ru>
2025-05-06 14:15:36 +03:00
Леонид Юрьев (Leonid Yuriev)
800c96f22f
mdbx: доработка перераспределения резерва при возврате страниц в GC.
В экстремальных случаях, масштабирование одного операнда и 32-бит не хватает для предотвращения потери значимости расчетного коэффициента.
Поэтому здесь реализован переход на фиксированную точку 32-точка-32 с одним 64-битным делением и двумя полными умножениями 32*32->64.

Для 32-битных систем можно сделать чуть легче, заменив 64-битую арифметику масштабированием (адаптивным сдвигом) обоих операндов, но пока не вижу в этом смысла.
2025-05-02 17:59:30 +03:00
Леонид Юрьев (Leonid Yuriev)
d1023dc6b5
mdbx: merge branch devel. 2025-04-29 12:03:50 +03:00
Леонид Юрьев (Leonid Yuriev)
859c350df0
mdbx: дополнение ChangeLog. 2025-04-29 08:39:35 +03:00
Леонид Юрьев (Leonid Yuriev)
76e2544cc0 mdbx: доработки gc_handle_dense() для экстремально-редких случаев. 2025-04-29 08:39:18 +03:00
Леонид Юрьев (Leonid Yuriev)
0a96b2ad97 mdbx-doc: дополнение раздела "Containers" в README. 2025-04-28 14:43:01 +03:00
Леонид Юрьев (Leonid Yuriev)
402a8e62be mdbx: merge branch master into devel. 2025-04-26 00:17:57 +03:00
Леонид Юрьев (Leonid Yuriev)
06300de34e mdbx: подсказки для coverity. 2025-04-26 00:15:52 +03:00
Леонид Юрьев (Leonid Yuriev)
da9f78d2f6 mdbx: несущественные доработки rkl. 2025-04-26 00:15:52 +03:00
Леонид Юрьев (Leonid Yuriev)
a5af0c1a85 mdbx: исправление глупой утечки памяти в rkl_destroy(). 2025-04-26 00:15:52 +03:00
Леонид Юрьев (Leonid Yuriev)
2b36fd5974
mdbx: новый код обновления GC. 2025-04-26 00:15:41 +03:00
Леонид Юрьев (Leonid Yuriev)
3338551860 mdbx: рефакторинг макроса TXN_FOREACH_DBI_FROM с выделением функции dbi_foreach_step(). 2025-04-24 23:26:22 +03:00
Леонид Юрьев (Leonid Yuriev)
1c7a5e18fe mdbx: подсказки для coverity. 2025-04-24 15:39:07 +03:00
Леонид Юрьев (Leonid Yuriev)
6627d14edf mdbx: упрощение старта транзакций и исправление возможности double-free при ошибке создания вложенной транзакции. 2025-04-24 11:11:31 +03:00
Леонид Юрьев (Leonid Yuriev)
7db9c40fe0 mdbx-tests: установка max-dbi для extra/cursor-closing. 2025-04-23 23:01:27 +03:00
Леонид Юрьев (Leonid Yuriev)
52c9ef8807 mdbx: merge branch stable into master. 2025-04-22 15:56:02 +03:00
Леонид Юрьев (Leonid Yuriev)
5c44dd201c
mdbx: обновление патча для старых версий buildroot. 2025-04-22 14:43:37 +03:00
Леонид Юрьев (Leonid Yuriev)
f4384800b5 mdbx: обновление ChangeLog. 2025-04-22 13:10:30 +03:00
Леонид Юрьев (Leonid Yuriev)
a971c76aff
v0.13.6 "Бузина".
Поддерживающий выпуск стабильной ветки с исправлением обнаруженных ошибок и устранением недочётов,
в память о погибшем украинском историке и писателе [Алесе Бузине](https://ru.ruwiki.ru/wiki/Бузина,_Олесь_Алексеевич).

За перечнем доработок и изменений обращайтесь к [ChangeLog](https://libmdbx.dqdkfa.ru/md__change_log.html).

git diff' stat: 15 files changed, 194 insertions(+), 36 deletions(-).
Signed-off-by: Леонид Юрьев (Leonid Yuriev) <leo@yuriev.ru>
2025-04-22 11:53:23 +03:00
Леонид Юрьев (Leonid Yuriev)
b6f918aa1c
mdbx: исправление опечатки в логировании (backport). 2025-04-22 11:17:39 +03:00
Леонид Юрьев (Leonid Yuriev)
011c3072da mdbx-tests: поддержка десятичных суффиксов для batch-параметров. 2025-04-21 21:38:17 +03:00
Леонид Юрьев (Leonid Yuriev)
02b56e185f mdbx: добавление rkl_find() и rkl_merge(). 2025-04-21 21:38:17 +03:00
Леонид Юрьев (Leonid Yuriev)
576fc94fef mdbx: исправление опечатки в логировании (косметика). 2025-04-21 21:30:26 +03:00
Леонид Юрьев (Leonid Yuriev)
a56f5acc3d mdbx: рефакторинг tree_rebalance() и реализации опции MDBX_opt_prefer_waf_insteadof_balance. 2025-04-20 18:46:08 +03:00
Леонид Юрьев (Leonid Yuriev)
072103ab67
mdbx-tests: исправление extra/cursor-closing для старых стандартов C++. 2025-04-20 00:45:16 +03:00
Леонид Юрьев (Leonid Yuriev)
ab2f661c97
mdbx: дополнение ChangeLog. 2025-04-20 00:01:59 +03:00
Леонид Юрьев (Leonid Yuriev)
668a1e42e3
mdbx: дополнение ChangeLog. 2025-04-19 23:52:19 +03:00
Леонид Юрьев (Leonid Yuriev)
5548ef20f6
mdbx: переупорядочивание атрибутов для совместимости с GCC-15 в режиме C23 (backport). 2025-04-19 23:44:07 +03:00
Леонид Юрьев (Leonid Yuriev)
679c1eb939
mdbx-tests: обнуление pid на входе в osal_actor_poll() (backport). 2025-04-19 23:43:43 +03:00
Леонид Юрьев (Leonid Yuriev)
76a588f91b
mdbx: исправление возврата MDBX_BAD_TXN вместо MDBX_EINVAL из mdbx_cursor_unbind() в особых случаях (backport). 2025-04-19 23:42:59 +03:00
Леонид Юрьев (Leonid Yuriev)
6b5515908b
mdbx: предотвращение возврата неожиданной ошибки MDBX_BUSY из mdbx_txn_lock(dont_wait=false) (backport). 2025-04-19 23:42:34 +03:00
Леонид Юрьев (Leonid Yuriev)
dc747483dd mdbx-tests: привязка кол-ва потоков/проверок к кол-ву процессоров в extra/cursor-closing. 2025-04-19 20:02:09 +03:00
Леонид Юрьев (Leonid Yuriev)
89de43293d mdbx: исправление возврата MDBX_BAD_TXN вместо MDBX_EINVAL из mdbx_cursor_unbind() в особых случаях. 2025-04-19 20:01:57 +03:00
Леонид Юрьев (Leonid Yuriev)
270cf399aa mdbx: упрощение очистки MDBX_TXN_HAS_CHILD. 2025-04-19 20:01:57 +03:00
Леонид Юрьев (Leonid Yuriev)
b5503b5670 mdbx: исправление форматирования (косметика). 2025-04-19 20:01:36 +03:00
Леонид Юрьев (Leonid Yuriev)
a71cefc288 mdbx: предотвращение возврата неожиданной ошибки MDBX_BUSY из mdbx_txn_lock(dont_wait=false). 2025-04-19 14:07:26 +03:00
Леонид Юрьев (Leonid Yuriev)
6d6a19e3c3 mdbx-tests: вывод информации о salt/seed в extra/cursor-closing. 2025-04-19 14:07:26 +03:00
Леонид Юрьев (Leonid Yuriev)
0d7d4db3f1 mdbx: понижение уровня отладочного логирования lru-reduce. 2025-04-19 14:07:26 +03:00
Леонид Юрьев (Leonid Yuriev)
0f505c1377 mdbx: переупорядочивание атрибутов для совместимости с GCC-15 в режиме C23. 2025-04-18 10:49:00 +03:00
Леонид Юрьев (Leonid Yuriev)
f6ce9381af mdbx-tests: обнуление pid на входе в osal_actor_poll(). 2025-04-18 10:47:10 +03:00
Леонид Юрьев (Leonid Yuriev)
214fa153e2
mdbx: дополнение ChangeLog. 2025-04-10 16:54:13 +03:00
Леонид Юрьев (Leonid Yuriev)
819551ce13
mdbx-tests: расширение и доработка сценария extra/cursor-closing (backport). 2025-04-10 16:37:42 +03:00
Леонид Юрьев (Leonid Yuriev)
a22c0c5c48
mdbx: подсказка для Coverity для подавления ложно-положительных предупреждений (backport). 2025-04-10 16:35:53 +03:00
Леонид Юрьев (Leonid Yuriev)
9540cabf5f
mdbx: возврат MDBX_EINVAL из mdbx_cursor_bind() при невозможности отвязки курсора от его текущей транзакции (backport). 2025-04-10 16:34:57 +03:00
Леонид Юрьев (Leonid Yuriev)
0e3b093eb5
mdbx: исправление неверной assert-проверки и микрооптимизация (backport).
В пути фиксации вложенных транзакций, условие в assert-проверке не было
корректным для случая, когда таблица уже существовала и её дескриптор
был открыт, использовался в завершаемой вложенной транзакции, но не
использовался в родительской.

Это исправление недочета также передаёт, уже загруженное из БД, кешируемое
состояние таблицы в родительскую транзакцию.
2025-04-10 16:34:19 +03:00
Леонид Юрьев (Leonid Yuriev)
5d38add405
mdbx: исправление ошибок merge/rebase (backport). 2025-04-10 16:33:10 +03:00
Леонид Юрьев (Leonid Yuriev)
2ceda89b05 mdbx-tests: расширение и доработка сценария extra/cursor-closing. 2025-04-10 12:26:01 +03:00
Леонид Юрьев (Leonid Yuriev)
5bd99d4da2 mdbx: подсказка для Coverity для подавления ложно-положительных предупреждений. 2025-04-10 12:25:50 +03:00
Леонид Юрьев (Leonid Yuriev)
a04053ee98 mdbx: возврат MDBX_EINVAL из mdbx_cursor_bind() при невозможности отвязки курсора от его текущей транзакции. 2025-04-10 12:25:32 +03:00
Леонид Юрьев (Leonid Yuriev)
f35c1fe5bc mdbx: исправление неверной assert-проверки и микрооптимизация.
В пути фиксации вложенных транзакций, условие в assert-проверке не было
корректным для случая, когда таблица уже существовала и её дескриптор
был открыт, использовался в завершаемой вложенной транзакции, но не
использовался в родительской.

Это исправление недочета также передаёт уже загруженное из БД кешируемое
состояние таблицы в родительскую транзакцию.
2025-04-10 12:24:39 +03:00
Леонид Юрьев (Leonid Yuriev)
4691c0b5c8 mdbx: исправление ошибок merge/rebase. 2025-04-10 12:18:23 +03:00
Леонид Юрьев (Leonid Yuriev)
b55a41f604
mdbx: дополнение ChangeLog. 2025-04-09 22:18:13 +03:00
Леонид Юрьев (Leonid Yuriev)
29bed7cf5d mdbx: игнорирование EAGAIN от flock() в случае копирования на NFS. 2025-04-09 22:18:07 +03:00
Леонид Юрьев (Leonid Yuriev)
f91c2bb8da mdbx-doc: TODO typo and SWIG-url. 2025-04-09 10:58:49 +03:00
Леонид Юрьев (Leonid Yuriev)
8d0eceee9f mdbx: отключение использования copy_file_range() для ядер linux 5.3-5.18 включительно. 2025-04-07 05:28:16 +03:00
Леонид Юрьев (Leonid Yuriev)
6cb1b6754e mdbx-doc: исправление повтора в комментарии. 2025-04-06 14:09:51 +03:00
Леонид Юрьев (Leonid Yuriev)
187bd59aa0 mdbx: добавление badge-ссылки на телеграм и рокировка параграфов в начале README. 2025-04-02 16:23:59 +03:00
Леонид Юрьев (Leonid Yuriev)
1c49548ea5 mdbx-dc: fix typos. 2025-04-01 21:18:11 +03:00
Леонид Юрьев (Leonid Yuriev)
4b9427685a mdbx: добавление внутренней опции MDBX_DEBUG_DPL_LIMIT. 2025-03-31 00:54:07 +03:00
Леонид Юрьев (Leonid Yuriev)
650569cc6a mdbx: merge branch master into devel. 2025-03-31 00:52:52 +03:00
Леонид Юрьев (Leonid Yuriev)
d8f46344b5
mdbx: добавление MDBX_VERSION_UNSTABLE и маркировка master-ветки для предотвращения ошибок сборки. 2025-03-31 00:51:23 +03:00
Леонид Юрьев (Leonid Yuriev)
ebf1e9d8ba mdbx-tests: расширение extra/details-rkl для проверки hole-итераторов. 2025-03-30 23:35:00 +03:00
Леонид Юрьев (Leonid Yuriev)
4c3df230d3 mdbx: hole-iterator для rkl. 2025-03-30 20:04:49 +03:00
Леонид Юрьев (Leonid Yuriev)
9ea8e9b2cf mdbx-tests: добавление extra/details-rkl. 2025-03-30 20:04:49 +03:00
Леонид Юрьев (Leonid Yuriev)
b8c1b835ed mdbx: добавление rkl с итераторами.
RKL — сортированный набор txnid, использующий внутри комбинацию
непрерывного интервала и списка. Обеспечивает хранение id записей при
переработке, очистку и обновлении GC, включая возврат остатков
переработанных страниц.

Итератор для RKL — обеспечивает изоляцию внутреннего устройства rkl от
остального кода, чем существенно его упрощает. Фактически именно
использованием rkl с итераторами ликвидируется "ребус" исторически
образовавшийся в gc-update.

--

При переработке GC записи преимущественно выбираются последовательно, но
это не гарантируется. В LIFO-режиме переработка и добавление записей в
rkl происходит преимущественно в обратном порядке, но из-за завершения
читающих транзакций могут быть «скачки» в прямом направлении. В
FIFO-режиме записи GC перерабатываются в прямом порядке и при этом
линейно, но не обязательно строго последовательно, при этом
гарантируется что между добавляемыми в rkl идентификаторами в GC нет
записей, т.е. между первой (минимальный id) и последней (максимальный
id) в GC нет записей и весь интервал может быть использован для возврата
остатков страниц в GC.

Таким образом, комбинация линейного интервала и списка (отсортированного
в порядке возрастания элементов) является рациональным решением, близким
к теоретически оптимальному пределу.

Реализация rkl достаточно проста/прозрачная, если не считать неочевидную
«магию» обмена непрерывного интервала и образующихся в списке
последовательностей. Однако, именно этот автоматически выполняемый без
лишних операций обмен оправдывает все накладные расходы.
2025-03-30 20:04:49 +03:00
Леонид Юрьев (Leonid Yuriev)
db163cbcfd mdbx: перемещение узлов в node_add_branch() после проверки переполнения страницы. 2025-03-30 17:41:33 +03:00
Леонид Юрьев (Leonid Yuriev)
936c25e671 mdbx: добавление assert-проверок для отлова ошибок приводящих к переполнению/повреждению страниц. 2025-03-30 17:41:33 +03:00
Леонид Юрьев (Leonid Yuriev)
56a6377622
mdbx: понижение уровня логирования для "skip update meta" (backport).
Спасибо [Илье Михееву](https://github.com/JkLondon) за сообщение о недочете.
2025-03-28 15:14:54 +03:00
Леонид Юрьев (Leonid Yuriev)
b308559dd9 mdbx: понижение уровня логирования для "skip update meta".
Спасибо [Илье Михееву](https://github.com/JkLondon) за сообщение о недочете.
2025-03-28 15:12:10 +03:00
Леонид Юрьев (Leonid Yuriev)
19dc93fc76
mdbx: дополнение ChangeLog. 2025-03-23 17:46:28 +03:00
Леонид Юрьев (Leonid Yuriev)
5f1d8dcb3e mdbx: уточнение типа адреса для донатов (backport). 2025-03-22 23:33:13 +03:00
Леонид Юрьев (Leonid Yuriev)
3d2b221256 mdbx++: вброс std::invalid_argument с явным сообщением "MDBX_EINVAL" (backport). 2025-03-22 23:32:57 +03:00
Леонид Юрьев (Leonid Yuriev)
ca1808d57f mdbx-test: расширение extra/cursor-closing (backport). 2025-03-22 23:32:57 +03:00
Леонид Юрьев (Leonid Yuriev)
aa98d6a88e
mdbx: обновление NOTICE. 2025-03-22 23:31:47 +03:00
Леонид Юрьев (Leonid Yuriev)
b4e65f5d21 mdbx: обновление NOTICE. 2025-03-22 23:29:43 +03:00
Леонид Юрьев (Leonid Yuriev)
390490edf4 mdbx: уточнение типа адреса для донатов. 2025-03-22 23:17:35 +03:00
Леонид Юрьев (Leonid Yuriev)
b9b14f0061
mdbx: устранение регресса при использовании курсоров для DBI=0 в читающих транзакциях (hotfix).
В результате рефакторинга и ряда оптимизаций для завершения/гашения
курсоров в читающих и пишущих транзакций стал использоваться общий код.
Причем за основу, был взят соответствующий фрагмент относящийся к
пишущим транзакциям, в которых пользователю не позволяется
использоваться курсоры для DBI=0 и поэтому эта итераций пропускалась.

В результате, при завершении читающих транзакциях, курсоры связанные с
DBI=0 не завершались должным образом, а при их повторном использовании
или явном закрытии после завершения читающей транзакции происходило
обращение к уже освобожденной памяти. Если же такие курсоры
отсоединялись или закрывались до завершения читающей транзакции, то
ошибка не имела шансов на проявление.

Спасибо Илье Михееву (https://github.com/JkLondon) и команде Erigon (https://erigon.tech) за сообщения о проблеме.
2025-03-22 20:01:52 +03:00
Леонид Юрьев (Leonid Yuriev)
94531a9cdc mdbx++: вброс std::invalid_argument с явным сообщением "MDBX_EINVAL" . 2025-03-22 19:43:23 +03:00
Леонид Юрьев (Leonid Yuriev)
f8e332a205 mdbx-test: расширение extra/cursor-closing. 2025-03-22 19:43:23 +03:00
Леонид Юрьев (Leonid Yuriev)
021d83b841 mdbx: устранение регресса при использовании курсоров для DBI=0 в читающих транзакциях.
В результате рефакторинга и ряда оптимизаций для завершения/гашения
курсоров в читающих и пишущих транзакций стал использоваться общий код.
Причем за основу, был взят соответствующий фрагмент относящийся к
пишущим транзакциям, в которых пользователю не позволяется
использоваться курсоры для DBI=0 и поэтому эта итераций пропускалась.

В результате, при завершении читающих транзакциях, курсоры связанные с
DBI=0 не завершались должным образом, а при их повторном использовании
или явном закрытии после завершения читающей транзакции происходило
обращение к уже освобожденной памяти. Если же такие курсоры
отсоединялись или закрывались до завершения читающей транзакции, то
ошибка не имела шансов на проявление.

Спасибо Илье Михееву (https://github.com/JkLondon) и команде Erigon (https://erigon.tech) за сообщения о проблеме.
2025-03-22 19:08:52 +03:00
Леонид Юрьев (Leonid Yuriev)
e3324cef91
mdbx: выпуск 0.13.5 "Труба".
Поддерживающий выпуск стабильной ветки с исправлением обнаруженных ошибок и устранением недочётов.

За перечнем доработок и изменений обращайтесь к [ChangeLog](https://libmdbx.dqdkfa.ru/md__change_log.html).

git diff' stat: 49 files changed, 2106 insertions(+), 1135 deletions(-)
Signed-off-by: Леонид Юрьев (Leonid Yuriev) <leo@yuriev.ru>
2025-03-21 21:14:00 +03:00
Леонид Юрьев (Leonid Yuriev)
4e33bad6e7
mdbx: отсечение хвоста ChangeLog с отделением в ChangeLog-01. 2025-03-21 00:31:54 +03:00
Леонид Юрьев (Leonid Yuriev)
a313dd2fae
mdbx: merge branch stable into master. 2025-03-21 00:11:00 +03:00
Леонид Юрьев (Leonid Yuriev)
bb664152b8
mdbx: дополнение ChangeLog (подготовка к выпуску). 2025-03-20 23:53:22 +03:00
Леонид Юрьев (Leonid Yuriev)
2e4962a2f3 mdbx-docs: изменение <title> и meta-title в index.html 2025-03-20 21:50:53 +03:00
Леонид Юрьев (Leonid Yuriev)
00917f8c96 mdbx: корректировка ChangeLog. 2025-03-20 19:15:56 +03:00
Леонид Юрьев (Leonid Yuriev)
999f8644f6
mdbx: дополнение ChangeLog. 2025-03-20 18:02:00 +03:00
Леонид Юрьев (Leonid Yuriev)
5d9fb63fb8 mdbx: усиление контроля сигнатур курсоров (backport). 2025-03-20 18:00:44 +03:00
Леонид Юрьев (Leonid Yuriev)
06f8573f5f mdbx: усиление контроля сигнатур курсоров. 2025-03-20 17:20:47 +03:00
Леонид Юрьев (Leonid Yuriev)
1e0a1014a4 mdbx-tests: корректировка обработки прерывания теста посредством SIGTERM/SIGINT (backport). 2025-03-20 14:23:19 +03:00
Леонид Юрьев (Leonid Yuriev)
7eb7931a23 mdbx-tests: корректировка обработки прерывания теста посредством SIGTERM/SIGINT. 2025-03-20 14:13:20 +03:00
Леонид Юрьев (Leonid Yuriev)
35349cf538 mdbx: добавление опции сборки MDBX_ENABLE_NON_READONLY_EXPORT и логирование соответствующих ситуаций (backport).
Закрывает [запрос](https://gitflic.ru/project/erthink/libmdbx/issue/16).
2025-03-20 13:55:07 +03:00
Леонид Юрьев (Leonid Yuriev)
8157d07b00 mdbx: использование при наличии EREMOTEIO вместо ENOTBLK в качестве MDBX_EREMOTE (backport). 2025-03-20 13:53:47 +03:00
Леонид Юрьев (Leonid Yuriev)
3c3628c798 mdbx-tests: кратное сокращение итераций тестов в зависимости от конфигурации Valgrind/Debug/CI (backport). 2025-03-20 01:58:02 +03:00
Леонид Юрьев (Leonid Yuriev)
3a0dbee58c mdbx-tests: устранение невыравненного доступа в extra/close-dbi для UBSAN (backport). 2025-03-20 01:57:38 +03:00
Леонид Юрьев (Leonid Yuriev)
e11d419d20 mdbx-tests: перехват и логирование исключений в extra-C++ тестах (backport). 2025-03-20 01:56:55 +03:00
Леонид Юрьев (Leonid Yuriev)
59343d9106 mdbx++: minor reflow Doxygen comments (backport). 2025-03-20 01:56:07 +03:00
Леонид Юрьев (Leonid Yuriev)
2127d3b7d7 mdbx-tests: расширение extra/dupfix-multiple (backport). 2025-03-20 01:55:41 +03:00
Леонид Юрьев (Leonid Yuriev)
065aef35ea mdbx++: добавление mdbx::cursor::put_multiple_samelength() (backport). 2025-03-20 01:55:10 +03:00
Леонид Юрьев (Leonid Yuriev)
9653c8f45b mdbx: рефакторинг проверок с выносом в cursor_check_multiple() (backport). 2025-03-20 01:52:21 +03:00
Леонид Юрьев (Leonid Yuriev)
7ed769e9c6 mdbx: поддержка MDBX_MULTIPLE с нулевым размером данных (backport). 2025-03-20 01:51:37 +03:00
Леонид Юрьев (Leonid Yuriev)
52a19fecca mdbx++: явное определение external-инстанцирования mdbx::buffer<> c API-атрибутами (backport). 2025-03-20 01:50:27 +03:00
Леонид Юрьев (Leonid Yuriev)
3282adf8bd mdbx: исправление регресса в пути обработки MDBX_MULTIPLE (backport).
Пакетная вставка значений посредством операции `MDBX_MULTIPLE` могла
приводить к падениям и повреждению структуры БД. Ошибка оставалось не
замеченной из-за специфических условий проявления, которые не
реализовались в тестах.

Проблема присутствовала во всех выпусках начиная с v0.13.1, но
соответствующая ошибка не связана с конкретным коммита в истории, а
является следствием нескольких доработок (шагов рефакторинга), которые
суммарно привели к регрессу.

Технически ошибка обусловлена не-обнулением переменной, которая не
обнулялась в некотором пути выполнения и исходно не требовала обнуления,
но такое обнуление потребовалось после ряда этапов оптимизации кода и
рефакторинга.

Основным условием проявления является пакетная вставка multi-значений в
dupsort-таблицу с фиксированным размером значений, при котором набор
значений соответствующий обновляемом ключу, перестаёт помещаться на
вложенной странице и преобразуется/выносится во вложенное дерево
страниц. Если такой вынос/преобразование происходило до исчерпания
переданного набора значений, то при следующей итерации повторно
производились действия соответствующие выносу данных в отдельное дерево
страниц. Что могла приводить как к разыменованию неверных указателей
(повреждению содержимого памяти) и/или к повреждению содержимого страниц
образующих структуру БД.

Исправление свелось к добавлению одной строчки кода, но также были
расширены тесты для покрытия соответствующих сценариев.
2025-03-20 01:49:42 +03:00
Леонид Юрьев (Leonid Yuriev)
529f2c2380 mdbx-tests: уменьшение кол-ва итераций в extra/crunched-delete для 32-битных сборок во избежания MDBX_MAP_FULL (backport). 2025-03-20 01:49:17 +03:00
Леонид Юрьев (Leonid Yuriev)
1dfe1e872e mdbx++: добавление mdbx::cursor::seek_multiple_samelength() (backport). 2025-03-20 01:48:58 +03:00
Леонид Юрьев (Leonid Yuriev)
767ba21977 mdbx: костыли для CLANG < 20 при использовании [[атрибутов]] C23 (backport). 2025-03-20 01:48:22 +03:00
Леонид Юрьев (Leonid Yuriev)
0a9c9840da mdbx-tests: существенное расширение extra/cursor-closing (backport). 2025-03-20 01:47:56 +03:00
Леонид Юрьев (Leonid Yuriev)
9c177de034 mdbx-tests: дополнение extra/txn (backport). 2025-03-20 01:47:24 +03:00
Леонид Юрьев (Leonid Yuriev)
5f37ea60d2 mdbx++: проверка __cpp_concepts >= 202002 для использования концептов C++ (backport). 2025-03-20 01:46:43 +03:00
Леонид Юрьев (Leonid Yuriev)
c457804fad mdbx: исправление затенения курсоров во вложенных транзакциях (backport). 2025-03-20 01:46:13 +03:00
Леонид Юрьев (Leonid Yuriev)
6c036add8b mdbx: переработка проверки курсоров на входе API-функций с добавлением cursor_check() (backport). 2025-03-20 01:44:20 +03:00
Леонид Юрьев (Leonid Yuriev)
5fd319bbc2 mdbx: переработка mdbx_txn_release_all_cursors_ex() (backport). 2025-03-20 01:42:50 +03:00
Леонид Юрьев (Leonid Yuriev)
682233ba28 mdbx++: переформатирование (временно) неиспользуемого кода (backport). 2025-03-20 01:41:58 +03:00
Леонид Юрьев (Leonid Yuriev)
c5936eb5da mdbx++: удаление исключения при запросе транзакции у отсоединённого курсора (backport). 2025-03-20 01:41:32 +03:00
Леонид Юрьев (Leonid Yuriev)
d8890bc169 mdbx++: добавление inplace_storage_size_rounding в capacity_policy для буферов (backport). 2025-03-20 01:41:05 +03:00
Леонид Юрьев (Leonid Yuriev)
73d52c1963 mdbx++: добавление mdbx::cursor_managed::withdraw_handle() (backport). 2025-03-20 01:40:41 +03:00
Леонид Юрьев (Leonid Yuriev)
58729a2fbd mdbx: корректировка описания MDBX_MVCC_RETARDED и соответствующего сообщения об ошибке (backport). 2025-03-20 01:40:11 +03:00
Леонид Юрьев (Leonid Yuriev)
5dfe3433a8 mdbx: устранение гонки в tbl_setup(MDBX_DUPFIXED | MDBX_INTEGERDUP) при работе в разных потоках (backport).
Проблема была в том, что в случаях фиксированного размера значений
clc.lmin/clc.lmax устанавливались в env->kvs[], а затем корректировались
по актуальному размеру данных в БД. Поэтому при конкурентном вызове из
разных потоков, один поток мог выполнять инициализацию, а второй
прочитать временные/промежуточные значения lmin/lmax.

В результате, при конкурентном старте транзакций в разных потоках при
использовании только-что открытого dbi-хендла, проверка допустимости
длины значения могла заканчиваться ложной ошибкой MDBX_BAD_VALSIZE.
2025-03-20 01:24:34 +03:00
Леонид Юрьев (Leonid Yuriev)
1720762080 mdbx: переименование cursor_validate() (backport). 2025-03-20 01:24:30 +03:00
Леонид Юрьев (Leonid Yuriev)
91570a084f mdbx: добавление MDBX_SEEK_AND_GET_MULTIPLE в API операций курсора (backport). 2025-03-20 01:17:47 +03:00
Леонид Юрьев (Leonid Yuriev)
0fff8d0704 mdbx-doc: doxygen-описания для doubtless-positioning констант (backport). 2025-03-20 01:17:11 +03:00
Леонид Юрьев (Leonid Yuriev)
484b488f92 mdbx-tests: добавление поддержки опции MDBX_VALIDATION и использование в стохастическом тесте (backport). 2025-03-20 01:16:48 +03:00
Леонид Юрьев (Leonid Yuriev)
2fbdaccf60 mdbx-tests: поддержка значений on/off для опций командной строки (backport). 2025-03-20 01:15:53 +03:00
Леонид Юрьев (Leonid Yuriev)
753b2270fd mdbx: добавление mdbx_cursor_close2() в API (backport). 2025-03-20 01:14:34 +03:00
Леонид Юрьев (Leonid Yuriev)
33ceba0a5a mdbx: добавление cursor_reset() и cursor_drown() (backport). 2025-03-20 01:13:44 +03:00
Леонид Юрьев (Leonid Yuriev)
2476fba287 mdbx: рефакторинг cursor_eot() для упрощения txn_done_cursors() (backport). 2025-03-20 01:12:59 +03:00
Леонид Юрьев (Leonid Yuriev)
2b6a768750 mdbx: косметический рефакторинг cursor_shadow() (backport). 2025-03-20 01:12:15 +03:00
Леонид Юрьев (Leonid Yuriev)
b6dcdcf2dc mdbx: запрещение unbind/close курсоров для вложенных транзакций (backport). 2025-03-20 01:10:11 +03:00
Леонид Юрьев (Leonid Yuriev)
175e4a2e1b mdbx: проверка владельца потока владеющего транзакцией только при MDBX_TXN_CHECKOWNER=ON (backport). 2025-03-20 01:06:56 +03:00
Леонид Юрьев (Leonid Yuriev)
f9d7eb5525 mdbx-doc: актуализация раздела MacOS в README (backport). 2025-03-20 01:01:05 +03:00
Леонид Юрьев (Leonid Yuriev)
69895e2b55 mdbx-make: поиск gnu-sed на Darwin/MacOS (backport). 2025-03-20 00:57:17 +03:00
Леонид Юрьев (Leonid Yuriev)
15bd9cfc89 mdbx: удаление const у транзакции в cursor_bind() и cursor_renew() (backport). 2025-03-20 00:52:16 +03:00
Леонид Юрьев (Leonid Yuriev)
d8f9f3ba58 mdbx: проверяем выравнивание размера БД на юнит выделения памяти, а не на размер страницы (backport).
Теоретически до этого коммита могла быть некоторая неувязка:
 - при открытии БД с размером страницы 4K на Windows (где размер секции кратен 64K) в режиме read-only,
 - после того как БД использовалась на POSIX (где размер отображения кратен размеру системной страницы).

Ранее ошибка могла возвращаться со стороны системы (например INVALID_PARAMETER) и по ней крайне сложно было понять в чем дело.
Теперь же будет логирование ошибки и возврат MDBX_WANNA_RECOVERY.
2025-03-20 00:46:45 +03:00
Леонид Юрьев (Leonid Yuriev)
4150f411dc mdbx: переработка проверка размера файла БД при открытии (backport).
Переработка 05cdf9d202. У предыдущего
варианта был недостаток, при необходимости выдачи предупреждения
и открытии БД с изменением геометрии, предупреждение не выдавалось,
что может затруднять анализ/разбор проблемных ситуаций.
2025-03-20 00:45:57 +03:00
Леонид Юрьев (Leonid Yuriev)
32ca9691c3 mdbx-doc: добавление ссылки на привязку к Zig (backport). 2025-03-20 00:44:54 +03:00
Леонид Юрьев (Leonid Yuriev)
4f59864ef5 mdbx-cmake: используем -flto=auto для GCC >= 11.4 (backport).
При сборке посредством GCC >= 11.4 больше не возникает предупреждений:
  lto-wrapper: warning: using serial compilation of # LTRANS jobs
  lto-wrapper: note: see the ‘-flto’ option documentation for more information

Однако, использование auto-режима не является оптимальным решением, так
как при параллельной сборке посредством make или ninja, каждая уже
запущенная ветвь компиляции породит потоки ещё для каждого ядра ЦПУ.

Таким образом реальная нагрузка может расти квадратично, т.е. чем больше
у вас ядер -- тем хуже и при 96 ядрах может работать 9216 потоков сборки.

Тем не менее, использование `job-server` в CMake пока не возможно, а при
сборке libmdbx не так много работы чтобы чтобы обрушить систему нагрузкой.
2025-03-20 00:41:13 +03:00
Леонид Юрьев (Leonid Yuriev)
f82b760b6e mdbx-cmake: избегаем двойной работы compiler.cmake без необходимости (backport). 2025-03-20 00:40:12 +03:00
Леонид Юрьев (Leonid Yuriev)
d6b359756c mdbx-cmake: расслабление условий для использования LTO с CLANG на Linux (backport). 2025-03-20 00:40:02 +03:00
Леонид Юрьев (Leonid Yuriev)
4d454d6e80 mdbx-cmake: расширение поиска LLVMgold.so в относительных lib-директориях (backport). 2025-03-20 00:35:47 +03:00
Леонид Юрьев (Leonid Yuriev)
44467d0883 mdbx-make: добавление цели ninja-assertions и её использование при make check (backport). 2025-03-20 00:30:01 +03:00
Леонид Юрьев (Leonid Yuriev)
49e6bd9296 mdbx++: использование mdbx_txn_release_all_cursors_ex() (backport). 2025-03-20 00:29:20 +03:00
Леонид Юрьев (Leonid Yuriev)
e37194affe
mdbx: дополнение ChangeLog. 2025-03-19 23:50:29 +03:00
Леонид Юрьев (Leonid Yuriev)
917e2827f5 mdbx-tests: кратное сокращение итераций тестов в зависимости от конфигурации Valgrind/Debug/CI. 2025-03-19 23:30:49 +03:00
Леонид Юрьев (Leonid Yuriev)
2fd1772503 mdbx-tests: устранение невыравненного доступа в extra/close-dbi для UBSAN. 2025-03-18 13:14:47 +03:00
Леонид Юрьев (Leonid Yuriev)
694626727f mdbx: использование cmp_lenfast() вместо cmp_lenfast(). 2025-03-18 10:46:55 +03:00
Леонид Юрьев (Leonid Yuriev)
2aa47f20c3 mdbx-tests: перехват и логирование исключений в extra-C++ тестах. 2025-03-18 10:46:55 +03:00
Leo Yuriev
e6891b295b mdbx++: minor reflow Doxygen comments. 2025-03-18 10:46:55 +03:00
Леонид Юрьев (Leonid Yuriev)
c0b1ab1466 mdbx-tests: расширение extra/dupfix-multiple. 2025-03-18 10:46:55 +03:00
Леонид Юрьев (Leonid Yuriev)
71d95d1a5f mdbx++: добавление mdbx::cursor::put_multiple_samelength(). 2025-03-18 10:46:55 +03:00
Леонид Юрьев (Leonid Yuriev)
7a923b3d41 mdbx: рефакторинг проверок с выносом в cursor_check_multiple(). 2025-03-18 10:46:55 +03:00
Леонид Юрьев (Leonid Yuriev)
8008afc6e1 mdbx: поддержка MDBX_MULTIPLE с нулевым размером данных. 2025-03-18 10:46:55 +03:00
Леонид Юрьев (Leonid Yuriev)
7ae11e0fdb mdbx++: явное определение external-инстанцирования mdbx::buffer<> c API-атрибутами. 2025-03-17 23:28:58 +03:00
Леонид Юрьев (Leonid Yuriev)
5c1745a7cd mdbx: добавление гистограммы количества multi-значений/дубликатов в chk. 2025-03-17 23:28:46 +03:00
Леонид Юрьев (Leonid Yuriev)
23a417fe19 mdbx: исправление регресса в пути обработки MDBX_MULTIPLE.
Пакетная вставка значений посредством операции `MDBX_MULTIPLE` могла
приводить к падениям и повреждению структуры БД. Ошибка оставалось не
замеченной из-за специфических условий проявления, которые не
реализовались в тестах.

Проблема присутствовала во всех выпусках начиная с v0.13.1, но
соответствующая ошибка не связана с конкретным коммита в истории, а
является следствием нескольких доработок (шагов рефакторинга), которые
суммарно привели к регрессу.

Технически ошибка обусловлена не-обнулением переменной, которая не
обнулялась в некотором пути выполнения и исходно не требовала обнуления,
но такое обнуление потребовалось после ряда этапов оптимизации кода и
рефакторинга.

Основным условием проявления является пакетная вставка multi-значений в
dupsort-таблицу с фиксированным размером значений, при котором набор
значений соответствующий обновляемом ключу, перестаёт помещаться на
вложенной странице и преобразуется/выносится во вложенное дерево
страниц. Если такой вынос/преобразование происходило до исчерпания
переданного набора значений, то при следующей итерации повторно
производились действия соответствующие выносу данных в отдельное дерево
страниц. Что могла приводить как к разыменованию неверных указателей
(повреждению содержимого памяти) и/или к повреждению содержимого страниц
образующих структуру БД.

Исправление свелось к добавлению одной строчки кода, но также были
расширены тесты для покрытия соответствующих сценариев.
2025-03-17 23:28:28 +03:00
Леонид Юрьев (Leonid Yuriev)
db44f4ed71 mdbx-tools: добавление опции -c (concise) в mdbx_dump. 2025-03-17 23:28:16 +03:00
Леонид Юрьев (Leonid Yuriev)
ef9fd1f3fb mdbx-tests: уменьшение кол-ва итераций в extra/crunched-delete для 32-битных сборок во избежания MDBX_MAP_FULL. 2025-03-17 23:27:51 +03:00
Леонид Юрьев (Leonid Yuriev)
2e6d9fd4d4 mdbx++: добавление mdbx::cursor::seek_multiple_samelength(). 2025-03-17 23:27:34 +03:00
Леонид Юрьев (Leonid Yuriev)
83e42d03bb mdbx: костыли для CLANG < 20 при использовании [[аттрибутов]] C23. 2025-03-17 23:27:14 +03:00
Леонид Юрьев (Leonid Yuriev)
dfd265d46f mdbx-tests: существенное расширение extra/cursor-closing. 2025-03-17 23:26:49 +03:00
Леонид Юрьев (Leonid Yuriev)
08d10ad0a1 mdbx-tests: дополнение extra/txn. 2025-03-17 23:26:30 +03:00
Леонид Юрьев (Leonid Yuriev)
8ebedde181 mdbx++: проверка __cpp_concepts >= 202002 для использования концептов C++. 2025-03-17 23:26:18 +03:00
Леонид Юрьев (Leonid Yuriev)
dcf35e5306 mdbx: исправление затенения курсоров во вложенных транзакциях. 2025-03-17 23:25:53 +03:00
Леонид Юрьев (Leonid Yuriev)
aeac971f0b mdbx: переработка проверки курсоров на входе API-функций с добавлением cursor_check(). 2025-03-17 23:25:30 +03:00
Леонид Юрьев (Leonid Yuriev)
6c8047a402 mdbx: переработка mdbx_txn_release_all_cursors_ex(). 2025-03-17 23:20:40 +03:00
Леонид Юрьев (Leonid Yuriev)
438d185250 mdbx++: переформатирование (временно) неиспользуемого кода. 2025-03-17 23:20:28 +03:00
Леонид Юрьев (Leonid Yuriev)
ee6843062d mdbx++: удаление исключения при запросе транзакции у отсоединённого курсора. 2025-03-17 23:20:03 +03:00
Леонид Юрьев (Leonid Yuriev)
70adf71770 mdbx++: добавление inplace_storage_size_rounding в capacity_policy для буферов. 2025-03-17 23:16:30 +03:00
Леонид Юрьев (Leonid Yuriev)
fa2c27fa08 mdbx++: добавление mdbx::cursor_managed::withdraw_handle(). 2025-03-17 23:16:12 +03:00
Леонид Юрьев (Leonid Yuriev)
7a72d1b273 mdbx: корректировка описания MDBX_MVCC_RETARDED и соответствующего сообщения об ошибке. 2025-03-17 23:15:48 +03:00
Леонид Юрьев (Leonid Yuriev)
3e91500fac mdbx: устранение гонки в tbl_setup(MDBX_DUPFIXED | MDBX_INTEGERDUP) при работе в разных потоках.
Проблема была в том, что в случаях фиксированного размера значений
clc.lmin/clc.lmax устанавливались в env->kvs[], а затем корректировались
по актуальному размеру данных в БД. Поэтому при конкурентном вызове из
разных потоков, один поток мог выполнять инициализацию, а второй
прочитать временные/промежуточные значения lmin/lmax.

В результате, при конкурентном старте транзакций в разных потоках при
использовании только-что открытого dbi-хендла, проверка допустимости
длины значения могла заканчиваться ложной ошибкой MDBX_BAD_VALSIZE.
2025-03-17 23:13:26 +03:00
Леонид Юрьев (Leonid Yuriev)
546b48b6eb mdbx: переименование cursor_validate(). 2025-03-17 23:01:30 +03:00
Леонид Юрьев (Leonid Yuriev)
2ffa5cf371 mdbx: добавление MDBX_SEEK_AND_GET_MULTIPLE в API операций курсора. 2025-03-17 22:58:57 +03:00
Леонид Юрьев (Leonid Yuriev)
b546dc69d2 mdbx-doc: doxygen-описания для doubtless-positioning констант. 2025-03-17 22:58:44 +03:00
Леонид Юрьев (Leonid Yuriev)
42706c45a0 mdbx-tests: добавление поддержки опции MDBX_VALIDATION и использование в стохастическом тесте. 2025-03-17 22:58:29 +03:00
Леонид Юрьев (Leonid Yuriev)
8dda33329b mdbx-tests: поддержка значений on/off для опций командной строки. 2025-03-17 22:58:08 +03:00
Леонид Юрьев (Leonid Yuriev)
b2bd8bae38 mdbx: добавление mdbx_cursor_close2() в API. 2025-03-17 22:57:38 +03:00
Леонид Юрьев (Leonid Yuriev)
1299653457 mdbx: добавление cursor_reset() и cursor_drown(). 2025-03-17 22:24:23 +03:00
Леонид Юрьев (Leonid Yuriev)
333069e7a8 mdbx: рефакторинг cursor_eot() для упрощения txn_done_cursors(). 2025-03-17 21:38:42 +03:00
Леонид Юрьев (Leonid Yuriev)
436998ca83 mdbx: косметический рефакторинг cursor_shadow(). 2025-03-17 21:06:45 +03:00
Леонид Юрьев (Leonid Yuriev)
b0665f7016 mdbx: запрещение unbind/close курсоров для вложенных транзакций. 2025-03-17 20:48:19 +03:00
Леонид Юрьев (Leonid Yuriev)
4fcfb07b97 mdbx: корректировка mdbx_panic() для вывода переданного сообщения через __assert_failed(). 2025-03-17 20:47:47 +03:00
Леонид Юрьев (Leonid Yuriev)
ca30365d3b mdbx-make: добавление цели ninja-assertions и её использование при make check. 2025-03-17 20:46:44 +03:00
Леонид Юрьев (Leonid Yuriev)
6424747636 mdbx++: использование mdbx_txn_release_all_cursors_ex(). 2025-03-17 20:45:09 +03:00
Леонид Юрьев (Leonid Yuriev)
183610b050 mdbx-doc: исправление url в sitemap. 2025-03-09 11:41:02 +03:00
Леонид Юрьев (Leonid Yuriev)
920d9b5b2f mdbx-doc: добавление ld+json в корневой index.hml 2025-03-05 12:54:51 +03:00
Леонид Юрьев (Leonid Yuriev)
283c962fea mdbx: исправление опечатки в ChangeLog. 2025-03-05 01:46:57 +03:00
Леонид Юрьев (Leonid Yuriev)
805d84480d mdbx: исправление опечатки в ChangeLog. 2025-03-05 01:44:21 +03:00
Леонид Юрьев (Leonid Yuriev)
8efcdeae9d mdbx: исправление опечатки в дате внутри ChangeLog. 2025-03-04 20:06:16 +03:00
Леонид Юрьев (Leonid Yuriev)
7504a8f8f2 mdbx: обновление ChangeLog. 2025-03-04 14:47:13 +03:00
Леонид Юрьев (Leonid Yuriev)
94a2abaf31 mdbx: добавление в API mdbx_txn_release_all_cursors_ex() и изменение семантики результата mdbx_txn_release_all_cursors() (backport).
По недосмотру в выпусках остался предварительный/черновой вариант
функции mdbx_txn_release_all_cursors(), который смешивает в возвращаемом
значении информацию об ошибке/успехе и количество обработанных курсоров.
За-за чего невозможно отличить одно от другого, например ошибку EPERM на
Linux от одного успешно закрытого курсора.

Теперь mdbx_txn_release_all_cursors() возвращает только код ошибки,
а для получения кол-ва закрытых курсоров в API добавлена функция mdbx_txn_release_all_cursors_ex().
2025-03-04 14:45:13 +03:00
Леонид Юрьев (Leonid Yuriev)
9c161cdafd mdbx: дополнение ChangeLog. 2025-03-04 14:27:53 +03:00
Леонид Юрьев (Leonid Yuriev)
a3265e11dc mdbx: добавление в API mdbx_txn_release_all_cursors_ex() и изменение семантики результата mdbx_txn_release_all_cursors().
По недосмотру в выпусках остался предварительный/черновой вариант
функции mdbx_txn_release_all_cursors(), который смешивает в возвращаемом
значении информацию об ошибке/успехе и количество обработанных курсоров.
За-за чего невозможно отличить одно от другого, например ошибку EPERM на
Linux от одного успешно закрытого курсора.

Теперь mdbx_txn_release_all_cursors() возвращает только код ошибки,
а для получения кол-ва закрытых курсоров в API добавлена функция mdbx_txn_release_all_cursors_ex().
2025-03-04 14:21:25 +03:00
Леонид Юрьев (Leonid Yuriev)
709d524d21 mdbx: проверка владельца потока владеющего транзакцией только при MDBX_TXN_CHECKOWNER=ON. 2025-03-04 10:52:30 +03:00
Леонид Юрьев (Leonid Yuriev)
0604accecf mdbx: проверка владельца потока владеющего транзакцией только при MDBX_TXN_CHECKOWNER=ON (backport). 2025-03-04 10:44:42 +03:00
Леонид Юрьев (Leonid Yuriev)
bc2f1c59cb mdbx: обновление ChangeLog. 2025-03-04 01:14:03 +03:00
Леонид Юрьев (Leonid Yuriev)
e0843429a1 mdbx-doc: актуализация раздела MacOS в README. 2025-03-04 00:02:39 +03:00
Леонид Юрьев (Leonid Yuriev)
329eee4e4f mdbx-make: поиск gnu-sed на Darwin/MacOS. 2025-03-03 23:12:55 +03:00
Леонид Юрьев (Leonid Yuriev)
4fd165f8d2 mdbx: дополнение ChangeLog. 2025-03-03 20:16:51 +03:00
Леонид Юрьев (Leonid Yuriev)
05e7a94619 mdbx-tests: исправление extra-open для 32-битных сборок Windows (БД еще меньше). 2025-03-03 02:31:32 +03:00
Леонид Юрьев (Leonid Yuriev)
40f655e2da mdbx: корректировка log_error() для устранение ложных ошибок при работе mdbx_chk с высоким уровнем логирования (backport).
Некая проблема была в том, что при высоком уровне логирования в логгер
также отправлялись неизбежные MDBX_NOTFOND при достижении конца
интегрируемых данных. В свою очередь, chk-логика формирования отчета
подсчитывала эти сообщения как ошибки при проверке БД...
2025-03-03 01:49:32 +03:00
Леонид Юрьев (Leonid Yuriev)
5e714ed946 mdbx: переделка env_owned_wrtxn() и мест её вызова (backport).
Цель в том чтобы избавить от коллизии блокировки возникающей внутри
dxb_sanitize_tail() при использовании Valgrind/ASAN, а также упросить
код.
2025-03-03 01:49:25 +03:00
Леонид Юрьев (Leonid Yuriev)
826cdb708f mdbx: корректировка log_error() для устранение ложных ошибок при работе mdbx_chk с высоким уровнем логирования.
Некая проблема была в том, что при высоком уровне логирования в логгер
также отправлялись неизбежные MDBX_NOTFOND при достижении конца
интегрируемых данных. В свою очередь, chk-логика формирования отчета
подсчитывала эти сообщения как ошибки при проверке БД...
2025-03-03 01:12:35 +03:00
Леонид Юрьев (Leonid Yuriev)
da24fda578 mdbx: добавление print-подобных функций в chk для удобства отладки. 2025-03-03 01:11:55 +03:00
Леонид Юрьев (Leonid Yuriev)
0fa21a3c0d mdbx: переделка env_owned_wrtxn() и мест её вызова.
Цель в том чтобы избавить от коллизии блокировки возникающей внутри
dxb_sanitize_tail() при использовании Valgrind/ASAN, а также упросить
код.
2025-03-02 23:29:40 +03:00
Леонид Юрьев (Leonid Yuriev)
d313008d82 mdbx: дополнительные проверки сигнатур курсоров при итерации связанных списков (backport). 2025-03-02 16:38:27 +03:00
Леонид Юрьев (Leonid Yuriev)
9277daa185 mdbx: более полная очистка курсоров при закрытии/отключении (backport). 2025-03-02 16:37:49 +03:00
Леонид Юрьев (Leonid Yuriev)
1792bdc763 mdbx-tests: расширение extra/dbi (backport). 2025-03-02 16:03:20 +03:00
Леонид Юрьев (Leonid Yuriev)
90635e7248 mdbx: исправление наследования dbi-хендла открытого в дочерней транзакции без изменения данных. 2025-03-02 16:03:20 +03:00
Леонид Юрьев (Leonid Yuriev)
1ec13c63ab mdbx: устранение сбоя аудита таблиц при инвалидации dbi-хендла вследствие отмены вложенной транзакции (backport). 2025-03-02 16:03:20 +03:00
Леонид Юрьев (Leonid Yuriev)
c712147eeb mdbx: исправление оплошности в спецификации формата при логировании имен таблиц (backport). 2025-03-02 16:03:20 +03:00
Леонид Юрьев (Leonid Yuriev)
23600241e1 mdbx: уменьшение в 16 раз предлагаемого размера БД для устранения проблем Valgrind/ASAN (backport). 2025-03-02 16:03:20 +03:00
Леонид Юрьев (Leonid Yuriev)
22c6763d57 mdbx-tests: удаление тестовой БД перед началом теста в extra/dupfix_addodd (backport). 2025-03-02 16:03:20 +03:00
Леонид Юрьев (Leonid Yuriev)
c585fcd613 mdbx-tests: расширение extra/open (backport). 2025-03-02 16:03:20 +03:00
Леонид Юрьев (Leonid Yuriev)
dd9f608320 mdbx: дополнительные проверки сигнатур курсоров при итерации связанных списков. 2025-03-02 11:46:10 +03:00
Леонид Юрьев (Leonid Yuriev)
28ca18972a mdbx: более полная очистка курсоров при закрытии/отключении. 2025-03-02 11:44:10 +03:00
Леонид Юрьев (Leonid Yuriev)
fbb93f9cfb mdbx: удаление const у транзакции в cursor_bind() и cursor_renew(). 2025-03-02 10:41:38 +03:00
Леонид Юрьев (Leonid Yuriev)
bc464521c0 mdbx-tests: расширение extra/dbi. 2025-03-02 00:42:55 +03:00
Леонид Юрьев (Leonid Yuriev)
9273e2ee60 mdbx: исправление наследования dbi-хендла открытого в дочерней транзакции без изменения данных. 2025-03-02 00:40:18 +03:00
Леонид Юрьев (Leonid Yuriev)
e035f102ab mdbx: устранение сбоя аудита таблиц при инвалидации dbi-хендла вследствие отмены вложенной транзакции. 2025-03-02 00:10:56 +03:00
Леонид Юрьев (Leonid Yuriev)
1240ed2ba3 mdbx: исправление оплошности в спецификации формата при логировании имен таблиц. 2025-03-02 00:10:56 +03:00
Леонид Юрьев (Leonid Yuriev)
6ca63b46d8 mdbx: уменьшение в 16 раз предлагаемого размера БД для устранения проблем Valgrind/ASAN. 2025-03-02 00:10:56 +03:00
Леонид Юрьев (Leonid Yuriev)
9fee0bc3a6 mdbx-tests: удаление тестовой БД перед началом теста в extra/dupfix_addodd. 2025-03-02 00:10:56 +03:00
Леонид Юрьев (Leonid Yuriev)
c14bb7814f mdbx-tests: исправление extra-open для 32-битных сборок. 2025-02-20 23:48:48 +03:00
Леонид Юрьев (Leonid Yuriev)
9b31c517e6 mdbx: проверяем выравнивание размера БД на юнит выделения памяти, а не на размер страницы.
Теоретически до этого коммита могла быть некоторая неувязка:
 - при открытии БД с размером страницы 4K на Windows (где размер секции кратен 64K) в режиме read-only,
 - после того как БД использовалась на POSIX (где размер отображения кратен размеру системной страницы).

Ранее ошибка могла возвращаться со стороны системы (например INVALID_PARAMETER) и по ней крайне сложно было понять в чем дело.
Теперь же будет логирование ошибки и возврат MDBX_WANNA_RECOVERY.
2025-02-20 23:11:28 +03:00
Леонид Юрьев (Leonid Yuriev)
66c747e4a9 mdbx-cmake: корректировка форматирования (косметика). 2025-02-20 23:11:28 +03:00
Леонид Юрьев (Leonid Yuriev)
54d8c0d290 mdbx: переработка проверка размера файла БД при открытии.
Переработка 05cdf9d202. У предыдущего
варианта был недостаток, при необходимости выдачи предупреждения
и открытии БД с изменением геометрии, предупреждение не выдавалось,
что может затруднять анализ/разбор проблемных ситуаций.
2025-02-20 23:11:28 +03:00
Леонид Юрьев (Leonid Yuriev)
26cd5ebc43 mdbx: дополнение ChangeLog. 2025-02-20 00:13:21 +03:00
Леонид Юрьев (Leonid Yuriev)
806f819bae mdbx-tests: дополнение extra-open. 2025-02-20 00:09:58 +03:00
Леонид Юрьев (Leonid Yuriev)
0ef0f49e2e mdbx: устранение излишнего предупреждения при смене размера БД во время открытия (backport).
Изменение геометрии (увеличение размера) больших БД может быть не
возможно после их открытия вследствие системных ограничений (отсутствия
свободного адресного пространства).

Поэтому API предусматривает возможность запросить изменение
геометрии/размера БД перед её открытием. В этом сценарии ранее могло
выдаваться лишнее/ненужное предупреждение о несоответствии файла БД
новому размеру. Теперь этот недостаток исправлен.

Спасибо Илье Михееву (Erigon) за сообщение об этом недочете.
2025-02-19 23:38:15 +03:00
Леонид Юрьев (Leonid Yuriev)
05cdf9d202 mdbx: устранение излишнего предупреждения при смене размера БД во время открытия.
Изменение геометрии (увеличение размера) больших БД может быть не
возможно после их открытия вследствие системных ограничений (отсутствия
свободного адресного пространства).

Поэтому API предусматривает возможность запросить изменение
геометрии/размера БД перед её открытием. В этом сценарии ранее могло
выдаваться лишнее/ненужное предупреждение о несоответствии файла БД
новому размеру. Теперь этот недостаток исправлен.

Спасибо Илье Михееву (Erigon) за сообщение об этом недочете.
2025-02-19 23:22:18 +03:00
Леонид Юрьев (Leonid Yuriev)
818740976b mdbx-doc: добавление ссылки на привязку к Zig. 2025-02-17 15:01:57 +03:00
Леонид Юрьев (Leonid Yuriev)
287bab36a1 mdbx-doc: обновление конфигурации doxygen. 2025-02-17 14:43:20 +03:00
Леонид Юрьев (Leonid Yuriev)
80de77b1ee mdbx-doc: опечатки в README (backport). 2025-02-16 22:15:32 +03:00
Леонид Юрьев (Leonid Yuriev)
5388d2273b mdbx-doc: опечатки в README. 2025-02-16 16:52:53 +03:00
Леонид Юрьев (Leonid Yuriev)
d2864029da
mdbx: информация о статусе Github. 2025-02-15 15:47:33 +03:00
Леонид Юрьев (Leonid Yuriev)
822213f75d
mdbx: информация о статусе Github (backport). 2025-02-15 15:46:44 +03:00
Леонид Юрьев (Leonid Yuriev)
b63ca3c12e mdbx: обновление патча для старых версий buildroot. 2025-02-14 21:39:39 +03:00
Леонид Юрьев (Leonid Yuriev)
aa2ff20faf mdbx: обновление патча для старых версий buildroot (backport). 2025-02-14 21:37:47 +03:00
Леонид Юрьев (Leonid Yuriev)
fcdd2e2db3 mdbx: обновление ChangeLog. 2025-02-14 15:23:28 +03:00
Леонид Юрьев (Leonid Yuriev)
75122b311d
mdbx: выпуск 0.13.4 "Sigma Boy".
Поддерживающий выпуск стабильной ветки с исправлением обнаруженных ошибок и устранением недочётов.

За перечнем доработок и изменений обращайтесь к [ChangeLog](https://libmdbx.dqdkfa.ru/md__change_log.html).

git diff' stat: 139 files changed, 391 insertions(+), 208 deletions(-)
Signed-off-by: Леонид Юрьев (Leonid Yuriev) <leo@yuriev.ru>
2025-02-14 12:56:12 +03:00
Леонид Юрьев (Leonid Yuriev)
79572b4850 mdbx: корректировка излишне строгого условия в assert-проверке внутри recalculate_subpage_thresholds() (backport). 2025-02-11 14:03:13 +03:00
Леонид Юрьев (Leonid Yuriev)
4730abe3e5 mdbx: корректировка излишне строгого условия в assert-проверке внутри recalculate_subpage_thresholds(). 2025-02-11 14:01:10 +03:00
Леонид Юрьев (Leonid Yuriev)
24f2b9b099 mdbx-conan: исправление опечатки в имени переменной version_json_pathname в verbose-выводе.
Спасибо Виктору Логунову (https://t.me/vl_username) за сообщение о проблеме.
2025-02-03 18:46:53 +03:00
Леонид Юрьев (Leonid Yuriev)
401454dadf mdbx-conan: исправление опечатки в имени переменной version_json_pathname в verbose-выводе.
Спасибо Виктору Логунову (https://t.me/vl_username) за сообщение о проблеме.
2025-02-03 18:46:01 +03:00
Леонид Юрьев (Leonid Yuriev)
9568209ee4 mdbx: добавление pnl_clone() и pnl_maxspan(). 2025-02-01 16:56:00 +03:00
Леонид Юрьев (Leonid Yuriev)
781c04f6e2 mdbx: корректировка излишне строгого условия в assert-проверке внутри recalculate_subpage_thresholds(). 2025-01-29 12:15:01 +03:00
Леонид Юрьев (Leonid Yuriev)
b7206c68a5 mdbx: дополнение ChangeLog. 2025-01-27 22:41:24 +03:00
Леонид Юрьев (Leonid Yuriev)
3a0b857e1d mdbx-cmake: используем -flto=auto для GCC >= 11.4
При сборке посредством GCC >= 11.4 больше не возникает предупреждений:
  lto-wrapper: warning: using serial compilation of # LTRANS jobs
  lto-wrapper: note: see the ‘-flto’ option documentation for more information

Однако, использование auto-режима не является оптимальным решением, так
как при параллельной сборке посредством make или ninja, каждая уже
запущенная ветвь компиляции породит потоки ещё для каждого ядра ЦПУ.

Таким образом реальная нагрузка может расти квадратично, т.е. чем больше
у вас ядер -- тем хуже и при 96 ядрах может работать 9216 потоков сборки.

Тем не менее, использование `job-server` в CMake пока не возможно, а при
сборке libmdbx не так много работы чтобы чтобы обрушить систему нагрузкой.
2025-01-27 21:30:01 +03:00
Леонид Юрьев (Leonid Yuriev)
6ccbce9afc mdbx-cmake: избегаем двойной работы compiler.cmake без необходимости. 2025-01-27 21:11:57 +03:00
Леонид Юрьев (Leonid Yuriev)
9d7495fa09 mdbx-cmake: расслабление условий для использования LTO с CLANG на Linux. 2025-01-27 20:41:44 +03:00
Леонид Юрьев (Leonid Yuriev)
c8f6d90e18 mdbx-cmake: расширение поиска LLVMgold.so в относительных lib-директориях. 2025-01-27 20:32:02 +03:00
Леонид Юрьев (Leonid Yuriev)
b46d2def80 mdbx: дополнение ChangeLog. 2025-01-27 12:12:38 +03:00
Леонид Юрьев (Leonid Yuriev)
21630ea115 mdbx: устранение регресса вероятности SIGSEGV при вытеснении/spilling страниц (backport).
Ошибка внесена коммитом `a6f7d74a32a3cbcc310916a624a31302dbebfa07` от
2024-03-07 и присутствует в выпусках v0.13.1, v0.13.2, v0.13.3. Проблема
оставалась незамеченной из-за специфических условий и низкой вероятности
проявления.

Суть ошибки:

- функция cursor_touch() подготавливает стек страниц курсора к внесению
  изменений, при этом все страницы в стеке (от корневой до листовой
  в текущей позиции курсора) должны стать доступными для модификации.

- микрооптимизация добавленная коммитом пропускала обход стека, если
  корневая страница уже доступна для модификации, но это
  допустимо/корректно только при отсутствии в стеке вытесненных/spilled
  страниц.

- если же складывалась ситуация когда в стека была вытесненная
  некорневая страница, то она так и оставалась недоступной для записи и
  при попытке её изменения возникал SIGSEGV.
2025-01-27 11:18:50 +03:00
Леонид Юрьев (Leonid Yuriev)
6d346d8630 mdbx-cmake: поддержка MacOS universal binaries (backport).
Thank Alain Picard (Castor Technologies) so much for this patch and supporting the Java bindings!
2025-01-27 11:18:50 +03:00
Леонид Юрьев (Leonid Yuriev)
778aee25fe mdbx: дополнение ChangeLog. 2025-01-27 11:01:10 +03:00
Леонид Юрьев (Leonid Yuriev)
cb8eec6d11 mdbx: устранение регресса вероятности SIGSEGV при вытеснении/spilling страниц.
Ошибка внесена коммитом `a6f7d74a32a3cbcc310916a624a31302dbebfa07` от
2024-03-07 и присутствует в выпусках v0.13.1, v0.13.2, v0.13.3. Проблема
оставалась незамеченной из-за специфических условий и низкой вероятности
проявления.

Суть ошибки:

- функция cursor_touch() подготавливает стек страниц курсора к внесению
  изменений, при этом все страницы в стеке (от корневой до листовой
  в текущей позиции курсора) должны стать доступными для модификации.

- микрооптимизация добавленная коммитом пропускала обход стека, если
  корневая страница уже доступна для модификации, но это
  допустимо/корректно только при отсутствии в стеке вытесненных/spilled
  страниц.

- если же складывалась ситуация когда в стека была вытесненная
  некорневая страница, то она так и оставалась недоступной для записи и
  при попытке её изменения возникал SIGSEGV.
2025-01-27 10:09:04 +03:00
Леонид Юрьев (Leonid Yuriev)
b59937adb8 mdbx-doc: исправление опечатки в упоминании mdbx_env_resurrect_after_fork() (backport). 2025-01-26 17:38:37 +03:00
Леонид Юрьев (Leonid Yuriev)
f6d91b3c5b mdbx-doc: исправление опечатки в упоминании mdbx_env_resurrect_after_fork(). 2025-01-26 17:36:40 +03:00
Леонид Юрьев (Leonid Yuriev)
11e1346f9d mdbx: исправление опечатки в cursor_touch() (backport).
При переделке курсоров было пропущено отрицание в условии, при оценке
кол-ва страниц, которые могут потребоваться для выполнения операции.

В текущем понимании ошибка не приводила к каким-либо проблемам, ибо
оценка делает по верхней границе с существенным запасом, а в худшем
случае это могло приводить к прерыванию транзакции из-за достижения
ограничения на кол-во грязных страниц.
2025-01-26 17:05:40 +03:00
Леонид Юрьев (Leonid Yuriev)
750fab2427 mdbx: дополнение ChangeLog. 2025-01-26 16:57:17 +03:00
Леонид Юрьев (Leonid Yuriev)
fffa78d912 mdbx: дополнение TODO. 2025-01-26 16:49:33 +03:00
Леонид Юрьев (Leonid Yuriev)
fc85d1c61f mdbx-cmake: поддержка MacOS universal binaries.
Thank Alain Picard (Castor Technologies) so much for this patch and supporting the Java bindings!
2025-01-26 16:37:34 +03:00
Леонид Юрьев (Leonid Yuriev)
340bd080c9 mdbx: исправление опечатки в cursor_touch().
При переделке курсоров было пропущено отрицание в условии, при оценке
кол-ва страниц, которые могут потребоваться для выполнения операции.

В текущем понимании ошибка не приводила к каким-либо проблемам, ибо
оценка делает по верхней границе с существенным запасом, а в худшем
случае это могло приводить к прерыванию транзакции из-за достижения
ограничения на кол-во грязных страниц.
2025-01-26 16:37:00 +03:00
Леонид Юрьев (Leonid Yuriev)
7074b94b2e mdbx: упрощение gcu_loose(). 2025-01-26 16:36:55 +03:00
Леонид Юрьев (Leonid Yuriev)
a59c5f9316 mdbx: упрощение gcu_loose() (backport). 2025-01-26 10:16:02 +03:00
Леонид Юрьев (Leonid Yuriev)
f39542a9f0 mdbx-doc: дополнение TODO. 2025-01-21 16:26:47 +03:00
Леонид Юрьев (Leonid Yuriev)
27a2166be7 mdbx-doc: исправление орфографии/опечатки в ChangeLog (backport). 2025-01-21 15:41:21 +03:00
Леонид Юрьев (Leonid Yuriev)
d89670bcea mdbx-doc: исправление орфографии/опечатки в ChangeLog. 2025-01-21 15:40:26 +03:00
Леонид Юрьев (Leonid Yuriev)
c615e4d0a6 mdbx-doc: доработка/актуализация раздела "Restrictions & Caveats" (backport). 2025-01-19 02:30:35 +03:00
Леонид Юрьев (Leonid Yuriev)
fce40169bd mdbx-doc: доработка/актуализация раздела "Restrictions & Caveats". 2025-01-19 02:14:19 +03:00
Леонид Юрьев (Leonid Yuriev)
560aa72f3d mdbx-doc: добавление в doxygen-документацию ссылки на архив сообщений телеграмм-группы 2020-2024 годов. 2025-01-19 01:23:31 +03:00
Леонид Юрьев (Leonid Yuriev)
cb7ba6b53f mdbx-doc: favicon для сайта с документацией. 2025-01-19 00:51:37 +03:00
Леонид Юрьев (Leonid Yuriev)
03685aba5a mdbx-doc: разделение актуальных и устаревших/неподдерживаемых привязок в README (backport). 2025-01-18 19:03:14 +03:00
Леонид Юрьев (Leonid Yuriev)
4a0a32a54b mdbx-doc: добавление в README ссылки на архив сообщений телеграмм-группы 2020-2024 годов (backport). 2025-01-18 19:03:11 +03:00
Леонид Юрьев (Leonid Yuriev)
1b9ad144ea mdbx: исправление верстки README. 2025-01-18 18:15:51 +03:00
Леонид Юрьев (Leonid Yuriev)
36abcc57f0 mdbx: обновление года в © (backport). 2025-01-18 10:58:31 +03:00
Леонид Юрьев (Leonid Yuriev)
0233eda949 mdbx-doc: добавление в README ссылки на архив сообщений телеграмм-группы 2020-2024 годов. 2025-01-17 22:41:26 +03:00
Леонид Юрьев (Leonid Yuriev)
78552a5c84 mdbx-doc: разделение актуальных и устаревших/неподдерживаемых привязок в README. 2025-01-17 20:39:25 +03:00
Леонид Юрьев (Leonid Yuriev)
beb5a81d12 mdbx-doc: обновление номера версии и даты в заголовках man-страниц. 2025-01-17 18:29:15 +03:00
Леонид Юрьев (Leonid Yuriev)
56d1dbef45 mdbx: обновление года в ©. 2025-01-15 19:36:07 +03:00
Леонид Юрьев (Leonid Yuriev)
761248cc21 mdbx-doc: дополнение описания mdbx_txn_commit(). 2025-01-15 14:56:26 +03:00
Леонид Юрьев (Leonid Yuriev)
72fb45e13d mdbx: дополнение ChangeLog. 2025-01-15 14:24:43 +03:00
Леонид Юрьев (Leonid Yuriev)
e529cd7d19 mdbx: корректировка ChangeLog. 2025-01-15 00:50:57 +03:00
Леонид Юрьев (Leonid Yuriev)
2c3b36da64 mdbx: рефакторинг txn_renew() транзакций с вычленением txn_basal_start(). 2025-01-15 00:50:57 +03:00
Леонид Юрьев (Leonid Yuriev)
314b8ce1f0 mdbx: переименование (косметика). 2025-01-15 00:50:57 +03:00
Леонид Юрьев (Leonid Yuriev)
7e772114bc mdbx: рефакторинг читающих транзакций в вычленением txn_ro_start(), txn_ro_seize(), txn_ro_slot(). 2025-01-15 00:50:36 +03:00
Леонид Юрьев (Leonid Yuriev)
0accf98ff7 mdbx: добавление опции сборки MDBX_ENABLE_NON_READONLY_EXPORT и логирование соответствующих ситуаций.
Закрывает [запрос](https://gitflic.ru/project/erthink/libmdbx/issue/16).
2025-01-14 13:26:54 +03:00
Леонид Юрьев (Leonid Yuriev)
e4054b56c3 mdbx: использование при наличии EREMOTEIO вместо ENOTBLK в качестве MDBX_EREMOTE. 2025-01-14 13:26:54 +03:00
Леонид Юрьев (Leonid Yuriev)
950db52fe8 mdbx: выделение basal/ro/nested txn-функций в отдельные файлы (без изменений кода). 2025-01-14 13:26:54 +03:00
Леонид Юрьев (Leonid Yuriev)
380385c1db mdbx: упрощение выхода по not-found пути из cursor_seek(). 2025-01-14 13:26:54 +03:00
Леонид Юрьев (Leonid Yuriev)
10e7e5c899 mdbx: рефакторинг mdbx_txn_commit_ex() 5/5 (вычленение txn_basal_end()). 2025-01-14 13:26:54 +03:00
Леонид Юрьев (Leonid Yuriev)
6d92a778a5 mdbx: оформление опции сборки MDBX_NOSUCCESS_PURE_COMMIT (выключено по умолчанию). 2025-01-14 13:26:54 +03:00
Леонид Юрьев (Leonid Yuriev)
c60f6afe5f mdbx: упрощение/выпрямление/рефакторинг txn_end() и затронутых зависимостей. 2025-01-14 13:26:54 +03:00
Леонид Юрьев (Leonid Yuriev)
a5bb555db3 mdbx: рефакторинг mdbx_txn_commit_ex() 4/5 (вычленение txn_basal_commit()). 2025-01-14 13:26:54 +03:00
Леонид Юрьев (Leonid Yuriev)
b9b784c18e mdbx: рефакторинг mdbx_txn_commit_ex() 3/5 (вычленение txn_nested_join()). 2025-01-14 13:26:54 +03:00
Леонид Юрьев (Leonid Yuriev)
c6cd482ea0 mdbx: рефакторинг mdbx_txn_commit_ex() 2/5 (struct commit_timestamp, latency_init/gcprof/done()). 2025-01-14 13:26:54 +03:00
Леонид Юрьев (Leonid Yuriev)
2b9401e372 mdbx: рефакторинг mdbx_txn_commit_ex() 1/5 (переменование локальных timestamp-переменных). 2025-01-14 13:26:54 +03:00
Леонид Юрьев (Leonid Yuriev)
6fe7baa1b8 mdbx: упрощение mdbx_txn_break(). 2025-01-14 13:26:54 +03:00
Леонид Юрьев (Leonid Yuriev)
1e5fef2c76 mdbx: рефакторинг txn-api с выносом отдельных txn-функций. 2025-01-14 13:26:54 +03:00
Леонид Юрьев (Leonid Yuriev)
0a4156fe6f mdbx: перенос check_env() из txn_end() в функции txn-api. 2025-01-14 13:26:54 +03:00
Леонид Юрьев (Leonid Yuriev)
a89d418c91 mdbx: рефакторинг mdbx_txn_straggler() с добавлением проверки env. 2025-01-14 13:26:54 +03:00
Леонид Юрьев (Leonid Yuriev)
585ccdf716 mdbx: изменение TXN_END_NAMES. 2025-01-14 13:26:54 +03:00
Леонид Юрьев (Leonid Yuriev)
81e2623a54 mdbx: рефакторинг затенения и завершения курсоров, с удалением TXN_END_EOTDONE и добавлением txn_may_have_cursors. 2025-01-14 13:26:54 +03:00
Леонид Юрьев (Leonid Yuriev)
b681b59434 mdbx: рефакторинг/вычленение txn_basal_create/destroy(). 2025-01-14 13:26:54 +03:00
Леонид Юрьев (Leonid Yuriev)
88d782e5eb mdbx: обновление патча для старых версий buildroot (backport). 2025-01-14 13:08:18 +03:00
Леонид Юрьев (Leonid Yuriev)
67460dd0fd mdbx: обновление патча для старых версий buildroot. 2025-01-14 13:04:25 +03:00
Леонид Юрьев (Leonid Yuriev)
3a1ac35009 mdbx: дополнение ChangeLog. 2025-01-13 16:55:41 +03:00
Леонид Юрьев (Leonid Yuriev)
3c60e1e94c mdbx-tests: переделка seed/salt ГПСЧ для более удобного контроля и воспроизведения тестов. 2025-01-13 16:55:41 +03:00
Леонид Юрьев (Leonid Yuriev)
a994a9bbcc mdbx: использование MDBX_GET_BOTH для проверки наличия добавляемого значения в таблице. 2025-01-13 16:55:41 +03:00
Леонид Юрьев (Leonid Yuriev)
84e2c70b98
mdbx: начало разработки ветки 0.14. 2025-01-13 16:54:52 +03:00
Леонид Юрьев (Leonid Yuriev)
d3daa23c63 mdbx: обновление ChangeLog (подготовка выпуска 0.13.4). 2025-01-13 13:13:26 +03:00
Леонид Юрьев (Leonid Yuriev)
bd45668fee mdbx: merge branch master into stable. 2025-01-12 22:08:00 +03:00
Леонид Юрьев (Leonid Yuriev)
92e2b6287e
mdbx: выпуск 0.13.3 "Королёв" (Korolev).
Поддерживающий выпуск с исправлением обнаруженных ошибок и устранением недочетов
в день рождения и в память об [Серге́е Па́вловиче Королёве](https://ru.wikipedia.org/wiki/Королёв,_Сергей_Павлович),
советском учёном и Главном конструкторе ракетно-космических систем.

За перечнем доработок и изменений обращайтесь к [ChangeLog](https://libmdbx.dqdkfa.ru/md__change_log.html).

git diff' stat: 67 files changed, 3514 insertions(+), 3004 deletions(-)
Signed-off-by: Леонид Юрьев (Leonid Yuriev) <leo@yuriev.ru>
2025-01-12 16:27:02 +03:00
Леонид Юрьев (Leonid Yuriev)
c751977bf7 mdbx-tools: добавление логирования ошибок/предупреждений по все утилиты. 2025-01-12 14:38:20 +03:00
Леонид Юрьев (Leonid Yuriev)
e5fe279632 mdbx: логирование ошибок при открытии lck-файла. 2025-01-12 14:30:39 +03:00
Леонид Юрьев (Leonid Yuriev)
8408a2eed3 mdbx: добавление разделителя между MDBX_BUILD_FLAGS_CONFIG и MDBX_BUILD_FLAGS (косметика). 2025-01-12 11:54:24 +03:00
Леонид Юрьев (Leonid Yuriev)
0297136648 mdbx: улучшение авто-переключения в режим without-lck при открытии БД на read-only-носителе. 2025-01-12 02:58:59 +03:00
Леонид Юрьев (Leonid Yuriev)
92a49c7c8c mdbx: устранение риска потери/перезаписи errno при неожиданных ошибках в close(). 2025-01-12 02:58:59 +03:00
Леонид Юрьев (Leonid Yuriev)
b75e16f4f8 mdbx: устранение null-dereference регресса в режиме readonly-without-lck. 2025-01-12 02:58:59 +03:00
Леонид Юрьев (Leonid Yuriev)
dcc8708d6a mdbx: дополнение ChangeLog (запланирован выпуск v0.13.3). 2025-01-11 02:35:48 +03:00
Леонид Юрьев (Leonid Yuriev)
9c8f90b713 mdbx: доработка эвристик для выбора/подстройки default-значений в mdbx_env_set_geometry(). 2025-01-11 02:30:53 +03:00
Леонид Юрьев (Leonid Yuriev)
820bd45818 mdbx++: использование только default-значений для геометрии по-умолчанию вместо min/max. 2025-01-10 23:50:05 +03:00
Леонид Юрьев (Leonid Yuriev)
3a02ca88ea mdbx-make: добавление цели ctest и её привязка к make check. 2025-01-10 23:50:05 +03:00
Леонид Юрьев (Leonid Yuriev)
16997a88b0 mdbx-tests: уменьшение кол-ва итераций в crunched-delete. 2025-01-10 23:50:05 +03:00
Леонид Юрьев (Leonid Yuriev)
b00e8ea13f mdbx: использование txl_contain() в audit(). 2025-01-08 13:29:10 +03:00
Леонид Юрьев (Leonid Yuriev)
faa9753d2d mdbx: перемещение и корректировка комментария размечающего внутренние поля пишущей транзакции. 2025-01-05 14:44:00 +03:00
Леонид Юрьев (Leonid Yuriev)
5ba257fafc mdbx: добавление упущенного static для txl_reserve(). 2025-01-05 14:44:00 +03:00
Леонид Юрьев (Leonid Yuriev)
bad6e3c2e2 mdbx: очистка флажка ENV_TXKEY внутри rthc_dtor().
На штатную работу это никак не влияет, но немного облегчит разбор
ситуаций когда глобальный конструктор не вызывается, либо делается
попытка вызвать его дважды (из-за ошибок rtc/libc, etc).
2025-01-05 14:40:56 +03:00
Леонид Юрьев (Leonid Yuriev)
5350ed8a3b mdbx: дополнение ChangeLog. 2025-01-04 11:54:11 +03:00
Леонид Юрьев (Leonid Yuriev)
fef7c25a65 mdbx-make: mkdir -p для повторной сборки без очистки. 2025-01-04 11:54:07 +03:00
Леонид Юрьев (Leonid Yuriev)
10ac9a9c50 mdbx-tests: добавление extra/txn. 2025-01-04 04:01:41 +03:00
Леонид Юрьев (Leonid Yuriev)
dc98f06d2c mdbx: логирование и возврат MDBX_INCOMPATIBLE при попытке запуска вложенных транзакций в режиме MDBX_WRITEMAP. 2025-01-03 22:14:00 +03:00
Леонид Юрьев (Leonid Yuriev)
0a364aefbb mdbx++: добавление txn::make_broken(). 2025-01-03 22:14:00 +03:00
Леонид Юрьев (Leonid Yuriev)
1bf008ac16 mdbx: доработка контроля потока-владельца транзакции.
1. Теперь допускается commit/abort вложенных транзакций из любого треда в режиме MDBX_NOSTICKYTHREADS.

2. Более наглядные/явные проверки без зависимости от больше/меньше.
Одна проверка внутри check_txn() для всех основных случаев (bad_bits != 0) и две проверки для abort/reset/break (bad_bits == 0).

+-------------------------------------------------------------------------------------------------------+
|          Три анализируемых txn->flags       |         Проверка txn->owner == osal_thread_self()       |
+-----------------+------------+--------------+-----------------------+---------------------------------+
| NOSTICKYTHREADS | TXN_RDONLY | TXN_FINISHED | usual (bad_bits != 0) | abort/reset/break (bad_bits==0) |
|      -          |     -      |     -        |     +                 |         +                       |
|      -          |     -      |     +        |     +                 |         +                       |
|      -          |     +      |     -        |     +                 |         +                       |
|      -          |     +      |     +        |     +                 |         -                       |
|      +          |     -      |     -        |     -                 |         -                       |
|      +          |     -      |     +        |     +                 |         +                       |
|      +          |     +      |     -        |     -                 |         -                       |
|      +          |     +      |     +        |     +                 |         -                       |
+-------------------------------------------------------------------------------------------------------+
2025-01-03 22:12:17 +03:00
Леонид Юрьев (Leonid Yuriev)
1e4e2eb3c8 mdbx-doc: исправление опечатки в комментарии. 2024-12-29 08:42:48 +03:00
Леонид Юрьев (Leonid Yuriev)
63dba2876d mdbx-doc: корректировка описания значения по-умолчанию MDBX_opt_txn_dp_limit. 2024-12-28 22:56:17 +03:00
Леонид Юрьев (Leonid Yuriev)
5ff5080935 mdbx: дополнение ChangeLog. 2024-12-28 09:52:19 +03:00
Леонид Юрьев (Leonid Yuriev)
df8b15f639 mdbx: const для транзакции в txn_take_gcprof(). 2024-12-28 09:38:08 +03:00
Леонид Юрьев (Leonid Yuriev)
26f6fd351a mdbx: подстройка dirty-pages-limit при старте транзакций. 2024-12-27 09:39:36 +03:00
Леонид Юрьев (Leonid Yuriev)
c8c541649c mdbx: доработка контроля длины ключа внутри cursor_seek().
Ранее проверка внутри cursor_seek() не позволяла искать ключи длиннее чем можно поместить в таблицу,
что при поиске/позиционировании не является ошибкой для ключей переменного размера.
2024-12-27 09:39:36 +03:00
Леонид Юрьев (Leonid Yuriev)
42561e3b8e mdbx: удаление лишних проверок внутри mdbx_dbi_close() (backport). 2024-12-22 19:35:12 +03:00
Леонид Юрьев (Leonid Yuriev)
98b28213ce mdbx: корректировка ChangeLog. 2024-12-22 18:30:58 +03:00
Леонид Юрьев (Leonid Yuriev)
214f5d4de4 mdbx: дополнение README. 2024-12-22 18:30:38 +03:00
Леонид Юрьев (Leonid Yuriev)
00c5bbcc5e mdbx: дополнение ChangeLog. 2024-12-22 10:39:49 +03:00
Леонид Юрьев (Leonid Yuriev)
471b14a147 mdbx-tests: проверка случая повторного закрытия dbi-хендла. 2024-12-22 10:39:49 +03:00
Леонид Юрьев (Leonid Yuriev)
bfc6795762 mdbx: устранение регресса не-отпускания мьютекса при попытки повторного закрытия dbi-хендла.
Ошибка была внесена 2024-10-23 коммитом v0.13.1-35-g3049bb87b5b14d83b16d121c186ce8fb3f21383e.
2024-12-22 10:39:49 +03:00
Леонид Юрьев (Leonid Yuriev)
a76e06a48e mdbx: исправление несогласованности MDBX_DPL_PREALLOC_FOR_RADIXSORT и assert-проверки внутри dpl_bytes2size(). 2024-12-22 10:39:49 +03:00
Леонид Юрьев (Leonid Yuriev)
b9e4c1ea73 mdbx: вычленение txl_contain(). 2024-12-22 10:39:49 +03:00
Леонид Юрьев (Leonid Yuriev)
bc56a613ab mdbx: чистка исходников pnl/dpl/txl. 2024-12-22 10:39:49 +03:00
Леонид Юрьев (Leonid Yuriev)
225fb79eb2 mdbx: переименование repnl/retxl. 2024-12-22 10:39:49 +03:00
Леонид Юрьев (Leonid Yuriev)
ffb7918525 mdbx: понижение уровня логирования "reserve depleted" при обновлении GC. 2024-12-22 10:39:49 +03:00
Леонид Юрьев (Leonid Yuriev)
0339aa56d9 mdbx: перенос prefault_write_activated в транзакцию. 2024-12-22 10:39:49 +03:00
Леонид Юрьев (Leonid Yuriev)
4059686534 mdbx: опечатки в комментариях. 2024-12-22 10:39:49 +03:00
Леонид Юрьев (Leonid Yuriev)
ab57ce7d5f mdbx: добавление ссылки на новые привязки к Python. 2024-12-21 19:36:54 +03:00
Леонид Юрьев (Leonid Yuriev)
462af2be48 mdbx: исправление ошибки редактирования README. 2024-12-20 11:46:01 +03:00
Леонид Юрьев (Leonid Yuriev)
d3a27d27f6 mdbx-doc: добавление yandex-метрики в генерируемый html. 2024-12-19 17:49:40 +03:00
Леонид Юрьев (Leonid Yuriev)
0d1c08677d mdbx: корректировка ChangeLog. 2024-12-18 13:01:52 +03:00
Леонид Юрьев (Leonid Yuriev)
e43cf69a0c mdbx-doc: изменение базы ссылок online-перевода с gitflic.ru на libmdbx.dqdkfa.ru. 2024-12-18 12:27:37 +03:00
Леонид Юрьев (Leonid Yuriev)
891fa1d435 mdbx: корректировка doxygen-комментариев. 2024-12-18 01:25:50 +03:00
Леонид Юрьев (Leonid Yuriev)
5a9eea8acc mdbx: дополнение ChangeLog. 2024-12-18 01:21:00 +03:00
Леонид Юрьев (Leonid Yuriev)
e15079ec68 mdbx: изменение log_if_error() ради устранения ложных "may be used uninitialized" предупреждений в LTO-сборках.
При включении LTO анализатор путей выполнения внутри GCC начинает укачивать из-за выражений вида `return LOG_IFERR(MDBX_EINVAL);`

Проблема в том, что несмотря на __builtin_assume() и __builtin_unreachable(), комплятор не хочет
видеть что функция log_if_error() всегда возвращает получаемое значение. А если допустить что значение
будет изменено, то вместо ошибки может быть MDBX_SUCCESS, и тогда в вызывающем как-бы может произойти
обращение к неинициализированным данным, что и беспокоит компилятор.

Например, при сборке mdbx_load:
  ‘txn_info.txn_space_dirty’ may be used uninitialized [-Wmaybe-uninitialized]

Проэтому проще пойти анализатору навстречу и упростить исходный код.
Теперь код ошибки явно пробрасывается через тело inline-функции, но это
требует 1-2 дополнительных процессорных инструкции на каждое применение
макроса LOG_IFERROR.

Также здесь откатывается коммит 81a8127084.
2024-12-17 22:00:33 +03:00
Леонид Юрьев (Leonid Yuriev)
ba6df2bb6d mdbx: выделение API-функций в api-файлы. 2024-12-17 19:00:39 +03:00
Леонид Юрьев (Leonid Yuriev)
4607184999 mdbx: макрос osal_malloc_usable_size() вместо непосредственного использования malloc_usable_size(). 2024-12-17 18:58:44 +03:00
Леонид Юрьев (Leonid Yuriev)
5168c80be8 mdbx: сбор затрат на pnl_merge() при включении MDBX_ENABLE_PROFGC. 2024-12-17 18:54:58 +03:00
Леонид Юрьев (Leonid Yuriev)
6ed4dcb4ea mdbx: добавление отладочных сообщений при возврате ошибок из API. 2024-12-16 13:31:07 +03:00
Леонид Юрьев (Leonid Yuriev)
122562cf9c mdbx-tests: переименование опции data.dups в data.multi. 2024-12-16 12:16:11 +03:00
Леонид Юрьев (Leonid Yuriev)
526ed28de1 mdbx: добавление mdbx_cursor_count_ex() в API. 2024-12-16 11:54:24 +03:00
Леонид Юрьев (Leonid Yuriev)
90b187c3ba mdbx: добавление проверок в inner_hollow(). 2024-12-16 11:30:10 +03:00
Леонид Юрьев (Leonid Yuriev)
a845522db7 mdbx: исправление регресса состояния dupsort-курсора после cursor_put(APPEND).
При добавлении нового ключа в append-режиме, в случае когда в текущей
(последней) позиции с ключом связаны несколько значений и
(соответственно) вложенный dupsort-курсор инициализирован, вставка
происходила без сброса вложенного курсора.

В результате вложенный курсор логически оставался стоять на
multivalue-данных связанных с предыдущей позицией основного курсора,
т.е. переходил в неконсистентное состояние.

Ошибка проявлялась возвратом неверных значений из mdbx_cursor_count()
или срабатывание assert-проверки в отладочных сборках.
2024-12-15 22:17:12 +03:00
Леонид Юрьев (Leonid Yuriev)
c66dac50c3 mdbx: доработка osal_bootid() для LXC.
Из LXC-контейнера не доступен файл хостовой системы "/proc/sys/kernel/random/boot_id".
Вместо него, при каждом старте контейнера, создается и заполняется
случайными данными собственный boot_id смонтированный через bind из tmpfs.
https://github.com/lxc/lxc/issues/3027

Поэтому полноценный контроль по boot_id не возможен, так как при
рестарте LXC-контейнера (но не хоста) boot_id будет меняться, хотя
данные в unified page cache сохраняются.

Таким образом, при рестарте LXC-контейнера, libmdbx будет производить
откат БД до крайней точки устойчивой фиксации, что может приводить к
утрате данных пользователя в случаях когда они могли быть сохранены.
Однако, улучшить ситуацию пока не представляется возможным, как минимум
до доступности boot_id хостовой системы изнутри LXC-контейнера.

Этот коммит частично улучшает ситуацию тем, что позволяет использовать
фейковый/замещенный boot_id размещенный в файловой системе с типом tmpfs
при работе внутри LXC-контейнера.
2024-12-13 22:30:40 +03:00
Леонид Юрьев (Leonid Yuriev)
ccdb6255e9 mdbx: возврат MDBX_EINVAL при попытке запустить вложенную читающую транзакцию. 2024-12-13 08:26:55 +03:00
Леонид Юрьев (Leonid Yuriev)
9803259cab mdbx: возврат MDBX_EINVAL при попытке запустить вложенную читающую транзакцию (backport). 2024-12-13 08:16:04 +03:00
Леонид Юрьев (Leonid Yuriev)
ea3f99f58f mdbx-cmake: удаление add_mdbx_option(). 2024-12-12 13:07:49 +03:00
Леонид Юрьев (Leonid Yuriev)
513518ca5e mdbx-cmake: синхронизация утилит между проектами. 2024-12-12 13:06:44 +03:00
Леонид Юрьев (Leonid Yuriev)
f2dc60aa53 mdbx-cmake: новые настройки cmake-format (косметика). 2024-12-12 11:20:34 +03:00
Леонид Юрьев (Leonid Yuriev)
b687e835e9
mdbx: выпуск 0.13.2 "Прошлогодний Снег" (Last Year's Snow).
Поддерживающий выпуск с исправлением обнаруженных ошибок и устранением недочетов
в день рождения и в память об [Алекса́ндре Миха́йловиче Тата́рском](https://ru.wikipedia.org/wiki/Татарский,_Александр_Михайлович),
российском режиссёре-мультипликаторе, создавшем такие знаменитые мультфильмы как "Падал прошлогодний снег",
"Пластилиновая ворона", заставку "Спокойной ночи, малыши!" и многие другие шедевры нашего детства.

За перечнем доработок и изменений обращайтесь к [ChangeLog](https://libmdbx.dqdkfa.ru/md__change_log.html).

git diff' stat: 151 files changed, 10647 insertions(+), 14952 deletions(-)
Signed-off-by: Леонид Юрьев (Leonid Yuriev) <leo@yuriev.ru>
2024-12-11 21:51:56 +03:00
Леонид Юрьев (Leonid Yuriev)
8867c2ddc2 mdbx: новые настройки clang-format (косметика). 2024-12-11 21:22:04 +03:00
Леонид Юрьев (Leonid Yuriev)
3c4d019d00 mdbx-cmake: явное ракрытие переменных в if-условиях для обхода бага-фичи CMake. 2024-12-11 21:04:00 +03:00
Леонид Юрьев (Leonid Yuriev)
dbf18b4c22 mdbx-make: исправление подстановки переменных SemVer. 2024-12-11 21:02:50 +03:00
Леонид Юрьев (Leonid Yuriev)
5652b360b9 mdbx: незначительные правки README. 2024-12-11 15:04:49 +03:00
Леонид Юрьев (Leonid Yuriev)
529b0357e8 mdbx-cmake: исправление ошибки при copy&paste. 2024-12-10 17:06:45 +03:00
Леонид Юрьев (Leonid Yuriev)
fe627ed2f2 mdbx: дополнение ChangeLog. 2024-12-08 09:33:08 +03:00
Леонид Юрьев (Leonid Yuriev)
7aed3a7609 mdbx: дополнение и корректировка README. 2024-12-08 09:33:08 +03:00
Леонид Юрьев (Leonid Yuriev)
1566a0006c mdbx: исправление регресса в mdbx_env_stat_ex(). 2024-12-07 19:04:27 +03:00
Леонид Юрьев (Leonid Yuriev)
9481c0e5c4 mdbx: опечатки/орфография. 2024-12-06 23:56:13 +03:00
Леонид Юрьев (Leonid Yuriev)
207ad31257 mdbx: добавление в README упоминаний о поддержке Conan. 2024-12-06 20:36:48 +03:00
Леонид Юрьев (Leonid Yuriev)
3a09b8fb0c mdbx: координаты для спонсирования в ETH.
// Пока Positive Technologies, пора заняться делом.
2024-12-06 18:44:41 +03:00
Леонид Юрьев (Leonid Yuriev)
d1b2ec0489 mdbx-cmake: удаление двойной проверки версии MSVC. 2024-12-04 23:50:33 +03:00
Леонид Юрьев (Leonid Yuriev)
367a118a8f mdbx: дополнение ChangeLog. 2024-12-03 00:23:37 +03:00
Леонид Юрьев (Leonid Yuriev)
e2ca81ae83 mdbx: поддержка Conan. 2024-12-03 00:23:37 +03:00
Леонид Юрьев (Leonid Yuriev)
44865dadc7 mdbx: переименование и доработка опций сборки. 2024-12-03 00:23:37 +03:00
Леонид Юрьев (Leonid Yuriev)
35177611d2 mdbx: исправление сборки при MDBX_ENABLE_DBI_SPARSE=OFF. 2024-12-03 00:11:28 +03:00
Леонид Юрьев (Leonid Yuriev)
99fa43f322 mdbx: удаление устаревшего CMakeSettings.json 2024-12-02 18:10:32 +03:00
Леонид Юрьев (Leonid Yuriev)
acb3cb0290 mdbx: исправление сборки при включении профилирования GC (опция MDBX_ENABLE_PROFGC). 2024-12-02 17:40:07 +03:00
Леонид Юрьев (Leonid Yuriev)
5327f42465 mdbx: дополнение ChangeLog. 2024-11-28 20:07:27 +03:00
Леонид Юрьев (Leonid Yuriev)
4c5be88038 mdbx-cmake: исправление semver_provide() для случая символических ссылок в путях. 2024-11-28 14:54:55 +03:00
Леонид Юрьев (Leonid Yuriev)
ad0b374eb5 mdbx: добавление MDBX_MAYBE_UNUSED для log_if_error(). 2024-11-28 00:05:36 +03:00
Леонид Юрьев (Leonid Yuriev)
c716531bd4 mdbx-cmake: использование CMAKE_C_STANDARD при выборе стандарта C. 2024-11-27 23:25:41 +03:00
Леонид Юрьев (Leonid Yuriev)
9d79d2ba95 mdbx: дополнение ChangeLog. 2024-11-27 21:17:49 +03:00
Леонид Юрьев (Leonid Yuriev)
76c9b42e86 mdbx: исправление GET_MULTIPLE для специальных случаев и/или одного значения. 2024-11-27 18:28:39 +03:00
Леонид Юрьев (Leonid Yuriev)
81a8127084 mdbx: устранение "may be used uninitialized" предупреждений в LTO-сбрках из-за усложнения SSA/CTF вследствие добавления LOG_IFERR(). 2024-11-27 18:28:39 +03:00
Леонид Юрьев (Leonid Yuriev)
28bd805ed8 mdbx: возможность логирования ошибок возвращаемых из API (return LOG_IFERR).
Возможность полезная, но пожалуй еще нуждается в доработке и/или
до-осмыслении. Основное неудобство в нестыковке с основным логированием.

С одной стороны, сообщение об ошибках следует выводить с
уровнем/severity MDBX_LOG_ERROR. Однако, это замусоривает и ломает
тесты.

Поэтому сейчас при возвращении ошибок из API сообщения логируются
MDBX_LOG_ERROR, но производится это только при включении уровня
логирования MDBX_LOG_DEBUG или более детальном.
2024-11-27 12:08:32 +03:00
Леонид Юрьев (Leonid Yuriev)
e754b442a2 mdbx: исправление GET_MULTIPLE для случая одного значения.
Регрессия была внесена коммитом d94f34b2c0.
2024-11-26 15:49:45 +03:00
Леонид Юрьев (Leonid Yuriev)
9daff17c82 mdbx: поддержка Semantic Versioning.
Было `MAJOR.MINOR.RELEASE.REVISION`
Теперь `MAJOR.MINOR.PATCH[.TWEAK][-PRERELEASE][+BUILDMETADATA]`

https://semver.org/

 - вместо квартета `MAJOR.MINOR.RELEASE.REVISION`
   триплет c опцинальным четвертым членом `MAJOR.MINOR.PATCH[.TWEAK]`

 - `TWEAK` не входит в тег git, а формируется автоматически и
   соответствует кол-ву коммитов после тега git и опускается если 0.

 - Поле `PRERELEASE` опционально и переносится в версию из тега git.

 - Поле `BUILDMETADATA` опционально, не входит в тег git, а
   добавляется во время сборки если задана опцией `MDBX_BUILD_METADATA`.
2024-11-24 20:46:21 +03:00
Леонид Юрьев (Leonid Yuriev)
bcf0a1273f mdbx-make: добавление .WAIT для устранения коллизий при распараллеливании сборки. 2024-11-24 19:59:40 +03:00
Леонид Юрьев (Leonid Yuriev)
6508bd5a97 mdbx-cmake: включение CMP0054. 2024-11-24 18:42:53 +03:00
Леонид Юрьев (Leonid Yuriev)
3110c2206f mdbx: дополнение ChangeLog. 2024-11-23 01:12:55 +03:00
Леонид Юрьев (Leonid Yuriev)
652587b33f mdbx: добавление проверки и использования __deprecated_enum. 2024-11-23 01:10:33 +03:00
Леонид Юрьев (Leonid Yuriev)
aa3b39d9ed mdbx: исправление потенциального повторного определения __has_exceptions_disabled. 2024-11-23 01:10:33 +03:00
Леонид Юрьев (Leonid Yuriev)
b6a851b3d6 mdbx-testing: добавление extra/probe.c++ просто для проверки компилируемости. 2024-11-23 01:10:33 +03:00
Леонид Юрьев (Leonid Yuriev)
8369b8ff64 mdbx-cmake: перемещение add_extra_test(). 2024-11-23 01:10:33 +03:00
Леонид Юрьев (Leonid Yuriev)
2194349644 mdbx: устранение зацикливания обновления GC при фиксации транзакций.
В продолжение 6c56ed97bb, включая исправление регрессов.
2024-11-22 20:14:45 +03:00
Леонид Юрьев (Leonid Yuriev)
1c9c49dd1a mdbx-build: поддержка переменной среды SOURCE_DATE_EPOCH в качестве MDBX_BUILD_TIMESTAMP для воспроизводимости сборок. 2024-11-22 20:14:45 +03:00
Леонид Юрьев (Leonid Yuriev)
881d4d4207 mdbx-build: добавление build-metadata и опции сборки MDBX_BUILD_METADATA. 2024-11-22 20:14:45 +03:00
Леонид Юрьев (Leonid Yuriev)
2b71df417e mdbx-windows: использование ntdll вместо CRT только при явном отключении C++ API.
Изменение поведения по-умолчанию, но без утраты контроля.

Без изменения:
  Определение опции MDBX_WITHOUT_MSVC_CRT в значение 0 или 1 позволяет явно выбирать между использование ntdll и CRT.
  При этом включение C++ API (MDBX_BUILD_CXX=1) требует использования CRT.

Ранее:
  По-умолчанию, когда не определены опции MDBX_WITHOUT_MSVC_CRT и MDBX_BUILD_CXX, делался выбор в пользу использования ntdll, вместо CRT.

Теперь:
  Функции ntdll будет использоваться вместо CRT только если явно выключена поддержка C++ API (задано MDBX_BUILD_CXX=0).
2024-11-21 19:47:26 +03:00
Леонид Юрьев (Leonid Yuriev)
5815ff2ef7 mdbx: переделка костыля namespace::attr для MSVC и Apple. 2024-11-21 19:47:26 +03:00
Леонид Юрьев (Leonid Yuriev)
ddea36c54a mdbx: освобождение памяти сброшенных/прерванных читающих транзакций передаваемых в mdbx_txn_commit().
Исторически в API была слабость/неоднозначность в жизненном цикле читающих транзакций:

 - В простейших сценариях читающие транзакции запускались посредством
   mdbx_txn_begin() и завершались посредством mdbx_txn_abort(), либо mdbx_txn_commit();

 - Для экономии накладных расходов были предусмотрены функции
   mdbx_txn_reset() и mdbx_txn_renew(), которые сбрасывали/прерывали
   читающую транзакцию без её освобождения/разрушения и затем перезапускали её.
   При этом транзакции сброшенные посредством mdbx_txn_reset() должны были
   быть либо перезапущены, либо освобождены посредством mdbx_txn_abort();

 - Заминка возникала при вызове mdbx_txn_commit() для читающих
   транзакций сброшенных/прерванных посредством mdbx_txn_reset().
   В таких ситуациях возвращалась ошибка MDBX_BAD_TXN, а транзакция
   не освобождалась.

Такое поведение вносило лишнюю асимметрию в API и способствовало
появлению ошибок утечки ресурсов, но поддерживалось для совместимости.

Этот коммит изменяет историческое поведение с нарушением совместимости,
но делает API более регулярным и уменьшает вероятность ошибок утечки
ресурсов.

Теперь mdbx_txn_commit() освобождает/разрушает читающие транзакции
сброшенные/прерванные посредством mdbx_txn_reset() возвращая при этом
MDBX_RESULT_TRUE вместо MDBX_SUCCESS, по аналогии обработки фиксации
аварийных пишущих транзакций.
2024-11-17 22:52:07 +03:00
Леонид Юрьев (Leonid Yuriev)
efaa46d7cd mdbx: предотвращение незначащих, но мешающих отладке, ошибок внутри copy2fd(). 2024-11-17 22:52:07 +03:00
Леонид Юрьев (Leonid Yuriev)
92dec0bca9 mdbx: исправление утечки памяти из-за регресса в txn_end() при добавлении парковки транзакций.
Если читающая транзакция была припаркована и затем вытеснена, то при её
завершении ресурсы не освобождались.
2024-11-17 22:52:07 +03:00
Леонид Юрьев (Leonid Yuriev)
c13efb791f mdbx-testing: логирование флагов/опций в copy-сценарии. 2024-11-17 22:52:07 +03:00
Леонид Юрьев (Leonid Yuriev)
12442bd1f4 mdbx-testing: корректировка контроля результата в copy-сценарии. 2024-11-17 22:51:56 +03:00
Леонид Юрьев (Leonid Yuriev)
f5b1e36b9e mdbx-testing: устранение лишней установки prng при завершении цикла тестов. 2024-11-17 10:29:54 +03:00
Леонид Юрьев (Leonid Yuriev)
f32d3f260f mdbx: безусловное прерывание транзакции при опции MDBX_CP_DISPOSE_TXN. 2024-11-16 11:18:47 +03:00
Леонид Юрьев (Leonid Yuriev)
47f96b6afa mdbx: дополнение ChangeLog. 2024-11-15 15:58:48 +03:00
Леонид Юрьев (Leonid Yuriev)
0306ba8136 mdbx-dist: отключение clang-format в амальгамированном исходном коде. 2024-11-13 19:16:26 +03:00
Леонид Юрьев (Leonid Yuriev)
a2984c604d mdbx-cmake: переформатирование cmake-скриптов. 2024-11-13 15:05:26 +03:00
Леонид Юрьев (Leonid Yuriev)
5862a4b542 mdbx-cmake: исправление упущенных POST_BUILD в add_custom_command(). 2024-11-13 15:05:26 +03:00
Леонид Юрьев (Leonid Yuriev)
e2b4245abe mdbx: дополнение .gitignore 2024-11-13 15:05:26 +03:00
Леонид Юрьев (Leonid Yuriev)
b5def26565 mdbx-make: добавление целей cmake-build и ninja. 2024-11-13 15:05:26 +03:00
Леонид Юрьев (Leonid Yuriev)
e27537dd9d mdbx-make: добавление префикса @ к именам служебных/временных файлов/каталогов. 2024-11-13 15:05:26 +03:00
Леонид Юрьев (Leonid Yuriev)
f550c65476 mdbx-make: использование VERSION.json с полной информацией вместо однострочного текстового файла. 2024-11-13 15:05:26 +03:00
Леонид Юрьев (Leonid Yuriev)
ab4bf2d7f0 mdbx-cmake: экспорт/импорт информации о версии в VERSION.json 2024-11-13 15:05:26 +03:00
Леонид Юрьев (Leonid Yuriev)
871bb7f56c mdbx: обмен порядка атрибутов pure|const/maybe_unused в определении функций. 2024-11-13 15:05:26 +03:00
Леонид Юрьев (Leonid Yuriev)
7aa5d9ab97 mdbx++: доработка использования std::experimental::filesystem. 2024-11-13 15:05:26 +03:00
Леонид Юрьев (Leonid Yuriev)
6893a79c70 mdbx-testing: добавление extra/cursor_closing. 2024-11-10 20:17:44 +03:00
Леонид Юрьев (Leonid Yuriev)
6f41276dbc mdbx++: поддержка вложенных пишущих транзакций. 2024-11-10 20:17:44 +03:00
Леонид Юрьев (Leonid Yuriev)
9da743515c mdbx-cmake: включение тестов использующих mdbx::path на Windows только для C++17 и выше. 2024-11-10 20:17:44 +03:00
Леонид Юрьев (Leonid Yuriev)
9b9d6c6d65 mdbx-cmake: очистка условий включения стандартов C и C++. 2024-11-10 20:17:44 +03:00
Леонид Юрьев (Leonid Yuriev)
bd7b272bca mdbx-tests: корректировка использования mdbx::default_buffer для совместимости с C++11. 2024-11-10 20:17:44 +03:00
Леонид Юрьев (Leonid Yuriev)
4cc1c7d8de mdbx: доработка MDBX_DEPRECATED_ENUM для старых компиляторов при включении С++11. 2024-11-10 20:17:44 +03:00
Леонид Юрьев (Leonid Yuriev)
10a93f4b9f mdbx: дополнение ChangeLog. 2024-11-07 11:40:16 +03:00
Леонид Юрьев (Leonid Yuriev)
ca8e9fe7b1 mdbx-testing: переименование скрипта stochastic.sh 2024-11-07 09:32:27 +03:00
Леонид Юрьев (Leonid Yuriev)
c0e5108d71 mdbx-testing: реализация/перенос поддержки сценария мелких транзакций в long_stochastic скрипт. 2024-11-07 09:32:27 +03:00
Леонид Юрьев (Leonid Yuriev)
00be608af9 mdbx-testing: проверка версии bash >= 4.3 2024-11-07 09:32:27 +03:00
Леонид Юрьев (Leonid Yuriev)
6c56ed97bb mdbx: доработка/исправление и постоянная активация корректирующей обратной связи при обновлении GC.
При обновлении GC, с помещением/возвратом страниц, возникает рекурсивная
зависимость, так как страницы, необходимые для CoW-модификации GC и
размещения списков возвращаемых страниц, берутся/выделяются из этих-же
списков и/или из GC. Эта рекуррентная зависимость разрешается путём
подготовки необходимого запаса страниц и двух-стадийным заполнением
списков, с повторением всего цикла при изменении ситуации/расклада, плюс
применение некоторых эвристик и поправок. Кроме корректной работы,
принципиально важным тут является минимизация количества
повторов/рестартов процесса, в том числе исключение возможности
бесконечного зацикливания.

Существующая реализация многократно/итеративно дорабатывалась. Поэтому
она неплохо обкатана и стабильна, но одновременно сложна и запутана.

Тем не менее, до последнего момента для текущей реализации были известны
условия/сценарии, в которых сходимость итеративного процесса обновления
GC нарушалась и при фиксации транзакции возвращалась ошибка
MDBX_PROBLEM. Эти условия/сценарии очень специфичны и далеки от реальных
практических случаев, поэтому этот недостаток не мешал использованию
библиотеки.

Этим коммитом добавляется и активируется еще один механизм нацеленный на
улучшение сходимости и минимизацию повторов/рестартов. Суть механизма в
формировании и учета поправки, которая на следующем цикле позволит
учесть все переходные процессы/затраты вне зависимости от их природы, и
этим обеспечить моментальную сходимость.

В текущем понимании, описанный выше недостаток полностью
устраняется/исправляется этим коммитом.
2024-11-07 09:32:27 +03:00
Леонид Юрьев (Leonid Yuriev)
acb15790b4 mdbx-testing: косметика в long_stochastic.sh для уменьшения объема вывода в консоль. 2024-11-06 13:43:40 +03:00
Леонид Юрьев (Leonid Yuriev)
029f14280b mdbx-testing: удаление /usr/bin/time так как rusage() есть в коде теста. 2024-11-06 13:15:25 +03:00
Леонид Юрьев (Leonid Yuriev)
af41bcf11e mdbx-testing: исправление опечатки для /usr/bin/banner. 2024-11-06 13:15:25 +03:00
Леонид Юрьев (Leonid Yuriev)
2669f285f9 mdbx-testing: проверка поддержки tee -p. 2024-11-06 13:14:58 +03:00
Леонид Юрьев (Leonid Yuriev)
157ede4e42 mdbx-testing: добавление опции --report-depth для сокращения накладных расходов. 2024-11-06 13:14:58 +03:00
Леонид Юрьев (Leonid Yuriev)
6067ba5f9d mdbx-testing: минимальная обработка SIGINT/SIGTERM/SIGHUP/SIGQUIT для прозрачности прерываний в логах. 2024-11-06 13:14:58 +03:00
Леонид Юрьев (Leonid Yuriev)
2c919c0efe mdbx-testing: предотвращение потери логов из-за отстрела gzip/lz4 в составе группы процессов. 2024-11-06 13:14:58 +03:00
Леонид Юрьев (Leonid Yuriev)
de36d94aca mdbx: предотвращение включения отладки только из-за активации assert-проверок. 2024-11-04 20:42:39 +03:00
Леонид Юрьев (Leonid Yuriev)
8571eac81b mdbx-cmake: корректировка пробы OpenMP. 2024-10-28 08:55:37 +03:00
Леонид Юрьев (Leonid Yuriev)
dc6f29a046 mdbx: импорт дополнений ChangeLog из ветки stable. 2024-10-27 23:00:34 +03:00
Леонид Юрьев (Leonid Yuriev)
90642bffab mdbx: импорт обновления патча для старых версий buildroot из ветки stable. 2024-10-27 22:59:20 +03:00
Леонид Юрьев (Leonid Yuriev)
6cfb2935f6 mdbx: обновление патча для старых версий buildroot. 2024-10-27 22:38:34 +03:00
Леонид Юрьев (Leonid Yuriev)
b16c2570f0
mdbx: выпуск 0.12.12 "Доллежаль".
Поддерживающий выпуск с исправлением обнаруженных ошибок и устранением недочетов,
в память о советском ученом-энергетике Николае Антоновиче Доллежаль в день 125-летия со дня его рождения.

Это последний выпуск куста стабильных версий 0.12.x, спустя более двух
лет после выпуска 0.12.1. Последующие выпуски 0.12.x будут формироваться
только в случае существенных проблем/ошибок, вероятность чего близка к
нулю. Для всех проектов находящихся в стадии активной разраборки
рекомендуется использовать ветку `master`.

Значимые исправления:
---------------------

 - Исправление упущенного `TXN_END_EOTDONE` при сбое старта читающей транзакции.
   Упомянутый флажок отсутствовал в пути разрушения транзакции при ошибке
   её запуска. Из-за чего делалась попытка разрушить курсоры, что приводило
   к падению **отладочных сборок**, так как в них соответствующий массив
   намеренно заполнен некорректными указателями.

 - Устранение возможности `SIGSEGV` внутри `coherency_check()` после
   изменения геометрии другим процессом с увеличением верхнего размера БД
   и увеличением БД больше предыдущего лимита.

 - Доработка `mdbx_close_dbi()` для возврата ошибки при попытке закрыть
   dbi-дескриптор таблицы, созданной и/или измененной в ещё выполняющейся
   транзакции. Такое преждевременное закрытие дескриптора является неверным
   использованием API и нарушением контракта/предусловий сформулированных
   в описании `mdbx_close_dbi()`. Однако, вместо возврата ошибки
   выполнялось некорректное закрытие дескриптора, что могло приводить к
   созданию таблицы с пустым именем, утечки страниц БД и/или нарушению
   структуры b-tree (неверной ссылкой на корень таблицы).
   Добавлен соответствующий тест `extra/early_close_dbi`.

Более подробная информация и история предыдущих выпусков доступна в [ChangeLog](https://libmdbx.dqdkfa.ru/md__change_log.html).

git diff' stat: 6 commits, 5 files changed, 239 insertions(+), 6 deletions(-)
Signed-off-by: Леонид Юрьев (Leonid Yuriev) <leo@yuriev.ru>
2024-10-27 11:07:49 +03:00
Леонид Юрьев (Leonid Yuriev)
b8f9b8659c mdbx: дополнение ChangeLog. 2024-10-26 23:23:17 +03:00
Леонид Юрьев (Leonid Yuriev)
ac8402283f mdbx-testing: добавление extra/early_close_dbi (backport). 2024-10-26 23:10:56 +03:00
Леонид Юрьев (Leonid Yuriev)
945899e4fd mdbx: доработка mdbx_close_dbi() для возврата ошибки при попытке закрыть dbi-хендл измененной в транзакции таблицы (backport). 2024-10-26 23:01:37 +03:00
Леонид Юрьев (Leonid Yuriev)
222150bb28 mdbx-testing: добавление теста пересоздания таблицы с другими флагами/опциями (backport). 2024-10-26 23:01:01 +03:00
Леонид Юрьев (Leonid Yuriev)
3d6e196422 mdbx: исправление отрытия таблицы с пустым/нулевым именем и устранение SIGSEGV при её закрытии. 2024-10-26 09:57:10 +03:00
Леонид Юрьев (Leonid Yuriev)
03077773cb mdbx-testing: добавление extra/early_close_dbi. 2024-10-26 09:57:10 +03:00
Леонид Юрьев (Leonid Yuriev)
5dc1f36b67 mdbx++: смена базового типа на intptr_t для размерных констант mdbx::env::geometry. 2024-10-26 09:49:22 +03:00
Леонид Юрьев (Leonid Yuriev)
80708f9591 mdbx: добавление T-макросов для парных char/wchar_t функций. 2024-10-26 09:49:22 +03:00
Леонид Юрьев (Leonid Yuriev)
3049bb87b5 mdbx: доработка mdbx_close_dbi() для возврата MDBX_DANGLING_DBI при попытке закрыть dbi-хендл измененной в транзакции таблицы. 2024-10-26 09:49:22 +03:00
Леонид Юрьев (Leonid Yuriev)
7232d7b5fc mdbx: уточнение описания mdbx_dbi_close() для случая хендлов измененных таблиц. 2024-10-23 13:28:26 +03:00
Леонид Юрьев (Leonid Yuriev)
964ee00116 mdbx: костыль для некорректной обработки [[gnu::pure]] в Apple Clang. 2024-10-23 11:26:09 +03:00
Леонид Юрьев (Leonid Yuriev)
b43eed2c2b mdbx++: переименование внутренних методов mdbx::buffer<>::silo::bin::inplace_lastbyte(). 2024-10-22 22:45:29 +03:00
Леонид Юрьев (Leonid Yuriev)
06dd50580c mdbx: корректировка API-макросов для Doxygen. 2024-10-22 22:45:29 +03:00
Леонид Юрьев (Leonid Yuriev)
bfce1cd24d mdbx: использование __has_c_attribute() и __has_cxx_attribute(), добавление __has_C23_or_CXX_attribute(). 2024-10-10 06:16:49 +03:00
Леонид Юрьев (Leonid Yuriev)
22233b0991 mdbx: перемещение MDBX_NORETURN в прототипах assert-failed для нового clang. 2024-10-10 06:15:17 +03:00
Леонид Юрьев (Leonid Yuriev)
d2b74e4da5 mdbx-cmake: включение стандарта C23. 2024-10-10 06:15:17 +03:00
Леонид Юрьев (Leonid Yuriev)
d40e4db13a mdbx: дополнение ChangeLog. 2024-10-08 18:14:15 +03:00
Леонид Юрьев (Leonid Yuriev)
57848b1d2d mdbx-testing: добавление логирования С++ исключений в extra/dupfix_multiple. 2024-10-08 18:13:29 +03:00
Леонид Юрьев (Leonid Yuriev)
ca2dbf0933 mdbx-testing: уменьшение кол-ва итераций extra/crunched-delete для CI. 2024-10-08 18:11:16 +03:00
Леонид Юрьев (Leonid Yuriev)
ecf862a4f6 mdbx: доработка osal_jitter() для уменьшения задержек в тестах под Windows. 2024-10-08 18:11:16 +03:00
Leonid Yuriev
bf58ec59f5 mdbx: допущение 4-байтового выравнивания данных MDBX_MULTIPLE для 32-битных сборок.
На 32-битных платформах элементы массивов 64-битных типов могут быть
выравнены на 4-байтовую границу. Из-за этого `mdbx_put(MDBX_MULTIPLE)`
могла возвращать ошибку `MDBX_BAD_VALSIZE`, считая что переданные
пользователем данные не выровнены.
2024-10-08 18:11:12 +03:00
Leonid Yuriev
486fb3c36d mdbx-testing: исправление максимальной длины значений в extra/crunched-delete. 2024-10-08 00:06:13 +03:00
Леонид Юрьев (Leonid Yuriev)
ce579bcb8e mdbx-testing: добавление extra/open. 2024-10-07 09:12:07 +03:00
Леонид Юрьев (Leonid Yuriev)
b11998de01 mdbx-cmake: добавление dll-костыля для Windows для работы исключений в тестах на C++. 2024-10-07 09:09:35 +03:00
Леонид Юрьев (Leonid Yuriev)
54dfc1f16d mdbx-testing: унификация extra-тестов и интеграция в ctest. 2024-10-07 09:05:08 +03:00
Леонид Юрьев (Leonid Yuriev)
0178d5b5c8 mdbx-testing: уменьшение кол-ва итераций extra/crunched_delete для Windows.
До этих изменений тесты на CI могли длиться несколько часов и завершаться по таймауту, что неприемлемо.
2024-10-07 09:05:08 +03:00
Леонид Юрьев (Leonid Yuriev)
9fa76a56fc mdbx: добавление #ifdef для iPhone. 2024-09-28 08:22:14 +03:00
Леонид Юрьев (Leonid Yuriev)
42ca4edec8 mdbx: дополнение ChangeLog. 2024-09-18 21:25:10 +03:00
Леонид Юрьев (Leonid Yuriev)
c96714423d mdbx-cmake: использование WIN32 вместо ${CMAKE_SYSTEM_NAME}. 2024-09-18 21:19:27 +03:00
Леонид Юрьев (Leonid Yuriev)
c964523978 mdbx-testing: добавление get_multiple_samelength() в extra/dupfix_multiple. 2024-09-18 21:19:27 +03:00
Леонид Юрьев (Leonid Yuriev)
ec41ec1561 mdbx++: добавление mdbx::cursor::get_multiple_samelength(). 2024-09-18 21:19:27 +03:00
Леонид Юрьев (Leonid Yuriev)
07309427fd mdbx++: переименование mdbx::txn::put_multiple_samelength(). 2024-09-18 21:19:27 +03:00
Леонид Юрьев (Leonid Yuriev)
f738552721 mdbx: возвращение ключа при MDBX_GET_MULTIPLE для единообразия C++ API. 2024-09-18 21:19:27 +03:00
Леонид Юрьев (Leonid Yuriev)
29d0a96818 mdbx: исправление условия внутри assert() в пути обработки MDBX_GET/NEXT/PREV_MULTIPLE. 2024-09-18 21:14:43 +03:00
Леонид Юрьев (Leonid Yuriev)
202cdbc4be mdbx-testing: подавление параноидальных предупреждений MSVC в extra-тестах. 2024-09-14 20:33:35 +03:00
Леонид Юрьев (Leonid Yuriev)
14a55ee244 mdbx++: подавление параноидального предупреждения MSVC. 2024-09-14 20:33:35 +03:00
Леонид Юрьев (Leonid Yuriev)
74f7d13455 mdbx: дополнение отладочного логирования внутри dxb_resize(). 2024-09-14 20:33:35 +03:00
Леонид Юрьев (Leonid Yuriev)
2e14404837 mdbx: дополнение ChangeLog. 2024-09-10 08:49:16 +03:00
Леонид Юрьев (Leonid Yuriev)
81807f16b2 mdbx: корректировка README. 2024-09-10 08:47:24 +03:00
Леонид Юрьев (Leonid Yuriev)
b36e3702e5 mdbx-doc: продолжение s/subDb/таблица/ в С++ API. 2024-09-09 09:22:50 +03:00
Леонид Юрьев (Leonid Yuriev)
c69f23ed70 mdbx: по-умолчанию MDBX_ENABLE_BIGFOOT=1 вне зависимости от разрядности платформы. 2024-09-09 00:09:17 +03:00
Леонид Юрьев (Leonid Yuriev)
fcc4748f23 mdbx: исправление опечатки/орфографии. 2024-09-08 20:48:44 +03:00
Леонид Юрьев (Leonid Yuriev)
450c1081fa mdbx++: добавление упущенных inline-реализаций mdbx::cursor::upper_bound() и mdbx::cursor::upper_bound_multivalue(). 2024-09-08 11:36:10 +03:00
Леонид Юрьев (Leonid Yuriev)
5fc7a6b107
mdbx: выпуск 0.13.1 "РДС-1"
Новая версия со сменой лицензии, существенным расширением API,
добавлением функционала и внутренними переработками. В том числе,
с незначительным нарушением обратной совместимости API библиотеки.

Список нововведений, доработок и изменений слишком велик для размещения
здесь, но вся информация есть в файле
[ChangeLog](https://libmdbx.dqdkfa.ru/md__change_log.html).

```
git diff' stat: 157 files changed, 41949 insertions(+), 33741 deletions(-)
Signed-off-by: Леонид Юрьев (Leonid Yuriev) <leo@yuriev.ru>
```
2024-08-30 00:01:07 +03:00
Леонид Юрьев (Leonid Yuriev)
2c17c7b149 mdbx: дополнение ChangeLog. 2024-08-22 00:09:52 +03:00
Леонид Юрьев (Leonid Yuriev)
94936fd4c9 mdbx-testing: добавление теста пересоздания таблицы с другими флагами/опциями. 2024-08-22 00:04:56 +03:00
Леонид Юрьев (Leonid Yuriev)
a17b190dc8 mdbx: обновленный clang-format (косметика). 2024-08-22 00:04:41 +03:00
Леонид Юрьев (Leonid Yuriev)
b4dcf148c5 mdbx: исправление assert-проверки при попытке создания таблицы с другими флагами/опциями.
Сообщение о проблеме https://t.me/libmdbx/6101
2024-08-22 00:04:41 +03:00
Леонид Юрьев (Leonid Yuriev)
2b5d7ed29a mdbx: вывод номера транзакции при ситуации зацикливания внутри gc_update(). 2024-08-22 00:04:41 +03:00
Alexey Sharov
77a35608f6 mdbx-testing: увеличение размера БД до 32Гб (вдвое) в "tmux-батарейном" тесте. 2024-08-13 23:17:19 +03:00
Леонид Юрьев (Leonid Yuriev)
7511f480a4 mdbx-testing: уменьшение мощности "tmux-батарейного" теста вдвое (под "более типовую" виртуалку). 2024-08-13 23:15:34 +03:00
Леонид Юрьев (Leonid Yuriev)
0c9f531c72 mdbx-testing: добавление опции --db-upto-gb в стохастический скрипт. 2024-08-13 23:14:49 +03:00
Леонид Юрьев (Leonid Yuriev)
f477fa13e0 mdbx-testing: включение ulimit -c unliminted в стохастическом скрипте. 2024-08-13 23:13:58 +03:00
Леонид Юрьев (Leonid Yuriev)
b054a69e72 mdbx: исправление и рефакторинг цикла чтения мета-страниц при старте читающих транзакций.
Сценарий достаточно запутанный/сложный.
2024-08-11 09:44:32 +03:00
Леонид Юрьев (Leonid Yuriev)
aca692212f mdbx-testing: исправление упущенной опции --extra в battery-tmux. 2024-08-11 09:44:32 +03:00
Леонид Юрьев (Leonid Yuriev)
8cc5e8c262 mdbx: дополнение ChangeLog. 2024-08-08 00:17:09 +03:00
Леонид Юрьев (Leonid Yuriev)
497aabcb2e mdbx: устранение возможности SIGSEGV внутри coherency_check() (backport).
Падение происходило в случае когда:

 - Некоторый процесс увеличивал размер БД с изменением геометрии (с
   увеличением предельного размера БД и её отображения в ОЗУ), затем
   задействовал страницу из добавленного сегмента в качестве корневой для
   FreeDB/GC и/или MainDB и фиксировал транзакцию.

 - Другой процесс, уже работавший с БД до изменения геометрии первым
   процессом, запускал транзакцию чтения. Падение происходило при проверке
   «когерентности» отображения страниц БД в ОЗУ, при проверке отметок
   модификации внутри корневых страниц, так как в этом случае они были вне
   границ текущего отображения БД в адресном пространстве этого процесса.

 Похоже что в ходе какого-то рефакторинга потерялась соответствующая
 проверка. Этот коммит добавляет такую проверку.
2024-08-07 21:22:57 +03:00
Леонид Юрьев (Leonid Yuriev)
cf6d441e1b mdbx: исправление упущенного TXN_END_EOTDONE при сбое старта читающей транзакции (backport).
Упомянутый флажок отсутствовал в пути разрушения транзакции при ошибке
её запуска. Из-за чего делалась попытка разрушить курсоры, что приводило
к падению отладочных сборок, так как в них соответствующий массив
намеренно заполнен некорректными указателями.
2024-08-07 19:29:57 +03:00
Леонид Юрьев (Leonid Yuriev)
edfa526138 mdbx: устранение возможности SIGSEGV внутри coherency_check().
Падение происходило в случае когда:

 - Некоторый процесс увеличивал размер БД с изменением геометрии (с
   увеличением предельного размера БД и её отображения в ОЗУ), затем
   задействовал страницу из добавленного сегмента в качестве корневой для
   FreeDB/GC и/или MainDB и фиксировал транзакцию.

 - Другой процесс, уже работавший с БД до изменения геометрии первым
   процессом, запускал транзакцию чтения. Падение происходило при проверке
   «когерентности» отображения страниц БД в ОЗУ, при проверке отметок
   модификации внутри корневых страниц, так как в этом случае они были вне
   границ текущего отображения БД в адресном пространстве этого процесса.

Похоже что в ходе какого-то рефакторинга потерялась соответствующая
проверка. Этот коммит добавляет её как временное решение, до переноса
проверки «когерентности» после изменения размера отображения (добавлено в
TODO).
2024-08-07 17:38:53 +03:00
Леонид Юрьев (Leonid Yuriev)
7bff3b3df6 mdbx: корректировка имен и ссылок в ChangeLog. 2024-08-03 15:14:23 +03:00
Леонид Юрьев (Leonid Yuriev)
7b09ecd9b7 mdbx-doc: дополнение описания mdbx_env_copy() и родственных функций. 2024-08-03 15:14:23 +03:00
Леонид Юрьев (Leonid Yuriev)
7ed4a551f4 mdbx: внутренние переименования subDb в таблицы. 2024-08-03 15:14:23 +03:00
Леонид Юрьев (Leonid Yuriev)
57e558a57d mdbx: использование термина "таблица" вместо "sub-database". 2024-08-03 15:14:23 +03:00
Леонид Юрьев (Leonid Yuriev)
dd5329c164 mdbx-doc: корректировка/исправление разметки Doxygen. 2024-08-03 12:50:44 +03:00
Леонид Юрьев (Leonid Yuriev)
dd0ee3f278 mdbx: дополнение ChangeLog. 2024-08-03 00:30:06 +03:00
Леонид Юрьев (Leonid Yuriev)
ee8c9225d6 mdbx-testing: повтор сценария с mdbx_txn_copy2pathname() до получения успешной копии для предотвращения сбоев test/CMakeLists.txt. 2024-08-02 23:33:22 +03:00
Леонид Юрьев (Leonid Yuriev)
69f85af242 mdbx-tools: добавление опций -d и -p для mdbx_copy. 2024-08-02 23:33:22 +03:00
Леонид Юрьев (Leonid Yuriev)
9eef3c3541 mdbx-tools: исправление сравнения thread_id для припаркованных транзакций. 2024-08-02 23:33:22 +03:00
Леонид Юрьев (Leonid Yuriev)
a0e278ff00 mdbx++: добавление обработки MDBX_EDEADLK в С++ API. 2024-08-02 23:33:22 +03:00
Леонид Юрьев (Leonid Yuriev)
d21ae28bb9 mdbx++: добавление обработки MDBX_MVCC_RETARDED в C++ API. 2024-08-02 23:33:22 +03:00
Леонид Юрьев (Leonid Yuriev)
b6b126195b mdbx: исправление упущенного TXN_END_EOTDONE при сбое старта читающей транзакции.
Упомянутый флажок отсутствовал в пути разрушения транзакции при ошибке
её запуска. Из-за чего делалась попытка разрушить курсоры, что приводило
к падению отладочных сборок, так как в них соответствующий массив
намеренно заполнен некорректными указателями.
2024-08-02 23:33:22 +03:00
Леонид Юрьев (Leonid Yuriev)
4c0290b576 mdbx-testing: добавление mdbx_txn_copy2pathname() в тестовый сценарий. 2024-08-02 23:33:22 +03:00
Леонид Юрьев (Leonid Yuriev)
e7488bc30c mdbx: добавление mdbx_txn_copy2fd() и mdbx_txn_copy2pathname(), включая дополнительные опции. 2024-08-02 19:06:14 +03:00
Леонид Юрьев (Leonid Yuriev)
f34ebc853d mdbx-testing: добавление mdbx_dbi_sequence() в jitter-сценарий. 2024-08-02 01:11:09 +03:00
Леонид Юрьев (Leonid Yuriev)
0a9d96affd mdbx: устранения ложной ошибки не-когерентности при использовании mdbx_dbi_sequence(MAIN_DBI) без других изменений.
Временная подпорка для coherency_check(), которую в перспективе
следует заменить вместе с переделкой установки mod_txnid.

Суть проблемы:
 - coherency_check() в качестве одного из критериев "когерентности"
   проверяет условие meta.maindb.mod_txnid == maindb.root->txnid;
 - при обновлении maindb.sequence высталяется DBI_DIRTY, что приведет
   к обновлению meta.maindb.mod_txnid = current_txnid;
 - однако, если в само дерево maindb обновление не вносились и оно
   не пустое, то корневая страницы останеться с прежним txnid и из-за
   этого ложно сработает coherency_check().

Временное (текущее) решение: Принудительно обновляем корневую
страницу в описанной выше ситуации. Это устраняет проблему, но и
не создает рисков регресса.

Итоговое решение, которое предстоит реализовать:
 - изменить семантику установки/обновления mod_txnid, привязав его
   строго к изменению b-tree, но не атрибутов;
 - обновлять mod_txnid при фиксации вложенных транзакций;
 - для dbi-хендлов пользовательских subDb (видимо) можно оставить
   DBI_DIRTY в качестве признака необходимости обновления записи
   subDb в MainDB, при этом взводить DBI_DIRTY вместе с обновлением
   mod_txnid, в том числе при обновлении sequence.
 - для MAIN_DBI при обновлении sequence не следует взводить DBI_DIRTY
   и/или обновлять mod_txnid, а только взводить MDBX_TXN_DIRTY.
 - альтернативно, можно перераспределить флажки-признаки dbi_state,
   чтобы различать состояние dirty-tree и dirty-attributes.
2024-08-01 22:08:26 +03:00
Леонид Юрьев (Leonid Yuriev)
dc7f15c63e mdbx-tools: отображение статусов parked/ousted для транзакций. 2024-07-27 12:47:21 +03:00
Леонид Юрьев (Leonid Yuriev)
9e3a36b74d mdbx: корректировка txn_end() для устранения лишних MDBX_TXN_OUSTED. 2024-07-27 12:44:06 +03:00
Леонид Юрьев (Leonid Yuriev)
7873118cdb mdbx++: добавление (рас)парковки транзакций чтения в C++ API. 2024-07-24 19:58:02 +03:00
Леонид Юрьев (Leonid Yuriev)
2e7d325cf1 mdbx: добавление поддержки MDBX_OUSTED в mdbx_strerror() и C++ API. 2024-07-24 15:57:55 +03:00
Леонид Юрьев (Leonid Yuriev)
cb743d44fc mdbx: новый/поправленный clang-format. 2024-07-24 11:27:41 +03:00
Леонид Юрьев (Leonid Yuriev)
a430b3b288 mdbx: исправление опечатки 0x%u в логировании. 2024-07-24 11:22:59 +03:00
Леонид Юрьев (Leonid Yuriev)
485d6d1f50 mdbx: корректировка/актуализация ChangeLog для v0.13.x 2024-07-23 16:15:05 +03:00
Леонид Юрьев (Leonid Yuriev)
69aa9e0fe1 mdbx: устранение лишней итерации внутри tree_rebalance().
Допускаем итерацию с не-вовлечением еще не-измененных страниц,
только когда страницы для объединения доступны справа и слева,

Т.е. допускаем итерацию для выбора лучшей альтернативы (справа или слева),
и избегаем этой итерации когда альтернативы нет.
2024-07-23 16:07:27 +03:00
Леонид Юрьев (Leonid Yuriev)
9309aa7e12 mdbx: уточнение комментариев в коде (косметика). 2024-07-23 15:58:59 +03:00
Леонид Юрьев (Leonid Yuriev)
ad0ba7a661 mdbx: добавление секции о v0.12.11 в ChangeLog. 2024-07-23 14:20:57 +03:00
Леонид Юрьев (Leonid Yuriev)
a6a7a291c7
mdbx: выпуск 0.12.11 "Лиза и Соня"
Поддерживающий выпуск с исправлением обнаруженных ошибок и устранением недочетов,
в память об убитых в Крыму девочках 2 и 9 лет.

Лиза и Соня погибли 23 Июня 2024 на глазах у родителей, в результате
удара по общественному городскому пляжу ракетами ATACMS с кассетными
боеприпасами. Всего пострадало более 150 граждан России, в том числе 27
детей. Ракеты были выпущенными украинскими бандеровцами/фашистами, но
полетные задания формировались и загружались военными США, а управление
и наведение ATACAMS невозможно без использования орбитальной группировки
военных спутников США.

Основные исправления:
---------------------

 - Исправление для ОС Windows нарезки `FILE_SEGMENT_ELEMENT`.
   Похоже что был потерян коммит входе работы над оптимизацией пути записи
   на диск в ОС Windows. В текущем понимании, вероятность проявления ошибки
   достаточно низкая, так как выявлена она была синтетическими тестами в
   ходе других доработок, а соответствующих сообщений/жалоб не поступало. К
   повреждению БД ошибка не приводила, так как сбой происходил до записи
   данных с возвратом `ERROR_INVALID_PARAMETER` из системного вызова, т.е.
   либо ошибка не проявлялась, либо транзакция не фиксировалась.

 - Устранение вероятности `SIGSEGV` при включении логирования
   уровня `MDBX_LOG_TRACE` в отладочных сборках.

 - Исправление генерации исключения `key_exists` в C++ API.

 - Исправление обработки курсоров, открытых в родительских транзакциях и
   закрытых до завершения вложенных транзакций. В описанной ситуации
   закрытые курсоры "воскрешались", что приводило к утечке памяти
   выделенной под такие курсоры.

 - Костыль для MSVC ARM/ARM64 для предотвращения ICE (Internal Compiler Error).

 - Устранение `MDBX_EINVAL` для случая вызова `mdbx_env_remove(".")`.

 - Исправление инверсии bool-результата `env::remove()`в C++ API.

Более подробная информация в [ChangeLog](https://libmdbx.dqdkfa.ru/md__change_log.html).

git diff' stat: 29 commits, 14 files changed, 379 insertions(+), 151 deletions(-)
Signed-off-by: Леонид Юрьев (Leonid Yuriev) <leo@yuriev.ru>
2024-07-23 13:14:14 +03:00
Леонид Юрьев (Leonid Yuriev)
a8ef8d7b72 mdbx: обновление ChangeLog. 2024-07-22 17:46:55 +03:00
Леонид Юрьев (Leonid Yuriev)
5c40f6983c mdbx: использование clang-format-19. 2024-07-22 14:53:28 +03:00
Леонид Юрьев (Leonid Yuriev)
cda8ebe0bd mdbx: проверка совместмости флагов GC/FreeDB (backport). 2024-07-22 14:53:28 +03:00
Леонид Юрьев (Leonid Yuriev)
f5bbadf3a5 mdbx: отмена корректировки assert-проверки внутри override_meta() (revert-backport).
This reverts commit a0b520fa32.
2024-07-22 12:33:16 +03:00
Леонид Юрьев (Leonid Yuriev)
58cad2995b mdbx: до-исправление коммита 796b7e44976132944847694fc8caa5b01c07a406 для отладочных сборок. 2024-07-21 23:20:10 +03:00
Леонид Юрьев (Leonid Yuriev)
0a36ed3ca1 mdbx: исправление опечатки в имени ior_sgv_gap4terminator. 2024-07-21 22:27:05 +03:00
Леонид Юрьев (Leonid Yuriev)
37217cb199 mdbx-windows: исправление нарезки FILE_SEGMENT_ELEMENT (backport).
Ошибка слишком грубая.
Похоже при переработке I/O под Windows при `git pull --rebase` потерялся коммит.

К повреждению БД проблема не приводила, так как сбой происходил во время записи данных с возвратом ERROR_INVALID_PARAMETER из системного вызова.
2024-07-20 22:54:15 +03:00
Леонид Юрьев (Leonid Yuriev)
6941ec17bc mdbx: обновление ChangeLog. 2024-07-20 16:43:45 +03:00
Леонид Юрьев (Leonid Yuriev)
6b2b15ebc8 mdbx: допускаем лишние/устаревшие флаги для GC/FreeDB для старых БД. 2024-07-20 14:05:16 +03:00
Леонид Юрьев (Leonid Yuriev)
5c643f72b5 mdbx-testing: уменьшение длительности/глубины тестирования extra/crunched_delete на MacOS. 2024-07-20 13:19:25 +03:00
Леонид Юрьев (Leonid Yuriev)
9d9a19ae17 mdbx: форматирование/пробелы/косметика. 2024-07-20 13:15:27 +03:00
Леонид Юрьев (Leonid Yuriev)
b1cc8b2e9f mdbx-windows: исправление нарезки FILE_SEGMENT_ELEMENT.
Ошибка слишком грубая.
Похоже при переработке I/O под Windows при `git pull --rebase` потерялся коммит.

К повреждению БД проблема не приводила, так как сбой происходил во время записи данных с возвратом ERROR_INVALID_PARAMETER из системного вызова.
2024-07-20 13:15:21 +03:00
Леонид Юрьев (Leonid Yuriev)
c46270ec56 mdbx-windows: исправление падения при логировании ошибки WriteFileGather(). 2024-07-20 13:15:21 +03:00
Леонид Юрьев (Leonid Yuriev)
7dee88e27f mdbx-test: вывод кадров стека для решения проблем Windows. 2024-07-18 20:57:10 +03:00
Леонид Юрьев (Leonid Yuriev)
242ebefdb7 mdbx-windows: добавление потерянного #include <wincrypt.h>. 2024-07-14 23:42:24 +03:00
Леонид Юрьев (Leonid Yuriev)
f20addd7fc mdbx-doc: доработка doxygen-ссылок. 2024-07-13 17:03:06 +03:00
Леонид Юрьев (Leonid Yuriev)
8a04337e79 mdbx: корректировка ChangeLog. 2024-07-13 16:15:21 +03:00
Леонид Юрьев (Leonid Yuriev)
8e8ac09e14 mdbx: корректировка mdbx_enumerate_subdb(). 2024-07-13 16:13:11 +03:00
Леонид Юрьев (Leonid Yuriev)
bdfec14992 mdbx: дополнение ChangeLog. 2024-07-12 14:55:17 +03:00
Леонид Юрьев (Leonid Yuriev)
32df0ad1eb mdbx: устранение регресса SIGSEGV при открытии БД с измененным размером страницы. 2024-07-12 11:43:12 +03:00
Леонид Юрьев (Leonid Yuriev)
2311706272 mdbx-testing: тестирование парковки транзакций. 2024-07-12 01:05:56 +03:00
Леонид Юрьев (Leonid Yuriev)
ec0ada7b8c mdbx: парковка читающих транзакций. 2024-07-12 01:05:56 +03:00
Леонид Юрьев (Leonid Yuriev)
f335a16c92 mdbx-testsing: отлючение лишнего/временного отладочного вывода. 2024-07-10 22:33:46 +03:00
Леонид Юрьев (Leonid Yuriev)
fe31958d46 mdbx: добавление UUID для идентификации БД. 2024-07-10 22:33:46 +03:00
Леонид Юрьев (Leonid Yuriev)
319753661a mdbx: рефакторинг coherency_check_written(). 2024-07-05 22:00:05 +03:00
Леонид Юрьев (Leonid Yuriev)
3798d47a71 mdbx-doc: несущественная корректировка doxygen-описаний. 2024-07-05 20:33:43 +03:00
Леонид Юрьев (Leonid Yuriev)
9acbe88566 mdbx: добавление mdbx_enumerate_subdb(). 2024-07-05 00:25:28 +03:00
Леонид Юрьев (Leonid Yuriev)
9fbf0099f2 mdbx-doc: добавление \see ссылок на MDBX_db_flags_t и MDBX_dbi_state_t. 2024-07-04 23:20:37 +03:00
Леонид Юрьев (Leonid Yuriev)
b4f395be50 mdbx++: добавление недостающего метода mdbx::env::limits::max_map_handles(). 2024-07-04 23:20:37 +03:00
Леонид Юрьев (Leonid Yuriev)
49c6e14b30 mdbx++: расширение API методами принимающими имена subDb через mdbx::slice. 2024-07-04 23:20:37 +03:00
Леонид Юрьев (Leonid Yuriev)
69df6e6ac0 mdbx: корректировка assert-проверки внутри meta_override().
После доработок/рефакторинга условие проверки стало неверным.
2024-06-28 14:00:49 +03:00
Леонид Юрьев (Leonid Yuriev)
a0b520fa32 mdbx: корректировка assert-проверки внутри override_meta() (backport).
После доработок/рефакторинга условие проверки стало неверным.
2024-06-28 12:44:51 +03:00
Леонид Юрьев (Leonid Yuriev)
796b7e4497 mdbx: устранение вероятности SIGSEGV при включении логирования MDBX_LOG_TRACE в отладочных сборках (backport). 2024-06-26 12:26:16 +03:00
Леонид Юрьев (Leonid Yuriev)
7abeac762f mdbx: устранение вероятности SIGSEGV при включении логирования MDBX_LOG_TRACE в отладочных сборках. 2024-06-26 09:44:42 +03:00
Леонид Юрьев (Leonid Yuriev)
d5fb37460b mdbx: исправление пары опечаток в COPYRIGHT. 2024-06-21 12:18:40 +03:00
Леонид Юрьев (Leonid Yuriev)
bdd0b487ae mdbx-doc: корректировки для Doxygen. 2024-06-20 13:53:23 +03:00
Леонид Юрьев (Leonid Yuriev)
9670cf5709 mdbx-testing: вывод "табло" с информацией о положении курсоров для удобства отладки. 2024-06-19 14:18:18 +03:00
Леонид Юрьев (Leonid Yuriev)
a10506fb6a mdbx-testing: технический возврат bool из проверочных функций в тесте для удобства ad-hoc доработок. 2024-06-19 14:18:18 +03:00
Леонид Юрьев (Leonid Yuriev)
d4c09f9b78 mdbx-testing: изменение порядка перебора режимов в "долгом стохастическом". 2024-06-19 14:18:18 +03:00
Леонид Юрьев (Leonid Yuriev)
f65642e38c mdbx: обновление ChangeLog. 2024-06-19 14:18:18 +03:00
Леонид Юрьев (Leonid Yuriev)
bcd955aeb9 mdbx++: исправление append_u8(). 2024-06-19 14:18:18 +03:00
Леонид Юрьев (Leonid Yuriev)
a79a318d61 mdbx++: поддержка MDBX_VALIDATION. 2024-06-19 14:18:18 +03:00
Леонид Юрьев (Leonid Yuriev)
3517db6178 mdbx-testing: добавление `extra/crunched_delete'. 2024-06-19 14:18:18 +03:00
Леонид Юрьев (Leonid Yuriev)
25efb58790 mdbx++: добавление перегрузок put/insert/upsert для mdbx::pair. 2024-06-19 14:18:18 +03:00
Леонид Юрьев (Leonid Yuriev)
74ff4dba0a mdbx-testing: добавление tmux-battery. 2024-06-19 14:18:18 +03:00
Леонид Юрьев (Leonid Yuriev)
a2753c9ae1 mdbx: доработка gc_update() с отключением нового/нестабильного кода. 2024-06-19 14:18:18 +03:00
Леонид Юрьев (Leonid Yuriev)
8e29fb1f26 mdbx-testing: исправление опечатки в keygen-setup. 2024-06-19 14:18:18 +03:00
Леонид Юрьев (Leonid Yuriev)
e12b4ab748 mdbx-testing: костыль для MSVC ARM/ARM64 для предотвращения ICE. 2024-06-19 14:18:18 +03:00
Леонид Юрьев (Leonid Yuriev)
e9c122af68 mdbx-windows: чистка результатов FormatMessageA() от концевых переводов строк. 2024-06-19 14:18:18 +03:00
Леонид Юрьев (Leonid Yuriev)
b940ae8fad mdbx++: использование \n вместо std::endl. 2024-06-19 14:18:18 +03:00
Леонид Юрьев (Leonid Yuriev)
5792eb31eb mdbx: добавление опций для subpage: limit, room_threshold, reserve_prereq, reserve_limit. 2024-06-19 14:18:18 +03:00
Леонид Юрьев (Leonid Yuriev)
0e831f42cc mdbx-testing: изменение уровня логирования по-умолчанию. 2024-06-19 14:18:18 +03:00
Леонид Юрьев (Leonid Yuriev)
2f66eb9fec mdbx-tools: преобразование из hex без переходов в mdbx_load. 2024-06-19 14:18:18 +03:00
Леонид Юрьев (Leonid Yuriev)
12eb2df57d mdbx: удаление DEFAULT_MAPSIZE. 2024-06-19 14:18:18 +03:00
Леонид Юрьев (Leonid Yuriev)
3de3d425a1 mdbx: изменение лицензии и реструктуризация исходного кода. 2024-06-19 14:18:18 +03:00
Леонид Юрьев (Leonid Yuriev)
341a8b8b5c mdbx++: исправление генерации исключения key_exists (backport).
Из-за совершенной при размножении кода ошибки, вместо отдельного
исключения `mdbx::key_exists` при ошибке `MDBX_KEYEXIST` вбрасывалось
исключении более общего/генерализированного типа `mdbx::exception`.
2024-06-13 11:12:12 +03:00
Леонид Юрьев (Leonid Yuriev)
ad0b13a544 mdbx-doc: исправление опечаток в README. 2024-06-04 12:48:48 +03:00
Леонид Юрьев (Leonid Yuriev)
ba42390a56 mdbx++: исправление append_u8() (backport).
Добавленные после предыдущего выпуска append-функции оказались ошибочны.
Алгоритмически там серия однотипных банальных ошибок (почти опечаток),
из-за которых добавляемые данные записывались в начало среза/slice, а не
в конец.

Исходные ошибки были выявлены тестами в другом проекте и исправлены
почти сразу, но плохой код всё-таки попал в stable-ветку.
Предположительно я спутал ветки и/или tmux-окна, и взял в stable-ветку не
исправленный коммит. Удивительно, что плохой код в devel-ветке не
нарушил работу части новых тестов. Поэтому проблема некоторое время
оставалась не замеченной.
2024-06-04 10:56:15 +03:00
Леонид Юрьев (Leonid Yuriev)
07cfe65ea0 mdbx: исправление закрытия курсоров при завершении вложенных транзакций (backport). 2024-05-25 18:17:10 +03:00
Леонид Юрьев (Leonid Yuriev)
75fd4ecf54 mdbx-testing: костыль для MSVC ARM/ARM64 для предотвращения ICE (backport). 2024-05-22 01:21:10 +03:00
Леонид Юрьев (Leonid Yuriev)
97b0b0192e mdbx-windows: чистка результатов FormatMessageA() от концевых переводов строк (backport). 2024-05-21 18:29:22 +03:00
Леонид Юрьев (Leonid Yuriev)
12e6c631f1 mdbx: уточнение макроса __always_inline для особо яблочных компиляторов (backport). 2024-05-20 18:26:45 +03:00
Леонид Юрьев (Leonid Yuriev)
458f713c53 mdbx++: использование \n вместо std::endl (backport). 2024-05-20 18:24:47 +03:00
Леонид Юрьев (Leonid Yuriev)
e9f5c0c308 mdbx++: упрощение buffer:silo::inplace_signature. 2024-05-19 22:07:20 +03:00
Леонид Юрьев (Leonid Yuriev)
95bc96dda3 mdbx: доработка update_gc() для улучшения сходимости с исправлением вероятности MDBX_PROBLEM. 2024-05-19 22:05:14 +03:00
Леонид Юрьев (Leonid Yuriev)
a5ed725ae3 mdbx: явное обноление txn и dbi_state у вложенных курсоров для вложенных транзакций. 2024-05-18 13:12:08 +03:00
Леонид Юрьев (Leonid Yuriev)
dd9ba2c769 mdbx: проверка на ноль дополнительных и пока не используемых полей в meta-страницах. 2024-05-13 21:18:30 +03:00
Леонид Юрьев (Leonid Yuriev)
d1565fd326 mdbx: отключение уже ненужной отладки внутри txn_merge() (backport). 2024-04-15 08:18:08 +03:00
Леонид Юрьев (Leonid Yuriev)
45377f20c5 mdbx: отключение уже ненужной отладки внутри txn_merge(). 2024-04-14 22:50:19 +03:00
Леонид Юрьев (Leonid Yuriev)
7c69493473 mdbx: добавление MDBX_opt_prefer_waf_insteadof_balance. 2024-04-05 00:19:08 +03:00
Леонид Юрьев (Leonid Yuriev)
f19753636d mdbx: обновление TODO. 2024-04-04 22:31:03 +03:00
Леонид Юрьев (Leonid Yuriev)
4dccc4ab6d mdbx-doc: исправление опечаток в doxygen-комментариях. 2024-04-04 17:32:49 +03:00
Леонид Юрьев (Leonid Yuriev)
e3d4cd5758 mdbx: исправление условий для MDBX_DEPRECATED (backport). 2024-04-04 15:24:10 +03:00
Леонид Юрьев (Leonid Yuriev)
2d2cec094e mdbx: устранение MDBX_EINVAL в случае mdbx_env_remove(".") (backport). 2024-04-04 15:24:04 +03:00
Леонид Юрьев (Leonid Yuriev)
41ebd6dcf3 mdbx++: исправление инверсии bool-результата env::remove() (backport). 2024-04-04 15:15:46 +03:00
Леонид Юрьев (Leonid Yuriev)
f40b2fc164 mdbx++: больше __cold для редко-используемых функций (backport). 2024-04-04 15:15:46 +03:00
Леонид Юрьев (Leonid Yuriev)
8f32f4ac98 mdbx++: buffer::append_bytes() (backport). 2024-04-04 15:15:46 +03:00
Леонид Юрьев (Leonid Yuriev)
c1d3afcbe1 mdbx++: добавление buffer::clear_and_reserve() (backport). 2024-04-04 15:15:46 +03:00
Леонид Юрьев (Leonid Yuriev)
e34d4de760 mdbx: дополнение ChangeLog. 2024-04-04 15:00:27 +03:00
Леонид Юрьев (Leonid Yuriev)
b9fd42b9b3 mdbx: объявление опции MDBX_COALESCE устаревшей. 2024-04-04 15:00:27 +03:00
Леонид Юрьев (Leonid Yuriev)
ae5d541efb mdbx: исправление условий для MDBX_DEPRECATED. 2024-04-04 15:00:27 +03:00
Леонид Юрьев (Leonid Yuriev)
e56c73b4e6 mdbx: добавление режима MDBX_NOSTICKYTHREADS вместо MDBX_NOTLS. 2024-04-04 12:52:50 +03:00
Леонид Юрьев (Leonid Yuriev)
1727b697a0 mdbx-doc: устранение отложенных недоработок в документации. 2024-04-04 12:52:30 +03:00
Леонид Юрьев (Leonid Yuriev)
d603de4a87 mdbx: исправление критической ошибки в TXN_FOREACH_DBI_FROM.
Какие-либо выпуски и стабильные ветки не были затронуты проблемой.
Ошибка была внесена 2023-11-05 коммитом e6af7d7c53 в ветку `devel`.

Большое спасибо команде Erigon и особенно Алексею Шарову за помощь в поиске причины проблемы.
2024-04-03 12:59:06 +03:00
Леонид Юрьев (Leonid Yuriev)
bdff60e6a7 mdbx: исправление форматирования (косметика). 2024-04-02 13:33:12 +03:00
Леонид Юрьев (Leonid Yuriev)
3670a30c00 mdbx-doc: доработка doxygen комментариев. 2024-04-01 16:06:09 +03:00
Леонид Юрьев (Leonid Yuriev)
01458065c4 mdbx-doc: базовое/минимальное описание mdbx_env_chk() и связанных элементов API. 2024-04-01 16:06:09 +03:00
Леонид Юрьев (Leonid Yuriev)
639ba8b7a5 mdbx: переименование mdbx_env_chk_encount_problem(). 2024-04-01 16:06:09 +03:00
Леонид Юрьев (Leonid Yuriev)
2cc6d68c07 mdbx++: добавление txn::open_map_accede(). 2024-04-01 16:06:09 +03:00
Леонид Юрьев (Leonid Yuriev)
2ce6ed33fa mdbx: устранение MDBX_EINVAL в случае mdbx_env_remove("."). 2024-04-01 16:06:09 +03:00
Леонид Юрьев (Leonid Yuriev)
5c3c7b9292 mdbx++: исправление инверсии bool-результата env::remove(). 2024-03-31 14:29:17 +03:00
Леонид Юрьев (Leonid Yuriev)
b36679ddcb mdbx++: buffer::append_bytes(). 2024-03-31 14:29:17 +03:00
Леонид Юрьев (Leonid Yuriev)
d4f7b4114b mdbx++: добавление buffer::clear_and_reserve(). 2024-03-31 14:28:50 +03:00
Леонид Юрьев (Leonid Yuriev)
e9a49e3715 mdbx++: добавление перегрузок со std::string_view для методов open_map/create_map_/drop_map/clear_map/rename_map(). 2024-03-31 14:27:19 +03:00
Леонид Юрьев (Leonid Yuriev)
cce5c8249c mdbx++: больше __cold для редко-используемых функций. 2024-03-30 18:01:44 +03:00
Леонид Юрьев (Leonid Yuriev)
7b1f8ba642 mdbx++: добавление в C++ API методов txn::rename_map(). 2024-03-30 18:01:44 +03:00
Леонид Юрьев (Leonid Yuriev)
5c84c405ac mdbx: добавление mdbx_setup_debug_nofmt() и возможности установки логера без функционала printf(). 2024-03-30 18:01:44 +03:00
Леонид Юрьев (Leonid Yuriev)
af060b4960 mdbx: вынесение статических переменных в структуру mdbx_static. 2024-03-30 18:01:44 +03:00
Леонид Юрьев (Leonid Yuriev)
f548f00d8e mdbx: диагностика/логирование для каждого случая возврата MDBX_CORRUPTED. 2024-03-30 18:01:44 +03:00
Леонид Юрьев (Leonid Yuriev)
5721296e16 mdbx: опечатки и орфография в ChangeLog. 2024-03-28 11:37:23 +03:00
Леонид Юрьев (Leonid Yuriev)
31e8f290e7 mdbx: опечатки и орфография в ChangeLog. 2024-03-28 11:35:32 +03:00
Леонид Юрьев (Leonid Yuriev)
dedcdd4c94 mdbx: документирование mdbx_preopen_snapinfo(). 2024-03-24 11:15:12 +03:00
Леонид Юрьев (Leonid Yuriev)
80e9667ead mdbx++: явное приведение к int внутри constexpr mdbx::memcmp(). 2024-03-23 21:38:54 +03:00
Леонид Юрьев (Leonid Yuriev)
d0799fd373 mdbx-doc: документирование mdbx_cursor_on_first_dup() и mdbx_cursor_on_last_dup(). 2024-03-23 02:18:06 +03:00
Леонид Юрьев (Leonid Yuriev)
fb17e8877c mdbx-doc: документирование mdbx_cursor_scan(). 2024-03-22 17:57:32 +03:00
Леонид Юрьев (Leonid Yuriev)
c153a34382 mdbx: доработка mdbx_cursor_scan().
- Упрощение и обеспечение возврата `MDBX_RESULT_FALSE`, как при
   отсутствии данных, так и при неуспешном поиске.

 - Инициализация внутренних переменных key и value для устойчивости
   в случае использования контекстно-некорректных операций
   позиционирования курсора.
2024-03-22 16:36:13 +03:00
Леонид Юрьев (Leonid Yuriev)
1d0ee509c2 mdbx-doc: исправление опечатки в MDBX_ENV_JUST_DELETE. 2024-03-22 16:36:13 +03:00
Леонид Юрьев (Leonid Yuriev)
27893f52f1 mdbx-doc: документирование mdbx_dbi_rename(). 2024-03-22 01:01:09 +03:00
Леонид Юрьев (Leonid Yuriev)
f8836aefa0 mdbx-doc: документирование mdbx_cursor_compare(). 2024-03-22 01:01:06 +03:00
Леонид Юрьев (Leonid Yuriev)
04f1200c3d
mdbx-doc: исправление опечатки в MDBX_ENV_JUST_DELETE (backport). 2024-03-22 00:28:03 +03:00
Леонид Юрьев (Leonid Yuriev)
183d1e1a44 mdbx: быстрый выход для не-активной среды. 2024-03-21 17:23:07 +03:00
Леонид Юрьев (Leonid Yuriev)
179d8d6d6b mdbx: не взводим MDBX_FATAL_ERROR для не-активной среды при проверке MDBX_ENV_CHECKPID. 2024-03-21 17:23:07 +03:00
Леонид Юрьев (Leonid Yuriev)
dfcd652e5c
mdbx: не взводим MDBX_FATAL_ERROR для не-активной среды при проверке MDBX_ENV_CHECKPID (backport). 2024-03-21 13:22:33 +03:00
Леонид Юрьев (Leonid Yuriev)
abca22e32d
mdbx: вливание ветки devel в master.
56 files changed, 8997 insertions(+), 4507 deletions(-)
2024-03-21 11:53:38 +03:00
Леонид Юрьев (Leonid Yuriev)
20d6d39ab3 mdbx: обновление ChangeLog. 2024-03-21 11:44:25 +03:00
Леонид Юрьев (Leonid Yuriev)
236afee80b mdbx: быстрая обработка режима MDBX_EXCLUSIVE для mdbx_env_resurrect_after_fork(). 2024-03-21 10:56:36 +03:00
Леонид Юрьев (Leonid Yuriev)
94a6bc140d mdbx-doc: документирование mdbx_env_resurrect_after_fork(). 2024-03-21 10:48:47 +03:00
Леонид Юрьев (Leonid Yuriev)
5fc3965f5b
mdbx: вливание ветки master в devel. 2024-03-21 01:54:20 +03:00
Леонид Юрьев (Leonid Yuriev)
25089e6491
mdbx: merge branch master into stable. 2024-03-21 01:10:00 +03:00
Леонид Юрьев (Leonid Yuriev)
61a073687f
mdbx: время учить Русский. 2024-03-19 15:56:37 +03:00
Леонид Юрьев (Leonid Yuriev)
baaa26bb32 mdbx: доработка update_gc() для улучшения сходимости. 2024-03-17 02:25:57 +03:00
Леонид Юрьев (Leonid Yuriev)
93f76f43ac mdbx-chk: не считаем ошибочными/проблемными записи нулевой длины в GC.
Технически такие записи не являются проблемными, а образовываются в
случае когда внутри update_gc() резервируется больше места, чем реально
остается номеров свободных страниц для возврата в GC.

Изначально такое избыточное резервирование считалось алгоритмическим
недостатком update_gc(). Поэтому утилита mdbx_chk была временно
доработана для выявления таких случаев в ходе стохастических тестов.

Постепенно все реальные недочеты update_gc() (если не считать
запутанности и неочевидности кода) были устранены, формирование пустых
записей в GC не наблюдалось и излишне строгий контроль в mdbx_chk не
создавал проблем.

В ходе же последних точечных доработок была предпринята попытка еще
немного уменьшить затраты ЦПУ внутри update_gc(), в частности уменьшить
кол-во циклов/повторов посредством улучшения сходимости, а также
уменьшить WAF. При этом образование пустых записей в GC стало возможным
в достаточно редких ситуациях, когда (например) для возврата в GC
остается только одна страница и добавление записи единичной длины
приводит к перебалансировке или разделению листовой страницы по
легковесному пути, без вовлечения других страниц дерева и без
переработки дополнительных записей GC, но с поглощением остававшейся на
возврат страницы.

Проще говоря, в актуальная версии MDBX пустые записи в GC могут
образовываться, когда это энергетически выгодно. Тогда как в предыдущих
выпусках в таких ситуациях выполнялось более дорогое обновление GC с
переработкой и возвратом дополнительных записей.
2024-03-16 23:23:08 +03:00
Леонид Юрьев (Leonid Yuriev)
aae6a0395a mdbx: исправление опечатки равно/неравно в условии внутри update_gc().
Существенных последствий ошибки не было (иначе бы давно было замечено).
Но в определенных сценариях, сходимость требовала еще одного цикла
повтора внутри update_gc().
2024-03-16 23:23:08 +03:00
Леонид Юрьев (Leonid Yuriev)
471085788c mdbx: исправление ошибки открытия БД на ФС только-для-чтения. 2024-03-16 23:23:08 +03:00
Леонид Юрьев (Leonid Yuriev)
3865e85248 mdbx: орфоргафия. 2024-03-16 20:45:35 +03:00
Леонид Юрьев (Leonid Yuriev)
a6f7d74a32 mdbx: микрооптимизация cursor_touch(). 2024-03-16 20:25:47 +03:00
Леонид Юрьев (Leonid Yuriev)
c70ef83e4a
mdbx: исправление опечатки равно/неравно в условии внутри update_gc().
Существенных последствий ошибки не было (иначе бы давно было замечено).
Но в определенных сценариях, сходимость требовала еще одного цикла
повтора внутри update_gc().
2024-03-15 06:10:10 +03:00
Леонид Юрьев (Leonid Yuriev)
8cc3dba7ae
mdbx: merge branch master into stable. 2024-03-14 19:48:12 +03:00
Леонид Юрьев (Leonid Yuriev)
872bddaee6
mdbx: обновление патча для старых версий buildroot. 2024-03-14 19:46:38 +03:00
Леонид Юрьев (Leonid Yuriev)
aea40fb79f
mdbx: выпуск 0.12.10 "СЭМ"
Поддерживающий выпуск с исправлением обнаруженных ошибок и устранением недочетов
в память Героя России гвардии майора Дмитрия Семёнова с позывным "СЭМ".

Значимые исправления и доработки:
---------------------------------

 - Устранение унаследованной от LMDB ошибки приводящей к повреждению БД при использовании `MDBX_DUPFIXED`.

 - Исправление ложной ошибки `MDBX_CORRUPTED (-30796)` в сценарии работы
   в режиме `MDBX_DUPFIXED` и нечетной длинной мульти-значений.

 - Исправление недочета корректировки сопутствующих курсоров при разделении страницы
   по сценарию добавления пустой страницы слева.

 - Доработка `rebalance()` ради уменьшения WAF.

 - Исправление assert-проверки внутри `check_txn()` для случая завершенных транзакций в режиме `MDBX_NO_TLS`.
   Последствий ошибки, кроме срабатывания assert-проверки в отладочных сборках, нет.

 - Устранение ошибки при открытии БД на файловой системе только-для-чтения.

 - Удалены излишне строгие проверки в утилите `mdbx_chk`, которые
   приводили к ложно-позитивным ошибкам при проверке БД после серии
   последних доработок.

Более подробная информация в [ChangeLog](https://libmdbx.dqdkfa.ru/md__change_log.html).

git diff' stat: 19 commits, 57 files changed, 751 insertions(+), 331 deletions(-)
Signed-off-by: Леонид Юрьев (Leonid Yuriev) <leo@yuriev.ru>
2024-03-13 14:57:38 +03:00
Леонид Юрьев (Leonid Yuriev)
0741c81cfd mdbx-chk: не считаем ошибкой под-страницы без данных пользователя.
Коммитом 36a7e7ac24 был скорректирован
подсчет места занимаемого заголовками и данными. В частности, байты
занимаемые заголовков вложенной под-страницы теперь относится к
заголовкам, а не пользовательским данным.

Однако, в случае когда на под-странице, созданной для хранения куста
мульти-значений (MDBX_DUPSORT), после серии удалений остаётся одно
значение, которое в MDBX может нулевой длины, получается под-страница с
нулевым количеством пользовательских данных. Этот коммит исправляет
2024-03-12 09:02:50 +03:00
Леонид Юрьев (Leonid Yuriev)
781b3f64d5 mdbx-chk: не считаем ошибочными/проблемными записи нулевой длины в GC.
Технически такие записи не являются проблемными, а образовываются в
случае когда внутри update_gc() резервируется больше места, чем реально
остается номеров свободных страниц для возврата в GC.

Изначально такое избыточное резервирование считалось алгоритмическим
недостатком update_gc(). Поэтому утилита mdbx_chk была временно
доработана для выявления таких случаев в ходе стохастических тестов.

Постепенно все реальные недочеты update_gc() (если не считать
запутанности и неочевидности кода) были устранены, формирование пустых
записей в GC не наблюдалось и излишне строгий контроль в mdbx_chk не
создавал проблем.

В ходе же последних точечных доработок была предпринята попытка еще
немного уменьшить затраты ЦПУ внутри update_gc(), в частности уменьшить
кол-во циклов/повторов посредством улучшения сходимости, а также
уменьшить WAF. При этом образование пустых записей в GC стало возможным
в достаточно редких ситуациях, когда (например) для возврата в GC
остается только одна страница и добавление записи единичной длины
приводит к перебалансировке или разделению листовой страницы по
легковесному пути, без вовлечения других страниц дерева и без
переработки дополнительных записей GC, но с поглощением остававшейся на
возврат страницы.

Проще говоря, в актуальная версии MDBX пустые записи в GC могут
образовываться, когда это энергетически выгодно. Тогда как в предыдущих
выпусках в таких ситуациях выполнялось более дорогое обновление GC с
переработкой и возвратом дополнительных записей.
2024-03-12 02:38:17 +03:00
Леонид Юрьев (Leonid Yuriev)
446d6c9421 mdbx: исправление ошибки открытия БД на ФС только-для-чтения. 2024-03-11 00:34:04 +03:00
Леонид Юрьев (Leonid Yuriev)
fff3fbd866 mdbx: обновление ChangeLog (подготовка к выпуску). 2024-03-06 22:58:31 +03:00
Леонид Юрьев (Leonid Yuriev)
3e850981c7 mdbx-test: расширение стохастического теста dupfixed-сценариями (backport). 2024-03-06 13:18:58 +03:00
Леонид Юрьев (Leonid Yuriev)
36a7e7ac24 mdbx-chk: исправление подсчета места затраченного на выравнивание в случае нечетного кол-ва dupfixed-элементов нечетного размера (backport). 2024-03-06 13:18:58 +03:00
Леонид Юрьев (Leonid Yuriev)
2d7fe42327 mdbx: корректировка условия в assert-проверке для MDBX_TXN_DRAINED_GC (backport). 2024-03-06 13:18:58 +03:00
Леонид Юрьев (Leonid Yuriev)
2b88c6261f mdbx-test: добавление в jitter простого теста txn_reset+txn_renew (backport). 2024-03-06 13:18:58 +03:00
Леонид Юрьев (Leonid Yuriev)
e515bd56e9 mdbx: исправление assert-проверки внутри check_txn() для случая завершенных транзакций в режиме MDBX_NO_TLS (backport).
По сообщению о проблеме https://t.me/libmdbx/5424
2024-03-06 13:18:58 +03:00
Леонид Юрьев (Leonid Yuriev)
20160ae98f mdbx: доработка rebalance() ради уменьшения WAF (backport).
После предыдущей серии доработок весной 2021 года, функция `rebalance()`
обеспечивала слияние мало заполненной страницы с менее заполненной
соседней, одновременно пытаясь не вовлекать соседних страниц, если те
еще не были скопированы/клонированы/изменены в текущей транзакции.

В целом, реализованная тактика представляется успешной. Однако, при
обновлении GC она иногда приводила к исчерпанию подготовленного резерва
извлеченных из GC страниц. Это не является проблемой, если не считать
вероятность срабатывания `assert(txn->mt_flags & MDBX_TXN_DRAINED_GC)`
в отладочных сборках.

Тем не менее, из этой ситуации можно сделать вывод, что поведение
`rebalance()`, как минимум, может быть обогащено опцией уменьшения WAF
ценой меньшей сбалансированности дерева. Технически при этом слияние
выполняется преимущественно с грязной страницей, если на ней достаточно
места и соседняя страница с другой стороны еще чистая.

ВАЖНО: Соответствующая опция в `enum MDBX_option_t` будет добавлена чуть
позже в следующую версию, а в текущих ветках `master` и `stable` это
именение поведение будет заглушено.
2024-03-06 13:18:58 +03:00
Леонид Юрьев (Leonid Yuriev)
ea97fbae74 mdbx: устранение yнаследованной от LMDB ошибки приводящей к повреждению БД при использовании MDBX_DUPFIXED (backport).
Тезисно:

 - Использование DUPFIXED (включая INTEGERDUP) могло приводить к
   повреждению БД и/или потере данных. Этот коммит устраняет эту угрозу.

 - Вероятность проявления существенно увеличивается с увеличением
   размера/длины мульти-значений/дубликатов (не ключей).

 - В MDBX проблема унаследована от LMDB, где существует более 11 лет,
   начиная с коммита ccc4d23e74
   и до настоящего времени.

Для вложенных страниц типа LEAF2 (которые содержат только значения
одинаковой длины, без таблицы смещений к ним), упомянутым выше коммитом,
было добавлено резервирование места (что в целом спорно, но в некоторых
сценариях позволяет уменьшить накладные расходы). Ошибка была в том, что
в коде не исключалась возможность превышения размера страницы БД, что
далее приводило к арифметическому переполнению, повреждению БД и/или
просписи памяти.
2024-03-06 13:18:58 +03:00
Леонид Юрьев (Leonid Yuriev)
a0a4af7701 mdbx: исправление me_dxb_mmap.current > me_dxb_mmap.limit и срабатывания соответствующей assert-проверки (backport).
Устранение упущения приводящего к нелогичной ситуации `me_dxb_mmap.curren > me_dxb_mmap.limit` при "дребезге" размера БД.
В текущем понимании, последствий кроме срабатывания assert-проверки нет, а вероятность проявления близка к нулю.
2024-03-06 13:18:58 +03:00
Леонид Юрьев (Leonid Yuriev)
74f2bc813b mdbx: продолжение очистки/рефакторинга унаследованных ребусов в cursor_put_nochecklen() (backport). 2024-03-06 13:18:58 +03:00
Леонид Юрьев (Leonid Yuriev)
1a18369015 mdbx-test: фиксация транзакции при ошибках теста для последующего анализа БД (backport). 2024-03-06 13:18:58 +03:00
Леонид Юрьев (Leonid Yuriev)
e2f2fd8652 mdbx: рефакторинг node_shrink() для ясности исходного кода (backport). 2024-03-06 13:18:58 +03:00
Леонид Юрьев (Leonid Yuriev)
fb6be62046 mdbx: исправление недочета корректировки сопутствующих курсоров при разделении страницы по сценарию добавления пустой страницы слева (backport). 2024-03-06 13:18:58 +03:00
Леонид Юрьев (Leonid Yuriev)
f7e6bd770a mdbx-test: соответствие протоколируемых имен тестов опциям командной строки (backport). 2024-03-06 13:18:58 +03:00
Леонид Юрьев (Leonid Yuriev)
ba5c74e54d mdbx-test: добавление extra/dupfixed_addodd (backport). 2024-03-06 13:18:58 +03:00
Леонид Юрьев (Leonid Yuriev)
049b71c148 mdbx: исправление ложной ошибки MDBX_CORRUPTED (-30796) в сценарии "odd dupfixed" (backport).
Повреждение БД и/или потери данных не происходило, проблема лишь в
возврате ложной ошибки.

Благодарю пользователя/разработчика @Dvirsw (https://t.me/Dvirsw) за
сообщения о проблеме и предоставление минимального/оптимального сценария
воспроизведения.

--

Проблема была из-за излишнего условия при контроле внутренего поля
mp_upper в ходе проверки структуры страниц БД.

Поле mp_upper указывает на нижнуюю границу заполнения страницы от конца
к началу. Вследствие того, что значения ключей выравниваетня на четную
границу, это поле четно во всех случаях за исключением LEAF2-страницы
(листовая страница вложенного дерева для множественных значений
финсированной/одинаковой длины одного ключа), на которой размещено
нечетное количество значений нечетной длины.

Ошибка не проявлялась в большинстве случаев (в том числе в
стохастических тестах), так как штатно лишняя проверка производилась
только при чтении страницы и перебалансировке ключей, но не при каждом
добавлении значения. Тем не менее, сценарии тестов требуют
доработки/расширения для явного добавления нечетных dupfixed-сценариев.
2024-03-06 13:18:58 +03:00
Леонид Юрьев (Leonid Yuriev)
5df3eb6449 mdbx-test: усиление сценариев тестовых целей в GNUmakefile и CMake. 2024-03-06 11:35:10 +03:00
Леонид Юрьев (Leonid Yuriev)
1549d3970c mdbx: корректировка условия в assert-проверке для MDBX_TXN_DRAINED_GC. 2024-03-05 15:22:09 +03:00
Леонид Юрьев (Leonid Yuriev)
1c174e84c4 mdbx: добавление mdbx_preopen_snapinfo() в API.
https://gitflic.ru/project/erthink/libmdbx/issue/15
2024-03-05 13:10:29 +03:00
Леонид Юрьев (Leonid Yuriev)
0b87ddc6d4 mdbx-test: добавление в jitter простого теста txn_reset+txn_renew. 2024-03-05 13:10:29 +03:00
Леонид Юрьев (Leonid Yuriev)
eca0f46368 mdbx: исправление assert-проверки внутри check_txn() для случая завершенных транзакций в режиме MDBX_NO_TLS.
По сообщению о проблеме https://t.me/libmdbx/5424
2024-03-05 13:10:29 +03:00
Леонид Юрьев (Leonid Yuriev)
fe498de323 mdbx: устранение в cursor_set() повторного сравнения с нулевым элементом на листовой странице. 2024-03-05 13:10:29 +03:00
Леонид Юрьев (Leonid Yuriev)
4ed05689bc mdbx: переименование setup_sdb() для читаемости кода (косметика). 2024-03-05 13:10:29 +03:00
Леонид Юрьев (Leonid Yuriev)
0c24b49bbf mdbx-test: расширение стохастического теста dupfixed-сценариями. 2024-03-05 13:10:29 +03:00
Леонид Юрьев (Leonid Yuriev)
d8db63a67d mdbx-test: чуть более разумное/удобное поведение при коллизии генерации не-уникальных пар ключ-значение. 2024-03-05 13:10:29 +03:00
Леонид Юрьев (Leonid Yuriev)
e29cb076d3 mdbx-test: доработка генератора ключей/значений для надежной генерации уникальных значений.
В текущем понимании коммитом этим устраняется застарелая проблема редких
сбоев стохастического теста из-за вероятности ошибочной генерации
повторяющихся пар key-value.
2024-03-05 13:10:28 +03:00
Леонид Юрьев (Leonid Yuriev)
9480599afa mdbx: доработка rebalance() ради уменьшения WAF.
После предыдущей серии доработок весной 2021 года, функция `rebalance()`
обеспечивала слияние мало заполненной страницы с менее заполненной
соседней, одновременно пытаясь не вовлекать соседних страниц, если те
еще не были скопированы/клонированы/изменены в текущей транзакции.

В целом, реализованная тактика представляется успешной. Однако, при
обновлении GC она иногда приводила к исчерпанию подготовленного резерва
извлеченных из GC страниц. Это не является проблемой, если не считать
вероятность срабатывания `assert(txn->mt_flags & MDBX_TXN_DRAINED_GC)`
в отладочных сборках.

Тем не менее, из этой ситуации можно сделать вывод, что поведение
`rebalance()`, как минимум, может быть обогащено опцией уменьшения WAF
ценой меньшей сбалансированности дерева. Технически при этом слияние
выполняется преимущественно с грязной страницей, если на ней достаточно
места и соседняя страница с другой стороны еще чистая.

Соответствующая опция в `enum MDBX_option_t` будет добавлена чуть позже.
2024-03-03 17:56:43 +03:00
Леонид Юрьев (Leonid Yuriev)
72e51ee370 mdbx: устранение yнаследованной от LMDB ошибки приводящей к повреждению БД при использовании MDBX_DUPFIXED.
Тезисно:

 - Использование DUPFIXED (включая INTEGERDUP) могло приводить к
   повреждению БД и/или потере данных. Этот коммит устраняет эту угрозу.

 - Вероятность проявления существенно увеличивается с увеличением
   размера/длины мульти-значений/дубликатов (не ключей).

 - В MDBX проблема унаследована от LMDB, где существует более 11 лет,
   начиная с коммита ccc4d23e74
   и до настоящего времени.

Для вложенных страниц типа LEAF2 (которые содержат только значения
одинаковой длины, без таблицы смещений к ним), упомянутым выше коммитом,
было добавлено резервирование места (что в целом спорно, но в некоторых
сценариях позволяет уменьшить накладные расходы). Ошибка была в том, что
в коде не исключалась возможность превышения размера страницы БД, что
далее приводило к арифметическому переполнению, повреждению БД и/или
просписи памяти.
2024-03-03 17:56:43 +03:00
Леонид Юрьев (Leonid Yuriev)
aa9d2387e5 mdbx: исправление me_dxb_mmap.current > me_dxb_mmap.limit и срабатывания соответствующей assert-проверки.
Устранение упущения приводящего к нелогичной ситуации `me_dxb_mmap.curren > me_dxb_mmap.limit` при "дребезге" размера БД.
В текущем понимании, последствий кроме срабатывания assert-проверки нет, а вероятность проявления близка к нулю.
2024-03-03 17:56:16 +03:00
Леонид Юрьев (Leonid Yuriev)
fa0017591d mdbx: продолжение очистки/рефакторинга унаследованных ребусов в cursor_put_nochecklen(). 2024-03-03 17:38:41 +03:00
Леонид Юрьев (Leonid Yuriev)
d7f259110c mdbx-test: фиксация транзакции при ошибках теста для последующего анализа БД. 2024-03-03 17:38:41 +03:00
Леонид Юрьев (Leonid Yuriev)
d53dc4572c mdbx: рефакторинг node_shrink() для ясности исходного кода. 2024-03-03 17:38:41 +03:00
Леонид Юрьев (Leonid Yuriev)
2e863cf7e0 mdbx: исправление недочета корректировки сопутствующих курсоров при разделении страницы по сценарию добавления пустой страницы слева. 2024-03-03 17:38:41 +03:00
Леонид Юрьев (Leonid Yuriev)
826441741d mdbx: добавление keysize_min() и valsize_min() в API. 2024-03-03 17:38:41 +03:00
Леонид Юрьев (Leonid Yuriev)
00c4e2636e mdbx-test: обновление ГПСЧ. 2024-03-03 17:38:41 +03:00
Леонид Юрьев (Leonid Yuriev)
3373631cff mdbx-test: унификация PRNG и изменение опции командной строки на --prng-seed. 2024-03-03 17:38:23 +03:00
Леонид Юрьев (Leonid Yuriev)
c5ac7b25c9 mdbx-test: доработка генератора пар key-value для поддержки коротких ключей в режиме MDBX_DUPFIXED. 2024-02-28 20:12:39 +03:00
Леонид Юрьев (Leonid Yuriev)
544c6bc1e4 mdbx-test: поддержка rnd/rand/random для опций --keylen и --datalen. 2024-02-28 20:12:39 +03:00
Леонид Юрьев (Leonid Yuriev)
164d112507 mdbx: доработка chk-функционала с устранением ошибок и недочетов. 2024-02-28 20:12:39 +03:00
Леонид Юрьев (Leonid Yuriev)
fb5dbbdf20 mdbx-test: соответствие протоколируемых имен тестов опциям командной строки (косметика). 2024-02-28 20:12:38 +03:00
Леонид Юрьев (Leonid Yuriev)
b1dcd07be4 mdbx: устранение ошибки в поддержке MDBX_ENABLE_DBI_LOCKFREE. 2024-02-28 20:12:38 +03:00
Леонид Юрьев (Leonid Yuriev)
603ce05435 mdbx: исправление vlen_min для режима dupfixed. 2024-02-28 20:12:38 +03:00
Леонид Юрьев (Leonid Yuriev)
ba719ef12a mdbx-test: доработка after-fork сценариев с устранением логической ошибки. 2024-02-28 20:12:38 +03:00
Леонид Юрьев (Leonid Yuriev)
f0cfedc26f mdbx-test: добавление extra/dupfixed_addodd. 2024-02-28 20:12:38 +03:00
Леонид Юрьев (Leonid Yuriev)
fbc83dd069 mdbx: исправление ложной ошибки MDBX_CORRUPTED (-30796) в сценарии "odd dupfixed".
Повреждение БД и/или потери данных не происходило, проблема лишь в
возврате ложной ошибки.

Благодарю пользователя/разработчика @Dvirsw (https://t.me/Dvirsw) за
сообщения о проблеме и предоставление минимального/оптимального сценария
воспроизведения.

--

Проблема была из-за излишнего условия при контроле внутренего поля
mp_upper в ходе проверки структуры страниц БД.

Поле mp_upper указывает на нижнуюю границу заполнения страницы от конца
к началу. Вследствие того, что значения ключей выравниваетня на четную
границу, это поле четно во всех случаях за исключением LEAF2-страницы
(листовая страница вложенного дерева для множественных значений
финсированной/одинаковой длины одного ключа), на которой размещено
нечетное количество значений нечетной длины.

Ошибка не проявлялась в большинстве случаев (в том числе в
стохастических тестах), так как штатно лишняя проверка производилась
только при чтении страницы и перебалансировке ключей, но не при каждом
добавлении значения. Тем не менее, сценарии тестов требуют
доработки/расширения для явного добавления нечетных dupfixed-сценариев.
2024-02-28 20:12:38 +03:00
Леонид Юрьев (Leonid Yuriev)
4f770999a8 mdbx: merge branch master into stable. 2023-12-12 10:15:27 +03:00
Леонид Юрьев (Leonid Yuriev)
185e43f3a8
mdbx: выпуск 0.12.9 "Ясень-4"
Стабилизирующий выпуск с исправлением обнаруженных ошибок и устранением недочетов.

Исправления и доработки:
------------------------

 - Ликвидация зависимости от ранее удаленной опции `MDBX_ENABLE_PREFAULT`, из-за
   чего опция `MDBX_ENABLE_MINCORE` не включалась автоматически, что приводило
   к не-активации соответствующего улучшения и не-достижению  декларируемого уровня
   производительности в сценариях использования в режиме `MDBX_WRITEMAP`.

 - Исправление авто-установки `MDBX_ENV_CHECKPID` при отключении использования
   функционала `madvise()` посредством опции сборки `MDBX_ENABLE_MADVISE=0`.
   Из-за чего при поддержке системой `madvise(MADV_DONTFORK)` не включался контроль pid.

 - Добавлена проверка переданного ключа на `NULL` при обработке `MDBX_GET_MULTIPLE`.

 - Добавлена проверка номеров корневых страниц в `coherency_check()`.

 - Обеспечен `const` для начала и конца диапазона в аргументах `mdbx_estimate_range()`.

 - Из разрабатываемой версии перенесены не-нарушающие совместимости доработки C++ API:

     - добавлен тип `mdbx::cursor::estimation_result`, а поведение методов
      `cursor::estimate()` унифицировано с `cursor::move()`;
     - для предотвращения незаметного неверного использования API, для инициализации
       возвращаемых по ссылке срезов, вместо пустых срезов задействован `slice::invalid()`;
     - добавлены дополнительные C++ операторы преобразования к типам C API;
     - для совместимости со старыми стандартами C++ и старыми версиями STL перенесены
       в public классы `buffer::move_assign_alloc` и `buffer::copy_assign_alloc`;
     - добавлен тип `mdbx::default_buffer`;
     - для срезов и буферов добавлены методы `hex_decode()`, `base64_decode()`, `base58_decode()`;
     - добавлен тип `mdbx::comparator` и функций `mdbx::default_comparator()`;
     - добавлены статические методы `buffer::hex()`, `base64()`, `base58()`;
     - для транзакций и курсоров добавлены методы `get_/set_context`;
     - добавлен метод `cursor::clone()`;

 - Поддержка base58 приведена в соответствии с черновиком RFC.

 - Переработка/исправление `to_hex()` и `from_hex()`.

 - Уменьшение `MDBX_opt_rp_augment_limit` по умолчанию до 1/3 от текущего количества страниц в БД.

Более подробная информация в [ChangeLog](https://libmdbx.dqdkfa.ru/md__change_log.html).

git diff' stat: 32 commits, 8 files changed, 667 insertions(+), 401 deletions(-)
Signed-off-by: Леонид Юрьев (Leonid Yuriev) <leo@yuriev.ru>
2023-12-11 23:24:05 +03:00
Леонид Юрьев (Leonid Yuriev)
f16c4303bf mdbx: исправление опечатки в комментарии. 2023-12-08 16:47:40 +03:00
Леонид Юрьев (Leonid Yuriev)
72332a8f9e mdbx: устранение несущественного замечания Coverity. 2023-12-08 15:09:01 +03:00
Леонид Юрьев (Leonid Yuriev)
6b72d88fde mdbx: исправление внутреннего регресса lck_serize_rc в сценарии повторного открытия БД одним процессом. 2023-12-05 15:32:18 +03:00
Леонид Юрьев (Leonid Yuriev)
4c139b3619 mdbx: устранение внутреннего регресса проверки состояние внутри транзакции.
После f0d523c507, при использовании
добавленного API блокировок, возможно ложно-положительное определение
состояние "внутри транзакции".
2023-12-05 15:32:18 +03:00
Леонид Юрьев (Leonid Yuriev)
169e69c52e mdbx: подстройка rp_augment_limit в зависимости от gc_time_limit.
Когда rp_augment_limit не задан пользователем посредством
`MDBX_opt_rp_augment_limit`, то как и ранее он подстраивается в
зависимости от текущего размера БД (актуального кол-ва страниц).

Теперь-же авто-устанавливаемое значение rp_augment_limit вычисляется
обратно-пропорционально `MDBX_opt_gc_time_limit`:

 - Если gc_time_limit == 0, то rp_augment_limit устанавливается в 1/3 от
   общего кол-ва страниц БД, но не меньше рационального минимума.
   Это соответствует прежнему поведению и обеспечивает достаточно глубокую
   переработку GC во всех не-экстремальных сценариях.

 - При gc_time_limit >= 16_секунд
   rp_augment_limit устанавливается в минимальное значение.

 - Когда 0 < gc_time_limit < 16_секунд
   rp_augment_limit устанавливается между минимумом и 1/3 от размера БД
   пропорционально остатку gc_time_limit до 16 секунд.

Соответственно, при больших значениях gc_time_limit, выбирается меньшее
значение rp_augment_limit, и контроль глубины переработки GC
ограничивается в основном по-времени.
2023-12-05 15:32:18 +03:00
Леонид Юрьев (Leonid Yuriev)
eeec44f56d mdbx: добавление MDBX_opt_gc_time_limit. 2023-12-05 15:32:18 +03:00
Леонид Юрьев (Leonid Yuriev)
508cf83c32 mdbx: исправление несущественной опечатки в комментарии. 2023-12-05 15:32:18 +03:00
Леонид Юрьев (Leonid Yuriev)
eb90ec6192 mdbx: новый размер MDBX_opt_rp_augment_limit по умолчанию в 1/3 от текущего кол-ва страниц в БД. 2023-12-05 15:32:18 +03:00
Леонид Юрьев (Leonid Yuriev)
e316bc8b29 mdbx-test: увеличения таймаута для smoke-теста на случай параллельного выполнения под Valgrind. 2023-12-05 15:32:18 +03:00
Леонид Юрьев (Leonid Yuriev)
5a86afaac3 mdbx-test: 3-часовой таймаут для extra_doubtless_positioning при использовании Valgrind. 2023-12-05 15:32:18 +03:00
Леонид Юрьев (Leonid Yuriev)
349759648d mdbx: правка rthc_afterfork() для Valgrind. 2023-12-05 15:32:18 +03:00
Леонид Юрьев (Leonid Yuriev)
43dbf8ec4f mdbx: правка txn_valgrind() для случая resurrect-after-fork. 2023-12-05 15:32:18 +03:00
Леонид Юрьев (Leonid Yuriev)
14558fa90b mdbx-test: правка тестов для совместимости с проблемными версиями glibc и glibc++. 2023-12-05 15:32:18 +03:00
Леонид Юрьев (Leonid Yuriev)
1db44c7914 mdbx++: перенос в public типов buffer::move_assign_alloc и buffer::copy_assign_alloc для старых стандартов C++. 2023-12-05 15:32:18 +03:00
Леонид Юрьев (Leonid Yuriev)
f97c127455 mdbx-test: тест для doubtless-API позиционирования курсоров. 2023-12-05 15:32:18 +03:00
Леонид Юрьев (Leonid Yuriev)
100f07e89a mdbx++: дополнительные операторы преобразования к типам C API. 2023-12-05 15:32:18 +03:00
Леонид Юрьев (Leonid Yuriev)
e68771df18 mdbx++: Добавление buffer_pair<> и buffer_pair_spec<>. 2023-12-05 15:32:18 +03:00
Леонид Юрьев (Leonid Yuriev)
ef69336189 mdbx-test: добавление теста для транскодеров hex/base64/base58. 2023-12-05 15:32:18 +03:00
Леонид Юрьев (Leonid Yuriev)
0e250a4457 mdbx++: добавление поддержки std::span<>. 2023-12-05 15:32:18 +03:00
Леонид Юрьев (Leonid Yuriev)
be8428257d mdbx++: переделка поддержки base58 по RFC-draft. 2023-12-05 15:32:18 +03:00
Леонид Юрьев (Leonid Yuriev)
dd47f1bfd9 mdbx++: переход на использование по-умолчанию default_allocator вместо legacy_allocator. 2023-12-05 15:32:18 +03:00
Леонид Юрьев (Leonid Yuriev)
1f2ff07796 mdbx++: переработка/исправление to_hex(). 2023-12-05 15:32:18 +03:00
Леонид Юрьев (Leonid Yuriev)
b412807fc1 mdbx++: добавление mdbx::default_buffer. 2023-12-05 15:32:18 +03:00
Леонид Юрьев (Leonid Yuriev)
8a6bddef44 mdbx++: поправка форматирования против бага clang-format. 2023-12-05 15:32:18 +03:00
Леонид Юрьев (Leonid Yuriev)
304cf25149 mdbx++: использование slice::invalid() для предотвращения незаметного неверного использования API. 2023-12-05 15:32:18 +03:00
Леонид Юрьев (Leonid Yuriev)
869cfb3fae mdbx++: исправление опечатки в doxygen-описании. 2023-12-05 15:32:18 +03:00
Леонид Юрьев (Leonid Yuriev)
0a58601cdf mdbx++: добавление pair::invalid() и взаимодействия с std::pair<>. 2023-12-05 15:32:18 +03:00
Леонид Юрьев (Leonid Yuriev)
355090f02e mdbx++: добавление is_xyz() для key_mode и value_mode. 2023-12-05 15:32:18 +03:00
Леонид Юрьев (Leonid Yuriev)
55142d8d6f mdbx++: добавление txn::commit_embark_read(). 2023-12-05 15:32:18 +03:00
Леонид Юрьев (Leonid Yuriev)
b9e2f6dc09 mdbx++: добавление MDBX_CXXnn_CONSTEXPR_ENUM. 2023-12-05 15:32:18 +03:00
Леонид Юрьев (Leonid Yuriev)
bf21ee7bde mdbx++: добавление операторов сравнения для mdbx::pair. 2023-12-05 15:32:18 +03:00
Леонид Юрьев (Leonid Yuriev)
0b68980489 mdbx++: добавление cursor::scan(predicate...) и т.п. 2023-12-05 15:32:18 +03:00
Леонид Юрьев (Leonid Yuriev)
4999264460 mdbx++: добавление error::boolean_or_throw(exception_thunk). 2023-12-05 15:32:18 +03:00
Леонид Юрьев (Leonid Yuriev)
25015c54e1 mdbx: обещанное "doubtless" API для позиционирования курсоров. 2023-12-05 15:32:18 +03:00
Леонид Юрьев (Leonid Yuriev)
5cf6542fa0 mdbx: добавление mdbx_cursor_scan_from() в API. 2023-12-05 15:32:18 +03:00
Леонид Юрьев (Leonid Yuriev)
bc562d5c06 mdbx: добавление mdbx_cursor_scan() в API. 2023-12-05 15:32:18 +03:00
Леонид Юрьев (Leonid Yuriev)
ed59ad22c6 mdbx: добавление mdbx_cursor_on_first/last_dup() в API. 2023-12-05 15:32:18 +03:00
Леонид Юрьев (Leonid Yuriev)
eee3e6eb6b mdbx++: добавление compare_positions() для курсоров. 2023-12-05 15:32:18 +03:00
Леонид Юрьев (Leonid Yuriev)
adcbb39379 mdbx: добавление mdbx_cursor_compare() в API. 2023-12-05 15:32:18 +03:00
Леонид Юрьев (Leonid Yuriev)
10abf73191 mdbx: рефакторинг и микро-оптимизация cursor_next|_prev() для dupsort-узлов.
- меньше сравнений и переходов.
 - вложенный курсор всегда сбрасывается/очищается при переходе с dupsort-узла.
2023-12-05 15:32:18 +03:00
Леонид Юрьев (Leonid Yuriev)
225f548339 mdbx++: добавление get_/set_context для транзакций и курсоров. 2023-12-05 15:32:13 +03:00
Леонид Юрьев (Leonid Yuriev)
990d4ea042 mdbx: обновление ChangeLog. 2023-11-28 21:48:56 +03:00
Леонид Юрьев (Leonid Yuriev)
8d67d23224 mdbx: исправление несущественной опечатки в комментарии. 2023-11-28 21:45:41 +03:00
Леонид Юрьев (Leonid Yuriev)
4a7a2034c0 mdbx: новый размер MDBX_opt_rp_augment_limit по умолчанию в 1/3 от текущего кол-ва страниц в БД. 2023-11-28 21:45:21 +03:00
Леонид Юрьев (Leonid Yuriev)
d963f3a971 mdbx: обновление ChangeLog. 2023-11-28 09:36:29 +03:00
Леонид Юрьев (Leonid Yuriev)
fb15c8ca0b mdbx++: дополнительные операторы преобразования к типам C API (backport). 2023-11-28 09:36:29 +03:00
Леонид Юрьев (Leonid Yuriev)
b6034e8045 mdbx-test: правка тестов для совместимости с проблемными версиями glibc и glibc++ (backport). 2023-11-28 09:36:29 +03:00
Леонид Юрьев (Leonid Yuriev)
ae01a8e891 mdbx++: перенос в public типов buffer::move_assign_alloc и buffer::copy_assign_alloc для старых стандартов C++ (backport). 2023-11-28 09:36:29 +03:00
Леонид Юрьев (Leonid Yuriev)
1277fe965d mdbx++: переделка поддержки base58 по RFC-draft (backport). 2023-11-28 09:36:10 +03:00
Леонид Юрьев (Leonid Yuriev)
7fc6a1b658 mdbx++: переработка/исправление to_hex() (backport). 2023-11-28 01:18:24 +03:00
Леонид Юрьев (Leonid Yuriev)
b1abcb8260 mdbx++: использование slice::invalid() для предотвращения незаметного неверного использования API (backport). 2023-11-28 01:18:24 +03:00
Леонид Юрьев (Leonid Yuriev)
7cd3dbbccb mdbx++: добавление mdbx::default_buffer (backport). 2023-11-28 01:18:24 +03:00
Леонид Юрьев (Leonid Yuriev)
352dd75ee8 mdbx++: исправление опечатки в doxygen-описании (backport). 2023-11-28 01:16:38 +03:00
Леонид Юрьев (Leonid Yuriev)
cf1541e4d7 mdbx++: добавление get/set_context для транзакций и курсоров (backport). 2023-11-28 01:16:38 +03:00
Леонид Юрьев (Leonid Yuriev)
446dbc9d6c mdbx++: добавление cursor::clone() (backport). 2023-11-28 01:13:24 +03:00
Леонид Юрьев (Leonid Yuriev)
64a5ad8c04 mdbx++: добавление методов buffer::hex_decode(), base64_decode(), base58_decode() (backport). 2023-11-28 01:13:24 +03:00
Леонид Юрьев (Leonid Yuriev)
0fd0d527d9 mdbx++: добавление типа mdbx::comparator и функций mdbx::default_comparator() (backport). 2023-11-28 01:13:24 +03:00
Леонид Юрьев (Leonid Yuriev)
4bed5d1779 mdbx++: добавление статических методов buffer::hex(), base64(), base58() (backport). 2023-11-28 01:13:24 +03:00
Леонид Юрьев (Leonid Yuriev)
6e4473777e mdbx: проверка pid только для запроса активной env (backport). 2023-11-28 01:10:23 +03:00
Леонид Юрьев (Leonid Yuriev)
992eee4f0f mdbx++: добавление cursor::clone(). 2023-11-26 16:56:34 +03:00
Леонид Юрьев (Leonid Yuriev)
d6a79a9c5f mdbx++: переименование slice::as_intXX() в slice::as_intXX_adapt() и добавление slice::as_intXX(). 2023-11-26 16:56:34 +03:00
Леонид Юрьев (Leonid Yuriev)
6facd20b2b mdbx++: добавление buffer::as_uint64() и т.д. 2023-11-26 16:56:34 +03:00
Леонид Юрьев (Leonid Yuriev)
e66df2c21b mdbx++: добавление методов buffer::hex_decode(), base64_decode(), base58_decode(). 2023-11-26 16:56:34 +03:00
Леонид Юрьев (Leonid Yuriev)
649bbb9d90 mdbx++: добавление типа mdbx::comparator и функций mdbx::default_comparator(). 2023-11-26 16:56:34 +03:00
Леонид Юрьев (Leonid Yuriev)
c8319aabe7 mdbx++: добавление статических методов buffer::hex(), base64(), base58(). 2023-11-26 16:56:19 +03:00
Леонид Юрьев (Leonid Yuriev)
7f21515940 mdbx: микро-оптимизация cursor_set().
Чуть меньше сравнений и переходов.
2023-11-26 12:25:13 +03:00
Леонид Юрьев (Leonid Yuriev)
5abb6a9bbf mdbx-doc: удаление устаревшего упоминания MDBX_EAGAIN. 2023-11-17 21:18:21 +03:00
Леонид Юрьев (Leonid Yuriev)
c014685c01 mdbx: обновление ChangeLog. 2023-11-12 21:46:52 +03:00
Леонид Юрьев (Leonid Yuriev)
476da5f8cf mdbx++: добавление cursor::estimation_result и переделка cursor::estimate() (backport). 2023-11-12 21:30:09 +03:00
Леонид Юрьев (Leonid Yuriev)
b905a6a391 mdbx: const для начала и конца диапазона в аргументах mdbx_estimate_range() (backport). 2023-11-12 21:30:09 +03:00
Леонид Юрьев (Leonid Yuriev)
d94f34b2c0 mdbx: рефакторинг обработки MDBX_GET_MULTIPLE добавление проверки key на NULL (backport). 2023-11-12 21:30:09 +03:00
Леонид Юрьев (Leonid Yuriev)
f5ce7322c4 mdbx: исправление авто-установки MDBX_ENV_CHECKPID для случая MDBX_ENABLE_MADVISE=0 (backport). 2023-11-12 21:30:09 +03:00
Леонид Юрьев (Leonid Yuriev)
ab5d290f11 mdbx: проверка номеров корневых страниц в coherency_check() (backport). 2023-11-12 21:29:18 +03:00
Леонид Юрьев (Leonid Yuriev)
6cef39c32f mdbx++: добавление cursor::estimation_result и переделка cursor::estimate(). 2023-11-12 20:37:57 +03:00
Леонид Юрьев (Leonid Yuriev)
44beae00ec mdbx: const для начала и конца диапазона в аргументах mdbx_estimate_range(). 2023-11-12 20:37:57 +03:00
Леонид Юрьев (Leonid Yuriev)
b7605e8033 mdbx: рефакторинг обработки MDBX_GET_MULTIPLE добавление проверки key на NULL. 2023-11-12 20:37:57 +03:00
Леонид Юрьев (Leonid Yuriev)
100e95957c mdbx-test: доработка логирования для использования после/из глобальных деструкторов. 2023-11-12 20:37:57 +03:00
Леонид Юрьев (Leonid Yuriev)
cfce4ef4d3 mdbx-test: явная установка append-режима для stdout/stderr. 2023-11-12 20:37:57 +03:00
Леонид Юрьев (Leonid Yuriev)
7eb2f4130e mdbx: использование const MDBX_txn где это возможно в API (backport). 2023-11-12 19:33:27 +03:00
Леонид Юрьев (Leonid Yuriev)
d9f49b17de mdbx-test: добавление тестов для mdbx_env_resurrect_after_fork(). 2023-11-12 01:49:53 +03:00
Леонид Юрьев (Leonid Yuriev)
af4dfe541b mdbx: добавление mdbx_env_resurrect_after_fork() в API. 2023-11-12 01:49:53 +03:00
Леонид Юрьев (Leonid Yuriev)
a22ec56938 mdbx: использование pthread_atfork(after_fork). 2023-11-12 01:49:53 +03:00
Леонид Юрьев (Leonid Yuriev)
ce74fae036 mdbx: рефакторинг и выделение env_open(). 2023-11-12 01:49:53 +03:00
Леонид Юрьев (Leonid Yuriev)
54efb8bd81 mdbx: не считаем ошибки ipc-unlock критичными в случае смены pid. 2023-11-12 01:49:53 +03:00
Леонид Юрьев (Leonid Yuriev)
a3e2300f58 mdbx: возможность вызова osal_lck_destroy() в дочернем процессе после fork(). 2023-11-12 01:49:53 +03:00
Леонид Юрьев (Leonid Yuriev)
7ad54f54b4 mdbx: объединение lck-списка и rthc-таблицы для упрощения (де)регистрации TLS-деструкторов. 2023-11-12 01:49:53 +03:00
Леонид Юрьев (Leonid Yuriev)
eddade7b99 mdbx: корректировка префиксов имён osal-ipc функций. 2023-11-12 01:00:41 +03:00
Леонид Юрьев (Leonid Yuriev)
97418d5c9c mdbx: рефакторинг env_handle_pathname() для одной точки выделения/освобождения памяти. 2023-11-12 01:00:41 +03:00
Леонид Юрьев (Leonid Yuriev)
1b2f5f25d4 mdbx: рефакторинг и вынос txn_abort() без кода входящих пероверок. 2023-11-12 01:00:41 +03:00
Леонид Юрьев (Leonid Yuriev)
2fe01eee89 mdbx: проверка pid только для запроса активной env. 2023-11-12 01:00:41 +03:00
Леонид Юрьев (Leonid Yuriev)
6477e6c5de mdbx: исправление авто-установки MDBX_ENV_CHECKPID для случая MDBX_ENABLE_MADVISE=0. 2023-11-12 01:00:41 +03:00
Леонид Юрьев (Leonid Yuriev)
dea6570fc1 mdbx: доработка coherency_check() для случая плохих номеров корневых страниц. 2023-11-11 12:29:15 +03:00
Леонид Юрьев (Leonid Yuriev)
9a6f8a1bf8 mdbx-test: увеличение tail-log до 3333 для CI. 2023-11-11 12:29:13 +03:00
Леонид Юрьев (Leonid Yuriev)
24d5b26bc5 mdbx-make: переименование целей. 2023-11-11 12:29:13 +03:00
Леонид Юрьев (Leonid Yuriev)
0916d24321 mdbx: использование опции ENABLE_MEMCHECK вместо ENABLE_VALGRIND и MDBX_USE_VALGRIND. 2023-11-11 12:29:13 +03:00
Леонид Юрьев (Leonid Yuriev)
c216e1afb7 mdbx-test: добавление проверки mdbx_dbi_rename(). 2023-11-11 12:29:10 +03:00
Леонид Юрьев (Leonid Yuriev)
c9c02dddfb mdbx: добавление mdbx_dbi_rename() в API. 2023-11-11 12:29:10 +03:00
Леонид Юрьев (Leonid Yuriev)
903d964f4d mdbx: интенсивное использование __restrict, в том числе при определении элементов структур. 2023-11-11 12:29:10 +03:00
Леонид Юрьев (Leonid Yuriev)
96504bf338 mdbx: отложенное освобождение имен связанных c dbi-хендлами и добавление опции MDBX_ENABLE_DBI_LOCKFREE.
Отложенное освобождение позволяет реализовать безопасное выполнение
fastpath/lockfree при повторном открытии из других потоков/транзакцйий
уже открытых subDB, что и происходит при активации добавленной опции
сборки `MDBX_ENABLE_DBI_LOCKFREE`.
2023-11-11 12:29:10 +03:00
Леонид Юрьев (Leonid Yuriev)
3622669a9f mdbx: проверка db-флагов и ре-инициализация MainDB при изменении флагов другим процессом. 2023-11-11 12:29:10 +03:00
Леонид Юрьев (Leonid Yuriev)
e6af7d7c53 mdbx: переработка инициализации, проверки и импорта dbi-хендлов в транзакциях.
Ранее инициализация в транзакциях структур данных, связанных с
dbi-хендлами и subDb, выполнялась непосредственно при запуске
транзакций. Что в сценариях с большим кол-вом dbi-дексприторов (например
libfpta) порождало заметные накладные расходы, которые расли линейно от
общего кол-ва открытых subDb, а не от реально используемых в транзакции.
При использовании одной-двух сотен хендлов, при старте каждой транзакции
могли копироваться и/или обнуляться десятки килобайт. Теперь этот
недостаток устранен.

Изменена схема инициализации, валидации и импорта хендлов открытых после
старта транзакции:

1) Инициализация теперь выполняется отложенна, а при старте транзации
   обнуляется только массив с однобайтовыми статустами dbi-хендлов.
   При этом доступнва опция сборки `MDBX_ENABLE_DBI_SPARSE`, при активации
   которой используется битовая карты, что снижает объем инициализации
   при старте транзакции в 8 раз (CHAR_BIT).

2) Переработана валидация dbi-хендлов на входах API, с уменьшением кол-ва
   проверок и ветвлений до теоретического минимума.

3) Переработ импорт dbi-хендов открытых после старта транзакци, теперь
   при этом не захватывается мьютекс.
2023-11-11 12:29:10 +03:00
Леонид Юрьев (Leonid Yuriev)
796e56b9b9 mdbx: добавление кода системной ошибки MDBX_EDEADLK. 2023-11-11 12:29:10 +03:00
Леонид Юрьев (Leonid Yuriev)
4b79d46d38 mdbx: удаление поля mt_dbxs из транзакции.
Явного выигрыша или проигрыша в производительности тут нет. Но теперь
меньше алиасинга указателей и чуть меньше полей в транзакциях.
2023-11-11 12:29:10 +03:00
Леонид Юрьев (Leonid Yuriev)
f317170706 mdbx: переименование внутренних полей и макросов для улучшения читаемости кода. 2023-11-11 12:29:10 +03:00
Леонид Юрьев (Leonid Yuriev)
81f386f831 mdbx: перемещение полей внутри MDBX_txn и MDBX_env.
В текущем понимании так префетчер ЦПУ может быть чуть более эффективным
и чуть меньше зазоров для выравнивания.
2023-11-11 12:29:10 +03:00
Леонид Юрьев (Leonid Yuriev)
54920cd07b mdbx: исправление assert-проверок внутри osal_txn_lock(). 2023-11-11 12:29:10 +03:00
Леонид Юрьев (Leonid Yuriev)
7a413406be mdbx-test: обновление исключений для Valgrind. 2023-11-11 12:29:07 +03:00
Леонид Юрьев (Leonid Yuriev)
07fc7b9227 mdbx-test: добавление опции --taillog в стохастический скрипт. 2023-10-30 22:01:35 +03:00
Леонид Юрьев (Leonid Yuriev)
ad4d00677b mdbx: PTHREAD_MUTEX_ERRORCHECK при MDBX_DEBUG > 0. 2023-10-29 18:44:54 +03:00
Леонид Юрьев (Leonid Yuriev)
1943db7d41 mdbx: merge branch master into devel. 2023-10-29 18:23:49 +03:00
Леонид Юрьев (Leonid Yuriev)
ed8c7ead4e mdbx: ликвидация ошибочной зависимости от удаленной опции MDBX_ENABLE_PREFAULT. 2023-10-29 12:20:54 +03:00
Леонид Юрьев (Leonid Yuriev)
5ebc2c523d mdbx: обновление ChangeLog. 2023-10-23 20:35:55 +03:00
Леонид Юрьев (Leonid Yuriev)
24f08aed28 mdbx-doc: обновление конфигурации Doxygen. 2023-10-23 20:25:03 +03:00
Леонид Юрьев (Leonid Yuriev)
c254c728d2 mdbx: костыль для ложно-положительного предупреждения Coverity. 2023-10-23 20:25:03 +03:00
Леонид Юрьев (Leonid Yuriev)
04511a7a99 mdbx: использование const MDBX_txn где это возможно в API. 2023-10-23 20:25:03 +03:00
Леонид Юрьев (Leonid Yuriev)
0e4c6d61a4 mdbx-tools: несущественный рефакторинг mdbx_load. 2023-10-23 20:25:03 +03:00
Леонид Юрьев (Leonid Yuriev)
4d3f7e1edc mdbx: добавление mdbx_txn_release_all_cursors() в API. 2023-10-23 20:25:03 +03:00
Леонид Юрьев (Leonid Yuriev)
d28a397b2d mdbx: добавление mdbx_cursor_unbind() в API. 2023-10-23 20:25:03 +03:00
Леонид Юрьев (Leonid Yuriev)
5f274eb4c6 mdbx: вывод информации из mdbx_env_chk() о boot-id в каждой мета-странице. 2023-10-23 20:25:03 +03:00
Леонид Юрьев (Leonid Yuriev)
a67b9b9729 mdbx: доработка env_info_snap(). 2023-10-23 20:25:03 +03:00
Леонид Юрьев (Leonid Yuriev)
224f26813e mdbx: возвращение MDBX_TXN_INVALID (INT32_MIN) из mdbx_txn_flags() при передаче невалидной транзакции. 2023-10-23 20:25:03 +03:00
Леонид Юрьев (Leonid Yuriev)
fc1685a178 mdbx: STATIC_ASSERT() для MDBX_TXN_RDONLY_PREPARE. 2023-10-23 20:25:03 +03:00
Леонид Юрьев (Leonid Yuriev)
cdbcf54af1 mdbx-tests: добавление --read-var-info=yes для Valgrind. 2023-10-23 20:25:03 +03:00
Леонид Юрьев (Leonid Yuriev)
786da2b089 mdbx-tools: вывод информации об уровне детализации/verbosity. 2023-10-23 20:25:03 +03:00
Леонид Юрьев (Leonid Yuriev)
253a56206b mdbx: переработка и перенос функционала утилиты mdbx_chk внутрь библиотеки. 2023-10-23 20:25:03 +03:00
Леонид Юрьев (Leonid Yuriev)
f0d523c507 mdbx: дополнение API функциями lock/unlock/upgrade/downgrade основной блокировки. 2023-10-23 20:25:03 +03:00
Леонид Юрьев (Leonid Yuriev)
dd9fc963d2 mdbx: изменение и расширение API функционалом проверки целостности структуры БД. 2023-10-23 20:25:03 +03:00
Леонид Юрьев (Leonid Yuriev)
e9ad618b58 mdbx: начало ветки 0.13, с новым функционалом и изменением API.
Планируется очистка от функций и возможностей ранее объявленных
устаревшими. В частности, будет удалена поддержка пользовательских
функций сравнения, которые были обьявлены устаревшими начиная с версии
0.9, более 33 месяцев назад.
2023-10-23 20:24:59 +03:00
Леонид Юрьев (Leonid Yuriev)
42ef1dcd39 mdbx: merge branch master into devel. 2023-10-23 19:49:50 +03:00
Леонид Юрьев (Leonid Yuriev)
93429d3a23 mdbx: merge branch master into stable. 2023-10-23 18:15:26 +03:00
Леонид Юрьев (Leonid Yuriev)
080875cd6d mdbx: Обновление ChangeLog. 2023-10-23 18:13:35 +03:00
Леонид Юрьев (Leonid Yuriev)
753cfd00eb mdbx: обновление патча для старых версий buildroot. 2023-10-23 18:12:21 +03:00
Леонид Юрьев (Leonid Yuriev)
311a6e5d10 mdbx++: добавление забытого исключения mdbx::duplicated_lck_file. 2023-10-23 16:33:34 +03:00
Леонид Юрьев (Leonid Yuriev)
e58b582639 mdbx: исправление MDBX_LAST_ADDED_ERRCODE. 2023-10-23 16:33:30 +03:00
Леонид Юрьев (Leonid Yuriev)
e2ed55853d mdbx: удаление устаревших mdbx_set_compare() и mdbx_set_dupsort(). 2023-10-23 16:17:05 +03:00
Леонид Юрьев (Leonid Yuriev)
02c7cf2a9c
mdbx: выпуск 0.12.8 "Владимир Уткин"
Стабилизирующий выпуск с исправлением обнаруженных ошибок и устранением недочетов,
в день 100-летия со дня рождения выдающегося советского и российского ученого и конструктора [Влади́мира Фёдоровича У́ткина](https://ru.wikipedia.org/wiki/Уткин,_Владимир_Фёдорович).

Исправления и доработки:
------------------------

 - Устранение регресса/ошибки в пути обработки `put(MDBX_MULTIPLE)` при пакетном/оптовом
   помещении в БД множественных значений одного ключа (aka multi-value или dupsort).
   Проявление проблемы зависит от компилятора и опций оптимизации/кодогенерации, но с большой вероятностью возвращется
   ошибка `MDBX_BAD_VALSIZE` (`-30781`), а в отладочных сборках срабатывает проверка `cASSERT(mc, !"Invalid key-size")`.
   Сценарии приводящие к другим проявлениям на данный момент не известны.

 - Реализована перезапись в `mdbx_put(MDBX_CURRENT)` всех текущих мульти-значений ключа
   при отсутствии флага `MDBX_NOOVERWRITE`. Ранее в такой ситуации возвращалась ошибка `MDBX_EMULTIVAL`.
   В текущем понимании новое поведение более удобно и не создаёт проблем совместимости с ранее написанным кодом.

 - Добавлена возможность использовать `mdbx_cursor_get(MDBX_GET_MULTIPLE)` без предварительной установки
   курсора, совмещая операцию пакетного получения данных с позиционированием курсора на передаваемый ключ.

 - Микрооптимизация и рефакторинг `cursor_put_nochecklen()` в продолжение исправления
   регресса/ошибки в пути обработки `put(MDBX_MULTIPLE)`.

 - Уточнение формулировок в описании API, в том числе пояснений о `SIGSEGV`
   и недопустимости прямого изменения данных.

Более подробная информация в [ChangeLog](https://libmdbx.dqdkfa.ru/md__change_log.html).

git diff' stat: 24 commits, 18 files changed, 624 insertions(+), 94 deletions(-)
Signed-off-by: Леонид Юрьев (Leonid Yuriev) <leo@yuriev.ru>
2023-10-17 18:16:29 +03:00
Леонид Юрьев (Leonid Yuriev)
83f19fc993 mdbx: обновление ChangeLog. 2023-10-14 14:37:08 +03:00
Леонид Юрьев (Leonid Yuriev)
d440485156 mdbx-doc: добавление определений макросов для Doxygen. 2023-10-14 14:36:14 +03:00
Леонид Юрьев (Leonid Yuriev)
25ec8e253f mdbx-doc: уточнение формулировок в описании API. 2023-10-14 14:16:49 +03:00
Леонид Юрьев (Leonid Yuriev)
248208cf5d mdbx: обновлене ChangeLog (планирование релиза). 2023-10-11 11:14:28 +03:00
Леонид Юрьев (Leonid Yuriev)
f02a0ffa21 mdbx: возможность использования MDBX_GET_MULTIPLE без предварительной установки курсора. 2023-10-10 22:34:05 +03:00
Леонид Юрьев (Leonid Yuriev)
2b0eae08f5 mdbx: обновление ChangeLog. 2023-10-09 22:12:06 +03:00
Леонид Юрьев (Leonid Yuriev)
5d9740bbcf mdbx-cmake: использование add_mdbx_option() для вывода информации об mdbx-опциях при сборке. 2023-10-09 21:49:58 +03:00
Леонид Юрьев (Leonid Yuriev)
39f2bb142a mdbx: сокращение излишнего вызова osal_thread_self(). 2023-10-09 20:58:01 +03:00
Леонид Юрьев (Leonid Yuriev)
e9b10db255 mdbx++: доработка использования filesystem для старых компиляторов. 2023-10-09 07:34:01 +03:00
Леонид Юрьев (Leonid Yuriev)
687622b8b1 mdbx: устранение предупреждений Valgrind при логировании в отладочных сборках.
Достаточно запутано:

 - Внутри `update_gc()` используется создание записей с резервированием
   посредством `put(MDBX_RESERVE)` в циклах с ранним выходом и последующим
   заполнением.

 - При этом в случае раннего выхода (из цикла из-за изменения набора
   страниц) зарезервированное место в добавленных записях остается
   незаполненным/неиницилизированным (подкрашенным в Valgrind или ASAN).

 - Чтение этих незаполненных/неиницилизированных данных штатно не
   происходит, но в отладочных сборках при включении детального уровне
   логирования выполняется отладочный вывод значений ключей и данных при
   позиционировании курсоров.

 - В свою очередь, `update_gc()` либо удаляет, либо заполняет
   зарезервированные записи, но для этого требуется позиционирование
   курсора, что в отладочных сборках приводит к чтению
   незаполненных/неиницилизированных записей и печали Valgrind/ASAN.

Теперь внутри `update_gc()` в отладочных сборках с поддержкой Valgrind
или ASAN место в резервируемых записях явно инициализируется.
2023-10-08 18:31:12 +03:00
Леонид Юрьев (Leonid Yuriev)
fd8a99acff mdbx: доработка mdbx_dump_val() используемой для логирования и отладки.
- Обеспечении терминирующего нуля даже при нехватке буфера и
   опосредованных предупреждений Valgrind из-за чтения внутри strlen()
   неинициализированных данных при последующем логировании/печати.

 - Ускорение за счет отказа от использования snpruintf().
2023-10-08 17:43:13 +03:00
Леонид Юрьев (Leonid Yuriev)
e21e91ad1f mdbx-doc: уточнение формулировок о SIGSEGV и недопустимости прямого изменения данных. 2023-10-08 11:55:30 +03:00
Леонид Юрьев (Leonid Yuriev)
6027348651 mdbx: обновление ChangeLog. 2023-10-08 09:42:56 +03:00
Леонид Юрьев (Leonid Yuriev)
1aead6869a mdbx: костыль для глушения/игнорирования EDEADLK в ряде сценариев при использовании Valgrind или ASAN.
Достаточно запутанно:

 - Для полноценного контроля при использовании Valgrind или ASAN
   требуется закрашивать/отравлять отображение файла БД выше границы
   распределенных страниц.

 - Производить такое подкрашивание/отравление необходимо в синхронизации
   с пишущими транзакциями и запросами на изменение геометрии, в том числе
   при изменении размера БД и/или геометрии другим процессом.

 - Для такой синхронизации логично и проще всего использовать основной
   мьютекс/механизм блокировки пишущих транзакций, что и происходит внутри
   txn_valgrind().

 - Однако, в этой схеме может возникать ошибка EDEADLK, когда
   txn_valgrind() вызывается при завершении читающей транзакции
   выполняющейся с дополнительной блокировкой пишущих транзакций.

 - Как таковая ошибка EDEADLK при этом проблем не создаёт и поэтому
   просто игнорируется. Но утилита mdbx_chk при работе в кооперативном
   (не эксклюзивном) режиме чтения-записи использует именно такой сценарий,
   а возникающую при этом ошибку EDEADLK засчитывает как проблему при
   проверке.

 = В результате, при использовании Valgrind или ASAN утилита mdbx_chk
   запущенная с опциями `-wc` всегда завершается неудачей из-за как минимум
   одной проблемы в ходе проверки. Что внешне выглядит как
   недочет/ошибка/регресс и создает проблемы при автоматизированном
   тестировании.

Добавленный костыль использует atomic-счетчик, который инкремируется до
и декремируется после попытки захвата блокировки изнутри txn_valgrind().
В свою очередь, код обрабатывающий ошибку захвата блокировки, игнорирует
EDEADLK при ненулевом значении счетчика. Активируется костыль только при
сборке с поддержкой Valgrind или включенном ASAN, и не оказывает
никакого влияния в остальных случаях.
2023-10-07 23:37:51 +03:00
Леонид Юрьев (Leonid Yuriev)
45721d4064 mdbx-test: устранение жалобы Valgrind на утечку памяти в одном из тестов.
Перед выходом из теста не разрушался курсор.
2023-10-07 18:28:38 +03:00
Леонид Юрьев (Leonid Yuriev)
6de15514df mdbx: устранение жалобы Valgrind на чтение неинициализированной памяти.
Маркер steady/weak в прототипе/заготовке мета-страницы не
инициализировался, но опосредованно читался кодом проверки
когерентности unified buffer/page cache.

Прочитанное не-инициализированное/случайное значение использовалось в
условии одного из ветвлений, но не оказывало какого-либо влияния, так
как в данном контексте все пути приводят к одному инварианту результата.
2023-10-07 18:27:32 +03:00
Леонид Юрьев (Leonid Yuriev)
215bee9ab7 mdbx: обновление ChangeLog. 2023-10-07 10:22:34 +03:00
Леонид Юрьев (Leonid Yuriev)
7d3f136a3a mdbx-cmake: добавление extra-тестов в область видимости ctest. 2023-10-07 09:08:34 +03:00
Леонид Юрьев (Leonid Yuriev)
eb348ca34c mdbx-test-extra: добавление теста dupfixed_multiple. 2023-10-07 09:08:28 +03:00
Леонид Юрьев (Leonid Yuriev)
cb48ee8f3d mdbx: перезапись в mdbx_put() всех мульти-значений ключа при отсутствии флага MDBX_NOOVERWRITE. 2023-10-07 09:08:28 +03:00
Леонид Юрьев (Leonid Yuriev)
a387284458 mdbx: микро-оптимизация и рефакториг cursor_put_nochecklen().
- удалены переменные-флаги dupdata_flag и do_sub;
 - вместо dupdata_flag используется условие dkey.iov_base != nullptr;
 - вместо do_sub используется условие flags & F_DUPDATA;
 - очищено использование dkey, добавлена инициализация dkey.iov_base в ключевых точках;
 - декларация части переменных перенеса ближе к месту использования.
2023-10-07 09:08:28 +03:00
Леонид Юрьев (Leonid Yuriev)
e7ae8214fd mdbx: исправление cursor_put_nochecklen(MDBX_MULTIPLE). 2023-10-06 21:57:25 +03:00
Леонид Юрьев (Leonid Yuriev)
e195f5bcf7 mdbx++: перегрузка txn::put_multiple() и добавление контроля POD. 2023-10-06 21:56:21 +03:00
Леонид Юрьев (Leonid Yuriev)
c256e8358c mdbx++: добавление slice::as_pod<typename>(). 2023-10-06 12:07:38 +03:00
Леонид Юрьев (Leonid Yuriev)
bc6d320bb2 mdbx: исправление несущественных предупреждений при MDBX_ENABLE_PROFGC=ON.
Thanks @Alain (reported via https://t.me/libmdbx).
2023-10-04 08:18:10 +03:00
Леонид Юрьев (Leonid Yuriev)
3d187abc1b mdbx: merge branch master into stable. 2023-06-18 17:18:16 +03:00
Леонид Юрьев (Leonid Yuriev)
7b12e7323f
mdbx: выпуск 0.12.7 "Артек"
Стабилизирующий выпуск с исправлением обнаруженных ошибок и устранением недочетов,
в день основания международного детского центра [«Арте́к»](https://ru.wikipedia.org/wiki/Артек).

Исправления и доработки:
------------------------

  - Исправление опечатки в имени переменной внутри `mdbx_env_turn_for_recovery()`.
  - Обходное решение проблем сборки посредством GCC с использование опций `-m32 -arch=i686 -Ofast`.
  - Доработка режима "восстановления" БД и переключения на заданную мета-страницу.

  Более подробная информация в [ChangeLog](https://libmdbx.dqdkfa.ru/md__change_log.html).

Мелочи:
-------

 - Незначительное уточнение CMake-пробника для `std::filesystem`,
   проверяющего необходимость линковки с дополнительными библиотеками C++.
 - Устранение минорных предупреждений старых компиляторов в тестах.
 - Устранение причины ложно-позитивного предупреждения новых версий GCC в C++ API.
 - Исправление ссылки на репозиторий бенчмарка ioarena.
 - Добавление перекрестных ссылок в doxygen-документацию по C++ API.
 - Уточнение ограничений в разделе [Restrictions & Caveats](https://libmdbx.dqdkfa.ru/intro.html#restrictions).
 - Исправление ссылок на описание `mdbx_canary_put()`.

14 files changed, 222 insertions(+), 56 deletions(-)
Signed-off-by: Леонид Юрьев (Leonid Yuriev) <leo@yuriev.ru>
2023-06-16 20:04:01 +03:00
Леонид Юрьев (Leonid Yuriev)
45aa39c68b mdbx: обновление ChangeLog. 2023-06-09 00:04:27 +03:00
Леонид Юрьев (Leonid Yuriev)
d02bdcf2bd mdbx: костыль для GCC при сборке с -m32 -arch=i686 -Ofast.
Обходное решение проблем сборки посредством GCC с использование опций `-m32 -arch=i686 -Ofast`.

Проблема обусловлена ошибкой GCC, из-за которой конструкция `__attribute__((__target__("sse2")))`
не включает полноценное использование инструкций SSE и SSE2, если это не было сделано посредством
опций командной строки, но была использована опция `-Ofast`.

В результате сборка заканчивалась сообщением об ошибке:
    gcc/i686-buildroot-linux-gnu/12.2.0/include/xmmintrin.h: In function 'diffcmp2mask_sse2':
    gcc/i686-buildroot-linux-gnu/12.2.0/include/xmmintrin.h:814:1: error: inlining failed in call to 'always_inline' '_mm_movemask_ps': target specific option mismatch
      814 | _mm_movemask_ps (__m128 __A)
2023-06-09 00:02:31 +03:00
Леонид Юрьев (Leonid Yuriev)
5561cec9c5 mdbx: дополнительный static_assert для контроля выравнивания 64-битного atomic-типа. 2023-06-09 00:02:31 +03:00
Леонид Юрьев (Leonid Yuriev)
ff6674b377 mdbx: не делаем неявных обновлений БД (изменения размера или статуса мета-страниц) в режиме восстановления.
Это позволяет обезопасить БД (снизить шанс её разрушения) если
пользователь при попытке восстановления, либо просто в качестве
эксперимента, задал утилите `mdbx_chk` неверную или опасную комбинацию
параметров.

При этом обычная проверка, как и явное переключение мета-страниц,
работают по-прежнему.
2023-06-09 00:02:31 +03:00
Леонид Юрьев (Leonid Yuriev)
ca6f04c52a mdbx: не учитываем geo.next при сверке геометрии после открытия БД.
Полная сверка геометрии на совпадение (включая geo.next) не является
ошибкой, но может приводить к выводу бессмысленного предупреждения о
пропуске обновлении/перезаписи геометрии при открытии БД в режиме
восстановления (с явным указанием мета-страницы).
2023-06-09 00:02:31 +03:00
Леонид Юрьев (Leonid Yuriev)
db6cf469c9 mdbx: доработка mdbx_env_turn_for_recovery() чтобы не обновлять мета-страницы при отсутствии изменений. 2023-06-09 00:02:31 +03:00
Леонид Юрьев (Leonid Yuriev)
d516e903d4 mdbx: исправление очепятки в mdbx_env_turn_for_recovery().
Исправление опечатки в имени переменной внутри `mdbx_env_turn_for_recovery()`,
что приводило к неверному поведению в некоторых ситуациях.

С точки зрения пользователя, с учетом актуальных сценариев использования
утилиты `mdbx_chk`, был только один специфический/редкий сценарий
проявления ошибки/проблемы - когда выполнялась проверка и активация
слабой/weak мета-страницы с НЕ-последней транзакцией после системной
аварии машины, где БД использовалась в хрупком/небезопасном режиме.
В сценарии, при успешной проверке целевой страницы и её последующей
активации выводилось сообщение об ошибке, связанной со срабатыванием
механизма контроля не-когерентности кэша файловой системы и отображенных
в ОЗУ данных БД. При этом БД успешно восстанавливалось и не было
каких-либо негативных последствия, кроме самого сообщения об ошибке.

Технически же ошибка проявлялась при "переключении" на мета-страницу,
когда у хотя-бы одной из двух других мета-страниц номер транзакции был
больше:

  * Если содержимое других мета-страниц было корректным, а номера
    связанных транзакций были больше, то результирующий номер транзакции в
    целевой/активируемой мета-страницы устанавливается без учета этих
    мета-страниц и мог быть меньше-или-равным.

  * В результате, если такие мета-страницы были в статусе слабых/weak, то
    при закрытии БД после переключения могла срабатывать защита от
    не-когерентности unified buffer/page cache, а в отладочных сборках могла
    срабатывать assert-проверка.

  * Если же такие мета-страницы были в статусе сильных/steady, то
    переключение на новую мета-страницу могло не давать эффекта либо
    приводить к появлению двух мета-страниц с одинаковым номером транзакции,
    что является ошибочной ситуацией.
2023-06-09 00:01:41 +03:00
Леонид Юрьев (Leonid Yuriev)
7aaae2ecd5 mdbx-doc: исправление ссылок на mdbx_canary_put(). 2023-06-01 08:48:38 +03:00
Леонид Юрьев (Leonid Yuriev)
bf1c753be3 mdbx: обновление ChangeLog. 2023-05-26 18:10:47 +03:00
Леонид Юрьев (Leonid Yuriev)
79edab2adf mdbx-doc: уточнение ограничений в разделе "Restrictions & Caveats". 2023-05-25 12:54:55 +03:00
Леонид Юрьев (Leonid Yuriev)
37792cc568 mdbx: обновление ChangeLog. 2023-05-23 15:45:27 +03:00
Леонид Юрьев (Leonid Yuriev)
e8d2a5bd09 mdbx++: добавление пары перекрестных ссылок в doxygen-документацию. 2023-05-23 15:35:36 +03:00
Леонид Юрьев (Leonid Yuriev)
2c2612ba23 mdbx: fix link to ioarena repo. 2023-05-14 15:57:28 +03:00
Леонид Юрьев (Leonid Yuriev)
60b483025c mdbx++: устранение ложно-позитивного предупреждения новых версий GCC. 2023-05-14 01:23:48 +03:00
Леонид Юрьев (Leonid Yuriev)
2abf80a199 mdbx-test-extra: устранение минорных предупреждений старых компиляторов. 2023-05-14 01:07:15 +03:00
Леонид Юрьев (Leonid Yuriev)
4fd21d2f7b mdbx-cmake: незначительное уточнение пробника для std::filesystem. 2023-05-14 01:06:52 +03:00
Леонид Юрьев (Leonid Yuriev)
c019631a8c
mdbx: выпуск 0.12.6 "ЦСКА"
Стабилизирующий выпуск с исправлением обнаруженных ошибок и устранением
недочетов, в день 100-летнего юбилея спортивного клуба [«ЦСКА»](https://ru.wikipedia.org/wiki/Центральный_спортивный_клуб_Армии).

Мелочи:
-------

 - Обновление патча для старых версий buildroot.
 - Использование clang-format-16.
 - Использование `enum`-типов вместо `int` для устранения предупреждений GCC 13,
   что могло ломать сборку в Fedora 38.

14 files changed, 117 insertions(+), 83 deletions(-)
Signed-off-by: Леонид Юрьев (Leonid Yuriev) <leo@yuriev.ru>
2023-04-29 21:30:35 +03:00
Леонид Юрьев (Leonid Yuriev)
35d4834647 mdbx: обновление ChangeLog. 2023-04-24 16:10:40 +03:00
Леонид Юрьев (Leonid Yuriev)
aee8caf9a0 mdbx: обновление патча для старых версий buildroot.
See https://buildroot.org/
2023-04-19 13:38:25 +03:00
Леонид Юрьев (Leonid Yuriev)
99c9bc2411 mdbx: использование clang-format-16. 2023-04-19 11:02:53 +03:00
Леонид Юрьев (Leonid Yuriev)
cf9145bb46 mdbx: использование enum-типов вместо int для устранения предупреждений GCC >= 13. 2023-04-19 10:19:11 +03:00
Леонид Юрьев (Leonid Yuriev)
4151e0e348
mdbx: merge branch master into stable. 2023-04-18 16:29:05 +03:00
Леонид Юрьев (Leonid Yuriev)
9b8291457b
mdbx: выпуск 0.12.5 "Динамо"
Стабилизирующий выпуск с исправлением обнаруженных ошибок и устранением
недочетов, в день 100-летнего юбилея спортивного общества [«Динамо»](https://ru.wikipedia.org/wiki/Динамо_(спортивное_общество)).

Благодарности:
--------------

 - Max <maxc0d3r@protonmail.com> за сообщение о проблеме экспорта из DSO/DLL
   устаревших функций API.

 - [`@calvin3721`](https://t.me/calvin3721) за сообщение о проблеме работы
   `MainDB` с флагами не по-умолчанию.

Исправления:
------------

 - Поправлен экспорт из DSO/DLL устаревших функций,
   которые заменены на inline в текущем API.

 - Устранено использование неверного компаратора при создании или пересоздании
   `MainDB` с флагами/опциями предполагающим использование специфического
   компаратора (не по-умолчанию).

Мелочи:
-------

 - Удалена дублирующая диагностика внутри `node_read_bigdata()`.

 - Исправлены ссылки в описании `mdbx_env_set_geometry()`.

 - Добавлен отдельный тест `extra/upsert_alldups` для специфического
   сценария замены/перезаписи одним значением всех multi-значений
   соответствующих ключу, т.е. замена всех «дубликатов» одним значением.

 - В C++ API добавлены варианты `buffer::key_from()` с явным именованием по типу данных.

 - Добавлен отдельный тест `extra/maindb_ordinal` для специфического
   сценария создания `MainDB` с флагами требующими использования
   компаратора не по-умолчанию.

 - Рефакторинг проверки "когерентности" мета-страниц.

 - Корректировка `osal_vasprintf()` для устранения предупреждений статических анализаторов.

16 files changed, 686 insertions(+), 247 deletions(-)
Signed-off-by: Леонид Юрьев (Leonid Yuriev) <leo@yuriev.ru>
2023-04-18 11:03:31 +03:00
Леонид Юрьев (Leonid Yuriev)
0f13d91a0e mdbx: минорное переформатирование и пополнение ChangeLog для прошлых выпусков. 2023-04-18 10:54:39 +03:00
Леонид Юрьев (Leonid Yuriev)
d40b69ec7a mdbx: обновление ChangeLog. 2023-04-16 21:24:58 +03:00
Леонид Юрьев (Leonid Yuriev)
7489c8ce28 mdbx: рефакторинг проверки "когерентности" мета-страниц. 2023-04-05 21:40:00 +03:00
Леонид Юрьев (Leonid Yuriev)
caddf07889 mdbx: корректировка osal_vasprintf() для устранения предупреждений статических анализаторов. 2023-04-05 21:40:00 +03:00
Леонид Юрьев (Leonid Yuriev)
74256efc64 mdbx: refine comment. 2023-04-05 21:40:00 +03:00
Леонид Юрьев (Leonid Yuriev)
e47a91bf7c mdbx-test: совместимость со libstdc++ без std::string_view. 2023-04-05 08:57:16 +03:00
Леонид Юрьев (Leonid Yuriev)
3ace3c27b8 mdbx++: добавление typename mdbx::default_allocator. 2023-04-01 11:10:40 +03:00
Леонид Юрьев (Leonid Yuriev)
bcebfb4b4c mdbx: дополнение ChangeLog. 2023-03-31 22:49:25 +03:00
Леонид Юрьев (Leonid Yuriev)
b5400f9a35 mdbx-test: добавление мини-теста для проверки MainDB с целочисленными ключами. 2023-03-31 22:31:09 +03:00
Леонид Юрьев (Leonid Yuriev)
8a44d57fab mdbx++: добавление вариантов buffer::key_from() с явным именованием по типу данных. 2023-03-31 00:56:05 +03:00
Леонид Юрьев (Leonid Yuriev)
fdb2b5b0f1 mdbx: обнуление компараторов при пересоздании MainDB. 2023-03-31 00:55:03 +03:00
Леонид Юрьев (Leonid Yuriev)
95cb73646e mdbx: корректировка отладочного кода для устранения срабатывания assert-проверки. 2023-03-26 22:54:50 +03:00
Леонид Юрьев (Leonid Yuriev)
b2d16d32aa mdbx: дополнение ChangeLog. 2023-03-20 15:56:19 +03:00
Леонид Юрьев (Leonid Yuriev)
e0be0d9a5e mdbx: корректировка экспорта устаревших функций API. 2023-03-20 15:48:04 +03:00
Леонид Юрьев (Leonid Yuriev)
2ba7051719 mdbx: удаление из node_read_bigdata() дублирующей диагностики.
Аналогичные проверки и предупреждения выполняются при чтении страниц в режиме `MDBX_VALIDATION`.
2023-03-20 14:39:09 +03:00
Леонид Юрьев (Leonid Yuriev)
04ed388761 mdbx-test: добавление extra/upsert_alldups. 2023-03-20 14:38:02 +03:00
Леонид Юрьев (Leonid Yuriev)
da4e2ab254 mdbx-doc: исправление ссылок в описании mdbx_env_set_geometry(). 2023-03-17 10:54:40 +03:00
Леонид Юрьев (Leonid Yuriev)
c81b007587 mdbx: merge branch master into stable.
Ветка 0.12 считается готовой к продуктовому использованию,
получает статус стабильной и далее будет получать только исправление ошибок.

Разработка будет продолжена в ветке 0.13, а ветка 0.11 становится архивной.
2023-03-04 00:00:24 +03:00
Леонид Юрьев (Leonid Yuriev)
53177e483c
mdbx: выпуск 0.12.4 "Арта-333"
Стабилизирующий выпуск с исправлением обнаруженных ошибок, устранением
недочетов и технических долгов. Ветка 0.12 считается готовой к
продуктовому использованию, получает статус стабильной и далее будет
получать только исправление ошибок. Разработка будет продолжена в ветке
0.13, а ветка 0.11 становится архивной.

Благодарности:
--------------

 - Max <maxc0d3r@protonmail.com> за сообщение о проблеме ERROR_SHARING_VIOLATION
   в режиме MDBX_EXCLUSIVE на Windows.
 - Alisher Ashyrov <https://t.me/a1is43ras4> за сообщение о проблеме
   с assert-проверкой и содействие в отладке.
 - Masatoshi Fukunaga <https://gitflic.ru/user/mah0x211> за сообщение о проблеме
   `put(MDBX_UPSERT+MDBX_ALLDUPS)` для случая замены всех значений в subDb.

Исправления (без корректировок новых функций):
----------------------------------------------

 - Устранен регресс после коммита 474391c83c,
   приводящий к возврату ERROR_SHARING_VIOLATION в Windows при открытии БД
   в режиме MDBX_EXCLUSIVE для чтения-записи.

 - Добавлено ограничение размера отображения при коротком read-only файле, для
   предотвращения ошибки ERROR_NOT_ENOUGH_MEMORY в Windows, которая возникает
   в этом случае и совсем не информативна для пользователя.

 - Произведен рефакторинг `dxb_resize()`, в том числе, для устранения срабатывания
   assert-проверки `size_bytes == env->me_dxb_mmap.current` в специфических
   многопоточных сценариях использования. Проверка срабатывала только в
   отладочных сборках, при специфическом наложении во времени читающей и
   пишущей транзакции в разных потоках, одновременно с изменением размера БД.
   Кроме срабатывание проверки, каких-либо других последствий не возникало.

 - Устранена проблема в `put(MDBX_UPSERT+MDBX_ALLDUPS)` для случая замены
   всех значений единственного ключа в subDb. В ходе этой операции subDb
   становится полностью пустой, без каких-либо страниц и именно эта
   ситуация не была учтена в коде, что приводило к повреждению БД
   при фиксации такой транзакции.

 - Устранена излишняя assert-проверка внутри `override_meta()`.
   Что в отладочных сборках могло приводить к ложным срабатываниям
   при восстановлении БД, в том числе при автоматическом откате слабых
   мета-страниц.

 - Скорректированы макросы `__cold`/`__hot`, в том числе для устранения проблемы
   `error: inlining failed in call to ‘always_inline FOO(...)’: target specific option mismatch`
   при сборке посредством GCC >10.x для SH4.

Ликвидация технических долгов и мелочи:
---------------------------------------

 - Исправлены многочисленные опечатки в документации.
 - Доработан тест для полной стохастической проверки `MDBX_EKEYMISMATCH` в режиме `MDBX_APPEND`.
 - Расширены сценарии запуска `mdbx_chk` из CMake-тестов для проверки как в обычном,
   так и эксклюзивном режимах чтения-записи.
 - Уточнены спецификаторы `const` и `noexcept` для нескольких методов в C++ API.
 - Устранено использование стека под буферы для `wchar`-преобразования путей.
 - Для Windows добавлена функция `mdbx_env_get_path()` для получения пути к БД
   в формате многобайтных символов.
 - Добавлены doxygen-описания для API с широкими символами.
 - Устранены предупреждения статического анализатора MSVC,
   все они были несущественные, либо ложные.
 - Устранено ложное предупреждение GCC при сборке для SH4.
 - Добавлена поддержка ASAN (Address Sanitizer) при сборке посредством MSVC.
 - Расширен набор перебираемых режимов в скрипте `test/long_stochastic.sh`,
   добавлена опция `--extra`.
 - В C++ API добавлена поддержка расширенных опций времени выполнения `mdbx::extra_runtime_option`,
   аналогично `enum MDBX_option_t` из C API.
 - Вывод всех счетчиков page-operations в `mdbx_stat`.

63 files changed, 1161 insertions(+), 569 deletions(-)
Signed-off-by: Леонид Юрьев (Leonid Yuriev) <leo@yuriev.ru>
2023-03-03 23:23:08 +03:00
Леонид Юрьев (Leonid Yuriev)
ad93633d10 mdbx-tools: вывод всех счетчиков page-operations в mdbx_stat. 2023-03-03 21:18:35 +03:00
Леонид Юрьев (Leonid Yuriev)
f17c55a872 mdbx: обновление ChangeLog. 2023-03-02 16:34:19 +03:00
Леонид Юрьев (Leonid Yuriev)
7db014c4fc mdbx++: добавление в C++ API поддержки расширенных опций времени выполнения enum MDBX_option_t.
https://gitflic.ru/project/erthink/libmdbx/issue/4
2023-03-01 23:22:50 +03:00
Леонид Юрьев (Leonid Yuriev)
22405885f6 mdbx: корректировка излишней assert-проверки внутри override_meta(). 2023-03-01 01:09:10 +03:00
Леонид Юрьев (Leonid Yuriev)
2ae7bfd9be mdbx-make: актуализация списков для целей cross-gcc и cross-qemu. 2023-02-28 22:52:28 +03:00
Леонид Юрьев (Leonid Yuriev)
8f87ab252e mdbx: дополнение ChangeLog. 2023-02-28 00:52:40 +03:00
Леонид Юрьев (Leonid Yuriev)
800bd55ab9 mdbx-test: добавление опции --extra в скрипт test/long_stochastic.sh 2023-02-28 00:50:48 +03:00
Леонид Юрьев (Leonid Yuriev)
5c52adf358 mdbx-test: расширение набора режимов перебираемых скриптом test/long_stochastic.sh 2023-02-28 00:50:48 +03:00
Leonid Yuriev
6d74b10db1 mdbx: поддержка ASAN (Address Sanitizer) при сборке посредством MSVC. 2023-02-28 00:50:30 +03:00
Леонид Юрьев (Leonid Yuriev)
359489e271 mdbx: исправление семантической опечатки в комментарии о режиме работы. 2023-02-27 16:59:10 +03:00
Леонид Юрьев (Leonid Yuriev)
5f690bbc4f mdbx-test: по-умолчанию работа в режиме MDBX_SYNC_DURABLE. 2023-02-27 16:59:10 +03:00
Леонид Юрьев (Leonid Yuriev)
1b6e32071c mdbx: повторное "устранение" предупреждений MSVC Static Analyzer (aka Prefast).
Никаких значимых изменений, только обход "странностей" в MSVC.

Как оказалось MSVC распространяет действие директивы
`pragma(warning(supppress:#))` строго на следующую строку, даже если эта
строка является продолжением комментария начатого в самой директиве
и/или не содержит синтаксических конструкций языка.

Поэтому большинство из добавленных ранее директив для подавления ложных
предупреждений, перестало работать после переформатирования исходного
кода.
2023-02-27 16:59:06 +03:00
Леонид Юрьев (Leonid Yuriev)
b415265d16
mdbx: release v0.11.14 (Sergey Kapitsa)
The stable bugfix release in memory of [Sergey Kapitsa](https://en.wikipedia.org/wiki/Sergey_Kapitsa) on his 95th birthday.

Fixes:
------

 - backport: Fixed insignificant typo of `||` inside `#if` byte-order condition.

 - backport: Fixed `SIGSEGV` or an erroneous call to `free()` in situations where
   errors occur when reopening by `mdbx_env_open()` of a previously used
   environment.

 - backport: Fixed `cursor_put_nochecklen()` internals for case when dupsort'ed named subDb
   contains a single key with multiple values (aka duplicates), which are replaced
   with a single value by put-operation with the `MDBX_UPSERT+MDBX_ALLDUPS` flags.
   In this case, the database becomes completely empty, without any pages.
   However exactly this condition was not considered and thus wasn't handled correctly.
   See [issue#8](https://gitflic.ru/project/erthink/libmdbx/issue/8) for more information.

 - backport: Fixed extra assertion inside `override_meta()`, which could
   lead to false-positive failing of the assertion in a debug builds during
   DB recovery and auto-rollback.

 - backport: Refined the `__cold`/`__hot` macros to avoid the
   `error: inlining failed in call to ‘always_inline FOO(...)’: target specific option mismatch`
   issue during build using GCC >10.x for SH4 arch.

Minors:
-------

 - backport: Using the https://libmdbx.dqdkfa.ru/dead-github
   for resources deleted by the Github' administration.
 - backport: Fixed English typos.
 - backport: Fixed proto of `__asan_default_options()`.
 - backport: Fixed doxygen-description of C++ API, especially of C++20 concepts.
 - backport: Refined `const` and `noexcept` for few C++ API methods.
 - backport: Fixed copy&paste typo of "Getting started".
 - backport: Update MithrilDB status.
 - backport: Resolve false-posirive `used uninitialized` warning from GCC >10.x
   while build for SH4 arch.

22 files changed, 250 insertions(+), 174 deletions(-)
Signed-off-by: Леонид Юрьев (Leonid Yuriev) <leo@yuriev.ru>
2023-02-14 15:10:41 +03:00
Леонид Юрьев (Leonid Yuriev)
29d12f1fc3 mdbx-doc: добавлено примечание к опции MDBX_HAVE_BUILTIN_CPU_SUPPORTS. 2023-02-14 12:09:44 +03:00
Леонид Юрьев (Leonid Yuriev)
257a534fbe mdbx: update ChangeLog. 2023-02-13 21:35:20 +03:00
Леонид Юрьев (Leonid Yuriev)
33b5aeb768 mdbx: refine __cold/__hot macros (backport).
В том числе для устранения проблемы
`error: inlining failed in call to ‘always_inline FOO(...)’: target specific option mismatch`
при сборке посредством GCC >10.x для SH4.
2023-02-13 21:35:20 +03:00
Леонид Юрьев (Leonid Yuriev)
f532e907e9 mdbx: fix false-positive SH4 GCC warning (backport). 2023-02-13 21:35:20 +03:00
Леонид Юрьев (Leonid Yuriev)
60736dbabb mdbx: fix English typos (backport).
Thanks to Dimitris Apostolou <dimitris.apostolou@icloud.com>
2023-02-13 21:35:20 +03:00
Леонид Юрьев (Leonid Yuriev)
24df8073ac mdbx: fix extra assertion inside override_meta() (backport). 2023-02-13 21:35:20 +03:00
Леонид Юрьев (Leonid Yuriev)
d504ca1747 mdbx: fix proto of __asan_default_options() (backport). 2023-02-13 21:35:14 +03:00
Леонид Юрьев (Leonid Yuriev)
b7ace5b216 mdbx-doc: fix doxygen-description of C++ API, especially of C++20 concepts (backport). 2023-02-13 21:28:33 +03:00
Леонид Юрьев (Leonid Yuriev)
2fabac18c0 mdbx++: refine const and noexcept for few C++ API methods (backport). 2023-02-13 21:27:55 +03:00
Леонид Юрьев (Leonid Yuriev)
51789f3605 mdbx: fix put(MDBX_UPSERT+MDBX_ALLDUPS) for case of replacement all values of a single key inside dupsorted subDb (backport).
Fixed cursor_put_nochecklen() internals for case when dupsort'ed named subDb
contains a single key with multiple values (aka duplicates), which are replaced
with a single value by put-operation with the `MDBX_UPSERT+MDBX_ALLDUPS` flags.

In this case, the database becomes completely empty, without any pages.
However exactly this condition was not considered and
thus wasn't handled correctly.

Fixes https://gitflic.ru/project/erthink/libmdbx/issue/8

Thanks Masatoshi Fukunaga <https://gitflic.ru/user/mah0x211> for reporting.
2023-02-13 21:27:55 +03:00
Леонид Юрьев (Leonid Yuriev)
6899142872 mdbx: remove extra assertion (backport).
The removed assertion could be triggered in debug builds when a reading
and writing transactions are overlapped simultaneously with a change of DB
size.

There were no other negative consequences.
2023-02-13 21:27:44 +03:00
Леонид Юрьев (Leonid Yuriev)
c44c8132e4 mdbx-doc: fix copy&paste typo of "Getting started" (backport). 2023-02-13 21:27:10 +03:00
Леонид Юрьев (Leonid Yuriev)
d376feb7bc mdbx: update MithrilDB status (backport). 2023-02-13 21:26:49 +03:00
Леонид Юрьев (Leonid Yuriev)
2ea9fbe51b mdbx: дополнение ChangeLog. 2023-02-13 20:57:43 +03:00
Леонид Юрьев (Leonid Yuriev)
57ca0d6e1b mdbx: корректировка макросов __cold/__hot.
В том числе для устранения проблемы
`error: inlining failed in call to ‘always_inline FOO(...)’: target specific option mismatch`
при сборке посредством GCC >10.x для SH4.
2023-02-13 20:57:43 +03:00
Леонид Юрьев (Leonid Yuriev)
b8092dd0db mdbx: устранение ложного предупреждения GCC при сборке для SH4. 2023-02-13 16:29:47 +03:00
Леонид Юрьев (Leonid Yuriev)
8fba5ac8d8 mdbx: устранение излишней assert-проверки внутри override_meta(). 2023-02-12 23:27:39 +03:00
Леонид Юрьев (Leonid Yuriev)
c9d11cbac1 mdbx: дополнение ChangeLog. 2023-02-11 07:35:56 +03:00
Леонид Юрьев (Leonid Yuriev)
25e958f081 mdbx: устранение всех предупреждений статического анализатора MSVC (все несущественные или ложные). 2023-02-11 00:26:06 +03:00
Леонид Юрьев (Leonid Yuriev)
7f5ea6d3b8 mdbx: корректировка прототипа __asan_default_options(). 2023-02-11 00:26:06 +03:00
Леонид Юрьев (Leonid Yuriev)
e51140fe48 mdbx-doc: корректировка doxygen-описания C++ API, в особенности C++20 concepts. 2023-02-11 00:26:06 +03:00
Леонид Юрьев (Leonid Yuriev)
bd35fe8970 mdbx-doc: добавление doxygen-описания для API с широкими символами. 2023-02-11 00:26:06 +03:00
Леонид Юрьев (Leonid Yuriev)
1684d17b0f mdbx-windows: поддержка char-версии mdbx_env_get_path(). 2023-02-11 00:26:06 +03:00
Леонид Юрьев (Leonid Yuriev)
ebbe98afa5 mdbx-windows: ликвидация макроса OSAL_MB2WIDE(). 2023-02-11 00:26:06 +03:00
Леонид Юрьев (Leonid Yuriev)
351a30f186 mdbx-windows: не расходуем стек под буферы для wchar-преобразования путей. 2023-02-09 22:37:31 +03:00
Леонид Юрьев (Leonid Yuriev)
2a41b24876 mdbx++: уточнение const и noexcept для нескольких методов. 2023-02-09 15:14:39 +03:00
Леонид Юрьев (Leonid Yuriev)
fb827959a9 mdbx: исправление put(MDBX_UPSERT+MDBX_ALLDUPS) для случая замены всех значений в subDb.
Fixed cursor_put_nochecklen() internals for case when dupsort'ed named subDb
contains a single key with multiple values (aka duplicates), which are replaced
with a single value by put-operation with the `MDBX_UPSERT+MDBX_ALLDUPS` flags.

In this case, the database becomes completely empty, without any pages.
However exactly this condition was not considered and
thus wasn't handled correctly.

Fixes https://gitflic.ru/project/erthink/libmdbx/issue/8

Thanks Masatoshi Fukunaga <https://gitflic.ru/user/mah0x211> for reporting.
2023-02-01 01:04:24 +03:00
Леонид Юрьев (Leonid Yuriev)
209f784ee7 mdbx: исправление assert-проверок внутри dxb_resize().
Устранение регресса после a484a1f89b.

Проверка `prev_limit_pgno >= used_pgno` правомочна только в части сценариев,
но не в общем случае.
2023-01-23 23:54:11 +03:00
Леонид Юрьев (Leonid Yuriev)
68ebbe1fde mdbx: Обновление ChangeLog. 2023-01-18 18:34:52 +03:00
Леонид Юрьев (Leonid Yuriev)
486711945d mdbx-doc: исправление copy&paste опечатки в "Getting started". 2023-01-18 18:34:25 +03:00
Леонид Юрьев (Leonid Yuriev)
3ade7c7ba1 mdbx: обновление статуса MithrilDB. 2023-01-16 19:12:08 +03:00
Леонид Юрьев (Leonid Yuriev)
c01f025bfa mdbx: обновление года на 2023. 2023-01-16 16:32:02 +03:00
Леонид Юрьев (Leonid Yuriev)
a484a1f89b mdbx: рефакторинг dxb_resize() и связанного кода.
В том числе, для устранения срабатывания assert-проверки
`size_bytes == env->me_dxb_mmap.current` в специфических многопоточных
сценариях использования.

Проверка срабатывала только в отладочных сборках, при специфическом
наложении во времени читающей и пишущей транзакции в разных потоках,
одновременно с изменением размера БД.

Кроме срабатывание проверки, каких-либо других последствий не возникало.
2023-01-16 02:20:56 +03:00
Леонид Юрьев (Leonid Yuriev)
0979a93a78 mdbx: добавлено примечание об ошибке MinGW MSYS2. 2023-01-12 17:01:27 +03:00
Леонид Юрьев (Leonid Yuriev)
a98c73f4f6 mdbx-cmake: вызов mdbx_chk в режиме чтения-записи для проверки MDBX_EXCLUSIVE в этом режиме. 2023-01-12 17:01:27 +03:00
Leonid Yuriev
9e15bd9b29 mdbx-windows: устранение регресса ERROR_SHARING_VIOLATION в режиме MDBX_EXCLUSIVE.
Спасибо maxc0d3r@protonmail.com за сообщение о проблеме.
2023-01-12 17:01:27 +03:00
Leonid Yuriev
0159f97e94 mdbx: ограничиваем размер отображения при коротком read-only файле.
Цель в предотвращении ошибки ERROR_NOT_ENOUGH_MEMORY в Windows, которая
совсем не информативна для пользователя и возникает в этом случае (когда
файл открыт read-only и короче запрошенного размера).
2023-01-12 01:53:22 +03:00
Леонид Юрьев (Leonid Yuriev)
56050f201f mdbx: обновление ChangeLog. 2023-01-10 15:03:38 +03:00
Леонид Юрьев (Leonid Yuriev)
525c4a55a4 mdbx: fix English typos.
Thanks to Dimitris Apostolou <dimitris.apostolou@icloud.com>
2023-01-10 14:16:08 +03:00
Леонид Юрьев (Leonid Yuriev)
702c67fc38 mdbx-test: доработка append-теста.
- добавлен speculum-контроль;
- с вероятностью 1/8 генерируются не-последовательные/не-упорядоченные ключи для проверки возврата MDBX_EKEYMISMATH;
- игнорирование расхождение хеша последовательности для не-последовательных ключей.
2023-01-09 23:51:34 +03:00
Леонид Юрьев (Leonid Yuriev)
3da23da7b3 mdbx: косметический рефакторинг контроля MDBX_APPEND. 2023-01-09 21:39:42 +03:00
Леонид Юрьев (Leonid Yuriev)
16cda5c2e8 mdbx: исправление опечаток в ChangeLog. 2023-01-08 12:40:44 +03:00
Леонид Юрьев (Leonid Yuriev)
f1fdb88938
mdbx: выпуск v0.12.3 "Акула"
Выпуск с существенными доработками и новой функциональностью в память о закрытом open-source проекте "Акула".

Благодарности:
--------------

 - [Alex Sharov](https://t.me/AskAlexSharov) и команде [Erigon](https://github.com/ledgerwatch/erigon) за тестирование.
 - [Simon Leier](https://t.me/leisim) за сообщение о сбоях и тестирование.

Новое:
------

 - Использование адреса [https://libmdbx.dqdkfa.ru/dead-github](https://libmdbx.dqdkfa.ru/dead-github)
   для отсылки к сохранённым в web.archive.org копиям ресурсов, уничтоженных администрацией Github.

 - Реализована prefault-запись при выделении страниц для read-write отображений.
   Это приводит к кратному снижению системных издержек и существенному увеличению
   производительности в соответствующих сценариях использования, когда:
    - размер БД и объём данных существенно больше ОЗУ;
    - используется режим `MDBX_WRITEMAP`;
    - не-мелкие транзакции (по ходу транзакции выделяется многие сотни или тысячи страниц).

   В режиме `MDBX_WRITEMAP` выделение/переиспользование страниц приводит
   к page-fault и чтению страницы с диска, даже если содержимое страницы
   не нужно (будет перезаписано). Это является следствием работы подсистемы
   виртуальной памяти, а штатный способ лечения через `MADV_REMOVE`
   работает не на всех ФС и обычно дороже получаемой экономии.

   Теперь в libmdbx используется "упреждающая запись" таких страниц,
   которая на системах с [unified page cache](https://www.opennet.ru/base/dev/ubc.txt.html)
   приводит к "вталкиванию" данных, устраняя необходимость чтения с диска при
   обращении к такой странице памяти.

   Новый функционал работает в согласованности с автоматическим управлением read-ahead
   и кэшем статуса присутствия страниц в ОЗУ, посредством [mincore()](https://man7.org/linux/man-pages/man2/mincore.2.html).

 - Добавлена опция `MDBX_opt_prefault_write_enable` для возможности принудительного
   включения/выключения prefault-записи.

 - Реализован динамический выбор между сквозной записью на диск и обычной записью
   с последующим [fdatasync()](https://man7.org/linux/man-pages/man3/fdatasync.3p.html)
   управляемый опцией `MDBX_opt_writethrough_threshold`.

   В долговечных (durable) режимах данные на диск могут быть сброшены двумя способами:
     - сквозной записью через файловый дескриптор открытый с `O_DSYNC`;
     - обычной записью с последующим вызовом `fdatasync()`.

   Первый способ выгоднее при записи малого количества страниц и/или если
   канал взаимодействия с диском/носителем имеет близкую к нулю задержку.
   Второй способ выгоднее если требуется записать много страниц и/или канал
   взаимодействия имеет весомую задержку (датацентры, облака). Добавленная
   опция `MDBX_opt_writethrough_threshold` позволяет во время выполнения
   задать порог для динамического выбора способа записи в зависимости от
   объема и конкретных условия использования.

 - Автоматическая установка `MDBX_opt_rp_augment_limit` в зависимости от размера БД.

 - Запрещение разного режима `MDBX_WRITEMAP` между процессами в режимах
   с отложенной/ленивой записью, так как в этом случае невозможно
   обеспечить сброс данных на диск во всех случаях на всех поддерживаемых платформах.

 - Добавлена опция сборки `MDBX_MMAP_USE_MS_ASYNC` позволяющая отключить
   использование системного вызова `msync(MS_ASYNC)`, в использовании
   которого нет необходимости на подавляющем большинстве актуальных ОС.
   По-умолчанию `MDBX_MMAP_USE_MS_ASYNC=0` (выключено) на Linux и других
   системах с unified page cache. Такое поведение (без использования
   `msync(MS_ASYNC)`) соответствует неизменяемой (hardcoded) логике LMDB. В
   результате, в простых/наивных бенчмарках, libmdbx опережает LMDB
   примерна также как при реальном применении.

   На всякий случай стоит еще раз отметить/напомнить, что на Windows
   предположительно libmdbx будет отставать от LMDB в сценариях с
   множеством мелких транзакций, так как libmdbx осознанно использует на
   Windows файловые блокировки, которые медленные (плохо реализованы в ядре
   ОС), но позволяют застраховать пользователей от массы неверных действий
   приводящих к повреждению БД.

 - Поддержка не-печатных имен для subDb.

 - Добавлен явный выбор `tls_model("local-dynamic")` для обзода проблемы
   `relocation R_X86_64_TPOFF32 against FOO cannot be used with -shared`
   из-за ошибки в CLANG приводящей к использованию неверного режима `ls_model`.

 - Изменение тактики слияния страниц при удалении.
   Теперь слияние выполняется преимущественно с уже измененной/грязной страницей.
   Если же справа и слева обе страницы с одинаковым статусом,
   то с наименее заполненной, как прежде. В сценариях с массивным удалением
   это позволяет увеличить производительность до 50%.

 - Добавлен контроль отсутствия LCK-файлов с альтернативным именованием.

Исправления (без корректировок новых функций):
----------------------------------------------

 - Изменение размера отображения если это требуется для сброса данных на
   диск при вызове `mdbx_env_sync()` из параллельного потока выполнения вне
   работающей транзакции.

 - Исправление регресса после коммита db72763de0 от 2022-10-08
   в логике возврата грязных страниц в режиме `MDBX_WRITEMAP`, из-за чего
   освободившиеся страницы использовались не немедленно, а попадали в
   retired-список совершаемой транзакции и происходил необоснованный рост
   размера транзакции.

 - Устранение SIGSEGV или ошибочного вызова `free()` в ситуациях
   повторного открытия среды посредством `mdbx_env_open()`.

 - Устранение ошибки совершенной в коммите fe20de136c от 2022-09-18,
   в результате чего на Linux в режиме `MDBX_WRITEMAP` никогда не вызывался `msync()`.
   Проблема существует только в релизе 0.12.2.

 - Добавление подсчета грязных страниц в `MDBX_WRITEMAP` для предоставления посредством `mdbx_txn_info()`
   актуальной информации об объеме изменений в процессе транзакций чтения-записи.

 - Исправление несущественной опечатки в условиях `#if` определения порядка байт.

 - Исправление сборки для случая `MDBX_PNL_ASCENDING=1`.

Ликвидация технических долгов и мелочи:
---------------------------------------

 - Доработка поддержки авто-слияния записей GC внутри `page_alloc_slowpath()`.
 - Устранение несущественных предупреждений Coverity.
 - Использование единого курсора для поиска в GC.
 - Переработка внутренних флагов связанных с выделением страниц из GC.
 - Доработка подготовки резерва перед обновлением GC при включенном BigFoot.
 - Оптимизация `pnl_merge()` для случаев неперекрывающихся объединяемых списков.
 - Оптимизация поддержки отсортированного списка страниц в `dpl_append()`.
 - Ускорение работы `mdbx_chk` при обработке пользовательских записей в `@MAIN`.
 - Переработка LRU-отметок для спиллинга.
 - Переработка контроля "некогерентности" Unified page cache для уменьшения накладных расходов.
 - Рефакторинг и микрооптимизация.

20 files changed, 4504 insertions(+), 2924 deletions(-)
Signed-off-by: Леонид Юрьев (Leonid Yuriev) <leo@yuriev.ru>
2023-01-07 00:11:51 +03:00
Леонид Юрьев (Leonid Yuriev)
68a8a15621 mdbx: изменение адреса ioarena. 2023-01-07 00:10:23 +03:00
Леонид Юрьев (Leonid Yuriev)
b86b71a948 mdbx: обновление ChangeLog. 2023-01-07 00:10:23 +03:00
Леонид Юрьев (Leonid Yuriev)
61e77e7b70 mdbx: контроль отсутствия дубликатов LCK-файла с альтернативными именами. 2023-01-07 00:10:23 +03:00
Леонид Юрьев (Leonid Yuriev)
08fb7d5838 mdbx: корректировка отключения MDBX_NOSUBDIR при открытии mdbx.dat без директории. 2023-01-07 00:10:19 +03:00
Леонид Юрьев (Leonid Yuriev)
f2a49b687a mdbx: обновление ChangeLog. 2023-01-04 00:19:48 +03:00
Леонид Юрьев (Leonid Yuriev)
c6b73c8a24 mdbx: добавление me_madv_threshold и рефакторинг/упрощение.
Для уменьшения затрат на MDBX_SHRINK_ALLOWED.
2023-01-03 20:20:03 +03:00
Леонид Юрьев (Leonid Yuriev)
24f2e878c1 mdbx: устранение несущественных предупреждений Valgrind. 2023-01-03 19:28:11 +03:00
Леонид Юрьев (Leonid Yuriev)
2c8d3e1e12 mdbx: исправление предупреждения UBSAN. 2023-01-03 19:28:11 +03:00
Леонид Юрьев (Leonid Yuriev)
ab55016599 mdbx: устранение ложного срабатывания assert внутри dpl_reserve(). 2023-01-03 19:28:11 +03:00
Леонид Юрьев (Leonid Yuriev)
f0c2927fc7 mdbx: перенос LRU-отметок в теневые страницы по отрицательному смещению.
Это позволяет избавиться от повторного поиска в "гзязном" списке
страниц, уже находящихся в стеке курсора, для обнлвления LRU-отметок.
2023-01-03 19:28:11 +03:00
Леонид Юрьев (Leonid Yuriev)
8519fde741 mdbx: микро-оптимизация cmp_reverse(). 2023-01-03 19:28:11 +03:00
Леонид Юрьев (Leonid Yuriev)
bcddeaba9f mdbx: изменение CMP2INT().
Решил вернуться к старому варианту. Вроде-бы все актуальные компиляторы
ведут себя с ним прилично (не хуже), а некоторые лучше.
2023-01-03 19:28:11 +03:00
Леонид Юрьев (Leonid Yuriev)
5317e516d2 mdbx: микро-оптимизация cmp_int(). 2023-01-03 19:28:11 +03:00
Леонид Юрьев (Leonid Yuriev)
be05037906 mdbx: перемещение debug/assert-макросов перед атомиками. 2023-01-03 19:28:11 +03:00
Леонид Юрьев (Leonid Yuriev)
f53dc70038 mdbx: добавление eq_fast() для сравнений на (не)равенство.
Цель в том, чтобы уменьшить кол-во условных и безусловных переходов при
сравнениях равно/неравно, в том числе избегать вызовов задаваемых
кастомных компаратаров и memcmp() для коротких ключей/значений.
2023-01-03 19:28:11 +03:00
Леонид Юрьев (Leonid Yuriev)
2322138a8e mdbx: корректировка сообщения об ошибке. 2023-01-03 19:28:11 +03:00
Леонид Юрьев (Leonid Yuriev)
37867a0b84 mdbx: не обходим проверку когерентности в режиме восстановления. 2023-01-03 19:28:11 +03:00
Леонид Юрьев (Leonid Yuriev)
f0c43fb24a mdbx: без необходимости не объединяем не-грязные страницы в дереве. 2023-01-03 19:28:11 +03:00
Леонид Юрьев (Leonid Yuriev)
adf433a1bc mdbx-make: доработка макро для bench-целей. 2023-01-03 19:28:11 +03:00
Леонид Юрьев (Leonid Yuriev)
48bd3fc4c8 mdbx: упрощение default_prefault_write(). 2022-12-31 23:35:53 +03:00
Леонид Юрьев (Leonid Yuriev)
7ffea70087 mdbx: доработка loose-пути в page_retire(). 2022-12-31 23:35:53 +03:00
Леонид Юрьев (Leonid Yuriev)
ef460a9229 mdbx: выделение cursor_get() для уменьшения кол-ва проверок. 2022-12-29 23:03:12 +03:00
Леонид Юрьев (Leonid Yuriev)
df63ff0e7e mdbx: выделение cursor_del() для уменьшения кол-ва проверок. 2022-12-29 23:03:12 +03:00
Леонид Юрьев (Leonid Yuriev)
66a5704949 mdbx: выделение cursor_put() для уменьшения кол-ва проверок. 2022-12-29 19:26:50 +03:00
Леонид Юрьев (Leonid Yuriev)
61d21b0a02 mdbx: не трогать LRU и dbi в cursor_touch() для вложенных курсоров. 2022-12-29 01:20:04 +03:00
Леонид Юрьев (Leonid Yuriev)
0941319940 mdbx: парочка незначительных likely. 2022-12-29 01:20:04 +03:00
Леонид Юрьев (Leonid Yuriev)
bb2e3967eb mdbx: уменьшение кол-ва вызовов realloc(). 2022-12-27 11:50:28 +03:00
Леонид Юрьев (Leonid Yuriev)
e458af602e mdbx: устранение ненужных условий в отладке (несущественно). 2022-12-27 11:50:28 +03:00
Леонид Юрьев (Leonid Yuriev)
d29acf4fdc mdbx: актуализация bits.md (внутренний справочник). 2022-12-27 11:50:28 +03:00
Леонид Юрьев (Leonid Yuriev)
a06fe4f168 mdbx: переработка контроля "некогерентности" для уменьшения накладных расходов.
Существует проблема https://libmdbx.dqdkfa.ru/dead-github/issues/269,
которая проявляется только при специфической неупорядоченности внутри
ядра ОС, когда страницы, записанные в файл отображенный в память,
становятся видны в памяти посредством работы unified page cache:

 - если записанная последней мета-страница "обгоняет" ранее записанные,
   т.е. когда записанное в файл позже становится видимым в отображении
   раньше, чем записанное ранее.

Теперь, вместо постоянной полной сверки записываемых страниц,
выполняется легковесная проверка при старте транзакций, с переключением
в режим "как раньше" при обнаружении проблемы.

В результате, в некоторых сценариях возвращается 5-10%
производительности, а в отдельных синтетических тестах до 30%.
2022-12-27 11:50:28 +03:00
Леонид Юрьев (Leonid Yuriev)
0498114469 mdbx: обнуление информации о задержках для невалидных транзакций в mdbx_txn_commit_ex(). 2022-12-27 11:50:28 +03:00
Леонид Юрьев (Leonid Yuriev)
85828f677a mdbx: пересоздание пустой MAIN_DBI при необходимости. 2022-12-27 11:50:28 +03:00
Леонид Юрьев (Leonid Yuriev)
9cbbdfa025 mdbx: добавление const к аргументам функций получения и контроля страниц. 2022-12-27 11:50:28 +03:00
Леонид Юрьев (Leonid Yuriev)
686145ec2e mdbx: рефакторинг с удалением cursor_spill() и MDBX_NOSPILL. 2022-12-27 11:50:23 +03:00
Леонид Юрьев (Leonid Yuriev)
fe55f25665 mdbx: использование msync(MS_ASYNC) для спиллинга в режиме MDBX_WRITEMAP вне зависимости от MDBX_AVOID_MSYNC и MDBX_MMAP_USE_MS_ASYNC. 2022-12-22 00:48:41 +03:00
Леонид Юрьев (Leonid Yuriev)
e9a2042df1 mdbx: добавление MDBX_NOTHROW_PURE_FUNCTION к некоторым функциям. 2022-12-22 00:48:41 +03:00
Леонид Юрьев (Leonid Yuriev)
fd98a635d9 mdbx: не возвращаем ошибку при попытке закрытия MAIN_DBI. 2022-12-22 00:48:41 +03:00
Леонид Юрьев (Leonid Yuriev)
722c6ecf43 mdbx: use attribute(tls_model(local-dynamic)) as workaround for CLANG bug. 2022-12-22 00:48:40 +03:00
Леонид Юрьев (Leonid Yuriev)
44493c6448 mdbx-tools: поддержка не-печатных имен subDb в mdbx_chk. 2022-12-22 00:48:40 +03:00
Леонид Юрьев (Leonid Yuriev)
7011743262 mdbx: поддержка не-печатных имен для subDb. 2022-12-21 22:29:03 +03:00
Леонид Юрьев (Leonid Yuriev)
b247b081af mdbx: переработка LRU-отметок для спиллинга.
Два существенных изменения:

1. Инкремент и обновление LRU происходит при изменении страницы,
   но не при доступе к ней.

2. Устранен регресс, из-за которого страницы в стеке курсора хоть
   помечались, но могли быть ошибочно пролиты на диск,
   так как dpl_age() возвращал не 0.
2022-12-21 22:29:03 +03:00
Леонид Юрьев (Leonid Yuriev)
bf2f3bfbbf mdbx: устранение чтения освобожденной памяти и жалоб ASAN при спиллинге.
Вероятность проявляния проблемы крайне низкая, но стало воспроизводиться после доработки спллинга.
2022-12-21 22:29:03 +03:00
Леонид Юрьев (Leonid Yuriev)
ffdff3f831 mdbx: обновление ChangeLog. 2022-12-14 10:58:31 +03:00
Леонид Юрьев (Leonid Yuriev)
07f2ccb752 mdbx: добавление опции MDBX_MMAP_USE_MS_ASYNC.
Суть в избавлении от лишнего вызова msync(MS_ASYNC) в режимах
MDBX_WRITEMAP+MDBX_SAFE_NOSYNC и т.п.

Гипотетически могут быть системы/платформы, на которых изменения в
разделяемой памяти не видны другим процессам до вызова msync(MS_ASYNC)
и/или до этого вызова не будет инициироваться вытеснение/запись таких
страниц на диск.

Поэтому использование msync(MS_ASYNC) вынесено под опцию
MDBX_MMAP_USE_MS_ASYNC, которая по-умолчанию включена только на системах
с MDBX_MMAP_INCOHERENT_FILE_WRITE или MDBX_MMAP_INCOHERENT_CPU_CACHE.
2022-12-14 02:05:52 +03:00
Леонид Юрьев (Leonid Yuriev)
23fedf6bba mdbx: контроль значений макросов-опций сборки. 2022-12-13 19:44:36 +03:00
Леонид Юрьев (Leonid Yuriev)
167011c2d5 mdbx: обновление ChangeLog. 2022-12-12 21:29:10 +03:00
Леонид Юрьев (Leonid Yuriev)
245a782912 mdbx: не игнорируем ошибки при открытии дескриптора с O_DSYNC. 2022-12-12 18:54:03 +03:00
Леонид Юрьев (Leonid Yuriev)
957c99d86f mdbx: добавление MDBX_opt_prefault_write_enable вместо MDBX_ENABLE_PREFAULT. 2022-12-12 18:54:03 +03:00
Леонид Юрьев (Leonid Yuriev)
b959e217b1 mdbx: рефакторинг обработки установки опций в значения по-умолчанию. 2022-12-12 18:54:03 +03:00
Леонид Юрьев (Leonid Yuriev)
54b15d7e41 mdbx: определение in-core БД (в tmpfs/ramfs/mfs) с отключением prefault-write.
Это вынужденный читинг для "починки" сравнительных бенчмарков при
размещении БД в /dev/shm.

Проблема в том, что актуальные ядра Linux для файлов размещенных в tmpfs
возвращают mincore=false. В результате, в простейших бенчмарках видно
двукратное снижение производительности, просто из-за вызовов write()
выполняемых для prefault.

Из-за этого, в таких синтетических тестах, новая libmdbx становится
существенно медленнее предыдущих версий, в том числе LMDB.
2022-12-12 18:54:03 +03:00
Леонид Юрьев (Leonid Yuriev)
69f7d6cdd8 mdbx-tools: несущественный рефакторинг mdbx_chk. 2022-12-11 20:33:11 +03:00
Леонид Юрьев (Leonid Yuriev)
0884f28f85 mdbx-tools: ускорение работы mdbx_chk при обработке пользовательских записей в @MAIN. 2022-12-11 20:33:11 +03:00
Леонид Юрьев (Leonid Yuriev)
1c93cff825 mdbx: дополнительные условия для prefault-write. 2022-12-11 00:14:40 +03:00
Леонид Юрьев (Leonid Yuriev)
1ae6a398ed mdbx-windows: исправление утечки overlapped-дескриптора. 2022-12-10 14:44:15 +03:00
Леонид Юрьев (Leonid Yuriev)
cd0ed2f155 mdbx: обновление ChangeLog. 2022-12-10 01:20:27 +03:00
Леонид Юрьев (Leonid Yuriev)
4ee8fff305 mdbx: +1 к подготавливаемому резерву в вырожденных случаях перед обновлением GC. 2022-12-09 19:18:17 +03:00
Леонид Юрьев (Leonid Yuriev)
1bb41ee8fc mdbx: отключение "экономии последовательностей" посредством MDBX_ENABLE_SAVING_SEQUENCES=0. 2022-12-09 18:07:16 +03:00
Леонид Юрьев (Leonid Yuriev)
a572902fde mdbx: автоматическая установка rp_augment_limit в "золотое сечение" от размера БД. 2022-12-09 18:07:16 +03:00
Леонид Юрьев (Leonid Yuriev)
ebc4976acb mdbx: перенос обновления geo-размера в map_resize(). 2022-12-09 18:07:16 +03:00
Леонид Юрьев (Leonid Yuriev)
fd7aaf5f35 mdbx: добавление ошибки MDBX_BACKLOG_DEPLETED и соответствующей логики в page_alloc_slowpath(). 2022-12-09 18:07:16 +03:00
Леонид Юрьев (Leonid Yuriev)
4b27c4c7c9 mdbx: предварительное вычисление me_maxgc_per_branch. 2022-12-08 16:29:18 +03:00
Леонид Юрьев (Leonid Yuriev)
3a77af7d8a mdbx: оптимизация поддержки сортировки в dpl_append(). 2022-12-07 00:06:07 +03:00
Леонид Юрьев (Leonid Yuriev)
a9163f6307 mdbx: доработка внутренних LRU-отметок для аккуратного спиллинга огромных транзакций. 2022-12-07 00:06:07 +03:00
Леонид Юрьев (Leonid Yuriev)
48eeb93628 mdbx: исправление падения в env_close() при закрытии среды пере-открытой в режиме только-для-чтения.
Ошибка не была замечена ранее из-за много-ходового сценария воспроизведения:
 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) сообщал об этой проблеме (теперь
понятно что это было), но из-за сложности сценария проблему не удалось
воспроизвести и идентифицировать.
2022-12-05 20:46:51 +03:00
Леонид Юрьев (Leonid Yuriev)
a772a9d3e1 mdbx: добавление проверки посредством mincore() с кэшированием присутствия страниц в памяти (опция сборки MDBX_ENABLE_MINCORE). 2022-12-05 10:41:05 +03:00
Леонид Юрьев (Leonid Yuriev)
be3ff92772 mdbx: предотвращение бесполезных page-faults в режиме MDBX_WRITEMAP (опция сборки MDBX_ENABLE_PREFAULT). 2022-12-05 10:03:00 +03:00
Леонид Юрьев (Leonid Yuriev)
dc27d5d30a mdbx: рефакторинг с формированием page_alloc_finalize() и сокрашением метрик MDBX_ENABLE_PROFGC. 2022-12-04 18:24:30 +03:00
Леонид Юрьев (Leonid Yuriev)
48a56d1d05 mdbx: запрещение разного MDBX_WRITEMAP между процессами в режимах с отложенной/ленивой записью.
Ранее упущенный не очевидный момент: При работе БД в режимах
не-синхронной/отложенной фиксации на диске, все процессы-писатели должны
иметь одинаковый режим MDBX_WRITEMAP.

В противном случае, сброс на диск следует выполнять дважды: сначала
msync(), затем fdatasync(). При этом msync() не обязан отрабатывать в
процессах без MDBX_WRITEMAP, так как файл в память отображен только для
чтения. Поэтому, в общем случае, различия по MDBX_WRITEMAP не позволяют
выполнить фиксацию данных на диск, после их изменения в другом процессе.

В режиме MDBX_UTTERLY_NOSYNC позволять совместную работу с MDBX_WRITEMAP
также не следует, поскольку никакой процесс (в том числе последний) не
может гарантированно сбросить данные на диск, а следовательно не должен
помечать какую-либо транзакцию как steady.

В результате, требуется либо запретить совместную работу процессам с
разным MDBX_WRITEMAP в режиме отложенной записи, либо отслеживать такое
смешивание и блокировать steady-пометки - что контрпродуктивно.
2022-12-04 18:10:54 +03:00
Леонид Юрьев (Leonid Yuriev)
db83bd34d2 mdbx-test: чтение актуальных флагов режима работы БД. 2022-12-04 18:10:50 +03:00
Леонид Юрьев (Leonid Yuriev)
23d236f70e mdbx: добавление MDBX_opt_writethrough_threshold и сопутствующие доработки. 2022-12-04 13:41:50 +03:00
Леонид Юрьев (Leonid Yuriev)
822952ef01 mdbx: внутреннее переименование MDBX_SYNC_KICK (косметика). 2022-12-04 13:41:50 +03:00
Леонид Юрьев (Leonid Yuriev)
9f2d30c1a9 mdbx: изменение размера отображения внутри env_sync() если это требуется для сброса данных на диск. 2022-12-04 13:41:32 +03:00
Леонид Юрьев (Leonid Yuriev)
47851135f3 mdbx-doc: using the https://libmdbx.dqdkfa.ru/dead-github for resources deleted by the Github' administration (backport). 2022-12-03 06:23:31 +03:00
Леонид Юрьев (Leonid Yuriev)
6139443ef1 mdbx: fix SIGSEGV/invalid-deref/invalid-free inside env_close() when mdbx_env_open() failed in re-open case (backport).
Thanks to [@leisim](https://t.me/leisim) for [reporting](https://t.me/libmdbx/3946) this issue.
2022-12-03 05:48:30 +03:00
Jan Biedermann
30f292d496 mdbx: fix typo of || inside #if byte-order condition (backport).
https://gitflic.ru/project/erthink/libmdbx/merge-request/4
2022-12-03 05:47:36 +03:00
Леонид Юрьев (Leonid Yuriev)
163486fa3a mdbx: добавление FIXME для MDBX_NOMETASYNC. 2022-12-01 03:00:40 +03:00
Леонид Юрьев (Leonid Yuriev)
512e6dbd08 mdbx: отключение безусловного предпочтения записи через дескриптор с O_DSYNC.
Требуется переработка = динамический выбор между write(O_DSYNC) и write()+fdatasync(),
в зависимости от количества записываемых линейных фрагментов.
2022-12-01 02:59:28 +03:00
Леонид Юрьев (Leonid Yuriev)
2776480f18 mdbx: оптимизация pnl_merge() для случаев неперекрывающихся объединяемых списков. 2022-11-29 02:50:34 +03:00
Леонид Юрьев (Leonid Yuriev)
b7734369a2 mdbx: кавычки для предупреждения о дырявости RISC-V для совместимости. 2022-11-29 02:50:34 +03:00
Леонид Юрьев (Leonid Yuriev)
01a39e7dc2 mdbx: добавление и использование ptr_disp() и ptr_dist().
Для уменьшения кастинга типов указателей и потенциальной нагрузки оптимизатора/кодогенератора алиасингом.
2022-11-29 02:50:34 +03:00
Леонид Юрьев (Leonid Yuriev)
d6b9a71825 mdbx-test: добавление исключений Valgrind для измененного кода. 2022-11-28 23:56:42 +03:00
Леонид Юрьев (Leonid Yuriev)
9cee1ff799 mdbx: определение ior_WriteFile_flag для ясности кода. 2022-11-28 23:56:42 +03:00
Леонид Юрьев (Leonid Yuriev)
8c74de57ea mdbx: исправление txn_commit() для случаев конкурентных и/или неверных вызовов при MDBX_ENABLE_PROFGC=1. 2022-11-28 23:56:42 +03:00
Леонид Юрьев (Leonid Yuriev)
05804e2f30 mdbx: доработка/оптимизация page_retire_ex(). 2022-11-28 23:56:42 +03:00
Леонид Юрьев (Leonid Yuriev)
7685b4080e mdbx: исправление возврата и подсчета "грязных" страниц в режиме MDBX_WRITEMAP.
Исправление регрессии после коммита db72763de0.

После отключения затратой поддержки списка "грязных" страниц логика
page_retire_ex() оказалась не полной и требовала доработки. Из-за этого
страницы добавленные или клонированные-и-измененные в текущей
транзакции, которые становились не нужными, не возвращались к доступным
для немедленного использования, а помещались в retired-список
становящихся доступными в последующих транзакциях.

В результате, в некоторых сценариях, особенно с интенсивным расщеплением
страниц из-за вставки ключей, происходило необоснованно сильное
потребление/выделение страниц БД. В свою очередь, это приводило к
использованию излишнего кол-ва страниц, увеличению GC, росту RSS и
размеру БД.
2022-11-28 23:56:42 +03:00
Леонид Юрьев (Leonid Yuriev)
c521a21f05 mdbx: перемещение mp_next в заголовке страницы для отделения от mp_txnid. 2022-11-28 23:56:42 +03:00
Леонид Юрьев (Leonid Yuriev)
c5ddf12602 mdbx: увеличение лимита MDBX_TXL_MAX до 2^26. 2022-11-28 15:51:36 +03:00
Леонид Юрьев (Leonid Yuriev)
07674ada47 mdbx: доработка подготовки резерва перед обновлением GC при включенном BigFoot. 2022-11-28 15:51:36 +03:00
Леонид Юрьев (Leonid Yuriev)
3757eb72f7 mdbx: экономия последовательностей при выделении одиночных страниц. 2022-11-28 15:51:36 +03:00
Леонид Юрьев (Leonid Yuriev)
b324844296 mdbx: Обновление ChangeLog. 2022-11-28 15:45:29 +03:00
Леонид Юрьев (Leonid Yuriev)
30972102e5 mdbx: исправление сборки при MDBX_PNL_ASCENDING=1. 2022-11-25 19:03:05 +03:00
Леонид Юрьев (Leonid Yuriev)
61eafe80c1 mdbx: использование https://libmdbx.dqdkfa.ru/dead-github для удаленных issues. 2022-11-23 01:18:25 +03:00
Леонид Юрьев (Leonid Yuriev)
a1333fc827 mdbx: fix SIGSEGV/invalid-deref/invalid-free inside env_close() when mdbx_env_open() failed in re-open case.
Thanks to [@leisim](https://t.me/leisim) for [reporting](https://t.me/libmdbx/3946) this issue.
2022-11-23 00:57:02 +03:00
Леонид Юрьев (Leonid Yuriev)
da023657f5 mdbx: переработка внутренних флагов связанных с выделением страниц из GC. 2022-11-23 00:56:09 +03:00
Леонид Юрьев (Leonid Yuriev)
141cce0c0f mdbx: использование size_t для npages (косметика). 2022-11-23 00:56:09 +03:00
Леонид Юрьев (Leonid Yuriev)
12ed2bcfbd mdbx: использование единого курсора для поиска в GC. 2022-11-23 00:56:09 +03:00
Леонид Юрьев (Leonid Yuriev)
1f93dfe5fd mdbx: обновление ChangeLog. 2022-11-19 23:19:30 +03:00
Леонид Юрьев (Leonid Yuriev)
543e52730d mdbx: доработка поддержки авто-слияния записей GC внутри page_alloc_slowpath(). 2022-11-19 23:19:30 +03:00
Леонид Юрьев (Leonid Yuriev)
c46c03e7c8 mdbx: fix nasty typo/rebase/merge bug with calling msync() on Linux. 2022-11-19 23:19:30 +03:00
Леонид Юрьев (Leonid Yuriev)
4a257133cb mdbx: устранение несущественных предупреждений Coverity. 2022-11-19 19:36:30 +03:00
Леонид Юрьев (Leonid Yuriev)
f73cd7a491 mdbx: упрощение page_alloc_slowpath().
Упрощение за счет удаления проверки флага `MDBX_ALLOC_GC`,
который всегда взведен при вызове page_alloc_slowpath().
2022-11-19 19:36:30 +03:00
Леонид Юрьев (Leonid Yuriev)
3e05d1a427 mdbx: оптимизация page_copy() для LEAF2 и добавление параноидального контроля от переполнения. 2022-11-19 19:36:30 +03:00
Леонид Юрьев (Leonid Yuriev)
e518edcfed mdbx: унифицирование инициализации mp_txnid внутри page_dirty(). 2022-11-19 19:36:30 +03:00
Леонид Юрьев (Leonid Yuriev)
3563ed00e3 mdbx: использование не-спаренного курсора и gc_cursor_init() внутри update_gc(). 2022-11-19 19:36:30 +03:00
Леонид Юрьев (Leonid Yuriev)
0f92baaa5e mdbx: обновление debug_begin.h и debug_end.h 2022-11-19 19:36:30 +03:00
Jan Biedermann
eaf063ca9b mdbx: fix typo of || inside #if byte-order condition.
https://gitflic.ru/project/erthink/libmdbx/merge-request/4
2022-11-19 19:36:30 +03:00
Леонид Юрьев (Leonid Yuriev)
6c840cf58e mdbx: подсчет грязных страниц в режиме MDBX_WRITEMAP для статистики. 2022-11-19 19:36:30 +03:00
Леонид Юрьев (Leonid Yuriev)
9b062cf0c7
mdbx: выпуск v0.12.2 (Иван Ярыгин)
Выпуск с существенными доработками и новой функциональностью
в память о российском борце [Иване Сергеевиче Ярыгине](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>
2022-11-11 17:35:32 +03:00
Леонид Юрьев (Leonid Yuriev)
d23f695ab3 mdbx: импорт ChangeLog для ветки stable. 2022-11-11 16:56:54 +03:00
Леонид Юрьев (Leonid Yuriev)
b274a35410 mdbx-doc: дополнение man-страниц утилит описанием опций -u и -U. 2022-11-11 16:44:51 +03:00
Леонид Юрьев (Leonid Yuriev)
9fca1734c7 mdbx: обновление ChangeLog. 2022-11-11 16:16:33 +03:00
Леонид Юрьев (Leonid Yuriev)
3704433aa9 mdbx: минорное удаление мертвого/ненужного кода из page_alloc_slowpath(). 2022-11-10 16:34:23 +03:00
Леонид Юрьев (Leonid Yuriev)
70e8006776 mdbx-docs: перенаправление github-ссылок на web-archive. 2022-11-10 15:54:31 +03:00
Леонид Юрьев (Leonid Yuriev)
8ffb0bb3d8 mdbx-cmake: поддержка всех основных опций при сборке посредством CMake. 2022-11-10 15:39:52 +03:00
Леонид Юрьев (Leonid Yuriev)
d6b47c7bd1
mdbx: release v0.11.13 (Swashplate)
The stable bugfix release in memory of [Boris Yuryev](https://ru.wikipedia.org/wiki/Юрьев,_Борис_Николаевич) on his 133rd birthday.

Fixes:
------

 - Fixed builds with older libc versions after using `fcntl64()` (backport).
 - Fixed builds with  older `stdatomic.h` versions,
   where the `ATOMIC_*_LOCK_FREE` macros mistakenly redefined using functions (backport).
 - Added workaround for `mremap()` defect to avoid assertion failure (backport).
 - Workaround for `encryptfs` bug(s) in the `copy_file_range` implementation  (backport).
 - Fixed unexpected `MDBX_BUSY` from `mdbx_env_set_option()`, `mdbx_env_set_syncbytes()`
   and `mdbx_env_set_syncperiod()` (backport).
 - CMake requirements lowered to version 3.0.2 (backport).
 - Added admonition of insecure for RISC-V (backport).

Minors:
-------

 - Minor clarification output of `--help` for `mdbx_test` (backport).
 - Added admonition of insecure for RISC-V (backport).
 - Stochastic scripts and CMake files synchronized with the `devel` branch.
 - Use `--dont-check-ram-size` for small-tests make-targets (backport).

30 files changed, 405 insertions(+), 136 deletions(-)
Signed-off-by: Леонид Юрьев (Leonid Yuriev) <leo@yuriev.ru>
2022-11-10 13:07:28 +03:00
Леонид Юрьев (Leonid Yuriev)
53d78bbad5 mdbx-make: use --dont-check-ram-size for small-tests targets. 2022-11-10 12:11:42 +03:00
Леонид Юрьев (Leonid Yuriev)
8f0c5bc7c7 mdbx: update ChangeLog. 2022-11-10 12:11:11 +03:00
Леонид Юрьев (Leonid Yuriev)
29eab4afdd mdbx-make: use --dont-check-ram-size for small-tests targets (backport). 2022-11-10 12:09:34 +03:00
Леонид Юрьев (Leonid Yuriev)
12717aac8c mdbx-test: sync stochastic scripts with devel branch. 2022-11-10 10:57:16 +03:00
Леонид Юрьев (Leonid Yuriev)
90f39c88a0 mdbx-test: add --dont-check-ram-size option to stochastic scripts. 2022-11-10 10:54:14 +03:00
Леонид Юрьев (Leonid Yuriev)
bc80fbbeea mdbx-test: fix stochastic scripts after prev commit. 2022-11-10 10:53:14 +03:00
Леонид Юрьев (Leonid Yuriev)
e992da9efe mdbx-cmake: синхронизация CMake-файлов. 2022-11-10 01:10:39 +03:00
Леонид Юрьев (Leonid Yuriev)
d7e4cb2e22 mdbx: add admonition of insecure for RISC-V (backport). 2022-11-10 01:10:39 +03:00
Леонид Юрьев (Leonid Yuriev)
af1d01ffb3 mdbx-windows: уточнение проверок макросов MinGW и устранение предупреждений (backport). 2022-11-10 01:10:39 +03:00
Леонид Юрьев (Leonid Yuriev)
cce052e869 mdbx-cmake: совместимость с CMake 3.0.2 для CI на старых системах (backport). 2022-11-10 00:48:54 +03:00
Леонид Юрьев (Leonid Yuriev)
094c2f345d mdbx: переименование packages/rpm (backport). 2022-11-10 00:48:54 +03:00
Леонид Юрьев (Leonid Yuriev)
7b2eee91af mdbx: добавлено описание параметров MDBX_debug_func и MDBX_debug_func (backport). 2022-11-10 00:48:54 +03:00
Леонид Юрьев (Leonid Yuriev)
d863629387 mdbx-test: минорное уточнение подсказки --help (backport). 2022-11-10 00:48:54 +03:00
Леонид Юрьев (Leonid Yuriev)
1b3b6e4479 mdbx: исправление неожиданного MDBX_BUSY из mdbx_env_set_option() (backport). 2022-11-10 00:48:54 +03:00
Леонид Юрьев (Leonid Yuriev)
5dcc0171fa mdbx-windows: попытка борьбы с ложно-положительными конфликтами LockFileEx() (backport). 2022-11-10 00:48:54 +03:00
Леонид Юрьев (Leonid Yuriev)
13c256026e mdbx-cmake: синхронизация конструкций CMake между проектами. 2022-11-09 23:37:50 +03:00
Леонид Юрьев (Leonid Yuriev)
ec0ec90f15 mdbx: корректировка ChangeLog. 2022-11-09 16:41:46 +03:00
Леонид Юрьев (Leonid Yuriev)
67f4098bfa mdbx: add admonition of insecure for RISC-V. 2022-11-09 12:39:06 +03:00
Леонид Юрьев (Leonid Yuriev)
1b0d747e7b mdbx: обновление ChangeLog. 2022-11-09 00:36:06 +03:00
Леонид Юрьев (Leonid Yuriev)
2dfdac2821 mdbx-windows: повтор чтения заголовка при ERROR_LOCK_VIOLATION. 2022-11-08 23:32:34 +03:00
Леонид Юрьев (Leonid Yuriev)
144cbbabb8 mdbx-test: поддержка MinGW в скриптах тестирования для CI. 2022-11-08 20:39:44 +03:00
Леонид Юрьев (Leonid Yuriev)
c270306580 mdbx-windows: уточнение проверок макросов MinGW и устранение предупреждений. 2022-11-08 20:39:44 +03:00
Леонид Юрьев (Leonid Yuriev)
2558903081 mdbx-make: исправление сборки тестов при MDBX_BUILD_CXX=NO. 2022-11-08 20:39:44 +03:00
Леонид Юрьев (Leonid Yuriev)
d315a9255a mdbx-test: смена расширения у C++ исходников. 2022-11-08 20:39:44 +03:00
Леонид Юрьев (Leonid Yuriev)
652ca2b5cb mdbx-windows: исправление минорных предупреждений MingGW. 2022-11-08 20:39:44 +03:00
Леонид Юрьев (Leonid Yuriev)
987509f90f mdbx-cmake: try fix libmdbx-rs/issues/10.
https://github.com/vorot93/libmdbx-rs/issues/10
2022-11-08 20:39:38 +03:00
Леонид Юрьев (Leonid Yuriev)
8c75ed59da mdbx-cmake: отключением LTO для G++ < 7.0, ибо падает. 2022-11-07 23:57:25 +03:00
Леонид Юрьев (Leonid Yuriev)
623ab21707 mdbx-cmake: совместимость с CMake 3.0.2 для CI на старых системах. 2022-11-07 23:37:27 +03:00
Леонид Юрьев (Leonid Yuriev)
425730c2b3 mdbx: чуток апостофов для имен в doxygen-комментариях (backport). 2022-11-07 16:44:00 +03:00
Леонид Юрьев (Leonid Yuriev)
471e854551 mdbx: workaround for encryptfs's copy_file_range() bug(s) (backport).
Выяснилось что утилита `mdbx_copy` и функции `mdbx_env_copy()` могут
создавать ПРОБЛЕМЫ если целевой файл расположен в encryptfs (такая
файловая система в Linux).

При этом может быть четыре исхода в зависимости от версии ядра и
положения звезд на небе:
 - всё хорошо;
 - плохие данные в копии без возврата ошибок;
 - ошибка EINVAL(22) при копировании;
 - oops или зависание ядра, отвал смонтированной encryptfs и т.п.

В текущем понимании, причина обусловлена ошибой в коде fs, которая
проявляется при использовании системного вызова `copy_file_range`.
2022-11-07 16:44:00 +03:00
Леонид Юрьев (Leonid Yuriev)
1bd0eb35bc mdbx-cmake: исправление для совместимости с CMake 3.8 (backport). 2022-11-07 16:43:32 +03:00
Леонид Юрьев (Leonid Yuriev)
79c65821ee mdbx: переименование packages/rpm. 2022-11-07 14:19:30 +03:00
Леонид Юрьев (Leonid Yuriev)
3ee223514d mdbx: очистка readers_refresh_flag для page_alloc_slowpath(). 2022-11-07 14:16:59 +03:00
Леонид Юрьев (Leonid Yuriev)
f7f94bb698 mdbx: чуть больше const для прозрачности. 2022-11-07 14:16:35 +03:00
Леонид Юрьев (Leonid Yuriev)
5d36d242a7 mdbx: обновление ChangeLog. 2022-11-07 00:53:44 +03:00
Леонид Юрьев (Leonid Yuriev)
f0c6aa4646 mdbx: workaround for false-positives from Valgrind bug. 2022-11-07 00:47:16 +03:00
Леонид Юрьев (Leonid Yuriev)
771c85a880 mdbx: уточнение txn_space_dirty в соответствии с обновленным учетом грязных страниц. 2022-11-07 00:44:38 +03:00
Леонид Юрьев (Leonid Yuriev)
4f1f9141f4 mdbx: добавление MDBX_ENABLE_PGOP_STAT и MDBX_ENABLE_PROFGC во внутреннюю строку с опциями сборки. 2022-11-07 00:44:38 +03:00
Леонид Юрьев (Leonid Yuriev)
f680c99116 mdbx: переделка page_alloc_slowpath() с добавлением профилирования GC. 2022-11-07 00:44:37 +03:00
Леонид Юрьев (Leonid Yuriev)
acaa1d82d9 mdbx: minor touch assertions for issue#7.
https://gitflic.ru/project/erthink/libmdbx/issue/7
2022-11-05 14:06:00 +03:00
Леонид Юрьев (Leonid Yuriev)
36eb40bccb mdbx: добавлено описание параметров MDBX_debug_func и MDBX_debug_func. 2022-11-05 14:06:00 +03:00
Леонид Юрьев (Leonid Yuriev)
47e7a646fd mdbx: переделка отслеживания mlocks для игнорирования EINVAL от madvise(). 2022-11-05 14:06:00 +03:00
Леонид Юрьев (Leonid Yuriev)
9cbd4e63ca mdbx-test: минорное уточнение подсказки --help. 2022-11-04 16:21:13 +03:00
Леонид Юрьев (Leonid Yuriev)
d4e67d14ce mdbx: исправление неожиданного MDBX_BUSY из mdbx_env_set_option(). 2022-11-03 17:23:32 +03:00
Леонид Юрьев (Leonid Yuriev)
91a6e84cab mdbx-windows: попытка борьбы с ложно-положительными конфликтами LockFileEx(). 2022-11-03 13:00:35 +03:00
Леонид Юрьев (Leonid Yuriev)
28e2e31949 mdbx: выделение специфической инициализации в osal_ctor(). 2022-11-02 11:09:32 +03:00
Леонид Юрьев (Leonid Yuriev)
8f8b9f3d2a mdbx: чуток апостофов для имен в doxygen-комментариях. 2022-11-02 00:02:33 +03:00
Леонид Юрьев (Leonid Yuriev)
836f6c2723 mdbx: обновление ChangeLog. 2022-10-24 12:58:41 +03:00
Леонид Юрьев (Leonid Yuriev)
9eaf86bde1 mdbx-tools: добавление опций -u и -U для использования mdbx_env_warmup(). 2022-10-24 12:50:15 +03:00
Леонид Юрьев (Leonid Yuriev)
7902b97a3d mdbx-test: простая проверка warmup. 2022-10-24 11:37:57 +03:00
Леонид Юрьев (Leonid Yuriev)
d661d4bac7 mdbx: добавление mdbx_env_warmup() 2022-10-24 11:37:57 +03:00
Леонид Юрьев (Leonid Yuriev)
b04f7814ef mdbx-cmake: исправление для совместимости с CMake 3.8 2022-10-24 01:01:01 +03:00
Леонид Юрьев (Leonid Yuriev)
4e95a079ee mdbx: переименование MDBX_COMMIT_PAGES в MDBX_AUXILARY_IOV_MAX. 2022-10-22 11:12:52 +03:00
Леонид Юрьев (Leonid Yuriev)
753fa13048 mdbx: удаление лишних комментариев. 2022-10-22 11:12:52 +03:00
Леонид Юрьев (Leonid Yuriev)
bbd139b2ae mdbx-cmake: создание VERSION.txt 2022-10-22 11:08:06 +03:00
Леонид Юрьев (Leonid Yuriev)
64d0e639c2 mdbx-cmake: синхронизация LTO-рецептов. 2022-10-22 11:08:06 +03:00
Леонид Юрьев (Leonid Yuriev)
cd616447da mdbx-cmake: set X86_32/X86_64/ARM/MIPS for Windows and Android. 2022-10-22 11:08:06 +03:00
Леонид Юрьев (Leonid Yuriev)
8833dc6871 mdbx: костыль для обхода ошибок encryptfs.
Выяснилось что утилита `mdbx_copy` и функции `mdbx_env_copy()` могут
создавать ПРОБЛЕМЫ если целевой файл расположен в encryptfs (такая
файловая система в Linux).

При этом может быть четыре исхода в зависимости от версии ядра и
положения звезд на небе:
 - всё хорошо;
 - плохие данные в копии без возврата ошибок;
 - ошибка EINVAL(22) при копировании;
 - oops или зависание ядра, отвал смонтированной encryptfs и т.п.

В текущем понимании, причина обусловлена ошибой в коде fs, которая
проявляется при использовании системного вызова `copy_file_range`.
2022-10-22 01:38:33 +03:00
Леонид Юрьев (Leonid Yuriev)
206dbecccf mdbx: добавление в ChangeLog ссылок с машинным переводом. 2022-10-14 16:20:57 +03:00
Леонид Юрьев (Leonid Yuriev)
6fdae12996 mdbx: workaround for mremap() defect (backport).
Есть основание полагать, что mremap() может возвращать MAP_FAILED, но НЕ
устанавливать errno в некоторых пограничных ситуациях. Например, когда
системных ресурсов не хватает на актуализацию/копирование/клонирование
состояния отображения на финальной стадии, в том числе из-за раскраски
исходного отображения разными флагами через madvise().
2022-10-14 14:05:44 +03:00
Леонид Юрьев (Leonid Yuriev)
2684c89d91 mdbx: workaround for older stdatomic.h versions, where the ATOMIC_*_LOCK_FREE macros mistakenly redefined using functions (backport). 2022-10-14 14:05:16 +03:00
Леонид Юрьев (Leonid Yuriev)
7b60363a31 mdbx: fix builds with older libc versions after using fcntl64() (backport). 2022-10-14 14:01:02 +03:00
Леонид Юрьев (Leonid Yuriev)
39c6387d23 mdbx: Обновление ChangeLog. 2022-10-14 11:47:34 +03:00
Леонид Юрьев (Leonid Yuriev)
80f9f73a5e mdbx: чуть больше контроля и паранойи для страховки от дефектов mremap().
Есть основание полагать, что mremap() может возвращать MAP_FAILED, но НЕ
устанавливать errno в некоторых пограничных ситуациях. Например, когда
системных ресурсов не хватает на актуализацию/копирование/клонирование
состояния отображения на финальной стадии, в том числе из-за раскраски
исходного отображения разными флагами через madvise().
2022-10-14 00:20:37 +03:00
Леонид Юрьев (Leonid Yuriev)
51a765a5a7 mdbx-make: вывод протокола при сбое тестовых целей. 2022-10-13 19:28:01 +03:00
Леонид Юрьев (Leonid Yuriev)
c4beb5a4a0 mdbx-test: не вызываем sudo при отсутствии. 2022-10-13 19:28:01 +03:00
Леонид Юрьев (Leonid Yuriev)
6c986ce904 mdbx: костыль для старых версий stdatomic.h, где макросы ATOMIC_*_LOCK_FREE ошибочно переопределяются через функции. 2022-10-13 19:28:01 +03:00
Леонид Юрьев (Leonid Yuriev)
f5fee949e3 mdbx: починка сборки для старых версий glibc после задействования fcntl64(). 2022-10-13 17:30:43 +03:00
Леонид Юрьев (Leonid Yuriev)
d94e65b870 mdbx: использование fcntl64(F_GETLK64/F_SETLK64/F_SETLKW64) при наличии.
Это решает проблему срабатывания проверочного утверждения при сборке для
платформ где тип off_t шире соответствующих полей структуры flock,
используемой для блокировки файлов.
2022-10-12 21:25:18 +03:00
Леонид Юрьев (Leonid Yuriev)
5a45c4a210 mdbx-windows: удаление ненужного вызова LockFileEx() внутри mdbx_env_copy(). 2022-10-12 21:25:18 +03:00
Леонид Юрьев (Leonid Yuriev)
686c908a95 mdbx: более осторожное преобразование к типу mdbx_tid_t для устранения предупреждений. 2022-10-12 21:25:18 +03:00
Леонид Юрьев (Leonid Yuriev)
e5fc056035 mdbx: изменение формата LCK и семантики некоторых внутренних полей.
Изменение формата LCK-файла означает что версии libmdbx использующие
разный формат не смогут работать с одной БД одновременно, а только
поочередно (LCK-файл переписывается при открытии первым открывающим БД
процессом).

1. Поле mti_unsynced_pages теперь 64-битное (чтобы не контролировать
переполнение) и перемещено для соблюдения выравнивания.

2. Поле mti_sync_timestamp переименовано в mti_eoos_timestamp
одновременно со сменой семантики. Теперь время отсчитывается не от
момента сброса данных на диск, а с момента входа в «грязное» состояние.

Скорее всего, текущая версия формата LCK не окончательная
и изменится до релиза.
2022-10-12 21:25:18 +03:00
Леонид Юрьев (Leonid Yuriev)
290630f118
mdbx: release v0.11.12 (Эребуни)
The stable bugfix release.

Fixes:
------

 - Fixed static assertion failure on platforms where the `off_t` type is wider
   than corresponding fields of `struct flock` used for file locking (backport).
   Now _libmdbx_ will use `fcntl64(F_GETLK64/F_SETLK64/F_SETLKW64)` if available.
 - Fixed assertion check inside `page_retire_ex()` (backport).

Minors:
-------

 - Fixed `-Wint-to-pointer-cast` warnings while casting to `mdbx_tid_t` (backport).
 - Removed needless `LockFileEx()` inside `mdbx_env_copy()` (backport).

11 files changed, 96 insertions(+), 49 deletions(-)
Signed-off-by: Леонид Юрьев (Leonid Yuriev) <leo@yuriev.ru>
2022-10-12 21:11:47 +03:00
Леонид Юрьев (Leonid Yuriev)
bdcc345455 mdbx: minor fix assertion check inside page_retire_ex() (backport). 2022-10-12 21:00:35 +03:00
Леонид Юрьев (Leonid Yuriev)
db9e2c6f07 mdbx: using fcntl64(F_GETLK64/F_SETLK64/F_SETLKW64) when available (backport).
This fixes issues (static assertion failure, etc) on platforms where the
`off_t` type is wider than corresponding fields of `struct flock`.
2022-10-12 20:59:05 +03:00
Леонид Юрьев (Leonid Yuriev)
50e9e0e561 mdbx-windows: drop needless LockFileEx() inside mdbx_env_copy() (backport). 2022-10-12 18:32:56 +03:00
Леонид Юрьев (Leonid Yuriev)
8505203080 mdbx: avoid -Wint-to-pointer-cast warnings while casting to mdbx_tid_t (backport). 2022-10-12 18:32:49 +03:00
Леонид Юрьев (Leonid Yuriev)
dd9780606b mdbx-test: небольшие правки скрипта long_stochastic.sh для работы в Windows. 2022-10-10 21:59:51 +03:00
Леонид Юрьев (Leonid Yuriev)
5242c5bfdc mdbx: улучшение эвристики включения авто-слияния записей GC. 2022-10-10 21:06:33 +03:00
Леонид Юрьев (Leonid Yuriev)
f5a6e0c04f mdbx-make: добавление gcc-riscv64-linux-gnu в список для цели cross-gcc. 2022-10-10 19:27:34 +03:00
Леонид Юрьев (Leonid Yuriev)
329af93436 mdbx: уменьшение в 42 раза значения по-умолчанию для me_options.dp_limit в отладочных сборках. 2022-10-10 19:26:38 +03:00
Леонид Юрьев (Leonid Yuriev)
22a84d656b mdbx: проверка атомарности C11-операций c 32/64-битными данными. 2022-10-10 19:24:14 +03:00
Леонид Юрьев (Leonid Yuriev)
e46ca81abd mdbx: обновление ChangeLog. 2022-10-10 17:03:07 +03:00
Леонид Юрьев (Leonid Yuriev)
25ab65b470 mdbx++: добавление env::limits::pairsize4page_max() и env::limits::valsize4page_max(). 2022-10-10 16:37:59 +03:00
Леонид Юрьев (Leonid Yuriev)
c3dd60fcb6 mdbx: добавление mdbx_env_get_pairsize4page_max() и mdbx_env_get_valsize4page_max(). 2022-10-10 16:33:51 +03:00
Леонид Юрьев (Leonid Yuriev)
9cdee2adb5 mdbx-cmake: добавлена поддержка опции MDBX_AVOID_MSYNC. 2022-10-10 15:55:20 +03:00
Леонид Юрьев (Leonid Yuriev)
98e29fe628 mdbx-windows: UNICODE-зависимое определение макросов MDBX_DATANAME, MDBX_LOCKNAME и MDBX_LOCK_SUFFIX. 2022-10-10 13:56:57 +03:00
Леонид Юрьев (Leonid Yuriev)
98a2bd785a mdbx-windows: перемещена декларация osal_mb2w() для ликвидации предупреждений. 2022-10-10 13:56:57 +03:00
Леонид Юрьев (Leonid Yuriev)
138a83c2be mdbx: добавлена несколько MDBX_MAYBE_UNUSED для ликвидации предупреждений. 2022-10-10 13:56:57 +03:00
Леонид Юрьев (Leonid Yuriev)
92d203a12c mdbx: исправление ложного срабатывания контроля "invalid page-address" в page_check().
При проверке использовалось глобальное значение me_dxb_mmap.current,
к которому не должны обращаться читающие транзакции. В результате,
в сложных много-поточных сценариях с изменением размера БД и её
переполнением, проверка могла выдавать ложно-положительный результат.

С точки зрения пользователя, ошибка могла проявляться как возврат
`MDBX_CORRUPTED` из читающей транзакции, когда включен "безопасный
режим" (дополнительный контроль), а в параллельной пишущей транзакции
происходит увеличение размера БД с последующим переполнением и откатом
этой транзакции. При этом никакого повреждения структуры БД нет.
2022-10-10 13:56:57 +03:00
Леонид Юрьев (Leonid Yuriev)
63b4d2289d mdbx: удаление utf8bom для устранения проблем амальгамации кода. 2022-10-10 13:56:57 +03:00
Леонид Юрьев (Leonid Yuriev)
688ec3e85c mdbx-test: добавление исключений Valgrind для нового кода. 2022-10-10 13:56:57 +03:00
Леонид Юрьев (Leonid Yuriev)
ae8e373143 mdbx-test: больше winnt-статусов как coredump. 2022-10-10 13:56:57 +03:00
Леонид Юрьев (Leonid Yuriev)
14eda2cd17 mdbx-windows: использование _CrtDbgReport() в отладочных сборках. 2022-10-10 13:56:57 +03:00
Леонид Юрьев (Leonid Yuriev)
ad09164604 mdbx: минорное исправление для устранения срабатывания проверочного утверждения в отладочных сборках.
Ассерт мог срабатывать из-за отсутствия бита P_LEAF2 в передаваемом проверочном значении.
На что-либо другое не влияло, но не следует понять почему этот недочет ны был выявлен тестами раньше.
2022-10-10 13:56:57 +03:00
Леонид Юрьев (Leonid Yuriev)
db72763de0 mdbx: отключение учета грязных страниц в не требующих этого режимах.
В режиме MDBX_WRITEMAP с опцией сборки MDBX_AVOID_MSYNC=0 отслеживание грязных страниц не требуется.
Эта доработка устраняет еще одну  из недоделок (пункт в TODO).
2022-10-10 13:56:57 +03:00
Леонид Юрьев (Leonid Yuriev)
940ef30659 mdbx: спиллинг грязных страниц с учетом их суммарного размера. 2022-10-10 13:56:57 +03:00
Леонид Юрьев (Leonid Yuriev)
f6eec7195b mdbx: защита от нуля только общей задержки в метриках транзакции.
Ранее, при конвертации очень коротких интервалов в формат фиксированной
точки 16-точка-16, всегда выполнялось замещение нуля единицей. Т.е. если
интервал был не нулевым, но меньше 15.259 микросекунд (1/65536 секунды),
то вместо 0 возвращалось 1.

Это приводило к тому, что сумма длительности отдельных стадий нередко
была больше чем общее время фиксации транзакции. Проблема усугублялась,
если получаемые значения аккумулировались по серии транзакций.

Теперь такая защита от нуля выполняется только для общего времени,
но не для отдельных стадий.

Было:
	latency(ms): preparation=72.69 gc=72.69 write=73.04 sync=141.40 ending=72.69 whole=142.14
	Аккумулированная сумма длительности этапов ВТРОЕ(!) больше общей длительности.

Стало:
	latency(ms): preparation=0.00 gc=0.02 write=0.79 sync=67.98 ending=0.00 whole=140.81
	Аккумулированная сумма длительности этапов меньше общей длительности,
	так как для каждой транзакции общая длительность возвращается не менее 15.259 микросекунд.
2022-10-10 13:56:57 +03:00
Леонид Юрьев (Leonid Yuriev)
92dabe1ad1 mdbx: исправление лишнего сброса данных на диск в режиме MDBX_SAFE_NOSYNC при обновлении GC. 2022-10-10 13:56:57 +03:00
Leonid Yuriev
0f7e5073db mdbx: поправлен сбор информации о задержках, чтобы включенный аудит не искажал затраты на GC. 2022-10-10 13:56:57 +03:00
Леонид Юрьев (Leonid Yuriev)
bee7431f76 mdbx++: добавлена фиксация транзакции с получением информации о задержках. 2022-10-10 13:56:57 +03:00
Леонид Юрьев (Leonid Yuriev)
3579496945 mdbx: отключение MDBX_HAVE_BUILTIN_CPU_SUPPORTS для e2k. 2022-10-10 13:56:57 +03:00
Леонид Юрьев (Leonid Yuriev)
24d7a4d605 mdbx: добавлено описание использования файловых дескрипторов в различных режимах. 2022-10-10 13:56:57 +03:00
Леонид Юрьев (Leonid Yuriev)
559f3005ca mdbx-test: чуть больше логирования ошибок. 2022-10-10 13:56:57 +03:00
Леонид Юрьев (Leonid Yuriev)
a95ee8daa3 mdbx: минорная доработка mdbx_env_create(). 2022-10-10 13:56:57 +03:00
Леонид Юрьев (Leonid Yuriev)
c17617b816 mdbx: облегченная assert_fail() для не-отладочных сборок. 2022-10-10 13:56:57 +03:00
Леонид Юрьев (Leonid Yuriev)
6eeb08de46 mdbx: использование mdbx_panic() вместо __assert_fail() в ряде внутренних проверок. 2022-10-10 13:56:57 +03:00
Леонид Юрьев (Leonid Yuriev)
66f2e3d596 mdbx: добавление в API функций mdbx_limits_pairsize4page_max() и mdbx_limits_valsize4page_max() с сопутствующими доработками. 2022-10-10 13:56:57 +03:00
Леонид Юрьев (Leonid Yuriev)
143e3dfb77 mdbx: преимущественное использование size_t для уменьшения накладных расходов на платформе Эльбрус. 2022-10-10 13:56:57 +03:00
Леонид Юрьев (Leonid Yuriev)
bcd5bad74a mdbx: добавлено MDBX_NORETURN к mdbx_panic() и mdbx_assert_fail(). 2022-10-10 13:56:57 +03:00
Леонид Юрьев (Leonid Yuriev)
375fa3a225 mdbx: небольшая чистка dlist_free(). 2022-10-10 13:56:57 +03:00
Леонид Юрьев (Leonid Yuriev)
2236b90567 mdbx: добавлена опция сборки MDBX_AVOID_MSYNC (объединённые коммиты и исправления). 2022-10-10 13:56:42 +03:00
Леонид Юрьев (Leonid Yuriev)
8aeb22b8bf mdbx: логирование ошибок при подготовке/записи/фиксации транзакций. 2022-10-06 20:35:26 +03:00
Леонид Юрьев (Leonid Yuriev)
474391c83c mdbx: поддержка асинхронного ввода-вывода для Windows и подготовка к io_ring (объединённые коммиты и исправления). 2022-10-06 20:35:07 +03:00
Леонид Юрьев (Leonid Yuriev)
9f64e2a10c mdbx: правка спилинга для устранения срабатывания проверочных утверждений в отладочных сборках. 2022-10-01 01:38:08 +03:00
Леонид Юрьев (Leonid Yuriev)
41b918f1fc mdbx: исправление проверочного утверждения внутри mdbx_txn_abort() для ошибочных транзакций. 2022-10-01 01:35:08 +03:00
Леонид Юрьев (Leonid Yuriev)
00515d50a9 mdbx: исправление проверочного утверждения в page_retire_ex(). 2022-10-01 01:33:48 +03:00
Леонид Юрьев (Leonid Yuriev)
32a3674dc8 mdbx: return MDBX_PROBLEM insted of MDBX_CORRUPTED on coherence troubles. 2022-09-22 19:48:44 +03:00
Леонид Юрьев (Leonid Yuriev)
f51ace3db8 mdbx-windows: always call debugger if it present when assertion check failed. 2022-09-22 18:27:28 +03:00
Леонид Юрьев (Leonid Yuriev)
beda291692 mdbx-windows: fix nasty clz() (i.e. using _BitScanReverse() bug. 2022-09-22 18:27:28 +03:00
Леонид Юрьев (Leonid Yuriev)
fe20de136c mdbx: require linux >= 4.0 2022-09-22 18:27:23 +03:00
Леонид Юрьев (Leonid Yuriev)
cf8540d84e mdbx: minor refine mdbx_env_create(). 2022-09-22 18:26:28 +03:00
Леонид Юрьев (Leonid Yuriev)
bec9312df5 mdbx: more off/on for clang-format. 2022-09-22 17:06:32 +03:00
Леонид Юрьев (Leonid Yuriev)
a089f73002 mdbx: fix minor MinGW warning. 2022-09-13 11:39:55 +03:00
Леонид Юрьев (Leonid Yuriev)
50ba4bc2f2
mdbx: release v0.11.11 (Тендра-1790)
The stable bugfix release.
It is planned that this will be the last release of the v0.11 branch.

Fixes:
------

 - Fixed an extra check for `MDBX_APPENDDUP` inside `mdbx_cursor_put()` which could result in returning `MDBX_EKEYMISMATCH` for valid cases.
 - Fixed an extra ensure/assertion check of `oldest_reader` inside `mdbx_txn_end()`.
 - Fixed derived C++ builds by removing `MDBX_INTERNAL_FUNC` for `mdbx_w2mb()` and `mdbx_mb2w()`.

10 files changed, 38 insertions(+), 21 deletions(-)
Signed-off-by: Леонид Юрьев (Leonid Yuriev) <leo@yuriev.ru>
2022-09-11 14:33:56 +03:00
Леонид Юрьев (Leonid Yuriev)
cf6576984d mdbx: update ChangeLog. 2022-09-11 13:08:21 +03:00
Леонид Юрьев (Leonid Yuriev)
4f3e1a60f1 mdbx: remove MDBX_INTERNAL_FUNC from mdbx_w2mb() and mdbx_mb2w() for C++ API. 2022-09-11 13:04:49 +03:00
Леонид Юрьев (Leonid Yuriev)
04f60af669 mdbx: minor fix extra ensure/assertion check of oldest_reader inside txn_end(). 2022-09-09 19:56:48 +03:00
Леонид Юрьев (Leonid Yuriev)
c6cd642ff1 mdbx: fix extra check for MDBX_APPENDDUP. 2022-09-09 19:48:24 +03:00
Леонид Юрьев (Leonid Yuriev)
c4efa8dce8 mdbx: update ChangeLog. 2022-09-09 19:01:59 +03:00
Леонид Юрьев (Leonid Yuriev)
2d5438d2c2 mdbx: fix regression ASAN/Valgring-enabled builds. 2022-09-06 13:03:04 +03:00
Леонид Юрьев (Leonid Yuriev)
29da09328e mdbx: removed description of deprecated usage of MDBX_NODUPDATA. 2022-09-06 13:03:04 +03:00
Леонид Юрьев (Leonid Yuriev)
52cb6b90a7 mdbx: fix extra check for MDBX_APPENDDUP. 2022-09-02 02:02:33 +03:00
Леонид Юрьев (Leonid Yuriev)
2d7c25b263 mdbx: minor fix extra ensure/assertion check of oldest_reader inside txn_end(). 2022-09-02 01:46:11 +03:00
Леонид Юрьев (Leonid Yuriev)
3230fb5788 mdbx: update ChangeLog. 2022-08-26 19:27:17 +03:00
Леонид Юрьев (Leonid Yuriev)
b73727d73e mdbx: add MDBX_HAVE_BUILTIN_CPU_SUPPORTS build option. 2022-08-26 19:17:09 +03:00
Леонид Юрьев (Leonid Yuriev)
b36a07a512
mdbx: release v0.12.1 (Positive Proxima)
The planned frontward release with new superior features on the day of 20 anniversary of [Positive Technologies](https://ptsecurty.com).

New:
----

 - The `Big Foot` feature which significantly reduces GC overhead for processing large lists of retired pages from huge transactions.
   Now _libmdbx_ avoid creating large chunks of PNLs (page number lists) which required a long sequences of free pages, aka large/overflow pages.
   Thus avoiding searching, allocating and storing such sequences inside GC.
 - Improved hot/online validation and checking of database pages both for more robustness and performance.
 - New solid and fast method to latch meta-pages called `Troika`.
   The minimum of memory barriers, reads, comparisons and conditional transitions are used.
 - New `MDBX_VALIDATION` environment options to extra validation of DB structure and pages content for carefully/safe handling damaged or untrusted DB.
 - Accelerated ×16/×8/×4 by AVX512/AVX2/SSE2/Neon implementations of search page sequences.
 - Added the `gcrtime_seconds16dot16` counter to the "Page Operation Statistics" that accumulates time spent for GC searching and reclaiming.
 - Copy-with-compactification now clears/zeroes unused gaps inside database pages.
 - The `C` and `C++` APIs has been extended and/or refined to simplify using `wchar_t` pathnames.
   On Windows the `mdbx_env_openW()`, `mdbx_env_get_pathW()`, `mdbx_env_copyW()`, `mdbx_env_open_for_recoveryW()` are available for now,
   but the `mdbx_env_get_path()` has been replaced in favor of `mdbx_env_get_pathW()`.
 - Added explicit error message for Buildroot's Microblaze toolchain maintainers.
 - Added `MDBX_MANAGE_BUILD_FLAGS` build options for CMake.
 - Speed-up internal `bsearch`/`lower_bound` implementation using branchless tactic, including workaround for CLANG x86 optimiser bug.
 - A lot internal refinement and micro-optimisations.
 - Internally counted volume of dirty pages (unused for now but for coming features).

Fixes:
------

 - Never use modern `__cxa_thread_atexit()` on Apple's OSes.
 - Don't check owner for finished transactions.
 - Fixed typo in `MDBX_EINVAL` which breaks MingGW builds with CLANG.

37 files changed, 7604 insertions(+), 7417 deletions(-)
Signed-off-by: Леонид Юрьев (Leonid Yuriev) <leo@yuriev.ru>
2022-08-24 16:24:22 +03:00
Леонид Юрьев (Leonid Yuriev)
b60d8e78c3 mdbx: merge branch master into devel. 2022-08-22 21:46:54 +03:00
Леонид Юрьев (Leonid Yuriev)
aaa9112c83
mdbx: release v0.11.10
The stable bugfix release.
It is planned that this will be the last release of the v0.11 branch.

New:
----

 - The C++ API has been refined to simplify support for `wchar_t` in path names.
 - Added explicit error message for Buildroot's Microblaze toolchain maintainers.

Fixes:
------

 - Never use modern `__cxa_thread_atexit()` on Apple's OSes.
 - Use `MultiByteToWideChar(CP_THREAD_ACP)` instead of `mbstowcs()`.
 - Don't check owner for finished transactions.
 - Fixed typo in `MDBX_EINVAL` which breaks MingGW builds with CLANG.

Minors:
-------

 - Fixed variable name typo.
 - Using `ldd` to check used dso.
 - Added `MDBX_WEAK_IMPORT_ATTRIBUTE` macro.
 - Use current transaction geometry for untouched parameters when `env_set_geometry()` called within a write transaction.
 - Minor clarified `iov_page()` failure case.

14 files changed, 263 insertions(+), 252 deletions(-)
Signed-off-by: Леонид Юрьев (Leonid Yuriev) <leo@yuriev.ru>
2022-08-22 13:32:24 +03:00
Леонид Юрьев (Leonid Yuriev)
26f52a19c3 mdbx: add explicit error message for Buildroot's Microblaze toolchain maintainers. 2022-08-22 12:59:42 +03:00
Леонид Юрьев (Leonid Yuriev)
5368551081 mdbx: minor clarify iov_page() failure case. 2022-08-22 12:59:42 +03:00
Леонид Юрьев (Leonid Yuriev)
0ccec20409 mdbx: don't deem meta pages with zero txnid equal.
Устранение крайне маловероятного регресса после перехода на мета-тройку:
 - процесс А открыает БД и читает мета-траницы для формирования тройки;
 - процесс Б постоянно коммитит новые транзакции;
 - есть шанс что процесс А при чтении разных мета страниц попадет на момент их обновления более одного раза,
   это может привести к ложной ошибке коллизии мета-страниц,
   так как для обновляемых мета-страниц будет виден нулевой номер транзакции.
2022-08-20 01:54:11 +03:00
Леонид Юрьев (Leonid Yuriev)
ceba040e32 mdbx: add meta_xyz_dump(). 2022-08-20 01:54:11 +03:00
Леонид Юрьев (Leonid Yuriev)
b617f25eaa mdbx: refine & rename internal xyz to troika. 2022-08-20 01:54:06 +03:00
Леонид Юрьев (Leonid Yuriev)
b759dfafd7 mdbx: counting large/overflow dirty pages (unused for now).
This is a basis for [Large/Overflow pages accounting for dirty-room](https://web.archive.org/web/20220414235959/https://github.com/erthink/libmdbx/issues/192).
2022-08-20 00:14:48 +03:00
Леонид Юрьев (Leonid Yuriev)
4cef1c2376 mdbx: avoid extra using F_ISSET() macro. 2022-08-18 01:39:06 +03:00
Леонид Юрьев (Leonid Yuriev)
08a8f844dc mdbx: ×4 ARM-Neon accelerated scan4seq(). 2022-08-18 01:10:27 +03:00
Леонид Юрьев (Leonid Yuriev)
8e2c276562 mdbx: merge branch 'master' into devel branch. 2022-08-18 01:04:35 +03:00
Леонид Юрьев (Leonid Yuriev)
4f02199648 mdbx: update ChangeLog. 2022-08-17 23:56:53 +03:00
Леонид Юрьев (Leonid Yuriev)
7b36f946cb mdbx: rework/speed up accessing to meta-pages, choosing and cache of ones (squashed). 2022-08-17 21:39:22 +03:00
Леонид Юрьев (Leonid Yuriev)
ef16dd2a22 mdbx: move global_ctor() to the end . 2022-08-17 21:31:11 +03:00
Леонид Юрьев (Leonid Yuriev)
f9ad835680 mdbx: drop E2K libc obsolete workarounds. 2022-08-17 21:29:51 +03:00
Леонид Юрьев (Leonid Yuriev)
9b3faee630 mdbx: drop obsolete Nexenta attributes API. 2022-08-17 21:29:51 +03:00
Леонид Юрьев (Leonid Yuriev)
316ddf9e01 mdbx: fix typo in MDBX_EINVAL which breaks MingGW builds with CLANG. 2022-08-16 11:09:52 +03:00
Леонид Юрьев (Leonid Yuriev)
3fbbe32adf mdbx: fix checking owner for finished write transactions inside txn_abort().
Fixed regression after 06734bf8ff.
2022-08-14 12:39:21 +03:00
Леонид Юрьев (Leonid Yuriev)
8467cc6d03 mdbx: use current txn geo for untouched parameters when env_set_geometry() called within a write transaction. 2022-08-13 16:56:29 +03:00
Леонид Юрьев (Leonid Yuriev)
9f0e2ecc67 mdbx: fix variable name typo. 2022-08-13 16:56:09 +03:00
Леонид Юрьев (Leonid Yuriev)
345c3d433f mdbx-make: add -DENABLE_UBSAN to ubsan-targets. 2022-08-11 19:39:14 +03:00
Леонид Юрьев (Leonid Yuriev)
1c5ef060c5 mdbx: reduce number of memory fences in the hot paths. 2022-08-11 18:45:00 +03:00
Леонид Юрьев (Leonid Yuriev)
34a4e7e102 mdbx: avoid Valgrind warnings. 2022-08-11 17:10:13 +03:00
Леонид Юрьев (Leonid Yuriev)
ae730ae2f3 mdbx: fix minor warnings for ASAN-enabled builds. 2022-08-11 12:33:56 +03:00
Леонид Юрьев (Leonid Yuriev)
18e557c6e8 mdbx: rename internal functions, types and macros (to be closer to MithrilDB). 2022-08-11 12:33:56 +03:00
Леонид Юрьев (Leonid Yuriev)
096d6a9bd6 mdbx: some micro-optimizations. 2022-08-10 22:09:42 +03:00
Леонид Юрьев (Leonid Yuriev)
d8f0c9dc44 mdbx: more __hot. 2022-08-10 15:46:45 +03:00
Леонид Юрьев (Leonid Yuriev)
78dc699709 mdbx: add ×16 accelerated scan4seq() (AVX512BW). 2022-08-10 13:23:04 +03:00
Леонид Юрьев (Leonid Yuriev)
c2bf9ebf17 mdbx: minor refine AVX2/SSE2-accelerated scan4seq(). 2022-08-10 11:43:24 +03:00
Леонид Юрьев (Leonid Yuriev)
3c28619562 mdbx: merge branch master into devel. 2022-08-09 19:04:27 +03:00
Леонид Юрьев (Leonid Yuriev)
0287a00ee3 mdbx++: refine/simplify wchar_t support for pathnames. 2022-08-09 18:54:22 +03:00
Леонид Юрьев (Leonid Yuriev)
2ff8d3c4f2 mdbx: native wchar_t pathname for Windows. 2022-08-09 18:27:43 +03:00
Леонид Юрьев (Leonid Yuriev)
98c53555ab mdbx: using e2k-frendly/cmov/branch-less bsearch.
https://gitflic.ru/project/erthink/bsearch-try
2022-08-09 18:25:05 +03:00
Леонид Юрьев (Leonid Yuriev)
c8b1392cbe mdbx: use MultiByteToWideChar(CP_THREAD_ACP) instead of mbstowcs(). 2022-08-09 16:12:24 +03:00
Леонид Юрьев (Leonid Yuriev)
6d85e35876 mdbx: never use modern __cxa_thread_atexit() on Apple's OSes. 2022-08-08 15:23:39 +03:00
Леонид Юрьев (Leonid Yuriev)
dd01aabaeb mdbx: add MDBX_WEAK_IMPORT_ATTRIBUTE macro. 2022-08-08 15:18:16 +03:00
Леонид Юрьев (Leonid Yuriev)
3de759a7be mdbx: fix page-boundary checking inside accelerated scan4seq(). 2022-08-07 22:24:00 +03:00
Леонид Юрьев (Leonid Yuriev)
d6603a0c0a mdbx: add ×8 accelerated scan4seq() (AVX2). 2022-08-07 17:08:51 +03:00
Леонид Юрьев (Leonid Yuriev)
15146d3823 mdbx: fix scan4seq() selection for non-implemented cases. 2022-08-07 15:14:50 +03:00
Леонид Юрьев (Leonid Yuriev)
d62d3e2aab mdbx: merge branch master into devel. 2022-08-07 12:42:43 +03:00
Леонид Юрьев (Leonid Yuriev)
fa854e40c3 mdbx: refine checking inside page_get(). 2022-08-07 12:29:51 +03:00
Леонид Юрьев (Leonid Yuriev)
5afc5c4e8c mdbx: reorganize/move fences to reduce overhead. 2022-08-07 12:29:51 +03:00
Леонид Юрьев (Leonid Yuriev)
c05a3b7bb9 mdbx: minor refine node_add(). 2022-08-07 12:29:50 +03:00
Леонид Юрьев (Leonid Yuriev)
1215bda188 mdbx: minor refine/speedup node_del(). 2022-08-07 12:29:50 +03:00
Леонид Юрьев (Leonid Yuriev)
0dd4532473 mdbx: reduce gap/backlog of linear scan inside dpl_search(). 2022-08-07 12:29:50 +03:00
Леонид Юрьев (Leonid Yuriev)
eac3d0499f mdbx: minor refine/speedup dpl_sort_slowpath(). 2022-08-07 12:29:50 +03:00
Леонид Юрьев (Leonid Yuriev)
a11c045f1e mdbx: using expect_with_probability() macro. 2022-08-07 12:28:35 +03:00
Леонид Юрьев (Leonid Yuriev)
c0f8ecd6f2 mdbx: add expect_with_probability() macro. 2022-08-07 11:56:23 +03:00
Леонид Юрьев (Leonid Yuriev)
8404cc1fd7 mdbx: reduce sorting-network to 8. 2022-08-07 11:56:23 +03:00
Леонид Юрьев (Leonid Yuriev)
654b020bc7 mdbx: add __restrict to quicksort internal pointers. 2022-08-07 11:56:23 +03:00
Леонид Юрьев (Leonid Yuriev)
77635116c6 mdbx: enable solib profiling with -pg and gprof with GLIBC >= 2.37.
However such profiling requires https://sourceware.org/bugzilla/show_bug.cgi?id=29438 to be fixed.
2022-08-07 11:56:23 +03:00
Леонид Юрьев (Leonid Yuriev)
a44eb1accb mdbx-cmake: add MDBX_MANAGE_BUILD_FLAGS build-time option. 2022-08-07 11:56:23 +03:00
Леонид Юрьев (Leonid Yuriev)
c06d072daf mdbx-make: support for MDBX_BUILD_CXX=YES/NO option. 2022-08-07 11:56:23 +03:00
Леонид Юрьев (Leonid Yuriev)
d28110373e mdbx: add simple SORT_CMP_SWAP() macro for MDBX_HAVE_CMOV=0 case. 2022-08-07 11:56:23 +03:00
Леонид Юрьев (Leonid Yuriev)
480dc2531e mdbx: ×4 accelerated scan4seq() (SSE2 only for now). 2022-08-07 11:56:23 +03:00
Леонид Юрьев (Leonid Yuriev)
f0a46da6a5 mdbx-make: using ldd to check used dso. 2022-08-06 19:42:38 +03:00
Леонид Юрьев (Leonid Yuriev)
06734bf8ff mdbx: don't check owner for finished transactions. 2022-08-06 13:19:49 +03:00
Леонид Юрьев (Leonid Yuriev)
c37fb50532 mdbx: more for __amd64__ macro. 2022-08-04 13:54:07 +03:00
Леонид Юрьев (Leonid Yuriev)
9eb6953778 mdbx: fix minor typo. 2022-08-04 13:54:07 +03:00
Леонид Юрьев (Leonid Yuriev)
bfac10418f mdbx-make: preserve MDBX_BUILD_OPTION for assertion-targets. 2022-08-04 13:54:07 +03:00
Леонид Юрьев (Leonid Yuriev)
268b33cbf7 mdbx: simplify/speedup scan4seq(). 2022-08-04 13:54:07 +03:00
Леонид Юрьев (Leonid Yuriev)
e444c70cb7
mdbx: release v0.11.9
The stable bugfix release.
It is planned that this will be the last release of the v0.11 branch.

Acknowledgements:
-----------------

 - [Alex Sharov](https://github.com/AskAlexSharov) and Erigon team for reporting and testing.
 - [Andrew Ashikhmin](https://gitflic.ru/user/yperbasis) for contributing.

New:
----

 - Ability to customise `MDBX_LOCK_SUFFIX`, `MDBX_DATANAME`, `MDBX_LOCKNAME` just by predefine ones during build.
 - Added to [`mdbx::env_managed`](https://libmdbx.dqdkfa.ru/group__cxx__api.html#classmdbx_1_1env__managed)'s methods a few overloads with `const char* pathname` parameter (C++ API).

Fixes:
------

 - Fixed hang copy-with-compactification of a corrupted DB
   or in case the volume of output pages is a multiple of `MDBX_ENVCOPY_WRITEBUF`.
 - Fixed standalone non-CMake build on MacOS (`#include AvailabilityMacros.h>`).
 - Fixed unexpected `MDBX_PAGE_FULL` error in rare cases with large database page sizes.

Minors:
-------

 - Minor fixes Doxygen references, comments, descriptions, etc.
 - Fixed copy&paste typo inside `meta_checktxnid()`.
 - Minor fix `meta_checktxnid()` to avoid assertion in debug mode.
 - Minor fix `mdbx_env_set_geometry()` to avoid returning `EINVAL` in particular rare cases.
 - Minor refine/fix batch-get testcase for large page size.
 - Added `--pagesize NN` option to long-stotastic test script.
 - Updated Valgrind-suppressions file for modern GCC.
 - Fixed `has no symbols` warning from Apple's ranlib.

18 files changed, 318 insertions(+), 178 deletions(-)
Signed-off-by: Леонид Юрьев (Leonid Yuriev) <leo@yuriev.ru>
2022-08-02 12:00:30 +03:00
Леонид Юрьев (Leonid Yuriev)
a441c9ffb1 mdbx: logging pgno of invalid root pages inside meta_checktxnid(). 2022-08-01 14:44:33 +03:00
Леонид Юрьев (Leonid Yuriev)
71c3d20c01 mdbx: skip extra cycle during search of page sequence. 2022-08-01 00:00:14 +03:00
Леонид Юрьев (Leonid Yuriev)
75d19b5806 mdbx: minor refine/speedup pnl_check(). 2022-07-27 21:08:54 +03:00
Леонид Юрьев (Leonid Yuriev)
dc39ecfb9f mdbx: auto-coalesce of GC's records with less overhead. 2022-07-27 21:08:54 +03:00
Леонид Юрьев (Leonid Yuriev)
47d5fa7fd4 mdbx: refine/speedup pnl_merge(). 2022-07-27 21:08:54 +03:00
Леонид Юрьев (Leonid Yuriev)
fe6c6b2068 mdbx: add MDBX_HAVE_CMOV macro/option. 2022-07-27 21:08:54 +03:00
Леонид Юрьев (Leonid Yuriev)
262fafd00e mdbx: fix unused warning for case MDBX_ENABLE_PGOP_STAT=0. 2022-07-27 00:03:27 +03:00
Леонид Юрьев (Leonid Yuriev)
289636834c mdbx: fix unused warning for case MDBX_DISABLE_VALIDATION. 2022-07-27 00:03:27 +03:00
Леонид Юрьев (Leonid Yuriev)
c4dd83fbdf mdbx: minor refine page_split(). 2022-07-27 00:03:22 +03:00
Леонид Юрьев (Leonid Yuriev)
cc51a7f76e mdbx: minor refine attributes-related macros for LCC. 2022-07-23 12:15:55 +03:00
Леонид Юрьев (Leonid Yuriev)
a82f59a998 mdbx: minor refine MDBX_UNALIGNED_OK. 2022-07-23 12:15:19 +03:00
Леонид Юрьев (Leonid Yuriev)
08e936a809 mdbx: re-verify atomic-ops and remove mo_SequentialConsistency. 2022-07-23 12:15:19 +03:00
Леонид Юрьев (Leonid Yuriev)
5e565433f7 mdbx: merge branch master into devel. 2022-07-23 11:56:17 +03:00
Леонид Юрьев (Leonid Yuriev)
6a1bf6035f mdbx: update ChangeLog. 2022-07-23 11:03:27 +03:00
Леонид Юрьев (Leonid Yuriev)
e963375302 mdbx: fix unexpected MDBX_PAGE_FULL in rare cases with large page-size. 2022-07-21 17:52:08 +03:00
yperbasis
0af84be269 Merged with fix_apple 2022-07-20 11:22:26 +00:00
yperbasis
23e7870e81 #include <AvailabilityMacros.h> 2022-07-19 18:35:19 +02:00
Леонид Юрьев (Leonid Yuriev)
fc53e57a64 mdbx-cmake: avoid has no symbols warning from Apple's ranlib. 2022-07-13 22:11:28 +03:00
Леонид Юрьев (Leonid Yuriev)
69b495d559 mdbx++: add to mdbx::env overloads with const char* pathname parameter. 2022-07-13 21:45:23 +03:00
Леонид Юрьев (Leonid Yuriev)
0018164fef mdbx: fix wrong } oops-like typo.
This is a `devel`-only 3-days old regression since a4a35ce9cb.
2022-07-11 20:29:33 +03:00
Леонид Юрьев (Leonid Yuriev)
ac4b6d7121 mdbx-test: always engage MDBX_DBG_DUMP. 2022-07-11 20:29:18 +03:00
Леонид Юрьев (Leonid Yuriev)
5ccfb5f30a mdbx-tools: use MDBX_DBG_DUMP, MDBX_DBG_ASSERT, MDBX_DBG_AUDIT inside mdbx_chk. 2022-07-11 20:28:07 +03:00
Леонид Юрьев (Leonid Yuriev)
149e708830 mdbx: rename MDBX_CACHE_METAPTR build-time option. 2022-07-10 10:05:39 +03:00
Леонид Юрьев (Leonid Yuriev)
12d2879a9f mdbx: extend descriptions for MDBX_VALIDATION and update TODO for done item. 2022-07-09 19:40:09 +03:00
Леонид Юрьев (Leonid Yuriev)
194f2f45d2 mdbx: refine/fix using nested cursor's db inside copy-with-compactification. 2022-07-09 18:07:54 +03:00
Леонид Юрьев (Leonid Yuriev)
b29c15f919 mdbx: merge branch 'master' into devel. 2022-07-09 17:34:25 +03:00
Леонид Юрьев (Leonid Yuriev)
e8dd208e96 mdbx: more cursor-checking for audit-without-debug. 2022-07-09 16:12:41 +03:00
Леонид Юрьев (Leonid Yuriev)
9bbf09b5c4 mdbx: minor update README for the locations of a source code. 2022-07-09 14:54:25 +03:00
Леонид Юрьев (Leonid Yuriev)
9d9df11509 mdbx: update ChangeLog. 2022-07-09 14:11:36 +03:00
Леонид Юрьев (Leonid Yuriev)
61d0d63ac2 mdbx-test: refine/fix batch-get testcase for large page size (i.e. when more pairs per page). 2022-07-09 13:30:40 +03:00
Леонид Юрьев (Leonid Yuriev)
e9a1042cc2 mdbx-test: add --pagesize NN option to long-stotastic script. 2022-07-09 13:30:40 +03:00
Леонид Юрьев (Leonid Yuriev)
f7f9eaff95 mdbx-test: update Valgrind-suppressions for modern GCC. 2022-07-09 13:30:13 +03:00
Леонид Юрьев (Leonid Yuriev)
9108a241a2 mdbx: minor fix/clarify debug logging inside page_alloc_slowpath(). 2022-07-08 23:44:03 +03:00
Леонид Юрьев (Leonid Yuriev)
9aa2aae93e mdbx: rework/simplify kick_longlived_readers(). 2022-07-08 23:44:03 +03:00
Леонид Юрьев (Leonid Yuriev)
9421bb424d mdbx: refine/simplify read-latch loop inside mdbx_txn_renew0().
1. Explicitly check and handle a race/collision case with `find_oldest_reader()`.
2. Handle "recovery mode" (me_stuck_meta >= 0) by the same code as for regular latch.
3. Add bailout error message for buggy compiler and/or hardware (paranoid).
2022-07-08 23:44:03 +03:00
Леонид Юрьев (Leonid Yuriev)
434ad8edc8 mdbx: refine bind_rslot(). 2022-07-08 23:44:03 +03:00
Леонид Юрьев (Leonid Yuriev)
a4a35ce9cb mdbx: rework find_oldest_reader().
1. Fix regression `assert: oldest >= lck->mti_oldest_reader.weak` after d4bf0a3332.
2. Add explicit check, kick and notice for stuck reader.
3. Made more e2k-frendly.
2022-07-08 23:44:03 +03:00
Леонид Юрьев (Leonid Yuriev)
d572052178 mdbx: refine meta_checktxnid() and meta_waittxnid(). 2022-07-08 23:44:03 +03:00
Леонид Юрьев (Leonid Yuriev)
6f6c581c6e mdbx: minor refine safe64_write(), safe64_read() and safe64_inc(). 2022-07-08 23:44:03 +03:00
Леонид Юрьев (Leonid Yuriev)
baea4c81c9 mdbx: simplify safe64_reset(). 2022-07-08 23:44:03 +03:00
Леонид Юрьев (Leonid Yuriev)
ad5a83586b mdbx: fix insignificant uint8_t-casting warnings. 2022-07-08 23:44:03 +03:00
Леонид Юрьев (Leonid Yuriev)
b5346ee765 mdbx: use unsigned constants for page flags (to avoid MSVC warnings). 2022-07-08 21:48:08 +03:00
Леонид Юрьев (Leonid Yuriev)
db0f4e3d1e mdbx: minor fix mdbx_page_check() for case debug-audit enforced. 2022-07-08 21:45:26 +03:00
Леонид Юрьев (Leonid Yuriev)
c0a274e8ec mdbx: merge branch master into devel. 2022-07-08 01:32:30 +03:00
Леонид Юрьев (Leonid Yuriev)
61825e9bc8 mdbx: update ChangeLog for v0.11.9 2022-07-08 01:17:21 +03:00
Леонид Юрьев (Leonid Yuriev)
c499f2bb36 mdbx: minor fix EINVAL from mdbx_env_set_geometry().
Silently growth `size_lower` to the `MIN_PAGENO` instead of returning `MDBX_EINVAL`.
2022-07-08 01:14:29 +03:00
Леонид Юрьев (Leonid Yuriev)
6e2a1ebfbd mdbx: minor fix meta_checktxnid() to avoid assertion in debug mode. 2022-07-08 01:08:54 +03:00
Леонид Юрьев (Leonid Yuriev)
a2c4f84f9c mdbx: update ChangeLog for v0.12.x 2022-07-08 01:03:23 +03:00
Леонид Юрьев (Leonid Yuriev)
f17bd06116 mdbx: update ChangeLog. 2022-07-08 00:27:19 +03:00
Леонид Юрьев (Leonid Yuriev)
2cfcfcf91c mdbx: merge branch erigon into devel. 2022-07-07 23:24:45 +03:00
Леонид Юрьев (Leonid Yuriev)
81ea7bd41e mdbx: fix copy&paste typo inside meta_checktxnid(). 2022-07-07 21:48:34 +03:00
Леонид Юрьев (Leonid Yuriev)
f16bee8fa1 mdbx: fix/setup zero mod_txnid during copy-with-compactification. 2022-07-07 21:48:34 +03:00
Леонид Юрьев (Leonid Yuriev)
c95143f41b mdbx: add poor_page(). 2022-07-07 21:48:34 +03:00
Леонид Юрьев (Leonid Yuriev)
19c5e4d424 mdbx-tools: use MDBX_VALIDATION. 2022-07-07 21:48:34 +03:00
Леонид Юрьев (Leonid Yuriev)
6076c510f8 mdbx-tools: refine assertions logging inside mdbx_chk. 2022-07-07 21:48:34 +03:00
Леонид Юрьев (Leonid Yuriev)
bc744a843a mdbx: refine/speedup PAGETYPE(). 2022-07-07 21:48:34 +03:00
Леонид Юрьев (Leonid Yuriev)
a812198c49 mdbx: rework/clone page_get() to three for the cases: any, large, branch-leaf. 2022-07-07 21:48:30 +03:00
Леонид Юрьев (Leonid Yuriev)
fbe97a79a3 mdbx: more checking for a large/overflow nodes and pages. 2022-07-07 21:46:19 +03:00
Леонид Юрьев (Leonid Yuriev)
3cc7f105a5 mdbx: fix copy&paste typo inside meta_checktxnid(). 2022-07-07 19:11:47 +03:00
Леонид Юрьев (Leonid Yuriev)
b31b270ffd mdbx: refine copy-with-compactification to clear/zero unused gaps on a DB pages. 2022-07-07 19:09:52 +03:00
Леонид Юрьев (Leonid Yuriev)
0b5cdee6ef mdbx: fix mdbx_env_compact() hand when DB is corrupted or the volume of an output pages aliquot to MDBX_ENVCOPY_WRITEBUF. 2022-07-07 19:05:35 +03:00
Леонид Юрьев (Leonid Yuriev)
6eefa05f3d mdbx: minor refine CC_RETIRING handling. 2022-07-07 14:10:19 +03:00
Леонид Юрьев (Leonid Yuriev)
ca3f188370 mdbx: extend CHECK_LEAF_TYPE() by adding CC_OVERFLOW. 2022-07-07 14:10:19 +03:00
Леонид Юрьев (Leonid Yuriev)
498514dae1 mdbx: remove unneeded CC_COPYING. 2022-07-07 14:10:19 +03:00
Леонид Юрьев (Leonid Yuriev)
1740f8227a mdbx: rework copy-with-compactification.
Кроме небольшого рефакторинга здесь реализуется более регулярный способ
обхода дерева при копировании с компактификаций. В частности, полная
инициализация курсоров позволяет выполнять больше проверок/контроля
структуры БД и избавиться от флажка CC_COPYING.

Beside a small refactoring, a more regular way of traversing the tree
when copying with compactification is implemented here. In particular,
full initialization of cursors allows to perform more checks/control of
the DB structure and get rid of the CC_COPYING flag.
2022-07-07 14:10:19 +03:00
Леонид Юрьев (Leonid Yuriev)
2d300d807b mdbx: extract node_read_bigdata(). 2022-07-07 14:10:19 +03:00
Леонид Юрьев (Leonid Yuriev)
d4ef9bf233 mdbx: rework page validation/checking, add MDBX_VALIDATION option (squashed).
Здесь основная часть изменений преобразующих отладочную проверку страниц
в регулярный и доступный пользователю осторожный/безопасный режим работы
с потенциально поврежденной БД.

Here the major part of the changes that transform a debugging check of
pages into a regular and user-accessible careful/safe mode for working
with a potentially corrupted database.
2022-07-07 14:10:09 +03:00
Леонид Юрьев (Leonid Yuriev)
6c5ff863ff mdbx: remove pp_txnid4chk(), preparing to rework of page checking/validation. 2022-07-05 15:32:09 +03:00
Леонид Юрьев (Leonid Yuriev)
d61c096313 mdbx: drop mdbx_recent_steady_txnid() and fix extra search for steady meta-page. 2022-07-02 22:53:48 +03:00
Леонид Юрьев (Leonid Yuriev)
b9835389f4 mdbx: add cache for pointers to last/steady meta-pages (off by default). 2022-07-02 22:52:31 +03:00
Леонид Юрьев (Leonid Yuriev)
720b4d56be mdbx: bigfoot feature.
Chunking long list of retired pages during huge transactions commit
to avoid use sequences of pages:
 - splits a long retired page-number-list into chunks
   which fits one per single overflow/large page;
 - this requires a few unique id for keys
   for create such records into GC/freeDB;
 - just use the necessary subsequent IDs following the current
   transaction ID and then take the last of ones to update a meta-page.

Thus avoids using/allocating/searching a sequence of free pages
but just increase txnid more than one during the commit
a huge write transaction with a long retired-pages-list.
2022-07-02 22:33:25 +03:00
Леонид Юрьев (Leonid Yuriev)
4f6b92248d mdbx: add pgop_stat.gcrtime for collect the time spent loading and searching inside GC. 2022-07-01 09:27:38 +03:00
Леонид Юрьев (Leonid Yuriev)
48c6051482 mdbx: minor fix meta_checktxnid() to avoid assertion in debug mode. 2022-06-29 13:37:26 +03:00
Леонид Юрьев (Leonid Yuriev)
23bbceb367 mdbx: minor fix EINVAL from mdbx_env_set_geometry().
Silently growth `size_lower` to the `MIN_PAGENO` instead of returning `MDBX_EINVAL`.
2022-06-27 13:55:44 +03:00
Леонид Юрьев (Leonid Yuriev)
46f61c3006 mdbx: merge branch master into devel. 2022-06-27 12:39:09 +03:00
Леонид Юрьев (Leonid Yuriev)
065e5849da mdbx: speedup GC-related pnl-merge and sequence-search. 2022-06-27 12:37:29 +03:00
Леонид Юрьев (Leonid Yuriev)
acce7d4b16 mdbx-test: remove obsolete coalesce option. 2022-06-27 12:37:29 +03:00
Леонид Юрьев (Leonid Yuriev)
e3a09db3da mdbx: always coalescing GC records, regardless to MDBX_COALESCE flag. 2022-06-27 12:37:29 +03:00
Леонид Юрьев (Leonid Yuriev)
ece2fe2514 mdbx: split page_alloc() and page_new() to fast- and slow/rare- parts. 2022-06-27 12:37:29 +03:00
Леонид Юрьев (Leonid Yuriev)
f1ccc717b4 mdbx: add update-gc context (extracted from bigfoot). 2022-06-27 11:27:05 +03:00
Леонид Юрьев (Leonid Yuriev)
7b735c272d mdbx: allow predefine/customise MDBX_LOCK_SUFFIX, MDBX_DATANAME, MDBX_LOCKNAME. 2022-06-25 22:14:06 +03:00
Леонид Юрьев (Leonid Yuriev)
55d3783699 mdbx: update patch for old buildroot versions. 2022-06-25 16:24:40 +03:00
Леонид Юрьев (Leonid Yuriev)
5d933d09d3 mdbx: minor refine/clarify descriptions of MDBX_LOCKNAME and MDBX_DATANAME. 2022-06-25 15:27:33 +03:00
Леонид Юрьев (Leonid Yuriev)
f5280ebf6e mdbx: bump version to 0.12.0 (not a release but preparation for changing feature set and API). 2022-06-25 15:14:46 +03:00
Леонид Юрьев (Leonid Yuriev)
8ef3bfcc95 mdbx++: refine descriptions for compare_fast() and compare_lexicographically(). 2022-06-18 12:08:34 +03:00
Леонид Юрьев (Leonid Yuriev)
51f8407a08 mdbx: add objdump -f -h -j .text to README. 2022-06-13 17:45:15 +03:00
Леонид Юрьев (Leonid Yuriev)
bd80e01eda
mdbx: release v0.11.8
The stable release with an important fixes and workaround for the critical macOS thread-local-storage issue.

Acknowledgements:
-----------------

 - [Masatoshi Fukunaga](https://github.com/mah0x211) for [Lua bindings](https://github.com/mah0x211/lua-libmdbx).

New:
----

 - Added most of transactions flags to the public API.
 - Added `MDBX_NOSUCCESS_EMPTY_COMMIT` build option to return non-success result (`MDBX_RESULT_TRUE`) on empty commit.
 - Reworked validation and import of DBI-handles into a transaction.
   Assumes  these changes will be invisible to most users, but will cause fewer surprises in complex DBI cases.
 - Added ability to open DB in without-LCK (exclusive read-only) mode in case no permissions to create/write LCK-file.

Fixes:
------

 - A series of fixes and improvements for automatically generated documentation (Doxygen).
 - Fixed copy&paste bug with could lead to `SIGSEGV` (nullptr dereference) in the exclusive/no-lck mode.
 - Fixed minor warnings from modern Apple's CLANG 13.
 - Fixed minor warnings from CLANG 14 and in-development CLANG 15.
 - Fixed `SIGSEGV` regression in without-LCK (exclusive read-only) mode.
 - Fixed `mdbx_check_fs_local()` for CDROM case on Windows.
 - Fixed nasty typo of typename which caused false `MDBX_CORRUPTED` error in a rare execution path,
   when the size of the thread-ID type not equal to 8.
 - Fixed write-after-free memory corruption on latest `macOS` during finalization/cleanup of thread(s) that executed read transaction(s).
   > The issue was suddenly discovered by a [CI](https://en.wikipedia.org/wiki/Continuous_integration)
   > after adding an iteration with macOS 11 "Big Sur", and then reproduced on recent release of macOS 12 "Monterey".
   > The issue was never noticed nor reported on macOS 10 "Catalina" nor others.
   > Analysis shown that the problem caused by a change in the behavior of the system library (internals of dyld and pthread)
   > during thread finalization/cleanup: now a memory allocated for a `__thread` variable(s) is released
   > before execution of the registered Thread-Local-Storage destructor(s),
   > thus a TLS-destructor will write-after-free just by legitime dereference any `__thread` variable.
   > This is unexpected crazy-like behavior since the order of resources releasing/destroying
   > is not the reverse of ones acquiring/construction order. Nonetheless such surprise
   > is now workarounded by using atomic compare-and-swap operations on a 64-bit signatures/cookies.
 - Fixed Elbrus/E2K LCC 1.26 compiler warnings (memory model for atomic operations, etc).

Minors:
-------

 - Refined `release-assets` GNU Make target.
 - Added logging to `mdbx_fetch_sdb()` to help debugging complex DBI-handels use cases.
 - Added explicit error message from probe of no-support for `std::filesystem`.
 - Added contributors "score" table by `git fame` to generated docs.
 - Added `mdbx_assert_fail()` to public API (mostly for backtracing).
 - Now C++20 concepts used/enabled only when `__cpp_lib_concepts >= 202002`.
 - Don't provide nor report package information if used as a CMake subproject.

Signed-off-by: Леонид Юрьев (Leonid Yuriev) <leo@yuriev.ru>
2022-06-12 23:47:18 +03:00
Леонид Юрьев (Leonid Yuriev)
ca7984f9a9 mdbx: update ChangeLog. 2022-06-12 23:23:40 +03:00
Леонид Юрьев (Leonid Yuriev)
99cfee6f4b mdbx-cmake: don't provide nor report package information if used as a subproject. 2022-06-12 23:14:33 +03:00
Леонид Юрьев (Leonid Yuriev)
280fa99831 mdbx++: use C++20 concepts only when __cpp_lib_concepts >= 202002. 2022-06-12 20:20:23 +03:00
Леонид Юрьев (Leonid Yuriev)
cdfaad18a2 mdbx-test: fix minor lcc 1.26 warning. 2022-06-12 20:19:06 +03:00
Леонид Юрьев (Leonid Yuriev)
9b9fe22669 mdbx++: refine descriptions for constexpr-enabled str-functions. 2022-06-12 20:17:40 +03:00
Леонид Юрьев (Leonid Yuriev)
a5a112796c mdbx++: push/pop warnings for lcc 1.26 2022-06-12 20:11:15 +03:00
Леонид Юрьев (Leonid Yuriev)
9285db6ec2 mdbx-test: add workaround for modern libstd++ with CLANG < 4.x 2022-06-12 20:09:27 +03:00
Леонид Юрьев (Leonid Yuriev)
3817236b68 mdbx: avoid memory-model from variables for C11 atomics (workaround for lcc 1.26). 2022-06-12 20:04:35 +03:00
Леонид Юрьев (Leonid Yuriev)
c082eb7d8a mdbx: use _tlv_atexit() on Darwin/MacOSX. 2022-06-11 21:16:29 +03:00
Леонид Юрьев (Leonid Yuriev)
abac366eac mdbx: rework/fix rthc-cleanup using pthread' tsd to avoid write-after-free (critical). 2022-06-11 21:16:29 +03:00
Леонид Юрьев (Leonid Yuriev)
63f8eb253d mdbx: do not enable ASAN.detect_leaks in macOS since unsupported. 2022-06-10 17:26:10 +03:00
Леонид Юрьев (Leonid Yuriev)
fc1a91169b mdbx: update ChangeLog. 2022-06-08 22:08:35 +03:00
Леонид Юрьев (Leonid Yuriev)
117be44c44 mdbx: fix nasty typename typo.
Спасибо Станиславу Очеретному за сообщение о проблеме.
2022-06-08 20:57:42 +03:00
Леонид Юрьев (Leonid Yuriev)
638d71a0c0 mdbx-cmake: sync/update modules. 2022-06-08 16:36:45 +03:00
Леонид Юрьев (Leonid Yuriev)
cdb16c9f00 mdbx: minor fix/clarify internal comment. 2022-06-07 18:13:49 +03:00
Леонид Юрьев (Leonid Yuriev)
bd66675081 mdbx: update ChangeLog. 2022-06-04 22:59:19 +03:00
Леонид Юрьев (Leonid Yuriev)
77f56541d0 mdbx-windows: refine/fix mdbx_check_fs_local() for CDROM case. 2022-06-04 02:08:24 +03:00
Леонид Юрьев (Leonid Yuriev)
73fbf5c8fa mdbx: fix minor typo in comment. 2022-06-04 02:07:19 +03:00
Леонид Юрьев (Leonid Yuriev)
d1e67645a2 mdbx: refine handling EACCESS while trying create LCK-file on a read-only filesystem. 2022-06-04 02:04:55 +03:00
Леонид Юрьев (Leonid Yuriev)
a4da10bc62 mdbx: minor fix LCK-initialization. 2022-06-03 22:40:18 +03:00
Леонид Юрьев (Leonid Yuriev)
e795fe7c3e mdbx: fix without-LCK (exclusive + readonly) mode. 2022-06-03 22:39:11 +03:00
Леонид Юрьев (Leonid Yuriev)
f6db64bea1 mdbx-cmake: add /bigobj for MSVC. 2022-06-02 20:35:40 +03:00
Леонид Юрьев (Leonid Yuriev)
b6138c39f0 mdbx: reorganize internal includes. 2022-06-02 20:35:40 +03:00
Леонид Юрьев (Leonid Yuriev)
535ad41ae6 mdbx: made internal noop macro compatible with MSVC. 2022-06-02 20:35:40 +03:00
Леонид Юрьев (Leonid Yuriev)
106f39327e mdbx: add mdbx_assert_fail() to public API. 2022-06-02 20:35:40 +03:00
Леонид Юрьев (Leonid Yuriev)
501691a3c0 mdbx: add __assert_fail() macro for Android. 2022-06-02 20:35:40 +03:00
Леонид Юрьев (Leonid Yuriev)
06b404499e mdbx-doc: add contributors "score" table by git fame. 2022-06-02 20:35:35 +03:00
Леонид Юрьев (Leonid Yuriev)
7260db2e74 mdbx-cmake: sync/refine cmake policy. 2022-06-02 20:35:35 +03:00
Леонид Юрьев (Leonid Yuriev)
d9407ee648 mdbx: minor refine page_alloc() internal logging. 2022-05-31 17:48:23 +03:00
Леонид Юрьев (Leonid Yuriev)
c7bde8be8d mdbx: rework internal DBI-handles serials, validation and import into a txn (squashed). 2022-05-31 17:48:10 +03:00
Леонид Юрьев (Leonid Yuriev)
f91dbc6864 mdbx: last fix 32-to-64 warnings from Apple's CLANG 13. 2022-05-24 12:34:19 +03:00
Леонид Юрьев (Leonid Yuriev)
5d2eb580fd mdbx: more minor fixes 32-to-64 warnings from Apple's CLANG 13. 2022-05-19 13:11:25 +03:00
Леонид Юрьев (Leonid Yuriev)
a8d3007e61 mdbx: update ChangeLog. 2022-05-17 13:38:53 +03:00
Леонид Юрьев (Leonid Yuriev)
70d9efdce4 mdbx: fix minor CLANG 14/15 warnings. 2022-05-17 13:36:38 +03:00
Леонид Юрьев (Leonid Yuriev)
d890d3c103 mdbx: update ChangeLog. 2022-05-16 21:31:42 +03:00
Леонид Юрьев (Leonid Yuriev)
21e1dc3248 mdbx: fix minor warnings from modern Apple's CLANG. 2022-05-15 23:42:57 +03:00
Леонид Юрьев (Leonid Yuriev)
cb14ea9e67 mdbx-cmake: explicit error message from probe for no-support of std::filesystem. 2022-05-13 14:05:03 +03:00
Леонид Юрьев (Leonid Yuriev)
5cd4190f2d mdbx: fix copy&paste bug.
Resolves https://gitflic.ru/project/erthink/libmdbx/issue/5
2022-05-12 14:21:19 +03:00
Леонид Юрьев (Leonid Yuriev)
caecdd1ac0 mdbx: filter-out INDENT/clang-format on/off switches during amalgamation. 2022-05-12 14:12:48 +03:00
Леонид Юрьев (Leonid Yuriev)
405de05ca9 mdbx: return MDBX_RESULT_TRUE on empty transaction commit. 2022-05-12 13:11:59 +03:00
Леонид Юрьев (Leonid Yuriev)
548d0a50b8 mdbx: minor refine logging inside page_alloc(). 2022-05-04 19:07:33 +03:00
Леонид Юрьев (Leonid Yuriev)
f57ca4fbf6 mdbx: minor refine logging inside fetch_sdb(). 2022-05-04 18:00:54 +03:00
Леонид Юрьев (Leonid Yuriev)
9230201ca9 mdbx: fix minor warning for MDBX_TXN_INVALID. 2022-05-03 14:16:19 +03:00
Леонид Юрьев (Leonid Yuriev)
43c85a68f3 mdbx: update ChangeLog. 2022-05-03 13:25:17 +03:00
Леонид Юрьев (Leonid Yuriev)
1ab76184ed mdbx-docs: undo > changes as a compromise for Doxygen and Markdown rendering on GitFlic. 2022-05-03 12:19:08 +03:00
Леонид Юрьев (Leonid Yuriev)
6e4094c714 mdbx-docs: refine Build options for Doxygen. 2022-05-03 12:05:16 +03:00
Леонид Юрьев (Leonid Yuriev)
838f8d8fab mdbx: move most of transactions flags to public API. 2022-05-03 11:49:28 +03:00
Леонид Юрьев (Leonid Yuriev)
34e8467409 mdbx-docs: ref to the Telegram group on a website. 2022-05-03 11:49:28 +03:00
Леонид Юрьев (Leonid Yuriev)
1b0519a94a mdbx-docs: fix Doxygen groups for struct MDBX_commit_latency and mdbx_txn_commit_ex(). 2022-05-03 11:49:28 +03:00
Леонид Юрьев (Leonid Yuriev)
447d6bfca5 mdbx: add logging to mdbx_fetch_sdb() to help users debugging complex DBI use cases. 2022-05-01 12:02:40 +03:00
Леонид Юрьев (Leonid Yuriev)
1791a2f1f8 mdbx: update patch for old buildroot versions. 2022-04-28 11:08:24 +03:00
Леонид Юрьев (Leonid Yuriev)
4677313feb mdbx-docs: add Improvements beyond LMDB to the main TOC. 2022-04-28 00:42:56 +03:00
Леонид Юрьев (Leonid Yuriev)
c8cad3704e mdbx-docs: minor refine Doxygen config and comments. 2022-04-26 01:02:07 +03:00
Леонид Юрьев (Leonid Yuriev)
e154a07fe8 mdbx: minor refine ChangeLog (cosmetics). 2022-04-26 00:52:13 +03:00
Леонид Юрьев (Leonid Yuriev)
ca0a80944e mdbx: use abf.rosalinux.ru instead of abf.io. 2022-04-25 12:52:29 +03:00
Леонид Юрьев (Leonid Yuriev)
d23c653fd8 mdbx: use libmdbx.dqdkfa.ru for online docs, release, etc. 2022-04-25 12:42:57 +03:00
Леонид Юрьев (Leonid Yuriev)
d99c9efe4a mdbx: add link to Lua bindings. 2022-04-25 12:34:03 +03:00
Леонид Юрьев (Leonid Yuriev)
a5ccbc2dfe mdbx-make: add .tar.xz, .tar.bz2, .zpaq archvive to release-assets. 2022-04-24 11:08:41 +03:00
Леонид Юрьев (Leonid Yuriev)
e08bf3d835 mdbx-make: alter filenames for release-assets target. 2022-04-24 00:06:52 +03:00
Леонид Юрьев (Leonid Yuriev)
bcf42ddf83 mdbx-make: use dots in tarball filename. 2022-04-23 23:23:15 +03:00
Леонид Юрьев (Leonid Yuriev)
ce229c7500 mdbx-docs: more refine/clarify Doxygen descriptions. 2022-04-23 23:23:15 +03:00
Леонид Юрьев (Leonid Yuriev)
9c569b41ed mdbx-docs: fix/refine C++ API docs by Doxygen. 2022-04-23 23:23:15 +03:00
Леонид Юрьев (Leonid Yuriev)
c9a214f038 mdbx: minor refine ChangeLog.md. 2022-04-23 23:23:15 +03:00
Леонид Юрьев (Leonid Yuriev)
08081109b7 mdbx: split-out TODO.md from ChangeLog.md. 2022-04-23 23:23:15 +03:00
Леонид Юрьев (Leonid Yuriev)
2ce00b6359 mdbx: update .gitignore. 2022-04-23 23:23:14 +03:00
Леонид Юрьев (Leonid Yuriev)
d744d103c8 mdbx: use https://web.archive.org/web/20220414235959 instead of todo4recovery://erased_by_github for md-files. 2022-04-23 17:46:46 +03:00
Леонид Юрьев (Leonid Yuriev)
40ec559c8c
mdbx: release v0.11.7
The stable risen release after the Github's intentional malicious disaster.

We have migrated to a reliable trusted infrastructure
-----------------------------------------------------

The origin for now is at [GitFlic](https://gitflic.ru/project/erthink/libmdbx)
since on 2022-04-15 the Github administration, without any warning nor
explanation, deleted _libmdbx_ along with a lot of other projects,
simultaneously blocking access for many developers.
For the same reason ~~Github~~ is blacklisted forever.

GitFlic already support Russian and English languages, plan to support more,
including 和 中文. You are welcome!

New:
----

 - Added the `tools-static` make target to build statically linked MDBX tools.
 - Support for Microsoft Visual Studio 2022.
 - Support build by MinGW' make from command line without CMake.
 - Added `mdbx::filesystem` C++ API namespace that corresponds to `std::filesystem` or `std::experimental::filesystem`.
 - Created [website](https://libmdbx.website.yandexcloud.net/) for online auto-generated documentation.
 - Used `todo4recovery://erased_by_github/` for dead (or temporarily lost) resources deleted by ~~Github~~.
 - Added `--loglevel=` command-line option to the `mdbx_test` tool.
 - Added few fast smoke-like tests into CMake builds.

Fixes:
------

 - Fixed a race between starting a transaction and creating a DBI descriptor that could lead to `SIGSEGV` in the cursor tracking code.
 - Clarified description of `MDBX_EPERM` error returned from `mdbx_env_set_geometry()`.
 - Fixed non-promoting the parent transaction to be dirty in case the undo of the geometry update failed during abortion of a nested transaction.
 - Resolved linking issues with `libstdc++fs`/`libc++fs`/`libc++experimental` for C++ `std::filesystem` or `std::experimental::filesystem` for legacy compilers.
 - Added workaround for GNU Make 3.81 and earlier.
 - Added workaround for Elbrus/LCC 1.25 compiler bug of class inline `static constexpr` member field.
 - [Fixed](https://github.com/ledgerwatch/erigon/issues/3874) minor assertion regression (only debug builds were affected).
 - Fixed detection of `C++20` concepts accessibility.
 - Fixed detection of Clang's LTO availability for Android.
 - Fixed build for ARM/ARM64 by MSVC.
 - Fixed non-x86 Windows builds with `MDBX_WITHOUT_MSVC_CRT=ON` and `MDBX_BUILD_SHARED_LIBRARY=ON`.

Minors:
-------

 - Resolve minor MSVC warnings: avoid `/INCREMENTAL[:YES]` with `/LTCG`, `/W4` with `/W3`, the `C5105` warning.
 - Switched to using `MDBX_EPERM` instead of `MDBX_RESULT_TRUE' to indicate that the geometry cannot be updated.
 - Added `NULL` checking during memory allocation inside `mdbx_chk`.
 - Resolved all warnings from MinGW while used without CMake.
 - Added inheretable `target_include_directories()` to `CMakeLists.txt` for easy integration.
 - Added build-time checks and paranoid runtime assertions for the `off_t` arguments of `fcntl()` which are used for locking.
 - Added `-Wno-lto-type-mismatch` to avoid false-positive warnings from old GCC during LTO-enabled builds.
 - Added checking for TID (system thread id) to avoid hang on 32-bit Bionic/Android  within `pthread_mutex_lock()`.
 - Reworked `MDBX_BUILD_TARGET` of CMake builds.
 - Added `CMAKE_HOST_ARCH` and `CMAKE_HOST_CAN_RUN_EXECUTABLES_BUILT_FOR_TARGET`.

Signed-off-by: Леонид Юрьев (Leonid Yuriev) <leo@yuriev.ru>
2022-04-22 23:28:56 +03:00
Леонид Юрьев (Leonid Yuriev)
42f1abd7e9 mdbx-cmake: fix/refine --allow-multiple-definition as workaround for std::filesystem and LCC < 1.25.23 2022-04-22 23:03:58 +03:00
Леонид Юрьев (Leonid Yuriev)
b327cafe1a mdbx-cmake: seeding cmake tests. 2022-04-22 22:07:42 +03:00
Леонид Юрьев (Leonid Yuriev)
fb4bd6158f mdbx-cmake: fix extra message space (cosmetics). 2022-04-22 21:35:05 +03:00
Леонид Юрьев (Leonid Yuriev)
a878b47343 mdbx-cmake: refine fetch_version macro for no-git-tags case (sync cmake modules). 2022-04-22 21:32:38 +03:00
Леонид Юрьев (Leonid Yuriev)
f866ad2fa3 mdbx: update ChangeLog. 2022-04-22 21:03:11 +03:00
Леонид Юрьев (Leonid Yuriev)
f341129afa mdbx-windows: fix for non-x86 with MDBX_WITHOUT_MSVC_CRT=ON and MDBX_BUILD_SHARED_LIBRARY=ON. 2022-04-22 18:31:49 +03:00
Леонид Юрьев (Leonid Yuriev)
03e7e3be74 mdbx-cmake: avoid tests with mdbx_chk when MDBX_BUILD_TOOLS=OFF. 2022-04-22 18:07:51 +03:00
Леонид Юрьев (Leonid Yuriev)
7770cae4b5 mdbx-cmake: provide CMAKE_HOST_ARCH and CMAKE_HOST_CAN_RUN_EXECUTABLES_BUILT_FOR_TARGET. 2022-04-22 17:45:28 +03:00
Леонид Юрьев (Leonid Yuriev)
039ebe1f14 mdbx-cmake: rework forming of MDBX_SYSTEM_ARCH identity. 2022-04-22 17:30:11 +03:00
Leonid Yuriev
5eeb260c08 mdbx-cmake: rework making MDBX_BUILD_TARGET value. 2022-04-22 17:28:56 +03:00
Леонид Юрьев (Leonid Yuriev)
1ec5687d36 mdbx: fix build for ARM/ARM64 by MSVC. 2022-04-22 17:18:58 +03:00
Леонид Юрьев (Leonid Yuriev)
e3b0602664 mdbx-cmake: set loglevel=verbose for smoke test to speedup CI. 2022-04-22 09:32:47 +03:00
Леонид Юрьев (Leonid Yuriev)
b1101fc33e mdbx-cmake: fix detection of Clang's LTO availability for Android. 2022-04-21 22:11:57 +03:00
Леонид Юрьев (Leonid Yuriev)
bdf5fb7a72 mdbx++: avoid clang++ minor/extra warnings. 2022-04-21 21:53:46 +03:00
Леонид Юрьев (Leonid Yuriev)
e00f827de7 mdbx++: fix/refine detection of C++20 concepts accessibility. 2022-04-21 21:53:29 +03:00
Леонид Юрьев (Leonid Yuriev)
abc2341cb4 cmake-test: avoid run tests in case cross-compilation without emulator. 2022-04-21 20:35:45 +03:00
Леонид Юрьев (Leonid Yuriev)
03f9ed8820 mdbx-cmake: add a few smoke-like tests. 2022-04-21 20:35:45 +03:00
Леонид Юрьев (Leonid Yuriev)
e875d2128e mdbx-test: add --loglevel= option. 2022-04-21 20:35:41 +03:00
Леонид Юрьев (Leonid Yuriev)
bb377fd20e mdbx: merge branch master into devel. 2022-04-21 15:44:23 +03:00
Леонид Юрьев (Leonid Yuriev)
a2aa6667e1 mdbx: avoid 32-bit Bionic/Android hang within pthread_mutex_lock(). 2022-04-21 15:41:25 +03:00
Леонид Юрьев (Leonid Yuriev)
51d66494fd mdbx-cmake: add -Wno-lto-type-mismatch to avoid false-positive warnings from GCC < 9.x 2022-04-21 15:40:53 +03:00
Леонид Юрьев (Leonid Yuriev)
ed9e51d31d mdbx: update ChangeLog. 2022-04-21 15:34:56 +03:00
Леонид Юрьев (Leonid Yuriev)
2921711638 mdbx: minor refine note about migration from dead github. 2022-04-21 15:34:56 +03:00
Леонид Юрьев (Leonid Yuriev)
6d15836171 mdbx: use todo4recovery://erased_by_github/ for dead (or temporarily lost) resources. 2022-04-21 15:34:53 +03:00
Леонид Юрьев (Leonid Yuriev)
3f840ecd89 mdbx-doc: reorder restrictions section. 2022-04-20 23:55:25 +03:00
Леонид Юрьев (Leonid Yuriev)
9aa53d1616 mdbx: update ChangeLog and captions. 2022-04-20 23:55:25 +03:00
Леонид Юрьев (Leonid Yuriev)
35b5abc103 mdbx: minor fix/refix Doxygen descriptions. 2022-04-20 18:04:04 +03:00
Леонид Юрьев (Leonid Yuriev)
8ece0dfa93 mdbx-docs: update Doxygen.in 2022-04-20 18:00:54 +03:00
Леонид Юрьев (Leonid Yuriev)
ed23956e11 mdbx: basic cleanup from dead github and another corrupted services. 2022-04-20 06:42:16 +03:00
Леонид Юрьев (Leonid Yuriev)
1a471ed04b
mdbx: github is dead and blacklisted forever.
Основной репозиторий проекта перемещен на
https://gitflic.ru/project/erthink/libmdbx, так как 15 апреля
2022 администрация Github без предупреждения и без объяснения
причин удалила libmdbx вместе с массой других проектов,
одновременно заблокировав доступ многим разработчикам. По той
же причине Github навсегда занесен в черный список.

   На случай если это была случайность или ошибка мы ждали 5
суток (три рабочих дня), но чуда не случилось. Github умер, как
и многие декларируемые либеральные ценности (свобода слова,
презумпция невиновности и право на суд, неприкосновенность
личности и частной собственности и т.д.).

---

   The origin repository of the project has been moved to
https://gitflic.ru/project/erthink/libmdbx since on April 15,
2022, the Github administration, without warning and without
explanation, deleted libmdbx along with a lot of other
projects, simultaneously blocking access to many developers.
For the same reason Github is blacklisted forever.

   In case it was an accident or a mistake, we waited 5 days
(three working days), but no miracle happened. So Github is
died, as well as many declared liberal values (freedom of
speech, presumption of innocence and right to trial,
inviolability of the person and private property, etc).

Signed-off-by: Леонид Юрьев (Leonid Yuriev) <leo@yuriev.ru>
2022-04-20 02:24:31 +03:00
Леонид Юрьев (Leonid Yuriev)
eb8bc865d1 mdbx: fix and check width of off_t for Android. 2022-04-19 20:00:29 +03:00
Леонид Юрьев (Leonid Yuriev)
682ff99f1c mdbx-make: add smoke-assertion, test-assertion, long-test-assertion targets. 2022-04-19 19:43:42 +03:00
Леонид Юрьев (Leonid Yuriev)
4628ac6863 mdbx: add paranoid assertions for off_t args of fcntl(). 2022-04-19 19:43:42 +03:00
Леонид Юрьев (Leonid Yuriev)
bb8f431817 mdbx-cmake: use target_include_directories() instead of include_directories(). 2022-04-13 11:05:50 +03:00
Леонид Юрьев (Leonid Yuriev)
8fb63c3675 mdbx: minor refine API descriptions. 2022-04-13 11:05:03 +03:00
Леонид Юрьев (Leonid Yuriev)
3792dd1007 mdbx: now we accept donations only in Russian Rubles. 2022-04-12 15:08:49 +03:00
Леонид Юрьев (Leonid Yuriev)
15cc7d5ed3 mdbx: update ChangeLog. 2022-04-12 10:20:50 +03:00
Леонид Юрьев (Leonid Yuriev)
9fa4e21165 mdbx: fix assertion regression.
Fixes https://github.com/ledgerwatch/erigon/issues/3874.

This was a minor regression after the c4a5325aaf
that affects only debug builgs (with enabled assertions) and only when the added
code catch a incoherency of unified page/buffer cache.
2022-04-11 21:52:46 +03:00
Леонид Юрьев (Leonid Yuriev)
3872c0ab74 mdbx: update ChangeLog. 2022-04-08 01:01:01 +03:00
Леонид Юрьев (Leonid Yuriev)
d71b293de5 mdbx: fix spelling. 2022-04-08 01:00:58 +03:00
Леонид Юрьев (Leonid Yuriev)
5ebcb90620 mdbx-cmake: rework linking detection for std::filesystem. 2022-04-07 21:57:49 +03:00
Леонид Юрьев (Leonid Yuriev)
39a4a89650 mdbx++: declare mdbx::filesystem if available. 2022-04-07 18:00:23 +03:00
Леонид Юрьев (Leonid Yuriev)
68ac48235e mdbx-ci: add if-failure step to github-actions to dump all *.err logs. 2022-04-06 12:44:30 +03:00
Леонид Юрьев (Leonid Yuriev)
eb3fc985d6 mdbx-make: refine GNUmakefile to avoid bug of GNU Make 3.81 2022-04-06 12:43:58 +03:00
Леонид Юрьев (Leonid Yuriev)
c9dfb7d8c2 mdbx-cmake: add workaround for LCC 1.25 bug of class inline static constexpr member field definition. 2022-04-06 12:43:58 +03:00
Леонид Юрьев (Leonid Yuriev)
437cd0d3d7 mdbx-cmake: minor refine version detection of Elbrus LCC compiler. 2022-04-06 12:43:58 +03:00
Леонид Юрьев (Leonid Yuriev)
f054ceeab8 mdbx-cmake: add linking with stdc++fs for LCC < 1.26 2022-04-06 12:43:58 +03:00
Леонид Юрьев (Leonid Yuriev)
7b95720f59 mdbx++: add support for legacy experimental/filesystem. 2022-04-05 22:20:39 +03:00
Леонид Юрьев (Leonid Yuriev)
64e23c9be0 mdbx-make: add probe4 prefix for error-log files. 2022-04-05 13:07:54 +03:00
Леонид Юрьев (Leonid Yuriev)
cabead30b5 mdbx: minor cosmetics. 2022-03-31 00:31:49 +03:00
Леонид Юрьев (Leonid Yuriev)
688d4495c5 mdbx: update ChangeLog. 2022-03-30 19:12:42 +03:00
Леонид Юрьев (Leonid Yuriev)
b6d36e2235 mdbx-ci: add 'build by make' step info mingw action. 2022-03-30 18:50:34 +03:00
Леонид Юрьев (Leonid Yuriev)
ebf7bf7583 mdbx-make: support for MinGW. 2022-03-30 18:13:44 +03:00
Леонид Юрьев (Leonid Yuriev)
6f37c8e57f mdbx: resolve all warnings from MinGW. 2022-03-30 18:13:08 +03:00
Leonid Yuriev
21da42d23d mdbx: fix built-in status of MDBX_WITHOUT_MSVC_CRT option. 2022-03-30 18:10:03 +03:00
Леонид Юрьев (Leonid Yuriev)
2497437060 mdbx: mark the parent transaction dirty in case the undo of the geometry update failed during abortion of a nested transaction. 2022-03-29 18:57:35 +03:00
Леонид Юрьев (Leonid Yuriev)
33a9395afe mdbx: use MDBX_EPERM to indicate that the geometry cannot be updated instead of `MDBX_RESULT_TRUE'. 2022-03-29 18:57:26 +03:00
Leonid Yuriev
7654c9d9a1 mdbx-test: handle MDBX_RESULT_TRUE from mdbx_env_set_geometry() under Windows. 2022-03-28 23:57:24 +03:00
Leonid Yuriev
27a513682a mdbx: disable C5105 warning for Visual Studio > 15.7 (MSVC > 19.14). 2022-03-28 23:57:24 +03:00
Leonid Yuriev
92cb0cc0db mdbx-cmake: forcing /INCREMENTAL:NO while using LTO/Interprocedutal optimization. 2022-03-28 23:57:24 +03:00
Leonid Yuriev
cf32f4cdb3 mdbx-cmake: removing /W3 when adding /W4 to avoid stupid MSVC warnings. 2022-03-28 23:57:24 +03:00
Leonid Yuriev
57978b0f7f mdbx-cmake: add remove_flag and remove_compile_flag macros. 2022-03-28 23:57:24 +03:00
Леонид Юрьев (Leonid Yuriev)
eb532b8907 mdbx-tools: add check for log-level. 2022-03-28 23:57:24 +03:00
Леонид Юрьев (Leonid Yuriev)
6f06641bf2 mdbx-tools: handle NULL from memory allocation functions. 2022-03-28 23:44:31 +03:00
Леонид Юрьев (Leonid Yuriev)
1ccc9b3e3b mdbx-make: add missed "--match=v[0-9]*" to git describe. 2022-03-28 09:19:39 +03:00
Леонид Юрьев (Leonid Yuriev)
ec0379ad93 mdbx-cmake: add missed "--match=v[0-9]*" to git describe. 2022-03-28 09:16:15 +03:00
Леонид Юрьев (Leonid Yuriev)
18789654fc mdbx: update ChangeLog. 2022-03-27 16:01:04 +03:00
Леонид Юрьев (Leonid Yuriev)
bac546bdfa mdbx-make: add tools-static target. 2022-03-27 14:01:18 +03:00
Леонид Юрьев (Leonid Yuriev)
a6b506be45 mdbx-test: minor refine Valgrind-suppressions for gcc-isra. 2022-03-25 13:54:34 +03:00
Леонид Юрьев (Leonid Yuriev)
6380f2e844 mdbx: minor reformat (cosmetics). 2022-03-25 13:47:37 +03:00
Леонид Юрьев (Leonid Yuriev)
fef90d2a3c mdbx: add explicit memset() for debugging. 2022-03-25 13:47:37 +03:00
Леонид Юрьев (Leonid Yuriev)
d522069080 mdbx: fix rare SIGSEGV in cursor tracking code.
The error was that the array of pointers in the transaction zeroed by the
value of env->me_numdbs and before txn->mt_numdbs was set to env->me_numdbs.
Thus, a cursor pointer(s) in the starting transaction could uninitialized if
another thread opened a dbi-handle(s) between the two aforementioned events.
2022-03-25 13:05:46 +03:00
Леонид Юрьев (Leonid Yuriev)
d5e4c198d8
mdbx: release v0.11.6
The stable release with the complete workaround for an incoherence flaw of Linux unified page/buffer cache.
Nonetheless the cause for this trouble may be an issue of Intel CPU cache/MESI.
See [issue#269](https://github.com/erthink/libmdbx/issues/269) for more information.

Acknowledgements:
-----------------

 - [David Bouyssié](https://github.com/david-bouyssie) for [Scala bindings](https://github.com/david-bouyssie/mdbx4s).
 - [Michelangelo Riccobene](https://github.com/mriccobene) for reporting and testing.

Fixes:
------

 - [Added complete workaround](https://github.com/erthink/libmdbx/issues/269) for an incoherence flaw of Linux unified page/buffer cache.
 - [Fixed](https://github.com/erthink/libmdbx/issues/272) cursor reusing for read-only transactions.
 - Fixed copy&paste typo inside `mdbx::cursor::find_multivalue()`.

Minors:
-------

 - Minor refine C++ API for convenience.
 - Minor internals refines.
 - Added `lib-static` and `lib-shared` targets for make.
 - Added minor workaround for AppleClang 13.3 bug.
 - Clarified error messages of a signature/version mismatch.

Signed-off-by: Леонид Юрьев (Leonid Yuriev) <leo@yuriev.ru>
2022-03-24 20:44:21 +03:00
Леонид Юрьев (Leonid Yuriev)
415d0d1dfb mdbx: update ChangeLog. 2022-03-24 19:19:53 +03:00
Леонид Юрьев (Leonid Yuriev)
50d5b2345e mdbx-test: add Valgrind-suppressions for bcmp/memcmp of page-check inside mdbx_iov_write().
Related to https://github.com/erthink/libmdbx/issues/269.
2022-03-24 12:11:50 +03:00
Леонид Юрьев (Leonid Yuriev)
d13534967a mdbx++: fix copy&paste typo inside mdbx::cursor::find_multivalue(). 2022-03-24 12:11:50 +03:00
Леонид Юрьев (Leonid Yuriev)
43070c7b26 mdbx: minor fix meta_checktxnid() for Valgrind/ASAN usage.
Related to https://github.com/erthink/libmdbx/issues/269.
2022-03-24 12:11:50 +03:00
Леонид Юрьев (Leonid Yuriev)
45f8197635 mdbx: clarify/refine error messages of a signature/version mismatch. 2022-03-24 00:19:34 +03:00
Леонид Юрьев (Leonid Yuriev)
3db02d2236 mdbx++: explicitly define noexcept copy-assignment ops as workaround for AppleClang > 13.3 bug.
Try workaround for https://github.com/erthink/libmdbx/issues/278
2022-03-23 18:06:28 +03:00
Леонид Юрьев (Leonid Yuriev)
b79f6712e3 mdbx: refine override_meta() to preserve format-signature for legacy/zero mod_txnid. 2022-03-23 00:48:41 +03:00
Леонид Юрьев (Leonid Yuriev)
de63041b7d mdbx: add MDBX_DBG_DONT_UPGRADE flag. 2022-03-23 00:48:41 +03:00
Леонид Юрьев (Leonid Yuriev)
a5c064c33e mdbx: preserve (don't upgrade) format-signature during turn to a specified meta-page while recovery. 2022-03-23 00:48:41 +03:00
Your Name
9c832c24a6 mdbx: minor fix error-message space. 2022-03-23 00:48:41 +03:00
Леонид Юрьев (Leonid Yuriev)
c4a5325aaf mdbx: check-and-retry against page arrival as a workaround for unified page/buffer cache incoherency.
Part 2 of 2 of the workaround for https://github.com/erthink/libmdbx/issues/269.
2022-03-08 17:09:29 +03:00
Леонид Юрьев (Leonid Yuriev)
93cf99a07c mdbx: drop the previous temporary workaround for unified page/buffer cache incoherency.
Related to https://github.com/erthink/libmdbx/issues/269.
2022-03-06 13:22:57 +03:00
Леонид Юрьев (Leonid Yuriev)
00ed61c685 mdbx: check-and-retry a mvcc-snapshot for unified page/buffer cache coherency.
Part 1 of 2 of the workaround for https://github.com/erthink/libmdbx/issues/269.
2022-03-06 13:22:38 +03:00
Леонид Юрьев (Leonid Yuriev)
f84d9f6208 mdbx++: minor refine mdbx::cursor::erase(). 2022-03-06 00:13:11 +03:00
Леонид Юрьев (Leonid Yuriev)
9569b864ff mdbx-test: fix quoting for tr args inside long_stochastic.sh 2022-03-05 15:37:32 +03:00
Леонид Юрьев (Leonid Yuriev)
8b69be8def mdbx: update ChangeLog. 2022-03-05 15:14:20 +03:00
Леонид Юрьев (Leonid Yuriev)
4c619e32f7 mdbx: move origin to https://gitflic.ru/project/erthink/libmdbx. 2022-03-05 14:39:02 +03:00
Леонид Юрьев (Leonid Yuriev)
ed86d9c066 mdbx: add link to Scala bindings.
Resolves https://github.com/erthink/libmdbx/issues/274.
2022-03-05 14:39:02 +03:00
Леонид Юрьев (Leonid Yuriev)
6fbaa54d3e mdbx: remove PayPal from links for sponsorship. 2022-03-05 14:39:02 +03:00
Леонид Юрьев (Leonid Yuriev)
9d8fc7b984 mdbx: minor mix result caching inside the get_reasonable_db_maxsize().
Change-Id: I9763ebb323d4f66e702e07f708e269e2479880ac
2022-03-05 14:31:02 +03:00
Леонид Юрьев (Leonid Yuriev)
8c2efe3aaa mdbx++: add full mdbx::env_managed::geometry() for convenience.
Change-Id: Ib132c776bfc503336582b672de1c44fcac286936
2022-03-05 14:31:02 +03:00
Леонид Юрьев (Leonid Yuriev)
4b130bd82c mdbx-make: add lib-static and lib-shared targets.
Resolve https://github.com/erthink/libmdbx/issues/275.
2022-03-05 14:28:15 +03:00
Леонид Юрьев (Leonid Yuriev)
44fb240955 mdbx: merge branch issue-269 into the devel branch. 2022-03-04 17:46:43 +03:00
Леонид Юрьев (Leonid Yuriev)
bfea3ca9fb mdbx: minor refine mdbx_page_search() internals. 2022-03-04 17:38:25 +03:00
Леонид Юрьев (Leonid Yuriev)
7ade182d64 mdbx: minor simplify/refine mdbx_env_set_geometry() internals. 2022-03-04 17:38:25 +03:00
Леонид Юрьев (Leonid Yuriev)
4adc7aa58d mdbx: add few checks to mdbx_validate_meta(). 2022-03-04 11:53:14 +03:00
Леонид Юрьев (Leonid Yuriev)
110cf09cf8 mdbx: simplify core-dbs update inside mdbx_txn_commit_ex(). 2022-03-04 11:49:54 +03:00
Леонид Юрьев (Leonid Yuriev)
fb25648b9c mdbx: force mdbx_page_get() to inline. 2022-03-04 11:49:01 +03:00
Леонид Юрьев (Leonid Yuriev)
78170a5750 mdbx: minor reduce/lowering debug "update oldest" logging. 2022-03-04 11:41:17 +03:00
Леонид Юрьев (Leonid Yuriev)
71d07b3a8e mdbx: adding cursors tracking/lists for read-only transactions.
Briefly, this commit fixes a missed flaw:
 - Cursor tracking is required to replacing shaded pages and adjusting the positions in writing transactions;
 - Thus, historically, an internal linked list was maintained for a read-write transactions, but not for a read-only.
   For this reason, the API for using cursors should be different for writing and reading transactions;
 - However, the libmdbx's API has been significantly improved, including the ability to reuse cursors and a uniform cursors behavior for any kind of transactions.
   My mistake is that due to working with MithrilDB, I forgot to make a same changes to libmdbx.

Fixes https://github.com/erthink/libmdbx/issues/272.
2022-03-03 15:00:28 +03:00
Леонид Юрьев (Leonid Yuriev)
96c93ac2f1 mdbx++: refine mdbx::env::operate_parameters() and related. 2022-03-03 14:13:10 +03:00
Леонид Юрьев (Leonid Yuriev)
6d61b18325 mdbx: fix auxiliary mdbx_txn_valgrind().
Fixed https://github.com/erthink/libmdbx/issues/273.
2022-02-27 15:42:48 +03:00
Леонид Юрьев (Leonid Yuriev)
d01e44db0c
mdbx: release v0.11.5
The stable release with the hotfix/workaround for a flaw of Linux 4.19 (at least) unified page/buffer cache.
See [issue#269](https://github.com/erthink/libmdbx/issues/269) for more information.

Acknowledgements:
-----------------

 - [Simon Leier](https://github.com/leisim) for reporting and testing.
 - [Kai Wetlesen](https://github.com/kaiwetlesen) for [RPMs](http://copr.fedorainfracloud.org/coprs/kwetlesen/libmdbx/).
 - [Tullio Canepa](https://github.com/canepat) for reporting C++ API issue and contributing.

Fixes:
------

 - [Added workaround](https://github.com/erthink/libmdbx/issues/269) for a flaw of Linux 4.19 (at least) unified page/buffer cache.
 - [Fixed/Reworked](https://github.com/erthink/libmdbx/pull/270) move-assignment operators for "managed" classes of C++ API.
 - Fixed potential `SIGSEGV` while open DB with overrided non-default page size.
 - [Made](https://github.com/erthink/libmdbx/issues/267) `mdbx_env_open()` idempotence in failure cases.
 - Refined/Fixed pages reservation inside `mdbx_update_gc()` to avoid non-reclamation in a rare cases.
 - Fixed typo in a retained space calculation for the hsr-callback.

Minors:
-------

 - Reworked functions for meta-pages, split-off non-volatile.
 - Disentangled C11-atomic fences/barriers and pure-functions (with `__attribute__((__pure__))`) to avoid compiler misoptimization.
 - Fixed hypotetic unaligned access to 64-bit dwords on ARM with `__ARM_FEATURE_UNALIGNED` defined.
 - Reasonable paranoia that makes clarity for code readers.
 - Minor fixes Doxygen references, comments, descriptions, etc.

Signed-off-by: Леонид Юрьев (Leonid Yuriev) <leo@yuriev.ru>
2022-02-23 20:06:30 +03:00
Леонид Юрьев (Leonid Yuriev)
464886ab61 mdbx++: rework/fix move-assignment operators for "managed" classes.
Replaces https://github.com/erthink/libmdbx/pull/270 and previous commit.
Fixed a half of https://github.com/torquem-ch/silkworm/issues/575.
2022-02-23 18:42:09 +03:00
Леонид Юрьев (Leonid Yuriev)
3c574fca99 mdbx++: fix move-assignment operators for "managed" classes.
The three same mistakes for `mdbx::env_managed`, `mdbx::txn_managed` and `mdbx::cursor_managed`.

A `derived_managed &operator=(derived_managed &&) = default;`
don't call an inherited `base::operator=()` since it hidded because the
`derived_managed(derived_managed &&) = default;` is also provided.

Replaces/overrides https://github.com/erthink/libmdbx/pull/270.
2022-02-23 17:58:56 +03:00
Леонид Юрьев (Leonid Yuriev)
6b45498985 mdbx: temporary workaround/hotfix for a flaw of Linux 4.19 (at least) unified page/buffer cache.
Temporary workaround for https://github.com/erthink/libmdbx/issues/269.
2022-02-22 23:58:57 +03:00
Леонид Юрьев (Leonid Yuriev)
1fd4101e2e mdbx: update ChangeLog. 2022-02-21 00:06:57 +03:00
Леонид Юрьев (Leonid Yuriev)
77f236db2a mdbx: clarify loglevel descriptions. 2022-02-17 02:30:16 +03:00
Леонид Юрьев (Leonid Yuriev)
a0728023cd mdbx: fix typo in a retained space calculation for the hsr-callback. 2022-02-16 15:44:06 +03:00
Леонид Юрьев (Leonid Yuriev)
3705d705d3 mdbx: refine/fix pages reserving inside mdbx_updage_gc() to avoid non-reclamation in a rare cases. 2022-02-16 12:18:53 +03:00
Леонид Юрьев (Leonid Yuriev)
72bc655ece mdbx++: fix env::is_empty() to check leaf-pages instead of branch ones.
Seems like an auto-completion mistake.
Thanks to https://igor@t.me/libmdbx for reporing.
2022-02-15 18:11:31 +03:00
Леонид Юрьев (Leonid Yuriev)
c5f1f73fca mdbx: avoid extra looping inside mdbx_page_alloc() for mdbx_prep_backlog(). 2022-02-15 03:17:05 +03:00
Леонид Юрьев (Leonid Yuriev)
925a673d57 mdbx: make mdbx_env_open() idempotence in failure cases.
Fixes https://github.com/erthink/libmdbx/issues/267.
2022-02-13 20:30:57 +03:00
Леонид Юрьев (Leonid Yuriev)
c27787eb31 mdbx: fix potential SIGSEGV for DB with non-default page size.
Related to https://github.com/erthink/libmdbx/issues/267.
2022-02-13 17:04:10 +03:00
Леонид Юрьев (Leonid Yuriev)
2b6fd968d2 mdbx: rework functions for meta-pages, split-off non-volatile, more const. 2022-02-09 22:56:29 +03:00
Леонид Юрьев (Leonid Yuriev)
ef7b4289c0 mdbx: rework unaligned access.
The three points:
 - disentangle C11-atomic fences/barriers and pure-functions (with `__attribute__((__pure__))`) to avoid compiler misoptimization;
 - fix hypotetic unaligned access to 64-bit dwords on ARM with `__ARM_FEATURE_UNALIGNED` defined;
 - reasonable paranoia that makes clarity for code readers.
2022-02-09 22:56:28 +03:00
Леонид Юрьев (Leonid Yuriev)
cbbfaf3202 mdbx-doc: fix doxygen-reference to the "Long-lived read transactions" paragraph. 2022-02-08 14:37:11 +03:00
Леонид Юрьев (Leonid Yuriev)
f6be8e3e9a mdbx: minor refine note about MDBX_WITHOUT_MSVC_CRT option. 2022-02-03 03:12:23 +03:00
Леонид Юрьев (Leonid Yuriev)
daa7f04f61
mdbx: release v0.11.4
The stable release with fixes for large and huge databases sized of 4..128 TiB.

Acknowledgements:
-----------------

 - Ledgerwatch, Binance and Positive Technologies teams for reporting, assistance in investigation and testing.
 - Alex Sharov for reporting, testing and provide resources for remote debugging/investigation.
 - Kris Zyp for Deno support.

New features, extensions and improvements:
------------------------------------------

 - Added treating the `UINT64_MAX` value as maximum for given option inside `mdbx_env_set_option()`.
 - Added `to_hex/to_base58/to_base64::output(std::ostream&)` overloads without using temporary string objects as buffers.
 - Added `--geometry-jitter=YES|no` option to the test framework.
 - Added support for [Deno](https://deno.land/) support by [Kris Zyp](https://github.com/kriszyp).

Fixes:
------

 - Fixed handling `MDBX_opt_rp_augment_limit` for GC's records from huge transactions (Erigon/Akula/Ethereum).
 - [Fixed](https://github.com/erthink/libmdbx/issues/258) build on Android (avoid including `sys/sem.h`).
 - [Fixed](https://github.com/erthink/libmdbx/pull/261) missing copy assignment operator for `mdbx::move_result`.
 - Fixed missing `&` for `std::ostream &operator<<()` overloads.
 - Fixed unexpected `EXDEV` (Cross-device link) error from `mdbx_env_copy()`.
 - Fixed base64 encoding/decoding bugs in auxillary C++ API.
 - Fixed overflow of `pgno_t` during checking PNL on 64-bit platforms.
 - [Fixed](https://github.com/erthink/libmdbx/issues/260) excessive PNL checking after sort for spilling.
 - Reworked checking `MAX_PAGENO` and DB upper-size geometry limit.
 - [Fixed](https://github.com/erthink/libmdbx/issues/265) build for some combinations of versions of  MSVC and Windows SDK.

Minors:
-------

 - Added workaround for CLANG bug [D79919/PR42445](https://reviews.llvm.org/D79919).
 - Fixed build test on Android (using `pthread_barrier_t` stub).
 - Disabled C++20 concepts for CLANG < 14 on Android.
 - Fixed minor `unused parameter` warning.
 - Added CI for Android.
 - Refine/cleanup internal logging.
 - Refined line splitting inside hex/base58/base64 encoding to avoid `\n` at the end.
 - Added workaround for modern libstdc++ with CLANG < 4.x
 - Relaxed txn-check rules for auxiliary functions.
 - Clarified a comments and descriptions, etc.
 - Using the `-fno-semantic interposition` option to reduce the overhead to calling self own public functions.

Signed-off-by: Леонид Юрьев (Leonid Yuriev) <leo@yuriev.ru>
2022-02-02 20:56:28 +03:00
Леонид Юрьев (Leonid Yuriev)
18290489b2 mdbx-cmake: don't use -fno-semantic-interposition for CLANG. 2022-02-02 19:22:50 +03:00
Леонид Юрьев (Leonid Yuriev)
92ab17a644 mdbx-test: resolve minor MSVC /W4 warnings. 2022-02-02 17:11:45 +03:00
Леонид Юрьев (Leonid Yuriev)
ac02490c97 mdbx-cmake: sync the compiler.cmake module with other projects. 2022-02-02 16:50:49 +03:00
Леонид Юрьев (Leonid Yuriev)
d498c2580b mdbx: more refine mdbx_env_set_geometry() description. 2022-02-02 14:28:45 +03:00
Леонид Юрьев (Leonid Yuriev)
e740df6e50 mdbx: fix description of mdbx_env_set_geometry().
Oops, I just noticed that there are a strange typos in the description.
It looks like a search&replace or cut&paste mistake.

Related to https://github.com/LWJGL/lwjgl3/issues/724.
2022-02-02 14:09:03 +03:00
Leonid Yuriev
b6a0d11b99 mdbx: add note to using recent MSVC & Windows SDK versions. 2022-02-02 14:09:03 +03:00
Leonid Yuriev
65defdd0a8 mdbx-test: fix calculation default size_now of DB. 2022-02-02 14:09:03 +03:00
Leonid Yuriev
5aeb7ccf25 mdbx: fix extra casting inside mdbx_limits_dbsize_max(). 2022-02-02 14:09:03 +03:00
Leonid Yuriev
79e1cc3bbc mdbx: add workaround for old MSVC and/or old Windows SDK.
Resolves https://github.com/erthink/libmdbx/issues/265.

Change-Id: I295b5d9d5ecd670ccf258791bf87379a3ca17f21
2022-02-01 13:28:59 +03:00
Leonid Yuriev
1c409a38d3 mdbx: add info about Deno support. 2022-01-31 21:36:57 +03:00
Leonid Yuriev
bd51e181fb mdbx: refine comment for mdbx_update_gc(). 2022-01-31 21:36:57 +03:00
Leonid Yuriev
dbba0fe18e mdbx-ci: disable broken spelling action. 2022-01-31 21:36:57 +03:00
Леонид Юрьев (Leonid Yuriev)
4e2a7465ab mdbx-make: fix/refine adding -Wno-unused-command-line-argument for tolerance to an old compilers.
More for https://github.com/erthink/libmdbx/issues/264.
2022-01-27 18:05:38 +03:00
Леонид Юрьев (Leonid Yuriev)
d93a13294a mdbx: explicit disable -Wattributes by #pragma for GCC < 9. 2022-01-27 17:20:12 +03:00
Leonid Yuriev
0fdeb7cd50 mdbx-make: add -Wno-unused-command-line-argument for CLANG > 12.
Resolves https://github.com/erthink/libmdbx/issues/264.
2022-01-26 18:50:57 +03:00
Leonid Yuriev
fe5199b9d1 mdbx: fix/rework PNL checking during search, refine/simplify spilled pages search.
Fixes https://github.com/erthink/libmdbx/issues/260.

Change-Id: I8824f6af2f770c46668ee577aeeeb93b30b17a13
2022-01-26 13:21:21 +03:00
Leonid Yuriev
bfc67a6f75 mdbx-test: ability to specify single test cases to run from a stochastic test script. 2022-01-26 12:10:29 +03:00
Leonid Yuriev
8adf242d02 mdbx-doc: more line-breaks for Doxygen. 2022-01-25 20:25:10 +03:00
Leonid Yuriev
f63f4f8924 mdbx-doc: fix typo in the Doxygen reference. 2022-01-25 20:24:18 +03:00
Leonid Yuriev
28fcf33cda mdbx-doc: fix/refine MDBX_dbi description. 2022-01-25 20:09:17 +03:00
Leonid Yuriev
207124e7fb mdbx: use -fno-semantic-interposition. 2022-01-25 18:19:56 +03:00
Leonid Yuriev
86d4e37327 mdbx: fix minor copy-paste errors in descriptions. 2022-01-25 01:08:14 +03:00
Leonid Yuriev
e7e82cb289 mdbx: minor refine README. 2022-01-23 00:16:01 +03:00
Leonid Yuriev
9ae56332d7 mdbx: fix Coverity warnings. 2022-01-22 18:01:31 +03:00
Leonid Yuriev
03d828834b mdbx: schedule the release of v0.11.4 for 2022-01-28. 2022-01-21 15:43:17 +03:00
Leonid Yuriev
97beb8ee53 mdbx: update ChangeLog. 2022-01-21 15:01:48 +03:00
Leonid Yuriev
03381fa469 mdbx++: clarify comments for mdbx::byte and char8_t usage.
Related to https://github.com/erthink/libmdbx/issues/263.
2022-01-21 15:00:34 +03:00
Leonid Yuriev
75b23524bd mdbx-doc: minor refine mdbx_env_set_geometry() description. 2022-01-21 11:44:05 +03:00
Leonid Yuriev
cd2c5f594c mdbx: rework checking MAX_PAGENO and DB upper-size limit.
Related to https://github.com/erthink/libmdbx/issues/260.
2022-01-21 02:25:27 +03:00
Leonid Yuriev
50b843ecb7 mdbx-test: more for --ignore-dbfull. 2022-01-21 02:13:48 +03:00
Leonid Yuriev
981b10d10a mdbx-test: add --geometry-jitter=YES|no option. 2022-01-21 02:13:48 +03:00
Leonid Yuriev
e1bcdb8e9a mdbx-make: prefer clang-format-13 or later. 2022-01-21 02:13:48 +03:00
Leonid Yuriev
ddef217047 mdbx: relax txn-check rules for auxiliary functions. 2022-01-21 02:13:48 +03:00
Leonid Yuriev
3e28cc2a25 mdbx: allow mdbx_dbi_flags_ex() for errored transactions. 2022-01-21 02:13:48 +03:00
Leonid Yuriev
f4cc7b3609 mdbx: add .err to .gitignore. 2022-01-21 02:13:48 +03:00
Leonid Yuriev
8779f665dc mdbx: fix excessive PNL checking after sort for spilling.
This bug triggered only in the DEBUG builds or when the assertion checking is forcibly enabled.
It does not affect any core logic and cannot lead to DB corruption, data loss, and so on.

Fixes https://github.com/erthink/libmdbx/issues/260.
2022-01-20 15:25:35 +03:00
Leonid Yuriev
52552ebfe3 mdbx++: fix MSVC warnings/errors. 2022-01-19 21:08:01 +03:00
Leonid Yuriev
a6acc9d1a3 mdbx++: fix minor comment typo.
Change-Id: Ic169523218b366be0688ab92b76c4120df829ba3
2022-01-17 11:06:27 +03:00
Leonid Yuriev
c588af6aca mdbx: update Copyright year.
Change-Id: Ib3e4c0ac94882c4b2a1e167dd98e6e26dbdf48a4
2022-01-17 11:06:14 +03:00
Leonid Yuriev
c076979225 mdbx++: add workaround for modern libstd++ with CLANG < 4.x
Change-Id: Ic82694f4f51bfdb2d6f6f072fdf9af791c0eb6f8
2022-01-17 11:06:07 +03:00
Leonid Yuriev
f2995ac75b mdbx++: add assertions for destination-overflow to from/to hex/base58/base64::write_bytes().
Change-Id: I5a1f23dcb1dc1942f9d72966a78080eacfacf546
2022-01-15 18:52:12 +03:00
Leonid Yuriev
b5b0a9a284 mdbx++: add to_hex/to_base58/to_base64::output(std::ostream&) without using temporary objects/buffers/strings.
Change-Id: Ideffd0e7f450307e14d780dcdeb2458c1c7e4c18
2022-01-15 18:52:12 +03:00
Leonid Yuriev
b139d8165b mdbx++: refine line splitting inside to_hex/to_base58/to_base64 to avoid \n at the end.
Change-Id: I53fb1f31ac5fc41567826a9035b0080fd62c8ade
2022-01-15 18:51:56 +03:00
Leonid Yuriev
ef2e390b10 mdbx++: fix from_base64::write_bytes().
Change-Id: I6c90997bffab45019a77edcf2a9dd35ae80069d3
2022-01-15 18:51:56 +03:00
Leonid Yuriev
51e6d4645c mdbx++: fix to_base64::write_bytes() for tail bytes.
Change-Id: I60defa11b7e44dcf2ac840160ab0c833484c9d39
2022-01-15 17:22:18 +03:00
Leonid Yuriev
2151be6cde mdbx: merge branch 'issue-260' into devel. 2022-01-15 15:31:45 +03:00
Leonid Yuriev
7a06cac680 mdbx: fix unexpected EXDEV (Cross-device link) error from mdbx_env_copy()
Fallback to `write()` if `copy_file_range()` syscall returns `EXDEV`.
2022-01-14 12:39:38 +03:00
yperbasis
c25df39cd0 Copy assignment operator for move_result 2022-01-13 21:07:40 +03:00
Leonid Yuriev
1813bf9e53 mdbx: avoid 32-bit pgno_t overflow during checking PNL on 64-bit platforms.
Related to https://github.com/erthink/libmdbx/issues/260.
2022-01-13 15:42:00 +03:00
Leonid Yuriev
70dab667b9 mdbx++: fix missing & for std::ostream &operator<<(). 2022-01-13 14:03:51 +03:00
Leonid Yuriev
30b3cc3407 mdbx: update FUNDING. 2022-01-08 20:22:04 +03:00
Leonid Yuriev
406cafb642 mdbx: treat the UINT64_MAX value as maximum for given option inside mdbx_env_set_option(). 2022-01-06 00:37:08 +03:00
Leonid Yuriev
9822412ebd mdbx: update ChangeLog. 2022-01-05 19:05:58 +03:00
Leonid Yuriev
899f0fef13 mdbx-ci: add Android for github.
Related to https://github.com/erthink/libmdbx/issues/258.
2022-01-05 17:47:57 +03:00
Leonid Yuriev
0df17ed359 mdbx++: disable using C++20 concepts for NDK's CLANG < 14. 2022-01-05 17:47:45 +03:00
Leonid Yuriev
9cf18176f0 mdbx-test: fix build for __ANDROID_API__ < 24 (using pthread_barrier_t stub). 2022-01-05 17:47:45 +03:00
Leonid Yuriev
b19ebf0c2e mdbx: fix minor unused parameter warning. 2022-01-05 17:47:45 +03:00
Leonid Yuriev
489f7a3136 mdbx-cmake: add workaround for LTO on Android (https://reviews.llvm.org/D79919). 2022-01-05 17:47:45 +03:00
Leonid Yuriev
066bb696e3 mdbx: avoid including sys/sem.h when MDBX_LOCKING != MDBX_LOCKING_SYSV.
Fixes https://github.com/erthink/libmdbx/issues/258.
2022-01-05 17:47:09 +03:00
Leonid Yuriev
024900ee9c mdbx: fix/refine handling options.rp_augment_limit detent while loading GC records. 2022-01-05 11:03:21 +03:00
Leonid Yuriev
ab83d173ac mdbx: minor refine logging for internal GC-related ops. 2022-01-05 10:50:39 +03:00
Leonid Yuriev
d316bde561 mdbx: minor cleanup comment. 2022-01-05 10:45:23 +03:00
Leonid Yuriev
38e9aa79e7 mdbx: support make dist for debug-includes. 2022-01-05 10:45:15 +03:00
Leonid Yuriev
a6340c6b03 mdbx: minor fix open-MADV logging. 2022-01-05 10:45:15 +03:00
Leonid Yuriev
070ba5ded4 mdbx: check for GCC/clang in debug-includes (insignificant internals). 2022-01-04 13:41:25 +03:00
213 changed files with 57858 additions and 41838 deletions

View File

@ -1,22 +0,0 @@
version: 2
jobs:
build:
branches:
ignore:
- gh-pages
docker:
- image: circleci/buildpack-deps:20.04
environment:
- TESTDB: /tmp/test.db
- TESTLOG: /tmp/test.log
steps:
- checkout
- run: ulimit -c unlimited && MDBX_BUILD_OPTIONS="-DNDEBUG=1 -DMDBX_FORCE_ASSERTIONS=1" make test-ubsan
- run:
command: |
mkdir -p /tmp/artifacts
mv -t /tmp/artifacts $TESTLOG $TESTDB core.*
when: on_fail
- store_artifacts:
path: /tmp/artifacts
destination: test-artifacts

View File

@ -1,6 +0,0 @@
freebsd_instance:
image_family: freebsd-12-1-snap
task:
install_script: pkg install -y gmake bash git
script: git fetch --tags --force && gmake MDBX_BUILD_OPTIONS="-DNDEBUG=1 -DMDBX_FORCE_ASSERTIONS=1" check

View File

@ -1,3 +1,3 @@
BasedOnStyle: LLVM
Standard: Cpp11
ReflowComments: true
Standard: c++20
ColumnLimit: 120

3
.cmake-format.yaml Normal file
View File

@ -0,0 +1,3 @@
format:
line_width: 120
tab_size: 2

12
.github/FUNDING.yml vendored
View File

@ -1,12 +0,0 @@
# These are supported funding model platforms
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
custom: ['https://sobe.ru/na/libmdbx', 'https://paypal.me/erthink', 'https://www.blockchain.com/en/eth/address/0x19291d8658f762f3baceae1700c0b9466572ceab', 'https://www.blockchain.com/en/btc/address/152u2KXNWWGHQS3qiBEoQaveWyPvaSWYGC']

View File

@ -1,11 +0,0 @@
\.def$
^AUTHORS$
^\.github/FUNDING\.yml$
^\.github/actions/spelling/
^\.github/workflows/ci\.yml$
^\.github/workflows/coverity\.yml$
^\.github/workflows/doxygen-github-pages\.yml$
^\.gitignore$
^\.travis\.yml$
^packages/buildroot/
^CMakeSettings\.json$

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +0,0 @@
# numbers
(?:[\\0][xX]|[uU]\+|#)[0-9a-fA-FgGrR]{2,}[uU]?[lL]{0,2}\b
# avoid false positive
-{1,2}force\W
# flags
(?!\w)-{1,2}[fDW]

View File

@ -1,65 +0,0 @@
name: MinGW
on:
pull_request:
push:
branches: mingw
paths-ignore:
- '.circleci/**'
- '.github/actions/spelling/**'
- 'docs/**'
- 'packages**'
- .cirrus.yml
- .clang-format
- .gitignore
- .travis.yml
- AUTHORS
- COPYRIGHT
- ChangeLog.md
- LICENSE
- README.md
- appveyor.yml
jobs:
build:
runs-on: [windows-latest]
steps:
- uses: actions/checkout@v2
- name: fetch tags
run: git fetch --unshallow --tags --prune --force
- name: append PATH
run: echo 'C:/ProgramData/chocolatey/lib/mingw/tools/install/mingw64/bin' >> $GITHUB_PATH
- name: update mingw64
# wanna version >= 10.2
run: choco upgrade mingw -y --no-progress && refreshenv
- name: configure-dll
shell: bash
run: |
mkdir build-dll && cd build-dll && \
cmake -G "MinGW Makefiles" -DMDBX_BUILD_SHARED_LIBRARY:BOOL=ON -DMDBX_INSTALL_STATIC:BOOL=OFF -DMDBX_ENABLE_TESTS:BOOL=ON ..
- name: build-dll
shell: bash
run: cd build-dll && cmake --build .
- name: test-dll
shell: bash
run: |
export "PATH=/c/programdata/chocolatey/lib/mingw/tools/install/mingw64/bin:$PATH" && \
cd build-dll && \
./mdbx_test.exe --progress --console=no --pathname=test.db --dont-cleanup-after basic && \
./mdbx_chk.exe -nvv test.db
- name: configure-static
shell: bash
run: |
mkdir build-static && cd build-static && \
cmake -G "MinGW Makefiles" -DMDBX_BUILD_SHARED_LIBRARY:BOOL=OFF -DMDBX_INSTALL_STATIC:BOOL=ON -DMDBX_ENABLE_TESTS:BOOL=ON ..
- name: build-static
shell: bash
run: |
cd build-static && cmake --build .
- name: test-static
shell: bash
run: |
export "PATH=/c/programdata/chocolatey/lib/mingw/tools/install/mingw64/bin:$PATH" && \
cd build-static && \
./mdbx_test.exe --progress --console=no --pathname=test.db --dont-cleanup-after basic && \
./mdbx_chk.exe -nvv test.db

View File

@ -1,37 +0,0 @@
name: CI
on:
pull_request:
push:
branches-ignore:
- coverity_scan
paths-ignore:
- '.circleci/**'
- '.github/actions/spelling/**'
- 'docs/**'
- 'packages**'
- .cirrus.yml
- .clang-format
- .gitignore
- .travis.yml
- AUTHORS
- COPYRIGHT
- ChangeLog.md
- LICENSE
- README.md
- appveyor.yml
jobs:
build:
runs-on: ${{ matrix.os }}
strategy:
matrix:
#, windows-latest
os: [ubuntu-latest, macos-latest, ubuntu-18.04]
steps:
- uses: actions/checkout@v2
- name: fetch tags
run: git fetch --unshallow --tags --prune --force
- name: make check
run: make MDBX_BUILD_OPTIONS="-DNDEBUG=1 -DMDBX_FORCE_ASSERTIONS=1" --keep-going all && MALLOC_CHECK_=7 MALLOC_PERTURB_=42 make MDBX_BUILD_OPTIONS="-DNDEBUG=1 -DMDBX_FORCE_ASSERTIONS=1" --keep-going check
shell: bash

View File

@ -1,55 +0,0 @@
name: Coverity
on:
push:
branches: coverity_scan
paths-ignore:
- '.circleci/**'
- '.github/actions/spelling/**'
- 'docs/**'
- 'packages**'
- .cirrus.yml
- .clang-format
- .gitignore
- .travis.yml
- AUTHORS
- COPYRIGHT
- ChangeLog.md
- LICENSE
- README.md
- appveyor.yml
env:
COVERITY_SCAN_PROJECT_NAME: 'ReOpen/libmdbx'
COVERITY_SCAN_TOKEN: ${{ secrets.COVERITY_SCAN_TOKEN }}
COVERITY_SCAN_BUILD_COMMAND: 'make MDBX_BUILD_OPTIONS=-DMDBX_DEBUG=2 CXXSTD=-std=gnu++17 build-test'
COVERITY_SCAN_NOTIFICATION_EMAIL: 'leo@yuriev.ru'
COVERITY_UNSUPPORTED_COMPILER_INVOCATION: 1
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: fetch tags
run: git fetch --unshallow --tags --prune --force
- name: Download Coverity Build Tool
run: |
wget -q https://scan.coverity.com/download/cxx/linux64 --post-data "token=$COVERITY_SCAN_TOKEN&project=$COVERITY_SCAN_PROJECT_NAME" -O cov-analysis-linux64.tar.gz
mkdir cov-analysis-linux64
tar xzf cov-analysis-linux64.tar.gz --strip 1 -C cov-analysis-linux64
- name: Build with cov-build
run: |
export PATH=`pwd`/cov-analysis-linux64/bin:$PATH
cov-build --dir cov-int $COVERITY_SCAN_BUILD_COMMAND
- name: Submit the result to Coverity Scan
run: |
tar czvf libmdbx.tgz cov-int
curl \
--form project=$COVERITY_SCAN_PROJECT_NAME \
--form token=$COVERITY_SCAN_TOKEN \
--form email=$COVERITY_SCAN_NOTIFICATION_EMAIL \
--form file=@libmdbx.tgz \
--form version=$GITHUB_SHA \
--form description="GithubActionCI $GITHUB_ACTION" \
https://scan.coverity.com/builds?project=$COVERITY_SCAN_PROJECT_NAME

View File

@ -1,29 +0,0 @@
name: doxygen-github-pages
on:
push:
branches: master
jobs:
build:
runs-on: ubuntu-20.04
steps:
- name: Set Actions Allow Unsecure Commands (temporary)
run: |
echo "ACTIONS_ALLOW_UNSECURE_COMMANDS=true" >> $GITHUB_ENV
- name: Checkout code
uses: actions/checkout@v2.3.1
with:
fetch-depth: 0
- name: Install doxygen
run: sudo apt install doxygen graphviz fonts-freefont-ttf
- name: Build html docs
run: make doxygen && cp -R .circleci docs/html/
- name: Deploy gh-pages
uses: JamesIves/github-pages-deploy-action@3.5.7
with:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
BRANCH: gh-pages
FOLDER: docs/html
CLEAN: true
SINGLE_COMMIT: true

View File

@ -1,58 +0,0 @@
# Based on the https://github.com/actions/upload-release-asset example
on:
push:
# Sequence of patterns matched against refs/tags
tags:
- 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10
name: Upload Release Asset
jobs:
build:
name: Upload Release Asset
runs-on: ubuntu-20.04
steps:
- name: Checkout code
uses: actions/checkout@v2
with:
fetch-depth: 0
- name: Build assets
run: |
make release-assets
- id: name
run: |
echo "::set-output name=tarball::$(ls *.tar.gz)"
echo "::set-output name=zip::$(ls *.zip)"
echo "::set-output name=suffix_unix::$(sed 's|^\([0-9]\{1,\}\.[0-9]\{1,\}\.[0-9]\{1,\}\)\.\(.*\)$|\1|' dist/VERSION.txt)"
echo "::set-output name=suffix_dos::$(sed 's|^\([0-9]\{1,\}\.[0-9]\{1,\}\.[0-9]\{1,\}\)\.\(.*\)$|\1|' dist/VERSION.txt | tr . _)"
- name: Create Release
id: create_release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ github.ref }}
release_name: Release ${{ github.ref }}
draft: true
prerelease: true
- name: Upload tarball
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ${{ steps.name.outputs.tarball }}
asset_name: libmdbx-amalgamated-${{ steps.name.outputs.suffix_unix }}.tar.gz
# asset_label: Amalgamated source tarball
asset_content_type: application/tar+gzip
- name: Upload zip
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ${{ steps.name.outputs.zip }}
asset_name: libmdbx-amalgamated-${{ steps.name.outputs.suffix_dos }}.zip
# asset_label: Amalgamated source zip-archive
asset_content_type: application/zip

View File

@ -1,13 +0,0 @@
name: Spell checking
on:
push:
pull_request:
jobs:
build:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 5
- uses: check-spelling/check-spelling@main

73
.gitignore vendored
View File

@ -1,46 +1,69 @@
*[~#]
@*
*.[ao]
*.autosave
*.bak
build.ninja
cmake-build-*
CMakeCache.txt
CMakeFiles/
cmake_install.cmake
CMakeLists.txt.user
core
CTestTestfile.cmake
DartConfiguration.tcl
dist/
*.dll
docs/Doxyfile
docs/html/
*.dSYM
*.dylib
*.err
*.exe
*.gcda
*.gcno
*.gcov
*.lo
*.orig
*.rej
*.so
*.dll
*.dylib
*.dSYM
*[~#]
.idea
.le.ini
.vs/
.vscode/
cmake-build-*
@*
core
mdbx_example
libmdbx.creator.user
CMakeLists.txt.user
*.lo
mdbx_chk
mdbx_copy
mdbx_drop
mdbx_dump
mdbx_example
mdbx_load
mdbx_stat
mdbx_test
.ninja_deps
.ninja_log
*.orig
*.rej
*.so
src/config.h
src/version.c
*.tar*
test/cmake_install.cmake
test/CTestTestfile.cmake
test_extra_crunched_delete
test_extra_cursor_closing
test_extra_dbi
test_extra_doubtless_positioning
test_extra_dupfix_addodd
test_extra_dupfix_multiple
test_extra_early_close_dbi
test_extra_hex_base64_base58
test_extra_maindb_ordinal
test_extra_open
test_extra_pcrf
test_extra_upsert_alldups
Testing/
test.log
test/tmp.db
test/tmp.db-lck
tmp.db
tmp.db-lck
valgrind.*
src/version.c
src/config.h
dist/
dist-check/
dist-checked.tag
*.tar*
docs/Doxyfile
docs/html/
buildflags.tag
version.c
.vs/
.vscode/
*.zip

40
.le.ini Normal file
View File

@ -0,0 +1,40 @@
tabsize=8
indentsize=8
autoindent=1
bsunindents=1
insert=1
inputmode=0
editmode=1
makebak=0
bakpath=
make=exec make
shell=exec $SHELL
run=exec make run
compile=exec make "$FNAME.o"
scroll=1
hscroll=32
rblock=0
savepos=1
savehst=1
noreg=1
match_case=1
linelen=72
leftmrg=0
flnmarg=0
leftadj=1
rightadj=0
helpcmd=exec /usr/share/le/help
usecolor=1
usetabs=1
scrollbar=0
statusline=0
backupext=.~%d~
backupnum=9
preferpagetop=1
wordwrap=0
syntaxhl=1
undo_enable=1
undo_min_levels=4
undo_max_memory=128
undo_glue=1
usemouse=0

View File

@ -1,89 +0,0 @@
language: c cpp
sudo: false
env:
global:
- secure: "M+W+heGGyRQJoBq2W0uqWVrpL4KBXmL0MFL7FSs7f9vmAaDyEgziUXeZRj3GOKzW4kTef3LpIeiu9SmvqSMoQivGGiomZShqPVl045o/OUgRCAT7Al1RLzEZ0efSHpIPf0PZ6byEf6GR2ML76OfuL6JxTVdnz8iVyO2sgLE1HbX1VeB+wgd/jfMeOBhCCXskfK6MLyZihfMYsiYZYSaV98ZDhDLSlzuuRIgzb0bMi8aL6AErs0WLW0NelRBeHkKPYfAUc85pdQHscgrJw6Rh/zT6+8BQ/q5f4IgWhiu4xoRg3Ngl7SNoedRQh93ADM3UG2iGl6HDFpVORaXcFWKAtuYY+kHQ0HB84BRYpQmeBuXNpltsfxQ3d1Q3u0RlE45zRvmr2+X1mFnkcNUAWISLPbsOUlriDQM8irGwRpho77/uYnRC00bJsHW//s6+uPf9zrAw1nI4f0y3PAWukGF/xs6HAI3FZPsuSSnx18Tj3Opgbc9Spop+V3hkhdiJoPGpNKTkFX4ZRXfkPgoRVJmtp4PpbpH0Ps/mCriKjMEfGGi0HcVCi0pEGLXiecdqJ5KPg5+22zNycEujQBJcNTKd9shN+R3glrbmhAxTEzGdGwxXXJ2ybwJ2PWJLMYZ7g98nLyX+uQPaA3BlsbYJHNeS5283/9pJsd9DzfHKsN2nFSc="
addons:
apt:
sources:
- ubuntu-toolchain-r-test
packages:
- cmake
- clang-format
update: true
matrix:
include:
- os: linux
dist: focal
compiler: gcc
env: CC=cc CXX=c++
- os: linux
dist: focal
compiler: clang
env: CC=clang CXX=clang++
- os: linux
dist: bionic
compiler: gcc
env: CC=cc CXX=c++
- os: linux
dist: bionic
compiler: clang
env: CC=clang CXX=clang++
- os: linux
dist: xenial
compiler: gcc
env: CC=cc CXX=c++
- os: linux
dist: xenial
compiler: clang
env: CC=clang CXX=clang++
- os: osx
osx_image: xcode11.3
env: CC=cc CXX=c++
- os: osx
osx_image: xcode9.4
env: CC=cc CXX=c++
before_script: |
if [ "${TRAVIS_BRANCH}" = "coverity_scan" ]; then
# call Coverity Scan manually of addons.coverity_scan for first job only
if [ "${TRAVIS_JOB_NUMBER}" = "${TRAVIS_BUILD_NUMBER}.1" ]; then
export COVERITY_SCAN_BRANCH=1
echo -n | openssl s_client -connect scan.coverity.com:443 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' | sudo tee -a /etc/ssl/certs/ca-
curl -s 'https://scan.coverity.com/scripts/travisci_build_coverity_scan.sh' -o coverity_scan.sh
else
echo 'echo "Skip CoverityScan for unrelated os/compiler"' > coverity_scan.sh
fi
fi
script: |
${CC} --version
${CXX} --version
git fetch --unshallow --tags --prune --force || exit 1
if [ ! -s ./coverity_scan.sh ]; then
make --keep-going all && MALLOC_CHECK_=7 MALLOC_PERTURB_=42 make --keep-going check
else
COVERITY_UNSUPPORTED_COMPILER_INVOCATION=1 \
COVERITY_SCAN_PROJECT_NAME="ReOpen/libmdbx" \
COVERITY_SCAN_NOTIFICATION_EMAIL="leo@yuriev.ru" \
COVERITY_SCAN_BUILD_COMMAND_PREPEND="" \
COVERITY_SCAN_BUILD_COMMAND="make MDBX_BUILD_OPTIONS=-DMDBX_DEBUG=2 CXXSTD=-std=gnu++17 build-test" \
COVERITY_SCAN_BRANCH_PATTERN="$TRAVIS_BRANCH" \
bash ./coverity_scan.sh || cat cov-int/scm_log.txt
fi
after_script: |
if [ "${TRAVIS_BRANCH}" != "coverity_scan" -a "${TRAVIS_JOB_NUMBER}" = "${TRAVIS_BUILD_NUMBER}.1" ] && make reformat && [[ -n $(git diff) ]]; then
echo "You must run 'make reformat' before submitting a pull request"
echo "-------------------------------------------------------------------------------"
git diff
sleep 1
echo "-------------------------------------------------------------------------------"
sleep 1
exit -1
fi
echo "-------------------------------------------------------------------------------"
sleep 1

34
AUTHORS
View File

@ -1,34 +0,0 @@
Contributors
============
- Alexey Naumov <alexey.naumov@gmail.com>
- Andrew Ashikhmin <andrey.ashikhmin@gmail.com>
- Chris Mikkelson <cmikk@qwest.net>
- Claude Brisson <claude.brisson@gmail.com>
- David Barbour <dmbarbour@gmail.com>
- David Wilson <dw@botanicus.net>
- dreamsxin <dreamsxin@126.com>
- Hallvard Furuseth <hallvard@openldap.org>, <h.b.furuseth@usit.uio.no>
- Heiko Becker <heirecka@exherbo.org>
- Howard Chu <hyc@openldap.org>, <hyc@symas.com>
- Ignacio Casal Quinteiro <ignacio.casal@nice-software.com>
- James Rouzier <rouzier@gmail.com>
- Jean-Christophe DUBOIS <jcd@tribudubois.net>
- John Hewson <john@jahewson.com>
- Klaus Malorny <klaus.malorny@knipp.de>
- Kurt Zeilenga <kurt.zeilenga@isode.com>
- Leonid Yuriev <leo@yuriev.ru>, <lyuryev@ptsecurity.ru>
- Lorenz Bauer <lmb@cloudflare.com>
- Luke Yeager <lyeager@nvidia.com>
- Martin Hedenfalk <martin@bzero.se>
- Ondrej Kuznik <ondrej.kuznik@acision.com>
- Orivej Desh <orivej@gmx.fr>
- Oskari Timperi <oskari.timperi@iki.fi>
- Pavel Medvedev <pmedvedev@gmail.com>
- 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>
- Zano Foundation <crypto.sowle@gmail.com>
- 장세연 <sasgas@castis.com>

File diff suppressed because it is too large Load Diff

View File

@ -1,26 +0,0 @@
{
"configurations": [
{
"name": "x64-Debug",
"generator": "Ninja",
"configurationType": "Debug",
"inheritEnvironments": [ "msvc_x64_x64" ],
"buildRoot": "${env.USERPROFILE}\\CMakeBuilds\\libmdbx\\build\\${name}",
"installRoot": "${env.USERPROFILE}\\CMakeBuilds\\libmdbx\\install\\${name}",
"cmakeCommandArgs": "",
"buildCommandArgs": "",
"ctestCommandArgs": ""
},
{
"name": "x64-Release",
"generator": "Ninja",
"configurationType": "Release",
"inheritEnvironments": [ "msvc_x64_x64" ],
"buildRoot": "${env.USERPROFILE}\\CMakeBuilds\\libmdbx\\build\\${name}",
"installRoot": "${env.USERPROFILE}\\CMakeBuilds\\libmdbx\\install\\${name}",
"cmakeCommandArgs": "",
"buildCommandArgs": "",
"ctestCommandArgs": ""
}
]
}

159
COPYRIGHT
View File

@ -1,7 +1,139 @@
Copyright 2015-2021 Leonid Yuriev <leo@yuriev.ru>.
Copyright 2011-2015 Howard Chu, Symas Corp.
Copyright 2015,2016 Peter-Service R&D LLC.
All rights reserved.
Copyright (c) 2015-2025 Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-----------------------------------------------------------------------
СМЕНА ЛИЦЕНЗИИ (THE LICENSE CHANGE)
OpenLDAP Public License → Apache 2.0
Briefly:
Historically, in 2015 an early MDBX source code was derived from the
"LMDB engine" created by Howard Chu <hyc@symas.com> in 2011-2015,
which based on btree.c written by Martin Hedenfalk <martin@bzero.se>.
By 2024, MDBX source code has actually been rewritten and has so
little in common with the original LMDB that I thought it admissible
to change the license. Below are more detailed explanations.
Кратко:
Исторически в 2015 году ранний исходный код MDBX был заимствован из
«LMDB engine», созданной Howard Chu <hyc@symas.com> в 2011-2015,
на основе btree.c, ранее созданного Martin Hedenfalk <martin@bzero.se>.
К 2024 году исходный код MDBX фактически переписан и имеет настолько
мало общего с первоначальным заимствованием из LMDB, что я счел
уместным сменить лицензию. Ниже более подробные пояснения.
---
Первоисточник текста формулирован на Русском языке, который является
родным для автора. Предполагается что все заинтересованные могут легко
воспользоваться машинным переводом, который при всех недостатках сможет
донести суть, намерения и местами даже передать тональность.
The original source of this text is in Russian, which is the author's
native language. It is assumed that all concerned can easily use machine
translation, which, with all the disadvantages, will be able to convey
the essence, intentions and, in some places, even convey the tonality of
a wording.
1. Причины
1.1. Лицензия Apache-2.0 является одной из самых популярных, так как
содержит ряд уточнений, проясняющих и упрощающих использование исходного
кода в производных работах и больших проектах. Эти особенности лицензии
Apache-2.0 я нахожу достаточно ценными и удобными. Соответственно,
переход на лицензию Apache-2.0 полезным в целом.
1.2. Проект OpenLDAP имеет определенную известность, в том числе, к
сожалению, среди специалистов славится кране плохим качеством кода и
сбоями при отходе от простых/базовых сценариев использования. Поэтому
использование лицензии OpenLDAP, в глазах части аудитории, бросает тень
на качества кода libmdbx, несмотря на то, что исходный код библиотеки
переписан, в том числе, с целью повышения качества, надежности,
стабильности и пригодности к тестированию.
Отмечу, что здесь не место для обсуждения объективности подобных мнений
и причин, равно как и не место для оценки компетентности специалистов
высказывающих такие суждения. Однако, здесь необходимо озвучить сам факт
наличия такой негативной коннотации качества кода при упоминании
OpenLDAP, совершенно без намерения как-либо задеть или обидеть
контрибьюторов OpenLDAP.
1.3. С точки зрения исходного кода, к настоящему времени libmdbx стала
совсем другим продуктом, о котором сейчас правильнее сказать что
разработка вдохновлена LMDB, нежели основывается на заимствовании кода.
Смена лицензии на переписанный код подчеркивает, что это действительно
новый исходный код.
2. Легитимность
2.1. Исходная лицензия OpenLDAP 2.8 и актуальная лицензия Apache 2.0
совпадают по базовым условиям. При этом лицензия Apache 2.0 уточняет,
определяет и проясняет многие аспекты. Поэтому смену лицензии я склонен
трактовать как уточнение, но НЕ как принципиальное изменение, которое
могло-бы нарушить чьи-либо права.
2.2. С процедурной точки зрения, у меня есть право сменить лицензию на
новый, написанный мной, исходный код. При этом объективно существует как
техническая, так и юридическая проблемы отделения «нового кода» от
«заимствованного», а также выделение/классификация кода, который
является общественным достоянием и/или общеупотребительным воплощением
«математических моделей и других публичных знаний».
Основываясь на собственной субъективной оценке кодовой базы, включая
соотношения «нового», «заимствованного» и «общеупотребительного»
исходного кода, я считаю что смена лицензии допустима. Одновременно с
этим, я понимаю и признаю, что можно найти повод, чтобы трактовать
ситуацию как «стакан наполовину полон/пуст». Поэтому декларирую
готовность принимать претензии и устранять их путем полного
переписывания оставшегося исходного кода, который попадает под критерии
«заимствованного» и кто-то из контрибьюторов которого будет против
изменения лицензии.
2.3. Вне зависимости от истории происхождения каждой строки исходного
кода и её буквального авторства, прошу не считать производимую смену
лицензии, и связанных с этим технических действий, как попытку плагиата,
присвоения чужого труда, присвоения авторства или принижения вклада
других авторов/контрибьторов. Безусловно проект MDBX/libmdbx не появился
бы без LMDB и всех участников проекта LMDB, в особенности Говарда Чу
(Howard Chu), Холлварда Фурусет (Hallvard Furuseth) и Мартина Хеденфок
(Martin Hedenfalk). Как-бы исходный код не переписывался он всё равно
будет основываться на базовых идеях и включать основные концепции LMDB.
3. Последствия и актуальные требования
Всё очень просто. Потребуется обеспечить требования новой лицензии в
соответствии с 4-м пунктом лицензции Apache 2.0.
В частности, при использовании/распространении libmdbx потребуется
обеспечить наличие файлов с текстом лицензии и файла NOTICE, а также
обеспечить пользователям возможность ознакомиться с их содержимым в
работах/продуктах использующих libmdbx.
-----------------------------------------------------------------------
Далее в справочных целях приведены уведомления об авторских правах из
первоначально заимствованного кода.
---
Original source code was derived from LMDB in 2015,
and later evolutionarily rewritten in 2015-2024:
Copyright (c) 2011-2015 Howard Chu, Symas Corp. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted only as authorized by the OpenLDAP
@ -11,12 +143,17 @@ A copy of this license is available in the file LICENSE in the
top-level directory of the distribution or, alternatively, at
<http://www.OpenLDAP.org/license.html>.
OpenLDAP is a registered trademark of the OpenLDAP Foundation.
LMDB itself devived code from btree.c written by Martin Hedenfalk:
Copyright (c) 2009, 2010 Martin Hedenfalk <martin@bzero.se>
Individual files and/or contributed packages may be copyright by
other parties and/or subject to additional restrictions.
Permission to use, copy, modify, and distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
This work also contains materials derived from public sources.
Additional information about OpenLDAP can be obtained at
<http://www.openldap.org/>.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

1067
ChangeLog-01.md Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

206
LICENSE
View File

@ -1,47 +1,177 @@
The OpenLDAP Public License
Version 2.8, 17 August 2003
Redistribution and use of this software and associated documentation
("Software"), with or without modification, are permitted provided
that the following conditions are met:
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
1. Redistributions in source form must retain copyright statements
and notices,
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
2. Redistributions in binary form must reproduce applicable copyright
statements and notices, this list of conditions, and the following
disclaimer in the documentation and/or other materials provided
with the distribution, and
1. Definitions.
3. Redistributions must contain a verbatim copy of this document.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
The OpenLDAP Foundation may revise this license from time to time.
Each revision is distinguished by a version number. You may use
this Software under terms of this license revision or under the
terms of any subsequent revision of the license.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
THIS SOFTWARE IS PROVIDED BY THE OPENLDAP FOUNDATION AND ITS
CONTRIBUTORS ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
SHALL THE OPENLDAP FOUNDATION, ITS CONTRIBUTORS, OR THE AUTHOR(S)
OR OWNER(S) OF THE SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
The names of the authors and copyright holders must not be used in
advertising or otherwise to promote the sale, use or other dealing
in this Software without specific, written prior permission. Title
to copyright in this Software shall at all times remain with copyright
holders.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
OpenLDAP is a registered trademark of the OpenLDAP Foundation.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
Copyright 1999-2003 The OpenLDAP Foundation, Redwood City,
California, USA. All Rights Reserved. Permission to copy and
distribute verbatim copies of this document is granted.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS

View File

@ -1,12 +1,15 @@
# This is thunk-Makefile for calling GNU Make 3.80 or above
all help options \
all help options cmake-build ninja \
clean install install-no-strip install-strip strip tools uninstall \
bench bench-clean bench-couple bench-quartet bench-triplet re-bench \
lib libmdbx mdbx mdbx_chk mdbx_copy mdbx_drop mdbx_dump mdbx_load mdbx_stat \
lib libs lib-static lib-shared tools-static \
libmdbx mdbx mdbx_chk mdbx_copy mdbx_drop mdbx_dump mdbx_load mdbx_stat \
check dist memcheck cross-gcc cross-qemu doxygen gcc-analyzer reformat \
release-assets tags test build-test mdbx_test smoke smoke-fault smoke-singleprocess \
test-asan test-leak test-singleprocess test-ubsan test-valgrind:
release-assets tags build-test mdbx_test \
smoke smoke-fault smoke-singleprocess smoke-assertion smoke-memcheck \
test test-assertion test-long test-long-assertion test-ci test-ci-extra \
test-asan test-leak test-singleprocess test-ubsan test-memcheck:
@CC=$(CC) \
CXX=`if test -n "$(CXX)" && which "$(CXX)" > /dev/null; then echo "$(CXX)"; elif test -n "$(CCC)" && which "$(CCC)" > /dev/null; then echo "$(CCC)"; else echo "c++"; fi` \
`which gmake || which gnumake || echo 'echo "GNU Make 3.80 or above is required"; exit 2;'` \

39
NOTICE Normal file
View File

@ -0,0 +1,39 @@
libmdbx (aka MDBX) is an extremely fast, compact, powerful, embeddedable,
transactional key-value storage engine with open-source code. MDBX has a
specific set of properties and capabilities, focused on creating unique
lightweight solutions.
Please visit https://libmdbx.dqdkfa.ru for more information, changelog,
documentation, C++ API description and links to the original git repo
with the source code. Questions, feedback and suggestions are welcome
to the Telegram' group https://t.me/libmdbx.
Donations are welcome to the Ethereum/ERC-20 `0xD104d8f8B2dC312aaD74899F83EBf3EEBDC1EA3A`.
Всё будет хорошо!
Copyright 2015-2025 Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru>
SPDX-License-Identifier: Apache-2.0
For notes about the license change, credits and acknowledgments,
please refer to the COPYRIGHT file within libmdbx source.
---
On 2022-04-15, without any warnings or following explanations, the
Github administration deleted _libmdbx_, my account and all other
projects (status 404). A few months later, without any involvement or
notification from/to me, the projects were restored/opened in the "public
read-only archive" status from some kind of incomplete backup. I regard
these actions of Github as malicious sabotage, and I consider the Github
service itself to have lost trust forever.
As a result of what has happened, I will never, under any circumstances,
post the primary sources (aka origins) of my projects on Github, or rely
in any way on the Github infrastructure.
Nevertheless, realizing that it is more convenient for users of
_libmdbx_ and other my projects to access ones on Github, I do not want
to restrict their freedom or create inconvenience, and therefore I place
mirrors (aka mirrors) of such repositories on Github since 2025. At the
same time, I would like to emphasize once again that these are only
mirrors that can be frozen, blocked or deleted at any time, as was the
case in 2022.

500
README.md
View File

@ -1,25 +1,18 @@
<!-- Required extensions: pymdownx.betterem, pymdownx.tilde, pymdownx.emoji, pymdownx.tasklist, pymdownx.superfences -->
> Please refer to the online [documentation](https://erthink.github.io/libmdbx/)
> with [`C` API description](https://erthink.github.io/libmdbx/group__c__api.html)
> and pay attention to the [`C++` API](https://github.com/erthink/libmdbx/blob/devel/mdbx.h%2B%2B).
>
> Questions, feedback and suggestions are welcome to the [Telegram' group](https://t.me/libmdbx).
>
> For NEWS take a look to the [ChangeLog](./ChangeLog.md).
libmdbx
========
<!-- section-begin overview -->
_libmdbx_ is an extremely fast, compact, powerful, embedded,
transactional [key-value database](https://en.wikipedia.org/wiki/Key-value_database),
with [permissive license](./LICENSE).
_libmdbx_ is an extremely fast, compact, powerful, embedded, transactional
[key-value database](https://en.wikipedia.org/wiki/Key-value_database),
with [Apache 2.0 license](https://gitflic.ru/project/erthink/libmdbx/blob?file=LICENSE).
_libmdbx_ has a specific set of properties and capabilities,
focused on creating unique lightweight solutions.
1. Allows **a swarm of multi-threaded processes to
[ACID]((https://en.wikipedia.org/wiki/ACID))ly read and update** several
[ACID](https://en.wikipedia.org/wiki/ACID)ly read and update** several
key-value [maps](https://en.wikipedia.org/wiki/Associative_array) and
[multimaps](https://en.wikipedia.org/wiki/Multimap) in a locally-shared
database.
@ -33,52 +26,148 @@ tree](https://en.wikipedia.org/wiki/B%2B_tree).
[WAL](https://en.wikipedia.org/wiki/Write-ahead_logging), but that might
be a caveat for write-intensive workloads with durability requirements.
4. **Compact and friendly for fully embedding**. Only ≈25KLOC of `C11`,
≈64K x86 binary code of core, no internal threads neither server process(es),
but implements a simplified variant of the [Berkeley
DB](https://en.wikipedia.org/wiki/Berkeley_DB) and
[dbm](https://en.wikipedia.org/wiki/DBM_(computing)) API.
5. Enforces [serializability](https://en.wikipedia.org/wiki/Serializability) for
4. Enforces [serializability](https://en.wikipedia.org/wiki/Serializability) for
writers just by single
[mutex](https://en.wikipedia.org/wiki/Mutual_exclusion) and affords
[wait-free](https://en.wikipedia.org/wiki/Non-blocking_algorithm#Wait-freedom)
for parallel readers without atomic/interlocked operations, while
**writing and reading transactions do not block each other**.
6. **Guarantee data integrity** after crash unless this was explicitly
5. **Guarantee data integrity** after crash unless this was explicitly
neglected in favour of write performance.
7. Supports Linux, Windows, MacOS, Android, iOS, FreeBSD, DragonFly, Solaris,
6. Supports Linux, Windows, MacOS, Android, iOS, FreeBSD, DragonFly, Solaris,
OpenSolaris, OpenIndiana, NetBSD, OpenBSD and other systems compliant with
**POSIX.1-2008**.
7. **Compact and friendly for fully embedding**. Only ≈25KLOC of `C11`,
≈64K x86 binary code of core, no internal threads neither server process(es),
but implements a simplified variant of the [Berkeley
DB](https://en.wikipedia.org/wiki/Berkeley_DB) and
[dbm](https://en.wikipedia.org/wiki/DBM_(computing)) API.
<!-- section-end -->
Historically, _libmdbx_ is a deeply revised and extended descendant of the amazing
Historically, _libmdbx_ is a deeply revised and extended descendant of the legendary
[Lightning Memory-Mapped Database](https://en.wikipedia.org/wiki/Lightning_Memory-Mapped_Database).
_libmdbx_ inherits all benefits from _LMDB_, but resolves some issues and adds [a set of improvements](#improvements-beyond-lmdb).
[![Telergam: Support | Discussions | News](https://img.shields.io/endpoint?color=scarlet&logo=telegram&label=Support%20%7C%20Discussions%20%7C%20News&url=https%3A%2F%2Ftg.sumanjay.workers.dev%2Flibmdbx)](https://t.me/libmdbx)
> Please refer to the online [documentation](https://libmdbx.dqdkfa.ru)
> with [`C` API description](https://libmdbx.dqdkfa.ru/group__c__api.html)
> and pay attention to the [`C++` API](https://gitflic.ru/project/erthink/libmdbx/blob?file=mdbx.h%2B%2B#line-num-1).
> Donations are welcome to the Ethereum/ERC-20 `0xD104d8f8B2dC312aaD74899F83EBf3EEBDC1EA3A`.
> Всё будет хорошо!
Telegram Group archive: [1](https://libmdbx.dqdkfa.ru/tg-archive/messages1.html),
[2](https://libmdbx.dqdkfa.ru/tg-archive/messages2.html), [3](https://libmdbx.dqdkfa.ru/tg-archive/messages3.html), [4](https://libmdbx.dqdkfa.ru/tg-archive/messages4.html),
[5](https://libmdbx.dqdkfa.ru/tg-archive/messages5.html), [6](https://libmdbx.dqdkfa.ru/tg-archive/messages6.html), [7](https://libmdbx.dqdkfa.ru/tg-archive/messages7.html).
## Github
### на Русском (мой родной язык)
Весной 2022, без каких-либо предупреждений или пояснений, администрация
Github удалила мой аккаунт и все проекты. Через несколько месяцев, без
какого-либо моего участия или уведомления, проекты были
восстановлены/открыты в статусе "public read-only archive" из какой-то
неполноценной резервной копии. Эти действия Github я расцениваю как
злонамеренный саботаж, а сам сервис Github считаю навсегда утратившим
какое-либо доверие.
Вследствие произошедшего, никогда и ни при каких условиях, я не буду
размещать на Github первоисточники (aka origins) моих проектов, либо
как-либо полагаться на инфраструктуру Github.
Тем не менее, понимая что пользователям моих проектов удобнее получать к
ним доступ именно на Github, я не хочу ограничивать их свободу или
создавать неудобство, и поэтому размещаю на Github зеркала (aka mirrors)
репозиториев моих проектов. При этом ещё раз акцентирую внимание, что
это только зеркала, которые могут быть заморожены, заблокированы или
удалены в любой момент, как это уже было в 2022.
### in English
In the spring of 2022, without any warnings or explanations, the Github
administration deleted my account and all projects. A few months later,
without any involvement or notification from me, the projects were
restored/opened in the "public read-only archive" status from some kind
of incomplete backup. I regard these actions of Github as malicious
sabotage, and I consider the Github service itself to have lost any
trust forever.
As a result of what has happened, I will never, under any circumstances,
post the primary sources (aka origins) of my projects on Github, or rely
in any way on the Github infrastructure.
Nevertheless, realizing that it is more convenient for users of my
projects to access them on Github, I do not want to restrict their
freedom or create inconvenience, and therefore I place mirrors of my
project repositories on Github. At the same time, I would like to
emphasize once again that these are only mirrors that can be frozen,
blocked or deleted at any time, as was the case in 2022.
## MithrilDB and Future
<!-- section-begin mithril -->
The next version is under active non-public development from scratch and will be
released as _**MithrilDB**_ and `libmithrildb` for libraries & packages.
The next version is under non-public development from scratch and will be
released as **MithrilDB** and `libmithrildb` for libraries & packages.
Admittedly mythical [Mithril](https://en.wikipedia.org/wiki/Mithril) is
resembling silver but being stronger and lighter than steel. Therefore
_MithrilDB_ is a rightly relevant name.
> _MithrilDB_ will be radically different from _libmdbx_ by the new
> database format and API based on C++17, as well as the [Apache 2.0
> License](https://www.apache.org/licenses/LICENSE-2.0). The goal of this
> revolution is to provide a clearer and robust API, add more features and
> new valuable properties of the database.
_MithrilDB_ is radically different from _libmdbx_ by the new database
format and API based on C++20. The goal of this revolution is to provide
a clearer and robust API, add more features and new valuable properties
of the database. All fundamental architectural problems of libmdbx/LMDB
have been solved there, but now the active development has been
suspended for top-three reasons:
1. For now _libmdbx_ mostly enough and Im busy for scalability.
2. Waiting for fresh [Elbrus CPU](https://wiki.elbrus.ru/) of [e2k architecture](https://en.wikipedia.org/wiki/Elbrus_2000),
especially with hardware acceleration of [Streebog](https://en.wikipedia.org/wiki/Streebog) and
[Kuznyechik](https://en.wikipedia.org/wiki/Kuznyechik), which are required for Merkle tree, etc.
3. The expectation of needs and opportunities due to the wide use of NVDIMM (aka persistent memory),
modern NVMe and [Ангара](https://ru.wikipedia.org/wiki/Ангара_(интерконнект)).
However, _MithrilDB_ will not be available for countries unfriendly to
Russia (i.e. acceded the sanctions, devil adepts and/or NATO). But it is
not yet known whether such restriction will be implemented only through
a license and support, either the source code will not be open at all.
Basically I am not inclined to allow my work to contribute to the
profit that goes to weapons that kill my relatives and friends.
NO OPTIONS.
Nonetheless, I try not to make any promises regarding _MithrilDB_ until release.
Contrary to _MithrilDB_, _libmdbx_ will forever free and open source.
Moreover with high-quality support whenever possible. Tu deviens
responsable pour toujours de ce que tu as apprivois. So I will continue
to comply with the original open license and the principles of
constructive cooperation, in spite of outright Github sabotage and
sanctions. I will also try to keep (not drop) Windows support, despite
it is an unused obsolete technology for us.
<!-- section-end -->
[![https://t.me/libmdbx](https://raw.githubusercontent.com/wiki/erthink/libmdbx/img/telegram.png)](https://t.me/libmdbx)
[![GithubCI](https://github.com/erthink/libmdbx/workflows/CI/badge.svg)](https://github.com/erthink/libmdbx/actions?query=workflow%3ACI)
[![AppveyorCI](https://ci.appveyor.com/api/projects/status/ue94mlopn50dqiqg/branch/master?svg=true)](https://ci.appveyor.com/project/leo-yuriev/libmdbx/branch/master)
[![CircleCI](https://circleci.com/gh/erthink/libmdbx/tree/master.svg?style=svg)](https://circleci.com/gh/erthink/libmdbx/tree/master)
[![CirrusCI](https://api.cirrus-ci.com/github/erthink/libmdbx.svg)](https://cirrus-ci.com/github/erthink/libmdbx)
[![Coverity Scan Status](https://scan.coverity.com/projects/12915/badge.svg)](https://scan.coverity.com/projects/reopen-libmdbx)
```
$ objdump -f -h -j .text libmdbx.so
*The Future will (be) [Positive](https://www.ptsecurity.com). Всё будет хорошо.*
libmdbx.so: формат файла elf64-e2k
архитектура: elbrus-v6:64, флаги 0x00000150:
HAS_SYMS, DYNAMIC, D_PAGED
начальный адрес 0x00000000??????00
Разделы:
Idx Name Разм VMA LMA Фа смещ. Выр. Флаги
10 .text 000e7460 0000000000025c00 0000000000025c00 00025c00 2**10 CONTENTS, ALLOC, LOAD, READONLY, CODE
$ cc --version
lcc:1.27.14:Jan-31-2024:e2k-v6-linux
gcc (GCC) 9.3.0 compatible
```
-----
@ -114,7 +203,7 @@ _MithrilDB_ is a rightly relevant name.
[MVCC](https://en.wikipedia.org/wiki/Multiversion_concurrency_control)
and [CoW](https://en.wikipedia.org/wiki/Copy-on-write).
- Multiple key-value sub-databases within a single datafile.
- Multiple key-value tables/sub-databases within a single datafile.
- Range lookups, including range query estimation.
@ -146,8 +235,7 @@ and [CoW](https://en.wikipedia.org/wiki/Copy-on-write).
- Append operation for efficient bulk insertion of pre-sorted data.
- No [WAL](https://en.wikipedia.org/wiki/Write-ahead_logging) nor any
transaction journal. No crash recovery needed. No maintenance is required.
- No [WAL](https://en.wikipedia.org/wiki/Write-ahead_logging) nor any transaction journal. No crash recovery needed. No maintenance is required.
- No internal cache and/or memory management, all done by basic OS services.
@ -158,7 +246,7 @@ transaction journal. No crash recovery needed. No maintenance is required.
- **Value size**: minimum `0`, maximum `2146435072` (`0x7FF00000`) bytes for maps, ≈½ pagesize for multimaps (`2022` bytes for default 4K pagesize, `32742` bytes for 64K pagesize).
- **Write transaction size**: up to `1327217884` pages (`4.944272` TiB for default 4K pagesize, `79.108351` TiB for 64K pagesize).
- **Database size**: up to `2147483648` pages (≈`8.0` TiB for default 4K pagesize, ≈`128.0` TiB for 64K pagesize).
- **Maximum sub-databases**: `32765`.
- **Maximum tables/sub-databases**: `32765`.
## Gotchas
@ -174,7 +262,8 @@ Thus syncing data to disk might be a bottleneck for write intensive workload.
but read transactions prevents recycling an old retired/freed pages, since it read ones. Thus altering of data during a parallel
long-lived read operation will increase the process work set, may exhaust entire free database space,
the database can grow quickly, and result in performance degradation.
Try to avoid long running read transactions.
Try to avoid long running read transactions, otherwise use [transaction parking](https://libmdbx.dqdkfa.ru/group__c__transactions.html#ga2c2c97730ff35cadcedfbd891ac9b12f)
and/or [Handle-Slow-Readers callback](https://libmdbx.dqdkfa.ru/group__c__err.html#ga2cb11b56414c282fe06dd942ae6cade6).
5. _libmdbx_ is extraordinarily fast and provides minimal overhead for data access,
so you should reconsider using brute force techniques and double check your code.
@ -184,9 +273,15 @@ On the other hand, if you make something suboptimally, you can notice detrimenta
## Comparison with other databases
For now please refer to [chapter of "BoltDB comparison with other
databases"](https://github.com/coreos/bbolt#comparison-with-other-databases)
which is also (mostly) applicable to _libmdbx_.
which is also (mostly) applicable to _libmdbx_ with minor clarification:
- a database could shared by multiple processes, i.e. no multi-process issues;
- no issues with moving a cursor(s) after the deletion;
- _libmdbx_ provides zero-overhead database compactification, so a database file could be shrinked/truncated in particular cases;
- excluding disk I/O time _libmdbx_ could be ≈3 times faster than BoltDB and up to 10-100K times faster than both BoltDB and LMDB in particular extreme cases;
- _libmdbx_ provides more features compared to BoltDB and/or LMDB.
<!-- section-end -->
<!-- section-begin improvements -->
Improvements beyond LMDB
@ -199,83 +294,84 @@ out-of-the-box, not silently and catastrophically break down. The list
below is pruned down to the improvements most notable and obvious from
the user's point of view.
## Added Features
## Some Added Features
1. Keys could be more than 2 times longer than _LMDB_.
> For DB with default page size _libmdbx_ support keys up to 2022 bytes
> and up to 32742 bytes for 64K page size. _LMDB_ allows key size up to
> 511 bytes and may silently loses data with large values.
> For DB with default page size _libmdbx_ support keys up to 2022 bytes
> and up to 32742 bytes for 64K page size. _LMDB_ allows key size up to
> 511 bytes and may silently loses data with large values.
2. Up to 30% faster than _LMDB_ in [CRUD](https://en.wikipedia.org/wiki/Create,_read,_update_and_delete) benchmarks.
> Benchmarks of the in-[tmpfs](https://en.wikipedia.org/wiki/Tmpfs) scenarios,
> that tests the speed of the engine itself, showned that _libmdbx_ 10-20% faster than _LMDB_,
> and up to 30% faster when _libmdbx_ compiled with specific build options
> which downgrades several runtime checks to be match with LMDB behaviour.
>
> These and other results could be easily reproduced with [ioArena](https://github.com/pmwkaa/ioarena) just by `make bench-quartet` command,
> including comparisons with [RockDB](https://en.wikipedia.org/wiki/RocksDB)
> and [WiredTiger](https://en.wikipedia.org/wiki/WiredTiger).
> Benchmarks of the in-[tmpfs](https://en.wikipedia.org/wiki/Tmpfs) scenarios,
> that tests the speed of the engine itself, showned that _libmdbx_ 10-20% faster than _LMDB_,
> and up to 30% faster when _libmdbx_ compiled with specific build options
> which downgrades several runtime checks to be match with LMDB behaviour.
>
> However, libmdbx may be slower than LMDB on Windows, since uses native file locking API.
> These locks are really slow, but they prevent an inconsistent backup from being obtained by copying the DB file during an ongoing write transaction.
> So I think this is the right decision, and for speed, it's better to use Linux, or ask Microsoft to fix up file locks.
>
> Noted above and other results could be easily reproduced with [ioArena](https://abf.io/erthink/ioarena) just by `make bench-quartet` command,
> including comparisons with [RockDB](https://en.wikipedia.org/wiki/RocksDB)
> and [WiredTiger](https://en.wikipedia.org/wiki/WiredTiger).
3. Automatic on-the-fly database size adjustment, both increment and reduction.
> _libmdbx_ manages the database size according to parameters specified
> by `mdbx_env_set_geometry()` function,
> ones include the growth step and the truncation threshold.
>
> Unfortunately, on-the-fly database size adjustment doesn't work under [Wine](https://en.wikipedia.org/wiki/Wine_(software))
> due to its internal limitations and unimplemented functions, i.e. the `MDBX_UNABLE_EXTEND_MAPSIZE` error will be returned.
> _libmdbx_ manages the database size according to parameters specified
> by `mdbx_env_set_geometry()` function,
> ones include the growth step and the truncation threshold.
>
> Unfortunately, on-the-fly database size adjustment doesn't work under [Wine](https://en.wikipedia.org/wiki/Wine_(software))
> due to its internal limitations and unimplemented functions, i.e. the `MDBX_UNABLE_EXTEND_MAPSIZE` error will be returned.
4. Automatic continuous zero-overhead database compactification.
> During each commit _libmdbx_ merges a freeing pages which adjacent with the unallocated area
> at the end of file, and then truncates unused space when a lot enough of.
> During each commit _libmdbx_ merges a freeing pages which adjacent with the unallocated area
> at the end of file, and then truncates unused space when a lot enough of.
5. The same database format for 32- and 64-bit builds.
> _libmdbx_ database format depends only on the [endianness](https://en.wikipedia.org/wiki/Endianness) but not on the [bitness](https://en.wiktionary.org/wiki/bitness).
> _libmdbx_ database format depends only on the [endianness](https://en.wikipedia.org/wiki/Endianness) but not on the [bitness](https://en.wiktionary.org/wiki/bitness).
6. LIFO policy for Garbage Collection recycling. This can significantly increase write performance due write-back disk cache up to several times in a best case scenario.
> LIFO means that for reuse will be taken the latest becomes unused pages.
> Therefore the loop of database pages circulation becomes as short as possible.
> In other words, the set of pages, that are (over)written in memory and on disk during a series of write transactions, will be as small as possible.
> Thus creates ideal conditions for the battery-backed or flash-backed disk cache efficiency.
6. The "Big Foot" feature than solves speific performance issues with huge transactions and extra-large page-number-lists.
7. Fast estimation of range query result volume, i.e. how many items can
7. LIFO policy for Garbage Collection recycling. This can significantly increase write performance due write-back disk cache up to several times in a best case scenario.
> LIFO means that for reuse will be taken the latest becomes unused pages.
> Therefore the loop of database pages circulation becomes as short as possible.
> In other words, the set of pages, that are (over)written in memory and on disk during a series of write transactions, will be as small as possible.
> Thus creates ideal conditions for the battery-backed or flash-backed disk cache efficiency.
8. Parking of read transactions with ousting and auto-restart, [Handle-Slow-Readers callback](https://libmdbx.dqdkfa.ru/group__c__err.html#ga2cb11b56414c282fe06dd942ae6cade6) to resolve an issues due to long-lived read transactions.
9. Fast estimation of range query result volume, i.e. how many items can
be found between a `KEY1` and a `KEY2`. This is a prerequisite for build
and/or optimize query execution plans.
> _libmdbx_ performs a rough estimate based on common B-tree pages of the paths from root to corresponding keys.
> _libmdbx_ performs a rough estimate based on common B-tree pages of the paths from root to corresponding keys.
8. `mdbx_chk` utility for database integrity check.
Since version 0.9.1, the utility supports checking the database using any of the three meta pages and the ability to switch to it.
10. Database integrity check API both with standalone `mdbx_chk` utility.
9. Support for opening databases in the exclusive mode, including on a network share.
11. Support for opening databases in the exclusive mode, including on a network share.
10. Zero-length for keys and values.
12. Extended information of whole-database, tables/sub-databases, transactions, readers enumeration.
> _libmdbx_ provides a lot of information, including dirty and leftover pages
> for a write transaction, reading lag and holdover space for read transactions.
11. Ability to determine whether the particular data is on a dirty page
or not, that allows to avoid copy-out before updates.
12. Extended information of whole-database, sub-databases, transactions, readers enumeration.
> _libmdbx_ provides a lot of information, including dirty and leftover pages
> for a write transaction, reading lag and holdover space for read transactions.
13. Extended update and delete operations.
> _libmdbx_ allows one _at once_ with getting previous value
> and addressing the particular item from multi-value with the same key.
13. Support of Zero-length for keys and values.
14. Useful runtime options for tuning engine to application's requirements and use cases specific.
15. Automated steady sync-to-disk upon several thresholds and/or timeout via cheap polling.
16. Sequence generation and three persistent 64-bit markers.
16. Ability to determine whether the particular data is on a dirty page
or not, that allows to avoid copy-out before updates.
17. Handle-Slow-Readers callback to resolve a database full/overflow issues due to long-lived read transaction(s).
18. Ability to determine whether the cursor is pointed to a key-value
pair, to the first, to the last, or not set to anything.
17. Extended update and delete operations.
> _libmdbx_ allows one _at once_ with getting previous value
> and addressing the particular item from multi-value with the same key.
18. Sequence generation and three persistent 64-bit vector-clock like markers.
## Other fixes and specifics
1. Fixed more than 10 significant errors, in particular: page leaks,
wrong sub-database statistics, segfault in several conditions,
wrong table/sub-database statistics, segfault in several conditions,
nonoptimal page merge strategy, updating an existing record with
a change in data size (including for multimap), etc.
@ -288,9 +384,9 @@ pre-opening is not needed.
4. Returning `MDBX_EMULTIVAL` error in case of ambiguous update or delete.
5. Guarantee of database integrity even in asynchronous unordered write-to-disk mode.
> _libmdbx_ propose additional trade-off by `MDBX_SAFE_NOSYNC` with append-like manner for updates,
> that avoids database corruption after a system crash contrary to LMDB.
> Nevertheless, the `MDBX_UTTERLY_NOSYNC` mode is available to match behaviour of the `MDB_NOSYNC` in LMDB.
> _libmdbx_ propose additional trade-off by `MDBX_SAFE_NOSYNC` with append-like manner for updates,
> that avoids database corruption after a system crash contrary to LMDB.
> Nevertheless, the `MDBX_UTTERLY_NOSYNC` mode is available to match LMDB's behaviour for `MDB_NOSYNC`.
6. On **MacOS & iOS** the `fcntl(F_FULLFSYNC)` syscall is used _by
default_ to synchronize data with the disk, as this is [the only way to
@ -317,20 +413,35 @@ named mutexes are used.
Historically, _libmdbx_ is a deeply revised and extended descendant of the
[Lightning Memory-Mapped Database](https://en.wikipedia.org/wiki/Lightning_Memory-Mapped_Database).
At first the development was carried out within the
[ReOpenLDAP](https://github.com/erthink/ReOpenLDAP) project. About a
[ReOpenLDAP](https://web.archive.org/web/https://github.com/erthink/ReOpenLDAP) project. About a
year later _libmdbx_ was separated into a standalone project, which was
[presented at Highload++ 2015
conference](http://www.highload.ru/2015/abstracts/1831.html).
Since 2017 _libmdbx_ is used in [Fast Positive Tables](https://github.com/erthink/libfpta),
and development is funded by [Positive Technologies](https://www.ptsecurity.com).
Since 2017 _libmdbx_ is used in [Fast Positive Tables](https://gitflic.ru/project/erthink/libfpta),
and until 2025 development was funded by [Positive Technologies](https://www.ptsecurity.com).
Since 2020 _libmdbx_ is used in Ethereum: [Erigon](https://github.com/erigontech/erigon), [Akula](https://github.com/akula-bft/akula),
[Silkworm](https://github.com/erigontech/silkworm), [Reth](https://github.com/paradigmxyz/reth), etc.
On 2022-04-15 the Github administration, without any warning nor
explanation, deleted _libmdbx_ along with a lot of other projects,
simultaneously blocking access for many developers. Therefore on
2022-04-21 I have migrated to a reliable trusted infrastructure.
The origin for now is at [GitFlic](https://gitflic.ru/project/erthink/libmdbx)
with backup at [ABF by ROSA Лаб](https://abf.rosalinux.ru/erthink/libmdbx).
For the same reason ~~Github~~ is blacklisted forever.
Since May 2024 and version 0.13 _libmdbx_ was re-licensed under Apache-2.0 license.
Please refer to the [`COPYRIGHT` file](https://gitflic.ru/project/erthink/libmdbx/blob/raw?file=COPYRIGHT) for license change explanations.
## Acknowledgments
Howard Chu <hyc@openldap.org> is the author of LMDB, from which
originated the _libmdbx_ in 2015.
Howard Chu <hyc@openldap.org> and Hallvard Furuseth
<hallvard@openldap.org> are the authors of _LMDB_, from which _libmdbx_
was forked in 2015.
Martin Hedenfalk <martin@bzero.se> is the author of `btree.c` code, which
was used to begin development of LMDB.
was used to begin development of _LMDB_.
<!-- section-end -->
@ -340,47 +451,51 @@ Usage
=====
<!-- section-begin usage -->
Currently, libmdbx is only available in a
[source code](https://en.wikipedia.org/wiki/Source_code) form.
Packages support for common Linux distributions is planned in the future,
since release the version 1.0.
## Never use tarballs nor zips automatically provided by Github !
Please don't use tarballs nor zips which are automatically provided by Github.
These archives do not contain version information and thus are unfit to build _libmdbx_.
Instead of ones just clone the git repository, either download a tarball or zip with the properly amalgamated source core.
Moreover, please vote for [ability of disabling auto-creation such unsuitable archives](https://github.community/t/disable-tarball).
## Source code embedding
_libmdbx_ provides two official ways for integration in source code form:
_libmdbx_ provides three official ways for integration in source code form:
1. Using the amalgamated source code.
> The amalgamated source code includes all files required to build and
> use _libmdbx_, but not for testing _libmdbx_ itself.
1. Using an amalgamated source code which available in the [releases section](https://gitflic.ru/project/erthink/libmdbx/release) on GitFlic.
> An amalgamated source code includes all files required to build and
> use _libmdbx_, but not for testing _libmdbx_ itself.
> Beside the releases an amalgamated sources could be created any time from the original clone of git
> repository on Linux by executing `make dist`. As a result, the desired
> set of files will be formed in the `dist` subdirectory.
2. Adding the complete original source code as a `git submodule`.
> This allows you to build as _libmdbx_ and testing tool.
> On the other hand, this way requires you to pull git tags, and use C++11 compiler for test tool.
2. Using [Conan Package Manager](https://conan.io/):
- optional: Setup your own conan-server;
- Create conan-package by `conan create .` inside the _libmdbx_' repo subdirectory;
- optional: Upload created recipe and/or package to the conan-server by `conan upload -r SERVER 'mdbx/*'`;
- Consume libmdbx-package from the local conan-cache or from conan-server in accordance with the [Conan tutorial](https://docs.conan.io/2/tutorial/consuming_packages.html).
3. Adding the complete source code as a `git submodule` from the [origin git repository](https://gitflic.ru/project/erthink/libmdbx) on GitFlic.
> This allows you to build as _libmdbx_ and testing tool.
> On the other hand, this way requires you to pull git tags, and use C++11 compiler for test tool.
_**Please, avoid using any other techniques.**_ Otherwise, at least
don't ask for support and don't name such chimeras `libmdbx`.
The amalgamated source code could be created from the original clone of git
repository on Linux by executing `make dist`. As a result, the desired
set of files will be formed in the `dist` subdirectory.
## Building and Testing
Both amalgamated and original source code provides build through the use
[CMake](https://cmake.org/) or [GNU
Make](https://www.gnu.org/software/make/) with
[bash](https://en.wikipedia.org/wiki/Bash_(Unix_shell)). All build ways
[bash](https://en.wikipedia.org/wiki/Bash_(Unix_shell)).
All build ways
are completely traditional and have minimal prerequirements like
`build-essential`, i.e. the non-obsolete C/C++ compiler and a
[SDK](https://en.wikipedia.org/wiki/Software_development_kit) for the
target platform. Obviously you need building tools itself, i.e. `git`,
target platform.
Obviously you need building tools itself, i.e. `git`,
`cmake` or GNU `make` with `bash`. For your convenience, `make help`
and `make options` are also available for listing existing targets
and build options respectively.
@ -388,8 +503,7 @@ and build options respectively.
The only significant specificity is that git' tags are required
to build from complete (not amalgamated) source codes.
Executing **`git fetch --tags --force --prune`** is enough to get ones,
or `git fetch --unshallow --tags --prune --force` after the Github's
[`actions/checkout@v2`](https://github.com/actions/checkout) either set **`fetch-depth: 0`** for it.
and `--unshallow` or `--update-shallow` is required for shallow cloned case.
So just using CMake or GNU Make in your habitual manner and feel free to
fill an issue or make pull request in the case something will be
@ -397,10 +511,10 @@ unexpected or broken down.
### Testing
The amalgamated source code does not contain any tests for or several reasons.
Please read [the explanation](https://github.com/erthink/libmdbx/issues/214#issuecomment-870717981) and don't ask to alter this.
Please read [the explanation](https://libmdbx.dqdkfa.ru/dead-github/issues/214#issuecomment-870717981) and don't ask to alter this.
So for testing _libmdbx_ itself you need a full source code, i.e. the clone of a git repository, there is no option.
The full source code of _libmdbx_ has a [`test` subdirectory](https://github.com/erthink/libmdbx/tree/master/test) with minimalistic test "framework".
The full source code of _libmdbx_ has a [`test` subdirectory](https://gitflic.ru/project/erthink/libmdbx/tree/master/test) with minimalistic test "framework".
Actually yonder is a source code of the `mdbx_test` console utility which has a set of command-line options that allow construct and run a reasonable enough test scenarios.
This test utility is intended for _libmdbx_'s developers for testing library itself, but not for use by users.
Therefore, only basic information is provided:
@ -410,8 +524,8 @@ Therefore, only basic information is provided:
This is the `basic` test scenario.
- The `Makefile` provide several self-described targets for testing: `smoke`, `test`, `check`, `memcheck`, `test-valgrind`,
`test-asan`, `test-leak`, `test-ubsan`, `cross-gcc`, `cross-qemu`, `gcc-analyzer`, `smoke-fault`, `smoke-singleprocess`,
`test-singleprocess`, 'long-test'. Please run `make --help` if doubt.
- In addition to the `mdbx_test` utility, there is the script [`long_stochastic.sh`](https://github.com/erthink/libmdbx/blob/master/test/long_stochastic.sh),
`test-singleprocess`, `long-test`. Please run `make --help` if doubt.
- In addition to the `mdbx_test` utility, there is the script [`stochastic.sh`](https://gitflic.ru/project/erthink/libmdbx/blob/master/test/stochastic.sh),
which calls `mdbx_test` by going through set of modes and options, with gradually increasing the number of operations and the size of transactions.
This script is used for mostly of all automatic testing, including `Makefile` targets and Continuous Integration.
- Brief information of available command-line options is available by `--help`.
@ -425,6 +539,7 @@ Anyway, no matter how thoroughly the _libmdbx_ is tested, you should rely only o
on the other hand it will allow to reproduce the problem and insure against regression in a future.
3. Actually you should rely on than you checked by yourself or take a risk.
### Common important details
#### Build reproducibility
@ -439,14 +554,16 @@ Of course, in addition to this, your toolchain must ensure the reproducibility o
For more information please refer to [reproducible-builds.org](https://reproducible-builds.org/).
#### Containers
There are no special traits nor quirks if you use libmdbx ONLY inside the single container.
But in a cross-container cases or with a host-container(s) mix the two major things MUST be
guaranteed:
There are no special traits nor quirks if you use _libmdbx_ ONLY inside
the single container. But in a cross-container(s) or with a host-container(s)
interoperability cases the three major things MUST be guaranteed:
1. Coherence of memory mapping content and unified page cache inside OS kernel for host and all container(s) operated with a some DB.
Basically this means must be only a single physical copy of each memory mapped DB' page in the system memory.
1. Coherence of memory mapping content and unified page cache inside OS
kernel for host and all container(s) operated with a DB. Basically this
means must be only a single physical copy of each memory mapped DB' page
in the system memory.
2. Uniqueness of PID values and/or a common space for ones:
2. Uniqueness of [PID](https://en.wikipedia.org/wiki/Process_identifier) values and/or a common space for ones:
- for POSIX systems: PID uniqueness for all processes operated with a DB.
I.e. the `--pid=host` is required for run DB-aware processes inside Docker,
either without host interaction a `--pid=container:<name|id>` with the same name/id.
@ -455,6 +572,12 @@ Basically this means must be only a single physical copy of each memory mapped D
including `ERROR_ACCESS_DENIED`,
but not the `ERROR_INVALID_PARAMETER` as for an invalid/non-existent PID.
3. The versions/builds of _libmdbx_ and `libc`/`pthreads` (`glibc`, `musl`, etc) must be be compatible.
- Basically, the `options:` string in the output of `mdbx_chk -V` must be the same for host and container(s).
See `MDBX_LOCKING`, `MDBX_USE_OFDLOCKS` and other build options for details.
- Avoid using different versions of `libc`, especially mixing different implementations, i.e. `glibc` with `musl`, etc.
Prefer to use the same LTS version, or switch to full virtualization/isolation if in doubt.
#### DSO/DLL unloading and destructors of Thread-Local-Storage objects
When building _libmdbx_ as a shared library or use static _libmdbx_ as a
part of another dynamic library, it is advisable to make sure that your
@ -490,11 +613,10 @@ lot of errors from make when trying to build. In this case, perhaps you
should use `gmake` instead of `make`, or even `gnu-make`, etc.
### FreeBSD and related platforms
As a rule, in such systems, the default is to use Berkeley Make. And GNU
Make is called by the gmake command or may be missing. In addition,
[bash](https://en.wikipedia.org/wiki/Bash_(Unix_shell)) may be absent.
As a rule on BSD and it derivatives the default is to use Berkeley Make and
[Bash](https://en.wikipedia.org/wiki/Bash_(Unix_shell)) is not installed.
You need to install the required components: GNU Make, bash, C and C++
So you need to install the required components: GNU Make, Bash, C and C++
compilers compatible with GCC or CLANG. After that, to build the
library, it is enough to execute `gmake all` (or `make all`) in the
directory with source code, and `gmake check` (or `make check`) to run
@ -503,19 +625,22 @@ the basic tests.
### Windows
For build _libmdbx_ on Windows the _original_ CMake and [Microsoft Visual
Studio 2019](https://en.wikipedia.org/wiki/Microsoft_Visual_Studio) are
recommended. Otherwise do not forget to add `ntdll.lib` to linking.
recommended. Please use the recent versions of CMake, Visual Studio and Windows
SDK to avoid troubles with C11 support and `alignas()` feature.
Building by MinGW, MSYS or Cygwin is potentially possible. However,
these scripts are not tested and will probably require you to modify the
`CMakeLists.txt` or `Makefile` respectively.
For build by MinGW the 10.2 or recent version coupled with a modern CMake are required.
So it is recommended to use [chocolatey](https://chocolatey.org/) to install and/or update the ones.
It should be noted that in _libmdbx_ was efforts to resolve
Another ways to build is potentially possible but not supported and will not.
The `CMakeLists.txt` or `GNUMakefile` scripts will probably need to be modified accordingly.
Using other methods do not forget to add the `ntdll.lib` to linking.
It should be noted that in _libmdbx_ was efforts to avoid
runtime dependencies from CRT and other MSVC libraries.
For this is enough to define the `MDBX_WITHOUT_MSVC_CRT` during build.
For this is enough to pass the `-DMDBX_WITHOUT_MSVC_CRT:BOOL=ON` option
during configure by CMake.
An example of running a basic test script can be found in the
[CI-script](appveyor.yml) for [AppVeyor](https://www.appveyor.com/). To
run the [long stochastic test scenario](test/long_stochastic.sh),
To run the [long stochastic test scenario](test/stochastic.sh),
[bash](https://en.wikipedia.org/wiki/Bash_(Unix_shell)) is required, and
such testing is recommended with placing the test data on the
[RAM-disk](https://en.wikipedia.org/wiki/RAM_drive).
@ -529,24 +654,27 @@ error when opening the database in a _WSL1_ environment.
### MacOS
Current [native build tools](https://en.wikipedia.org/wiki/Xcode) for
MacOS include GNU Make, CLANG and an outdated version of bash.
Therefore, to build the library, it is enough to run `make all` in the
MacOS include GNU Make, CLANG and an outdated version of Bash.
However, the build script uses GNU-kind of `sed` and `tar`.
So the easiest way to install all prerequirements is to use [Homebrew](https://brew.sh/),
just by `brew install bash make cmake ninja gnu-sed gnu-tar --with-default-names`.
Next, to build the library, it is enough to run `make all` in the
directory with source code, and run `make check` to execute the base
tests. If something goes wrong, it is recommended to install
[Homebrew](https://brew.sh/) and try again.
To run the [long stochastic test scenario](test/long_stochastic.sh), you
To run the [long stochastic test scenario](test/stochastic.sh), you
will need to install the current (not outdated) version of
[bash](https://en.wikipedia.org/wiki/Bash_(Unix_shell)). To do this, we
recommend that you install [Homebrew](https://brew.sh/) and then execute
`brew install bash`.
[Bash](https://en.wikipedia.org/wiki/Bash_(Unix_shell)).
Just install it as noted above.
### Android
We recommend using CMake to build _libmdbx_ for Android.
I recommend using CMake to build _libmdbx_ for Android.
Please refer to the [official guide](https://developer.android.com/studio/projects/add-native-code).
### iOS
To build _libmdbx_ for iOS, we recommend using CMake with the
To build _libmdbx_ for iOS, I recommend using CMake with the
["toolchain file"](https://cmake.org/cmake/help/latest/variable/CMAKE_TOOLCHAIN_FILE.html)
from the [ios-cmake](https://github.com/leetal/ios-cmake) project.
@ -554,8 +682,8 @@ from the [ios-cmake](https://github.com/leetal/ios-cmake) project.
## API description
Please refer to the online [_libmdbx_ API reference](https://erthink.github.io/libmdbx/)
and/or see the [mdbx.h](mdbx.h) header.
Please refer to the online [_libmdbx_ API reference](https://libmdbx.dqdkfa.ru/docs)
and/or see the [mdbx.h++](mdbx.h%2B%2B) and [mdbx.h](mdbx.h) headers.
<!-- section-begin bindings -->
@ -564,17 +692,25 @@ Bindings
| Runtime | Repo | Author |
| ------- | ------ | ------ |
| Haskell | [libmdbx-hs](https://hackage.haskell.org/package/libmdbx) | [Francisco Vallarino](https://github.com/fjvallarino) |
| NodeJS | [lmdbx-js](https://github.com/kriszyp/lmdbx-js) | [Kris Zyp](https://github.com/kriszyp/)
| NodeJS | [node-mdbx](https://www.npmjs.com/package/node-mdbx/) | [Сергей Федотов](mailto:sergey.fedotov@corp.mail.ru) |
| Ruby | [ruby-mdbx](https://rubygems.org/gems/mdbx/) | [Mahlon E. Smith](https://github.com/mahlonsmith) |
| Go | [mdbx-go](https://github.com/torquem-ch/mdbx-go) | [Alex Sharov](https://github.com/AskAlexSharov) |
| [Nim](https://en.wikipedia.org/wiki/Nim_(programming_language)) | [NimDBX](https://github.com/snej/nimdbx) | [Jens Alfke](https://github.com/snej)
| Rust | [libmdbx-rs](https://github.com/vorot93/libmdbx-rs) | [Artem Vorotnikov](https://github.com/vorot93) |
| Rust | [mdbx](https://crates.io/crates/mdbx) | [gcxfd](https://github.com/gcxfd) |
| Python | [PyPi/libmdbx](https://pypi.org/project/libmdbx/) | [Lazymio](https://github.com/wtdcode) |
| Java | [mdbxjni](https://github.com/castortech/mdbxjni) | [Castor Technologies](https://castortech.com/) |
| Python (draft) | [python-bindings](https://github.com/erthink/libmdbx/commits/python-bindings) branch | [Noel Kuntze](https://github.com/Thermi)
| .NET (obsolete) | [mdbx.NET](https://github.com/wangjia184/mdbx.NET) | [Jerry Wang](https://github.com/wangjia184) |
| Go | [mdbx-go](https://github.com/torquem-ch/mdbx-go) | [Alex Sharov](https://github.com/AskAlexSharov) |
| Ruby | [ruby-mdbx](https://rubygems.org/gems/mdbx/) | [Mahlon E. Smith](https://github.com/mahlonsmith) |
| Zig | [mdbx-zig](https://github.com/theseyan/lmdbx-zig) | [Sayan J. Das](https://github.com/theseyan) |
##### Obsolete/Outdated/Unsupported:
| Runtime | Repo | Author |
| ------- | ------ | ------ |
| .NET | [mdbx.NET](https://github.com/wangjia184/mdbx.NET) | [Jerry Wang](https://github.com/wangjia184) |
| Scala | [mdbx4s](https://github.com/david-bouyssie/mdbx4s) | [David Bouyssié](https://github.com/david-bouyssie) |
| Rust | [mdbx](https://crates.io/crates/mdbx) | [gcxfd](https://github.com/gcxfd) |
| Haskell | [libmdbx-hs](https://hackage.haskell.org/package/libmdbx) | [Francisco Vallarino](https://github.com/fjvallarino) |
| Lua | [lua-libmdbx](https://github.com/mah0x211/lua-libmdbx) | [Masatoshi Fukunaga](https://github.com/mah0x211) |
| NodeJS, [Deno](https://deno.land/) | [lmdbx-js](https://github.com/kriszyp/lmdbx-js) | [Kris Zyp](https://github.com/kriszyp/)
| NodeJS | [node-mdbx](https://www.npmjs.com/package/node-mdbx/) | [Сергей Федотов](mailto:sergey.fedotov@corp.mail.ru) |
| Nim | [NimDBX](https://github.com/snej/nimdbx) | [Jens Alfke](https://github.com/snej)
<!-- section-end -->
@ -585,10 +721,17 @@ Bindings
Performance comparison
======================
All benchmarks were done in 2015 by [IOArena](https://github.com/pmwkaa/ioarena)
Over the past 10 years, _libmdbx_ has had a lot of significant
improvements and innovations. _libmdbx_ has become a slightly faster in
simple cases and many times faster in complex scenarios, especially with
a huge transactions in gigantic databases. Therefore, on the one hand,
the results below are outdated. However, on the other hand, these simple
benchmarks are evident, easy to reproduce, and are close to the most
common use cases.
The following all benchmark results were obtained in 2015 by [IOArena](https://abf.io/erthink/ioarena)
and multiple [scripts](https://github.com/pmwkaa/ioarena/tree/HL%2B%2B2015)
runs on Lenovo Carbon-2 laptop, i7-4600U 2.1 GHz (2 physical cores, 4 HyperThreading cores), 8 Gb RAM,
SSD SAMSUNG MZNTD512HAGL-000L1 (DXT23L0Q) 512 Gb.
runs on my laptop (i7-4600U 2.1 GHz, SSD MZNTD512HAGL-000L1).
## Integral performance
@ -612,16 +755,17 @@ Here showed sum of performance metrics in 3 benchmarks:
2. Performance gap is too high to compare in any meaningful way.
![Comparison #1: Integral Performance](https://raw.githubusercontent.com/wiki/erthink/libmdbx/img/perf-slide-1.png)
![Comparison #1: Integral Performance](https://libmdbx.dqdkfa.ru/img/perf-slide-1.png)
--------------------------------------------------------------------------------
## Read Scalability
Summary performance with concurrent read/search queries in 1-2-4-8
threads on the machine with 4 logical CPUs in HyperThreading mode (i.e. actually 2 physical CPU cores).
threads on the machine with 4 logical CPUs in HyperThreading mode (i.e.
actually 2 physical CPU cores).
![Comparison #2: Read Scalability](https://raw.githubusercontent.com/wiki/erthink/libmdbx/img/perf-slide-2.png)
![Comparison #2: Read Scalability](https://libmdbx.dqdkfa.ru/img/perf-slide-2.png)
--------------------------------------------------------------------------------
@ -643,7 +787,7 @@ In the benchmark each transaction contains combined CRUD operations (2
inserts, 1 read, 1 update, 1 delete). Benchmark starts on an 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/erthink/libmdbx/img/perf-slide-3.png)
![Comparison #3: Sync-write mode](https://libmdbx.dqdkfa.ru/img/perf-slide-3.png)
--------------------------------------------------------------------------------
@ -670,7 +814,7 @@ and after full run the database contains 100,000 small key-value
records.
![Comparison #4: Lazy-write mode](https://raw.githubusercontent.com/wiki/erthink/libmdbx/img/perf-slide-4.png)
![Comparison #4: Lazy-write mode](https://libmdbx.dqdkfa.ru/img/perf-slide-4.png)
--------------------------------------------------------------------------------
@ -684,17 +828,17 @@ records.
execution time, cross marks standard deviation.
**1,000,000 transactions in async-write mode**.
In case of a crash all data is consistent and conforms to the one of last successful 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
In case of a crash all data is consistent and conforms to the one of
last successful 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.
In the benchmark each transaction contains combined CRUD operations (2
inserts, 1 read, 1 update, 1 delete). Benchmark starts on an 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/erthink/libmdbx/img/perf-slide-5.png)
![Comparison #5: Async-write mode](https://libmdbx.dqdkfa.ru/img/perf-slide-5.png)
--------------------------------------------------------------------------------
@ -718,10 +862,6 @@ All benchmark data is gathered by
[getrusage()](http://man7.org/linux/man-pages/man2/getrusage.2.html)
syscall and by scanning the data directory.
![Comparison #6: Cost comparison](https://raw.githubusercontent.com/wiki/erthink/libmdbx/img/perf-slide-6.png)
![Comparison #6: Cost comparison](https://libmdbx.dqdkfa.ru/img/perf-slide-6.png)
<!-- section-end -->
--------------------------------------------------------------------------------
#### This is a mirror of the origin repository that was moved to [abf.io](https://abf.io/erthink/) because of discriminatory restrictions for Russian Crimea.

44
TODO.md Normal file
View File

@ -0,0 +1,44 @@
TODO
----
- [SWIG](https://www.swig.org/).
- Параллельная lto-сборка с устранением предупреждений.
- Интеграция c DTrace и аналогами.
- Новый стиль обработки ошибок с записью "трассы" и причин.
- Формирование отладочной информации посредством gdb.
- Поддержка WASM.
- Ранняя/не-отложенная очистка GC.
- Явная и автоматические уплотнение/дефрагментация.
- Нелинейная обработка GC.
- Перевести курсоры на двусвязный список вместо односвязного.
- Внутри `txn_renew()` вынести проверку когерентности mmap за/после изменение размера.
- [Migration guide from LMDB to MDBX](https://libmdbx.dqdkfa.ru/dead-github/issues/199).
- [Support for RAW devices](https://libmdbx.dqdkfa.ru/dead-github/issues/124).
- [Support MessagePack for Keys & Values](https://libmdbx.dqdkfa.ru/dead-github/issues/115).
- Packages for [Astra Linux](https://astralinux.ru/), [ALT Linux](https://www.altlinux.org/), [ROSA Linux](https://www.rosalinux.ru/), etc.
Done
----
- Рефакторинг gc-get/gc-put c переходом на "интервальные" списки.
- [Engage new terminology](https://libmdbx.dqdkfa.ru/dead-github/issues/137).
- [More flexible support of asynchronous runtime/framework(s)](https://libmdbx.dqdkfa.ru/dead-github/issues/200).
- [Move most of `mdbx_chk` functional to the library API](https://libmdbx.dqdkfa.ru/dead-github/issues/204).
- [Simple careful mode for working with corrupted DB](https://libmdbx.dqdkfa.ru/dead-github/issues/223).
- [Engage an "overlapped I/O" on Windows](https://libmdbx.dqdkfa.ru/dead-github/issues/224).
- [Large/Overflow pages accounting for dirty-room](https://libmdbx.dqdkfa.ru/dead-github/issues/192).
- [Get rid of dirty-pages list in MDBX_WRITEMAP mode](https://libmdbx.dqdkfa.ru/dead-github/issues/193).
Cancelled
--------
- [Replace SRW-lock on Windows to allow shrink DB with `MDBX_NOSTICKYTHREADS` option](https://libmdbx.dqdkfa.ru/dead-github/issues/210).
Доработка не может быть реализована, так как замена SRW-блокировки
лишает лишь предварительную проблему, но не главную. На Windows
уменьшение размера отображенного в память файла не поддерживается ядром
ОС. Для этого необходимо снять отображение, изменить размер файла и
затем отобразить обратно. В свою очередь, для это необходимо
приостановить работающие с БД потоки выполняющие транзакции чтения, либо
готовые к такому выполнению. Но в режиме MDBX_NOSTICKYTHREADS нет
возможности отслеживать работающие с БД потоки, а приостановка всех
потоков неприемлема для большинства приложений.

View File

@ -1,113 +0,0 @@
version: 0.11.3.{build}
environment:
matrix:
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
CMAKE_GENERATOR: Visual Studio 14 2015
TOOLSET: 140
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
CMAKE_GENERATOR: Visual Studio 16 2019
TOOLSET: 142
MDBX_BUILD_SHARED_LIBRARY: OFF
MDBX_WITHOUT_MSVC_CRT: OFF
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
CMAKE_GENERATOR: Visual Studio 16 2019
TOOLSET: 142
MDBX_BUILD_SHARED_LIBRARY: ON
MDBX_WITHOUT_MSVC_CRT: ON
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
CMAKE_GENERATOR: Visual Studio 16 2019
TOOLSET: 142
MDBX_BUILD_SHARED_LIBRARY: OFF
MDBX_WITHOUT_MSVC_CRT: ON
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
CMAKE_GENERATOR: Visual Studio 16 2019
TOOLSET: 142
MDBX_BUILD_SHARED_LIBRARY: ON
MDBX_WITHOUT_MSVC_CRT: OFF
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
CMAKE_GENERATOR: Visual Studio 15 2017
TOOLSET: 141
branches:
except:
- coverity_scan
configuration:
- Debug
# MSVC-2019 hangs during code generation/optimization due to its own internal errors.
# I have found out that the problem occurs because of the /Ob2 option (see https://github.com/erthink/libmdbx/issues/116).
# So the simplest workaround is to using RelWithDebiInfo configuration for testing (cmake will uses /Ob1 option), instead of Release.
# - Release
- RelWithDebInfo
platform:
- Win32
- x64
# MSVC-2019 may hang up during code generation/optimization due to its own internal errors.
matrix:
allow_failures:
- image: Visual Studio 2019
configuration: Release
# Enable RDP for troubleshooting
#init:
# - ps: iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1'))
before_build:
- git clean -x -f -d
- git submodule sync
- git fetch --tags --prune --force
- git submodule update --init --recursive
- git submodule foreach --recursive git fetch --tags --prune --force
- cmake --version
build_script:
- ps: |
Write-Output "*******************************************************************************"
Write-Output "Configuration: $env:CONFIGURATION"
Write-Output "Platform: $env:PLATFORM"
Write-Output "Toolchain: $env:CMAKE_GENERATOR v$env:TOOLSET"
Write-Output "Options: MDBX_WITHOUT_MSVC_CRT=$env:MDBX_WITHOUT_MSVC_CRT MDBX_BUILD_SHARED_LIBRARY=$env:MDBX_BUILD_SHARED_LIBRARY"
Write-Output "*******************************************************************************"
md _build -Force | Out-Null
cd _build
$generator = $env:CMAKE_GENERATOR
if ($env:TOOLSET -lt 142) {
if ($env:PLATFORM -eq "x64") {
$generator = "$generator Win64"
}
& cmake -G "$generator" -D CMAKE_CONFIGURATION_TYPES="Debug;Release;RelWithDebInfo" -D MDBX_WITHOUT_MSVC_CRT:BOOL=$env:MDBX_WITHOUT_MSVC_CRT -D MDBX_BUILD_SHARED_LIBRARY:BOOL=$env:MDBX_BUILD_SHARED_LIBRARY ..
} else {
& cmake -G "$generator" -A $env:PLATFORM -D CMAKE_CONFIGURATION_TYPES="Debug;Release;RelWithDebInfo" -DMDBX_WITHOUT_MSVC_CRT:BOOL=$env:MDBX_WITHOUT_MSVC_CRT -D MDBX_BUILD_SHARED_LIBRARY:BOOL=$env:MDBX_BUILD_SHARED_LIBRARY ..
}
if ($LastExitCode -ne 0) {
throw "Exec: $ErrorMessage"
}
Write-Output "*******************************************************************************"
& cmake --build . --config $env:CONFIGURATION
if ($LastExitCode -ne 0) {
throw "Exec: $ErrorMessage"
}
Write-Output "*******************************************************************************"
test_script:
- ps: |
if (($env:PLATFORM -ne "ARM") -and ($env:PLATFORM -ne "ARM64")) {
& ./$env:CONFIGURATION/mdbx_test.exe --progress --console=no --pathname=test.db --dont-cleanup-after basic > test.log
Get-Content test.log | Select-Object -last 42
if ($LastExitCode -ne 0) {
throw "Exec: $ErrorMessage"
} else {
& ./$env:CONFIGURATION/mdbx_chk.exe -nvv test.db | Tee-Object -file chk.log | Select-Object -last 42
}
}
on_failure:
- ps: Push-AppveyorArtifact \projects\libmdbx\_build\test.log
- ps: Push-AppveyorArtifact \projects\libmdbx\_build\test.db
- ps: Push-AppveyorArtifact \projects\libmdbx\_build\chk.log

File diff suppressed because it is too large Load Diff

View File

@ -1,19 +1,9 @@
## Copyright (c) 2012-2021 Leonid Yuriev <leo@yuriev.ru>.
##
## Licensed under the Apache License, Version 2.0 (the "License");
## you may not use this file except in compliance with the License.
## You may obtain a copy of the License at
##
## http://www.apache.org/licenses/LICENSE-2.0
##
## Unless required by applicable law or agreed to in writing, software
## distributed under the License is distributed on an "AS IS" BASIS,
## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
## See the License for the specific language governing permissions and
## limitations under the License.
##
# Copyright (c) 2012-2025 Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> ###############################################
# SPDX-License-Identifier: Apache-2.0
if(CMAKE_VERSION VERSION_LESS 3.12)
if(CMAKE_VERSION VERSION_LESS 3.8.2)
cmake_minimum_required(VERSION 3.0.2)
elseif(CMAKE_VERSION VERSION_LESS 3.12)
cmake_minimum_required(VERSION 3.8.2)
else()
cmake_minimum_required(VERSION 3.12)
@ -22,32 +12,47 @@ endif()
cmake_policy(PUSH)
cmake_policy(VERSION ${CMAKE_MINIMUM_REQUIRED_VERSION})
unset(MEMCHECK_OPTION_NAME)
if(NOT DEFINED ENABLE_MEMCHECK)
if(DEFINED MDBX_USE_VALGRIND)
set(MEMCHECK_OPTION_NAME "MDBX_USE_VALGRIND")
elseif(DEFINED ENABLE_VALGRIND)
set(MEMCHECK_OPTION_NAME "ENABLE_VALGRIND")
else()
set(MEMCHECK_OPTION_NAME "ENABLE_MEMCHECK")
endif()
if(MEMCHECK_OPTION_NAME STREQUAL "ENABLE_MEMCHECK")
option(ENABLE_MEMCHECK "Enable integration with valgrind, a memory analyzing tool" OFF)
elseif(${MEMCHECK_OPTION_NAME})
set(ENABLE_MEMCHECK ON)
else()
set(ENABLE_MEMCHECK OFF)
endif()
endif()
include(CheckLibraryExists)
check_library_exists(gcov __gcov_flush "" HAVE_GCOV)
option(ENABLE_GCOV
"Enable integration with gcov, a code coverage program" OFF)
option(ENABLE_GCOV "Enable integration with gcov, a code coverage program" OFF)
option(ENABLE_GPROF
"Enable integration with gprof, a performance analyzing tool" OFF)
option(ENABLE_GPROF "Enable integration with gprof, a performance analyzing tool" OFF)
if(CMAKE_CXX_COMPILER_LOADED)
include(CheckIncludeFileCXX)
check_include_file_cxx(valgrind/memcheck.h HAVE_VALGRIND_MEMCHECK_H)
else()
include(CheckIncludeFile)
check_include_file(valgrind/memcheck.h HAVE_VALGRIND_MEMCHECK_H)
endif()
option(MDBX_USE_VALGRIND "Enable integration with valgrind, a memory analyzing tool" OFF)
if(MDBX_USE_VALGRIND AND NOT HAVE_VALGRIND_MEMCHECK_H)
message(FATAL_ERROR "MDBX_USE_VALGRIND option is set but valgrind/memcheck.h is not found")
endif()
option(ENABLE_ASAN
"Enable AddressSanitizer, a fast memory error detector based on compiler instrumentation" OFF)
option(ENABLE_ASAN "Enable AddressSanitizer, a fast memory error detector based on compiler instrumentation" OFF)
option(ENABLE_UBSAN
"Enable UndefinedBehaviorSanitizer, a fast undefined behavior detector based on compiler instrumentation" OFF)
"Enable UndefinedBehaviorSanitizer, a fast undefined behavior detector based on compiler instrumentation" OFF)
if(ENABLE_MEMCHECK)
if(CMAKE_CXX_COMPILER_LOADED)
include(CheckIncludeFileCXX)
check_include_file_cxx(valgrind/memcheck.h HAVE_VALGRIND_MEMCHECK_H)
else()
include(CheckIncludeFile)
check_include_file(valgrind/memcheck.h HAVE_VALGRIND_MEMCHECK_H)
endif()
if(NOT HAVE_VALGRIND_MEMCHECK_H)
message(FATAL_ERROR "${MEMCHECK_OPTION_NAME} option is set but valgrind/memcheck.h is not found")
endif()
endif()
cmake_policy(POP)

View File

@ -1,19 +1,9 @@
## Copyright (c) 2012-2021 Leonid Yuriev <leo@yuriev.ru>.
##
## Licensed under the Apache License, Version 2.0 (the "License");
## you may not use this file except in compliance with the License.
## You may obtain a copy of the License at
##
## http://www.apache.org/licenses/LICENSE-2.0
##
## Unless required by applicable law or agreed to in writing, software
## distributed under the License is distributed on an "AS IS" BASIS,
## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
## See the License for the specific language governing permissions and
## limitations under the License.
##
# Copyright (c) 2012-2025 Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> ###############################################
# SPDX-License-Identifier: Apache-2.0
if(CMAKE_VERSION VERSION_LESS 3.12)
if(CMAKE_VERSION VERSION_LESS 3.8.2)
cmake_minimum_required(VERSION 3.0.2)
elseif(CMAKE_VERSION VERSION_LESS 3.12)
cmake_minimum_required(VERSION 3.8.2)
else()
cmake_minimum_required(VERSION 3.12)
@ -22,6 +12,21 @@ endif()
cmake_policy(PUSH)
cmake_policy(VERSION ${CMAKE_MINIMUM_REQUIRED_VERSION})
macro(add_option HIVE NAME DESCRIPTION DEFAULT)
list(APPEND ${HIVE}_BUILD_OPTIONS ${HIVE}_${NAME})
if(NOT ${DEFAULT} STREQUAL "AUTO")
option(${HIVE}_${NAME} "${DESCRIPTION}" ${DEFAULT})
elseif(NOT DEFINED ${HIVE}_${NAME})
set(${HIVE}_${NAME}_AUTO ON)
endif()
endmacro()
macro(set_if_undefined VARNAME)
if(NOT DEFINED "${VARNAME}")
set("${VARNAME}" ${ARGN})
endif()
endmacro()
macro(add_compile_flags languages)
foreach(_lang ${languages})
string(REPLACE ";" " " _flags "${ARGN}")
@ -36,15 +41,31 @@ macro(add_compile_flags languages)
unset(_flags)
endmacro(add_compile_flags)
macro(remove_flag varname flag)
string(REGEX REPLACE "^(.*)( ${flag} )(.*)$" "\\1 \\3" ${varname} ${${varname}})
string(REGEX REPLACE "^((.+ )*)(${flag})(( .+)*)$" "\\1\\4" ${varname} ${${varname}})
endmacro(remove_flag)
macro(remove_compile_flag languages flag)
foreach(_lang ${languages})
if(CMAKE_CXX_COMPILER_LOADED AND _lang STREQUAL "CXX")
remove_flag(${_lang}_FLAGS ${flag})
endif()
if(CMAKE_C_COMPILER_LOADED AND _lang STREQUAL "C")
remove_flag(${_lang}_FLAGS ${flag})
endif()
endforeach()
unset(_lang)
endmacro(remove_compile_flag)
macro(set_source_files_compile_flags)
foreach(file ${ARGN})
get_filename_component(_file_ext ${file} EXT)
set(_lang "")
if("${_file_ext}" STREQUAL ".m")
set(_lang OBJC)
# CMake believes that Objective C is a flavor of C++, not C,
# and uses g++ compiler for .m files.
# LANGUAGE property forces CMake to use CC for ${file}
# CMake believes that Objective C is a flavor of C++, not C, and uses g++ compiler for .m files. LANGUAGE property
# forces CMake to use CC for ${file}
set_source_files_properties(${file} PROPERTIES LANGUAGE C)
elseif("${_file_ext}" STREQUAL ".mm")
set(_lang OBJCXX)
@ -58,167 +79,446 @@ macro(set_source_files_compile_flags)
set(_flags "${_flags} ${CMAKE_${_lang}_FLAGS}")
endif()
# message(STATUS "Set (${file} ${_flags}")
set_source_files_properties(${file} PROPERTIES COMPILE_FLAGS
"${_flags}")
set_source_files_properties(${file} PROPERTIES COMPILE_FLAGS "${_flags}")
endif()
endforeach()
unset(_file_ext)
unset(_lang)
endmacro(set_source_files_compile_flags)
macro(fetch_version name source_root_directory parent_scope)
set(${name}_VERSION "")
set(${name}_GIT_DESCRIBE "")
set(${name}_GIT_TIMESTAMP "")
set(${name}_GIT_TREE "")
set(${name}_GIT_COMMIT "")
set(${name}_GIT_REVISION 0)
set(${name}_GIT_VERSION "")
if(GIT AND EXISTS "${source_root_directory}/.git")
execute_process(COMMAND ${GIT} describe --tags --long --dirty=-dirty
OUTPUT_VARIABLE ${name}_GIT_DESCRIBE
OUTPUT_STRIP_TRAILING_WHITESPACE
WORKING_DIRECTORY ${source_root_directory}
RESULT_VARIABLE rc)
if(rc OR "${name}_GIT_DESCRIBE" STREQUAL "")
message(FATAL_ERROR "Please fetch tags and/or install latest version of git ('describe --tags --long --dirty' failed)")
endif()
execute_process(COMMAND ${GIT} show --no-patch --format=%cI HEAD
OUTPUT_VARIABLE ${name}_GIT_TIMESTAMP
OUTPUT_STRIP_TRAILING_WHITESPACE
WORKING_DIRECTORY ${source_root_directory}
RESULT_VARIABLE rc)
if(rc OR "${name}_GIT_TIMESTAMP" STREQUAL "%cI")
execute_process(COMMAND ${GIT} show --no-patch --format=%ci HEAD
OUTPUT_VARIABLE ${name}_GIT_TIMESTAMP
OUTPUT_STRIP_TRAILING_WHITESPACE
WORKING_DIRECTORY ${source_root_directory}
RESULT_VARIABLE rc)
if(rc OR "${name}_GIT_TIMESTAMP" STREQUAL "%ci")
message(FATAL_ERROR "Please install latest version of git ('show --no-patch --format=%cI HEAD' failed)")
macro(semver_parse str)
set(_semver_ok FALSE)
set(_semver_err "")
set(_semver_major 0)
set(_semver_minor 0)
set(_semver_patch 0)
set(_semver_tweak_withdot "")
set(_semver_tweak "")
set(_semver_extra "")
set(_semver_prerelease_withdash "")
set(_semver_prerelease "")
set(_semver_buildmetadata_withplus "")
set(_semver_buildmetadata "")
if("${str}" MATCHES
"^v?(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)(\\.(0|[1-9][0-9]*))?([-+]-*[0-9a-zA-Z]+.*)?$")
set(_semver_major ${CMAKE_MATCH_1})
set(_semver_minor ${CMAKE_MATCH_2})
set(_semver_patch ${CMAKE_MATCH_3})
set(_semver_tweak_withdot ${CMAKE_MATCH_4})
set(_semver_tweak ${CMAKE_MATCH_5})
set(_semver_extra "${CMAKE_MATCH_6}")
if("${_semver_extra}" STREQUAL "")
set(_semver_ok TRUE)
elseif("${_semver_extra}" MATCHES "^([.-][a-zA-Z0-9-]+)*(\\+[^+]+)?$")
set(_semver_prerelease_withdash "${CMAKE_MATCH_1}")
if(NOT "${_semver_prerelease_withdash}" STREQUAL "")
string(SUBSTRING "${_semver_prerelease_withdash}" 1 -1 _semver_prerelease)
endif()
endif()
execute_process(COMMAND ${GIT} show --no-patch --format=%T HEAD
OUTPUT_VARIABLE ${name}_GIT_TREE
OUTPUT_STRIP_TRAILING_WHITESPACE
WORKING_DIRECTORY ${source_root_directory}
RESULT_VARIABLE rc)
if(rc OR "${name}_GIT_TREE" STREQUAL "")
message(FATAL_ERROR "Please install latest version of git ('show --no-patch --format=%T HEAD' failed)")
endif()
execute_process(COMMAND ${GIT} show --no-patch --format=%H HEAD
OUTPUT_VARIABLE ${name}_GIT_COMMIT
OUTPUT_STRIP_TRAILING_WHITESPACE
WORKING_DIRECTORY ${source_root_directory}
RESULT_VARIABLE rc)
if(rc OR "${name}_GIT_COMMIT" STREQUAL "")
message(FATAL_ERROR "Please install latest version of git ('show --no-patch --format=%H HEAD' failed)")
endif()
execute_process(COMMAND ${GIT} describe --tags --abbrev=0 "--match=v[0-9]*"
OUTPUT_VARIABLE last_release_tag
OUTPUT_STRIP_TRAILING_WHITESPACE
WORKING_DIRECTORY ${source_root_directory}
RESULT_VARIABLE rc)
if(rc)
message(FATAL_ERROR "Please install latest version of git ('describe --tags --abbrev=0 --match=v[0-9]*' failed)")
endif()
if (last_release_tag)
set(git_revlist_arg "${last_release_tag}..HEAD")
else()
execute_process(COMMAND ${GIT} tag --sort=-version:refname
OUTPUT_VARIABLE tag_list
OUTPUT_STRIP_TRAILING_WHITESPACE
WORKING_DIRECTORY ${source_root_directory}
RESULT_VARIABLE rc)
if(rc)
message(FATAL_ERROR "Please install latest version of git ('tag --sort=-version:refname' failed)")
set(_semver_buildmetadata_withplus "${CMAKE_MATCH_2}")
if(NOT "${_semver_buildmetadata_withplus}" STREQUAL "")
string(SUBSTRING "${_semver_buildmetadata_withplus}" 1 -1 _semver_buildmetadata)
endif()
string(REGEX REPLACE "\n" ";" tag_list "${tag_list}")
set(git_revlist_arg "HEAD")
foreach(tag IN LISTS tag_list)
if(NOT last_release_tag)
string(REGEX MATCH "^v[0-9]+(\.[0-9]+)+" last_release_tag "${tag}")
set(git_revlist_arg "${tag}..HEAD")
endif()
endforeach(tag)
endif()
execute_process(COMMAND ${GIT} rev-list --count "${git_revlist_arg}"
OUTPUT_VARIABLE ${name}_GIT_REVISION
OUTPUT_STRIP_TRAILING_WHITESPACE
WORKING_DIRECTORY ${source_root_directory}
RESULT_VARIABLE rc)
if(rc OR "${name}_GIT_REVISION" STREQUAL "")
message(FATAL_ERROR "Please install latest version of git ('rev-list --count ${git_revlist_arg}' failed)")
endif()
string(REGEX MATCH "^(v)?([0-9]+)\\.([0-9]+)\\.([0-9]+)(.*)?" git_version_valid "${${name}_GIT_DESCRIBE}")
if(git_version_valid)
string(REGEX REPLACE "^(v)?([0-9]+)\\.([0-9]+)\\.([0-9]+)(.*)?" "\\2;\\3;\\4" ${name}_GIT_VERSION ${${name}_GIT_DESCRIBE})
set(_semver_ok TRUE)
else()
string(REGEX MATCH "^(v)?([0-9]+)\\.([0-9]+)(.*)?" git_version_valid "${${name}_GIT_DESCRIBE}")
if(git_version_valid)
string(REGEX REPLACE "^(v)?([0-9]+)\\.([0-9]+)(.*)?" "\\2;\\3;0" ${name}_GIT_VERSION ${${name}_GIT_DESCRIBE})
else()
message(AUTHOR_WARNING "Bad ${name} version \"${${name}_GIT_DESCRIBE}\"; falling back to 0.0.0 (have you made an initial release?)")
set(${name}_GIT_VERSION "0;0;0")
endif()
set(_semver_err
"Поля prerelease и/или buildmetadata (строка `-foo+bar` в составе `0.0.0[.0][-foo][+bar]`) не соответствуют SemVer-спецификации"
)
endif()
endif()
if(NOT ${name}_GIT_VERSION OR NOT ${name}_GIT_TIMESTAMP OR ${name}_GIT_REVISION STREQUAL "")
if(GIT AND EXISTS "${source_root_directory}/.git")
message(WARNING "Unable to retrieve ${name} version from git.")
endif()
set(${name}_GIT_VERSION "0;0;0;0")
set(${name}_GIT_TIMESTAMP "")
set(${name}_GIT_REVISION 0)
# Try to get version from VERSION file
set(version_file "${source_root_directory}/VERSION.txt")
if(NOT EXISTS "${version_file}")
set(version_file "${source_root_directory}/VERSION")
endif()
if(EXISTS "${version_file}")
file(STRINGS "${version_file}" ${name}_VERSION LIMIT_COUNT 1 LIMIT_INPUT 42)
endif()
if(NOT ${name}_VERSION)
message(WARNING "Unable to retrieve ${name} version from \"${version_file}\" file.")
set(${name}_VERSION_LIST ${${name}_GIT_VERSION})
string(REPLACE ";" "." ${name}_VERSION "${${name}_GIT_VERSION}")
else()
string(REPLACE "." ";" ${name}_VERSION_LIST ${${name}_VERSION})
endif()
else()
list(APPEND ${name}_GIT_VERSION ${${name}_GIT_REVISION})
set(${name}_VERSION_LIST ${${name}_GIT_VERSION})
string(REPLACE ";" "." ${name}_VERSION "${${name}_GIT_VERSION}")
set(_semver_err "Версионная отметка в целом не соответствует шаблону `0.0.0[.0][-foo][+bar]` SemVer-спецификации")
endif()
endmacro(semver_parse)
function(_semver_parse_probe str expect)
semver_parse(${str})
if(expect AND NOT _semver_ok)
message(FATAL_ERROR "semver_parse(${str}) expect SUCCESS, got ${_semver_ok}: ${_semver_err}")
elseif(NOT expect AND _semver_ok)
message(FATAL_ERROR "semver_parse(${str}) expect FAIL, got ${_semver_ok}")
endif()
endfunction()
function(semver_parse_selfcheck)
_semver_parse_probe("0.0.4" TRUE)
_semver_parse_probe("v1.2.3" TRUE)
_semver_parse_probe("10.20.30" TRUE)
_semver_parse_probe("10.20.30.42" TRUE)
_semver_parse_probe("1.1.2-prerelease+meta" TRUE)
_semver_parse_probe("1.1.2+meta" TRUE)
_semver_parse_probe("1.1.2+meta-valid" TRUE)
_semver_parse_probe("1.0.0-alpha" TRUE)
_semver_parse_probe("1.0.0-beta" TRUE)
_semver_parse_probe("1.0.0-alpha.beta" TRUE)
_semver_parse_probe("1.0.0-alpha.beta.1" TRUE)
_semver_parse_probe("1.0.0-alpha.1" TRUE)
_semver_parse_probe("1.0.0-alpha0.valid" TRUE)
_semver_parse_probe("1.0.0-alpha.0valid" TRUE)
_semver_parse_probe("1.0.0-alpha-a.b-c-somethinglong+build.1-aef.1-its-okay" TRUE)
_semver_parse_probe("1.0.0-rc.1+build.1" TRUE)
_semver_parse_probe("2.0.0-rc.1+build.123" TRUE)
_semver_parse_probe("1.2.3-beta" TRUE)
_semver_parse_probe("10.2.3-DEV-SNAPSHOT" TRUE)
_semver_parse_probe("1.2.3-SNAPSHOT-123" TRUE)
_semver_parse_probe("1.0.0" TRUE)
_semver_parse_probe("2.0.0" TRUE)
_semver_parse_probe("1.1.7" TRUE)
_semver_parse_probe("2.0.0+build.1848" TRUE)
_semver_parse_probe("2.0.1-alpha.1227" TRUE)
_semver_parse_probe("1.0.0-alpha+beta" TRUE)
_semver_parse_probe("1.2.3----RC-SNAPSHOT.12.9.1--.12+788" TRUE)
_semver_parse_probe("1.2.3----R-S.12.9.1--.12+meta" TRUE)
_semver_parse_probe("1.2.3----RC-SNAPSHOT.12.9.1--.12" TRUE)
_semver_parse_probe("1.0.0+0.build.1-rc.10000aaa-kk-0.1" TRUE)
_semver_parse_probe("99999999999999999999999.999999999999999999.99999999999999999" TRUE)
_semver_parse_probe("v1.0.0-0A.is.legal" TRUE)
_semver_parse_probe("1" FALSE)
_semver_parse_probe("1.2" FALSE)
# _semver_parse_probe("1.2.3-0123" FALSE) _semver_parse_probe("1.2.3-0123.0123" FALSE)
_semver_parse_probe("1.1.2+.123" FALSE)
_semver_parse_probe("+invalid" FALSE)
_semver_parse_probe("-invalid" FALSE)
_semver_parse_probe("-invalid+invalid" FALSE)
_semver_parse_probe("-invalid.01" FALSE)
_semver_parse_probe("alpha" FALSE)
_semver_parse_probe("alpha.beta" FALSE)
_semver_parse_probe("alpha.beta.1" FALSE)
_semver_parse_probe("alpha.1" FALSE)
_semver_parse_probe("alpha+beta" FALSE)
_semver_parse_probe("alpha_beta" FALSE)
_semver_parse_probe("alpha." FALSE)
_semver_parse_probe("alpha.." FALSE)
_semver_parse_probe("beta" FALSE)
_semver_parse_probe("1.0.0-alpha_beta" FALSE)
_semver_parse_probe("-alpha." FALSE)
_semver_parse_probe("1.0.0-alpha.." FALSE)
_semver_parse_probe("1.0.0-alpha..1" FALSE)
_semver_parse_probe("1.0.0-alpha...1" FALSE)
_semver_parse_probe("1.0.0-alpha....1" FALSE)
_semver_parse_probe("1.0.0-alpha.....1" FALSE)
_semver_parse_probe("1.0.0-alpha......1" FALSE)
_semver_parse_probe("1.0.0-alpha.......1" FALSE)
_semver_parse_probe("01.1.1" FALSE)
_semver_parse_probe("1.01.1" FALSE)
_semver_parse_probe("1.1.01" FALSE)
_semver_parse_probe("1.2" FALSE)
_semver_parse_probe("1.2.3.DEV" FALSE)
_semver_parse_probe("1.2-SNAPSHOT" FALSE)
_semver_parse_probe("1.2.31.2.3----RC-SNAPSHOT.12.09.1--..12+788" FALSE)
_semver_parse_probe("1.2-RC-SNAPSHOT" FALSE)
_semver_parse_probe("-1.0.3-gamma+b7718" FALSE)
_semver_parse_probe("+justmeta" FALSE)
_semver_parse_probe("9.8.7+meta+meta" FALSE)
_semver_parse_probe("9.8.7-whatever+meta+meta" FALSE)
_semver_parse_probe(
"99999999999999999999999.999999999999999999.99999999999999999----RC-SNAPSHOT.12.09.1--------------------------------..12"
FALSE)
endfunction()
macro(git_get_versioninfo source_root_directory)
set(_git_describe "")
set(_git_timestamp "")
set(_git_tree "")
set(_git_commit "")
set(_git_last_vtag "")
set(_git_trailing_commits 0)
set(_git_is_dirty FALSE)
execute_process(
COMMAND ${GIT} show --no-patch --format=%cI HEAD
OUTPUT_VARIABLE _git_timestamp
OUTPUT_STRIP_TRAILING_WHITESPACE
WORKING_DIRECTORY ${source_root_directory}
RESULT_VARIABLE _rc)
if(_rc OR "${_git_timestamp}" STREQUAL "%cI")
execute_process(
COMMAND ${GIT} show --no-patch --format=%ci HEAD
OUTPUT_VARIABLE _git_timestamp
OUTPUT_STRIP_TRAILING_WHITESPACE
WORKING_DIRECTORY ${source_root_directory}
RESULT_VARIABLE _rc)
if(_rc OR "${_git_timestamp}" STREQUAL "%ci")
message(FATAL_ERROR "Please install latest version of git (`show --no-patch --format=%cI HEAD` failed)")
endif()
endif()
list(GET ${name}_VERSION_LIST 0 "${name}_VERSION_MAJOR")
list(GET ${name}_VERSION_LIST 1 "${name}_VERSION_MINOR")
list(GET ${name}_VERSION_LIST 2 "${name}_VERSION_RELEASE")
list(GET ${name}_VERSION_LIST 3 "${name}_VERSION_REVISION")
execute_process(
COMMAND ${GIT} show --no-patch --format=%T HEAD
OUTPUT_VARIABLE _git_tree
OUTPUT_STRIP_TRAILING_WHITESPACE
WORKING_DIRECTORY ${source_root_directory}
RESULT_VARIABLE _rc)
if(_rc OR "${_git_tree}" STREQUAL "")
message(FATAL_ERROR "Please install latest version of git (`show --no-patch --format=%T HEAD` failed)")
endif()
execute_process(
COMMAND ${GIT} show --no-patch --format=%H HEAD
OUTPUT_VARIABLE _git_commit
OUTPUT_STRIP_TRAILING_WHITESPACE
WORKING_DIRECTORY ${source_root_directory}
RESULT_VARIABLE _rc)
if(_rc OR "${_git_commit}" STREQUAL "")
message(FATAL_ERROR "Please install latest version of git (`show --no-patch --format=%H HEAD` failed)")
endif()
execute_process(
COMMAND ${GIT} status --untracked-files=no --porcelain
OUTPUT_VARIABLE _git_status
OUTPUT_STRIP_TRAILING_WHITESPACE
WORKING_DIRECTORY ${source_root_directory}
RESULT_VARIABLE _rc)
if(_rc)
message(FATAL_ERROR "Please install latest version of git (`status --untracked-files=no --porcelain` failed)")
endif()
if(NOT "${_git_status}" STREQUAL "")
set(_git_commit "DIRTY-${_git_commit}")
set(_git_is_dirty TRUE)
endif()
unset(_git_status)
execute_process(
COMMAND ${GIT} describe --tags --abbrev=0 "--match=v[0-9]*"
OUTPUT_VARIABLE _git_last_vtag
OUTPUT_STRIP_TRAILING_WHITESPACE
WORKING_DIRECTORY ${source_root_directory}
RESULT_VARIABLE _rc)
if(_rc OR "${_git_last_vtag}" STREQUAL "")
execute_process(
COMMAND ${GIT} tag
OUTPUT_VARIABLE _git_tags_dump
OUTPUT_STRIP_TRAILING_WHITESPACE
WORKING_DIRECTORY ${source_root_directory}
RESULT_VARIABLE _rc)
execute_process(
COMMAND ${GIT} rev-list --count --no-merges --remove-empty HEAD
OUTPUT_VARIABLE _git_whole_count
OUTPUT_STRIP_TRAILING_WHITESPACE
WORKING_DIRECTORY ${source_root_directory}
RESULT_VARIABLE _rc)
if(_rc)
message(
FATAL_ERROR
"Please install latest version of git (`git rev-list --count --no-merges --remove-empty HEAD` failed)")
endif()
if(_git_whole_count GREATER 42 AND "${_git_tags_dump}" STREQUAL "")
message(FATAL_ERROR "Please fetch tags (`describe --tags --abbrev=0 --match=v[0-9]*` failed)")
else()
message(NOTICE "Falling back to version `0.0.0` (have you made an initial release?")
endif()
set(_git_last_vtag "0.0.0")
set(_git_trailing_commits ${_git_whole_count})
execute_process(
COMMAND ${GIT} describe --tags --dirty --long --always
OUTPUT_VARIABLE _git_describe
OUTPUT_STRIP_TRAILING_WHITESPACE
WORKING_DIRECTORY ${source_root_directory}
RESULT_VARIABLE _rc)
if(_rc OR "${_git_describe}" STREQUAL "")
execute_process(
COMMAND ${GIT} describe --tags --all --dirty --long --always
OUTPUT_VARIABLE _git_describe
OUTPUT_STRIP_TRAILING_WHITESPACE
WORKING_DIRECTORY ${source_root_directory}
RESULT_VARIABLE _rc)
if(_rc OR "${_git_describe}" STREQUAL "")
message(FATAL_ERROR "Please install latest version of git (`describe --tags --all --long` failed)")
endif()
endif()
else()
execute_process(
COMMAND ${GIT} describe --tags --dirty --long "--match=v[0-9]*"
OUTPUT_VARIABLE _git_describe
OUTPUT_STRIP_TRAILING_WHITESPACE
WORKING_DIRECTORY ${source_root_directory}
RESULT_VARIABLE _rc)
if(_rc OR "${_git_describe}" STREQUAL "")
message(FATAL_ERROR "Please install latest version of git (`describe --tags --long --match=v[0-9]*`)")
endif()
execute_process(
COMMAND ${GIT} rev-list --count "${_git_last_vtag}..HEAD"
OUTPUT_VARIABLE _git_trailing_commits
OUTPUT_STRIP_TRAILING_WHITESPACE
WORKING_DIRECTORY ${source_root_directory}
RESULT_VARIABLE _rc)
if(_rc OR "${_git_trailing_commits}" STREQUAL "")
message(FATAL_ERROR "Please install latest version of git (`rev-list --count ${_git_last_vtag}..HEAD` failed)")
endif()
endif()
endmacro(git_get_versioninfo)
macro(semver_provide name source_root_directory build_directory_for_json_output build_metadata parent_scope)
set(_semver "")
set(_git_describe "")
set(_git_timestamp "")
set(_git_tree "")
set(_git_commit "")
set(_version_from "")
set(_git_root FALSE)
find_program(GIT git)
if(GIT)
execute_process(
COMMAND ${GIT} rev-parse --show-toplevel
OUTPUT_VARIABLE _git_root
ERROR_VARIABLE _git_root_error
OUTPUT_STRIP_TRAILING_WHITESPACE
WORKING_DIRECTORY ${source_root_directory}
RESULT_VARIABLE _rc)
if(_rc OR "${_git_root}" STREQUAL "")
if(EXISTS "${source_root_directory}/.git")
message(ERROR "`git rev-parse --show-toplevel` failed '${_git_root_error}'")
else()
message(VERBOSE "`git rev-parse --show-toplevel` failed '${_git_root_error}'")
endif()
else()
set(_source_root "${source_root_directory}")
if(NOT CMAKE_VERSION VERSION_LESS 3.19)
file(REAL_PATH "${_git_root}" _git_root)
file(REAL_PATH "${_source_root}" _source_root)
endif()
if(_source_root STREQUAL _git_root AND EXISTS "${_git_root}/VERSION.json")
message(
FATAL_ERROR
"Несколько источников информации о версии, допустим только один из: репозиторий git, либо файл VERSION.json"
)
endif()
endif()
endif()
if(EXISTS "${source_root_directory}/VERSION.json")
set(_version_from "${source_root_directory}/VERSION.json")
if(CMAKE_VERSION VERSION_LESS 3.19)
message(FATAL_ERROR "Требуется CMake версии >= 3.19 для чтения VERSION.json")
endif()
file(
STRINGS "${_version_from}" _versioninfo_json NEWLINE_CONSUME
LIMIT_COUNT 9
LIMIT_INPUT 999
ENCODING UTF-8)
string(JSON _git_describe GET ${_versioninfo_json} git_describe)
string(JSON _git_timestamp GET "${_versioninfo_json}" "git_timestamp")
string(JSON _git_tree GET "${_versioninfo_json}" "git_tree")
string(JSON _git_commit GET "${_versioninfo_json}" "git_commit")
string(JSON _semver GET "${_versioninfo_json}" "semver")
unset(_json_object)
if(NOT _semver)
message(FATAL_ERROR "Unable to retrieve ${name} version from \"${_version_from}\" file.")
endif()
semver_parse("${_semver}")
if(NOT _semver_ok)
message(FATAL_ERROR "SemVer `${_semver}` from ${_version_from}: ${_semver_err}")
endif()
elseif(_git_root AND _source_root STREQUAL _git_root)
set(_version_from git)
git_get_versioninfo(${source_root_directory})
semver_parse(${_git_last_vtag})
if(NOT _semver_ok)
message(FATAL_ERROR "Git tag `${_git_last_vtag}`: ${_semver_err}")
endif()
if(_git_trailing_commits GREATER 0 AND "${_semver_tweak}" STREQUAL "")
set(_semver_tweak ${_git_trailing_commits})
endif()
elseif(GIT)
message(
FATAL_ERROR
"Нет источника информации о версии (${source_root_directory}), требуется один из: репозиторий git, либо VERSION.json"
)
else()
message(FATAL_ERROR "Требуется git для получения информации о версии")
endif()
if(NOT _git_describe
OR NOT _git_timestamp
OR NOT _git_tree
OR NOT _git_commit
OR "${_semver_major}" STREQUAL ""
OR "${_semver_minor}" STREQUAL ""
OR "${_semver_patch}" STREQUAL "")
message(ERROR "Unable to retrieve ${name} version from ${_version_from}.")
endif()
set(_semver "${_semver_major}.${_semver_minor}.${_semver_patch}")
if("${_semver_tweak}" STREQUAL "")
set(_semver_tweak 0)
elseif(_semver_tweak GREATER 0)
string(APPEND _semver ".${_semver_tweak}")
endif()
if(NOT "${_semver_prerelease}" STREQUAL "")
string(APPEND _semver "-${_semver_prerelease}")
endif()
if(_git_is_dirty)
string(APPEND _semver "-DIRTY")
endif()
set(_semver_complete "${_semver}")
if(NOT "${build_metadata}" STREQUAL "")
string(APPEND _semver_complete "+${build_metadata}")
endif()
set(${name}_VERSION "${_semver_complete}")
set(${name}_VERSION_PURE "${_semver}")
set(${name}_VERSION_MAJOR ${_semver_major})
set(${name}_VERSION_MINOR ${_semver_minor})
set(${name}_VERSION_PATCH ${_semver_patch})
set(${name}_VERSION_TWEAK ${_semver_tweak})
set(${name}_VERSION_PRERELEASE "${_semver_prerelease}")
set(${name}_GIT_DESCRIBE "${_git_describe}")
set(${name}_GIT_TIMESTAMP "${_git_timestamp}")
set(${name}_GIT_TREE "${_git_tree}")
set(${name}_GIT_COMMIT "${_git_commit}")
if(${parent_scope})
set(${name}_VERSION_MAJOR "${${name}_VERSION_MAJOR}" PARENT_SCOPE)
set(${name}_VERSION_MINOR "${${name}_VERSION_MINOR}" PARENT_SCOPE)
set(${name}_VERSION_RELEASE "${${name}_VERSION_RELEASE}" PARENT_SCOPE)
set(${name}_VERSION_REVISION "${${name}_VERSION_REVISION}" PARENT_SCOPE)
set(${name}_VERSION "${${name}_VERSION}" PARENT_SCOPE)
set(${name}_GIT_DESCRIBE "${${name}_GIT_DESCRIBE}" PARENT_SCOPE)
set(${name}_GIT_TIMESTAMP "${${name}_GIT_TIMESTAMP}" PARENT_SCOPE)
set(${name}_GIT_TREE "${${name}_GIT_TREE}" PARENT_SCOPE)
set(${name}_GIT_COMMIT "${${name}_GIT_COMMIT}" PARENT_SCOPE)
set(${name}_GIT_REVISION "${${name}_GIT_REVISION}" PARENT_SCOPE)
set(${name}_GIT_VERSION "${${name}_GIT_VERSION}" PARENT_SCOPE)
set(${name}_VERSION
"${_semver_complete}"
PARENT_SCOPE)
set(${name}_VERSION_PURE
"${_semver}"
PARENT_SCOPE)
set(${name}_VERSION_MAJOR
${_semver_major}
PARENT_SCOPE)
set(${name}_VERSION_MINOR
${_semver_minor}
PARENT_SCOPE)
set(${name}_VERSION_PATCH
${_semver_patch}
PARENT_SCOPE)
set(${name}_VERSION_TWEAK
"${_semver_tweak}"
PARENT_SCOPE)
set(${name}_VERSION_PRERELEASE
"${_semver_prerelease}"
PARENT_SCOPE)
set(${name}_GIT_DESCRIBE
"${_git_describe}"
PARENT_SCOPE)
set(${name}_GIT_TIMESTAMP
"${_git_timestamp}"
PARENT_SCOPE)
set(${name}_GIT_TREE
"${_git_tree}"
PARENT_SCOPE)
set(${name}_GIT_COMMIT
"${_git_commit}"
PARENT_SCOPE)
endif()
endmacro(fetch_version)
if(_version_from STREQUAL "git")
string(
CONFIGURE
"{
\"git_describe\" : \"@_git_describe@\",
\"git_timestamp\" : \"@_git_timestamp@\",
\"git_tree\" : \"@_git_tree@\",
\"git_commit\" : \"@_git_commit@\",
\"semver\" : \"@_semver@\"\n}"
_versioninfo_json
@ONLY ESCAPE_QUOTES)
file(WRITE "${build_directory_for_json_output}/VERSION.json" "${_versioninfo_json}")
endif()
endmacro(semver_provide)
cmake_policy(POP)

323
conanfile.py Normal file
View File

@ -0,0 +1,323 @@
import shutil
import json
import os
import re
import subprocess
from conan.tools.files import rm
from conan.tools.scm import Git
from conan.tools.apple import is_apple_os
from conan.tools.cmake import CMakeToolchain, CMake, cmake_layout, CMakeDeps
from conan import ConanFile
required_conan_version = '>=2.7'
def semver_parse(s):
m = re.match('^v?(?P<major>0|[1-9]\d*)\.(?P<minor>0|[1-9]\d*)\.(?P<patch>0|[1-9]\d*)(\\.(?P<tweak>0|[1-9]\d*))?(?:-(?P<prerelease>(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+(?P<buildmetadata>[0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$', s)
return m.groupdict() if m else None
def semver_string(semver):
s = str(semver['major']) + '.' + \
str(semver['minor']) + '.' + str(semver['patch'])
if not semver['tweak'] is None and semver['tweak'] != 0:
s += '.' + str(semver['tweak'])
if not semver['prerelease'] is None and semver['prerelease'] != '':
s += '-' + semver['prerelease']
return s
def semver_string_with_buildmetadata(semver):
s = semver_string(semver)
if not semver['buildmetadata'] is None and semver['buildmetadata'] != '':
s += '+' + semver['buildmetadata']
return s
class libmdbx(ConanFile):
name = 'mdbx'
package_type = 'library'
description = 'One of the fastest embeddable key-value ACID database without WAL. libmdbx surpasses the legendary LMDB in terms of reliability, features and performance.'
license = 'Apache-2.0'
author = 'Leo Yuriev <leo@yuriev.ru>'
homepage = 'https://libmdbx.dqdkfa.ru'
url = 'https://gitflic.ru/project/erthink/libmdbx.git'
topics = ('embedded-database', 'key-value', 'btree', 'LMDB', 'storage-engine',
'data-storage', 'nosql', 'ACID', 'MVCC', 'MDBX')
no_copy_source = True
test_type = 'explicit'
build_policy = 'missing'
revision_mode = 'scm'
languages = 'C', 'C++'
provides = 'libmdbx'
implements = ['auto_shared_fpic']
# upload_policy = 'skip'
# exports_sources = 'LICENSE', 'NOTICE', 'CMakeLists.txt', '*.h', '*.h++', '*.c', '*.c++', 'ntdll.def', 'man1/*', 'cmake/*', 'config.h.in'
settings = 'os', 'compiler', 'build_type', 'arch'
options = {
'mdbx.64bit_atomic': ['Auto', True, False],
'mdbx.64bit_cas': ['Auto', True, False],
'mdbx.apple.speed_insteadof_durability': ['Default', True, False],
'mdbx.avoid_msync': ['Auto', True, False],
'mdbx.build_cxx': ['Default', True, False],
'mdbx.build_tools': ['Default', True, False],
'mdbx.cacheline_size': ['Auto', 16, 32, 64, 128, 256],
'mdbx.disable_validation': ['Default', True, False],
'mdbx.enable_bigfoot': ['Default', True, False],
'mdbx.enable_dbi_lockfree': ['Default', True, False],
'mdbx.enable_dbi_sparse': ['Default', True, False],
'mdbx.enable_pgop_stat': ['Default', True, False],
'mdbx.enable_profgc': ['Default', True, False],
'mdbx.enable_refund': ['Default', True, False],
'mdbx.env_checkpid': ['Default', True, False],
'mdbx.force_assertions': ['Default', True, False],
'mdbx.have_builtin_cpu_supports': ['Auto', True, False],
'mdbx.locking': ['Auto', 'WindowsFileLocking', 'SystemV', 'POSIX1988', 'POSIX2001', 'POSIX2008'],
'mdbx.mmap_incoherent_file_write': ['Auto', True, False],
'mdbx.mmap_needs_jolt': ['Auto', True, False],
'mdbx.trust_rtc': ['Default', True, False],
'mdbx.txn_checkowner': ['Default', True, False],
'mdbx.unaligned_ok': ['Auto', True, False],
'mdbx.use_copyfilerange': ['Auto', True, False],
'mdbx.use_mincore': ['Auto', True, False],
'mdbx.use_ofdlocks': ['Auto', True, False],
'mdbx.use_sendfile': ['Auto', True, False],
'mdbx.without_msvc_crt': ['Default', True, False],
'shared': [True, False],
}
default_options = {
'mdbx.64bit_atomic': 'Auto',
'mdbx.64bit_cas': 'Auto',
'mdbx.apple.speed_insteadof_durability': 'Default',
'mdbx.avoid_msync': 'Auto',
'mdbx.build_cxx': 'Default',
'mdbx.build_tools': 'Default',
'mdbx.cacheline_size': 'Auto',
'mdbx.disable_validation': 'Default',
'mdbx.enable_bigfoot': 'Default',
'mdbx.enable_dbi_lockfree': 'Default',
'mdbx.enable_dbi_sparse': 'Default',
'mdbx.enable_pgop_stat': 'Default',
'mdbx.enable_profgc': 'Default',
'mdbx.enable_refund': 'Default',
'mdbx.env_checkpid': 'Default',
'mdbx.force_assertions': 'Default',
'mdbx.have_builtin_cpu_supports': 'Auto',
'mdbx.locking': 'Auto',
'mdbx.mmap_incoherent_file_write': 'Auto',
'mdbx.mmap_needs_jolt': 'Auto',
'mdbx.trust_rtc': 'Default',
'mdbx.txn_checkowner': 'Default',
'mdbx.unaligned_ok': 'Auto',
'mdbx.use_copyfilerange': 'Auto',
'mdbx.use_mincore': 'Auto',
'mdbx.use_ofdlocks': 'Auto',
'mdbx.use_sendfile': 'Auto',
'mdbx.without_msvc_crt': 'Default',
'shared': True,
}
options_description = {
'mdbx.64bit_atomic': 'Advanced: Assume 64-bit operations are atomic and not splitted to 32-bit halves. ',
'mdbx.64bit_cas': 'Advanced: Assume 64-bit atomic compare-and-swap operation is available. ',
'mdbx.apple.speed_insteadof_durability': 'Disable using `fcntl(F_FULLFSYNC)` for a performance reasons at the cost of durability on power failure. ',
'mdbx.avoid_msync': 'Disable in-memory database updating with consequent flush-to-disk/msync syscall in `MDBX_WRITEMAP` mode. ',
'mdbx.build_cxx': 'Build C++ portion. ',
'mdbx.build_tools': 'Build CLI tools (mdbx_chk/stat/dump/load/copy/drop). ',
'mdbx.cacheline_size': 'Advanced: CPU cache line size for data alignment to avoid cache line false-sharing. ',
'mdbx.disable_validation': 'Disable some checks to reduce an overhead and detection probability of database corruption to a values closer to the LMDB. ',
'mdbx.enable_bigfoot': 'Chunking long list of retired pages during huge transactions commit to avoid use sequences of pages. ',
'mdbx.enable_dbi_lockfree': 'Support for deferred releasing and a lockfree path to quickly open DBI handles. ',
'mdbx.enable_dbi_sparse': 'Support for sparse sets of DBI handles to reduce overhead when starting and processing transactions. ',
'mdbx.enable_pgop_stat': 'Gathering statistics for page operations. ',
'mdbx.enable_profgc': 'Profiling of GC search and updates. ',
'mdbx.enable_refund': 'Online database zero-cost auto-compactification during write-transactions. ',
'mdbx.env_checkpid': "Checking PID inside libmdbx's API against reuse database environment after the `fork()`. ",
'mdbx.force_assertions': 'Forces assertion checking even for release builds. ',
'mdbx.have_builtin_cpu_supports': 'Advanced: Assume the compiler and target system has `__builtin_cpu_supports()`. ',
'mdbx.locking': 'Advanced: Choices the locking implementation. ',
'mdbx.mmap_incoherent_file_write': "Advanced: Assume system don't have unified page cache and/or file write operations incoherent with memory-mapped files. ",
'mdbx.mmap_needs_jolt': 'Advanced: Assume system needs explicit syscall to sync/flush/write modified mapped memory. ',
'mdbx.trust_rtc': 'Advanced: Does a system have battery-backed Real-Time Clock or just a fake. ',
'mdbx.txn_checkowner': 'Checking transaction owner thread against misuse transactions from other threads. ',
'mdbx.unaligned_ok': 'Advanced: Assume a target CPU and/or the compiler support unaligned access. ',
'mdbx.use_copyfilerange': 'Advanced: Use `copy_file_range()` syscall. ',
'mdbx.use_mincore': "Use Unix' `mincore()` to determine whether database pages are resident in memory. ",
'mdbx.use_ofdlocks': 'Advanced: Use POSIX OFD-locks. ',
'mdbx.use_sendfile': 'Advancedc: Use `sendfile()` syscall. ',
'mdbx.without_msvc_crt': 'Avoid dependence from MSVC CRT and use ntdll.dll instead. ',
}
build_metadata = None
def config_options(self):
if self.settings.get_safe('os') != 'Linux':
self.options.rm_safe('mdbx.use_copyfilerange')
self.options.rm_safe('mdbx.use_sendfile')
if self.settings.get_safe('os') == 'Windows':
self.default_options['mdbx.avoid_msync'] = True
self.options.rm_safe('mdbx.env_checkpid')
self.options.rm_safe('mdbx.locking')
self.options.rm_safe('mdbx.mmap_incoherent_file_write')
self.options.rm_safe('mdbx.use_mincore')
self.options.rm_safe('mdbx.use_ofdlocks')
else:
self.options.rm_safe('mdbx.without_msvc_crt')
if is_apple_os(self):
self.options.rm_safe('mdbx.mmap_incoherent_file_write')
else:
self.options.rm_safe('mdbx.apple.speed_insteadof_durability')
def fetch_versioninfo_from_git(self):
git = Git(self, folder=self.recipe_folder)
git_timestamp = git.run('show --no-patch --format=%cI HEAD')
git_tree = git.run('show --no-patch --format=%T HEAD')
git_commit = git.run('show --no-patch --format=%H HEAD')
if git.run('rev-list --tags --count') == 0:
git.run('fetch --tags')
git_last_vtag = git.run('describe --tags --abbrev=0 --match=v[0-9]*')
if git_last_vtag == '':
git_describe = git.run('describe --all --long --always')
git_semver = semver_parse(
'0.0.0.' + git.run('rev-list --count --remove-empty --no-merges HEAD'))
else:
git_describe = git.run('describe --tags --long --match=v[0-9]*')
git_version = '.'.join(
map(str, re.split('[-v.]+', git.run('describe --tags --match=v[0-9]*'))[1:5]))
git_semver = semver_parse(git_last_vtag)
if git_semver['prerelease'] is None or git_semver['prerelease'] == '':
git_since_vtag = git.run(
'rev-list ' + git_last_vtag + '.. --count')
if int(git_since_vtag) > 0:
git_semver['tweak'] = int(git_since_vtag)
else:
git_semver['tweak'] = None
info = {'git_describe': git_describe, 'git_timestamp': git_timestamp,
'git_tree': git_tree, 'git_commit': git_commit, 'semver': semver_string(git_semver)}
return info
def export_sources(self):
subprocess.run(['make', '-C', self.recipe_folder, 'DIST_DIR=' +
self.export_sources_folder, '@dist-checked.tag'], check=True)
rm(self, 'Makefile', self.export_sources_folder)
rm(self, 'GNUmakefile', self.export_sources_folder)
# json.dump(self.fetch_versioninfo_from_git(), open(os.path.join(
# self.export_sources_folder, 'VERSION.json'), 'w', encoding='utf-8'))
def source(self):
version_json_pathname = os.path.join(
self.export_sources_folder, 'VERSION.json')
version_json = json.load(
open(os.path.join(version_json_pathname), encoding='utf-8'))['semver']
if version_json != semver_string(semver_parse(self.version)):
self.output.error('Package/Recipe version "' + self.version +
'" mismatch VERSION.json "' + version_json + '"')
def set_version(self):
if self.build_metadata is None and not self.version is None:
self.build_metadata = self.version
semver = semver_parse(self.build_metadata)
if semver:
self.build_metadata = semver['buildmetadata']
else:
self.build_metadata = re.match(
'^[^0-9a-zA-Z]*([0-9a-zA-Z]+[-.0-9a-zA-Z]*)', self.build_metadata).group(1)
if self.build_metadata is None:
self.build_metadata = ''
version_json_pathname = os.path.join(
self.recipe_folder, 'VERSION.json')
if os.path.exists(version_json_pathname):
self.version = json.load(
open(version_json_pathname, encoding='utf-8'))['semver']
version_from = "'" + version_json_pathname + "'"
else:
self.version = self.fetch_versioninfo_from_git()['semver']
version_from = 'Git'
self.output.verbose('Fetch version from ' +
version_from + ': ' + self.version)
if self.build_metadata != '':
self.version += '+' + self.build_metadata
def layout(self):
cmake_layout(self)
def handle_option(self, tc, name, define=False):
opt = self.options.get_safe(name)
if not opt is None:
value = str(opt).lower()
if value != 'auto' and value != 'default':
name = name.upper().replace('.', '_')
if define:
if value == 'false' or value == 'no' or value == 'off':
tc.preprocessor_definitions[name] = 0
elif value == 'true' or value == 'yes' or value == 'on':
tc.preprocessor_definitions[name] = 1
else:
tc.preprocessor_definitions[name] = int(opt)
self.output.highlight(
name + '=' + str(tc.preprocessor_definitions[name]) + ' (' + str(opt) + ')')
else:
tc.cache_variables[name] = opt
self.output.highlight(
name + '=' + str(tc.cache_variables[name]) + ' (' + str(opt) + ')')
def generate(self):
tc = CMakeToolchain(self)
if self.build_metadata is None:
self.build_metadata = semver_parse(self.version)['buildmetadata']
if not self.build_metadata is None and self.build_metadata != '':
tc.variables['MDBX_BUILD_METADATA'] = self.build_metadata
self.output.highlight('MDBX_BUILD_METADATA is ' +
str(tc.variables['MDBX_BUILD_METADATA']))
self.handle_option(tc, 'mdbx.64bit_atomic', True)
self.handle_option(tc, 'mdbx.64bit_cas', True)
self.handle_option(tc, 'mdbx.apple.speed_insteadof_durability')
self.handle_option(tc, 'mdbx.avoid_msync')
self.handle_option(tc, 'mdbx.build_tools')
self.handle_option(tc, 'mdbx.build_cxx')
self.handle_option(tc, 'mdbx.cacheline_size', True)
self.handle_option(tc, 'mdbx.disable_validation')
self.handle_option(tc, 'mdbx.enable_bigfoot')
self.handle_option(tc, 'mdbx.enable_dbi_lockfree')
self.handle_option(tc, 'mdbx.enable_dbi_sparse')
self.handle_option(tc, 'mdbx.enable_pgop_stat')
self.handle_option(tc, 'mdbx.enable_profgc')
self.handle_option(tc, 'mdbx.enable_refund')
self.handle_option(tc, 'mdbx.env_checkpid')
self.handle_option(tc, 'mdbx.force_assertions')
self.handle_option(tc, 'mdbx.have_builtin_cpu_supports', True)
self.handle_option(tc, 'mdbx.mmap_incoherent_file_write', True)
self.handle_option(tc, 'mdbx.mmap_needs_jolt')
self.handle_option(tc, 'mdbx.trust_rtc')
self.handle_option(tc, 'mdbx.txn_checkowner')
self.handle_option(tc, 'mdbx.unaligned_ok', True)
self.handle_option(tc, 'mdbx.use_copyfilerange', True)
self.handle_option(tc, 'mdbx.use_mincore')
self.handle_option(tc, 'mdbx.use_ofdlocks')
self.handle_option(tc, 'mdbx.use_sendfile', True)
self.handle_option(tc, 'mdbx.without_msvc_crt')
opt = self.options.get_safe('mdbx.locking', 'auto')
if not opt is None:
value = str(opt).lower()
if value != 'auto' and value != 'default':
map = {'windowsfilelocking': -1, 'systemv': 5, 'posix1988': 1988,
'posix2001': 2001, 'posix2008': 2008}
value = map[value]
tc.cache_variables['MDBX_LOCKING'] = value
self.output.highlight('MDBX_LOCKING=' +
str(tc.cache_variables['MDBX_LOCKING']))
tc.generate()
def build(self):
cmake = CMake(self)
cmake.configure()
cmake.build()
def package(self):
cmake = CMake(self)
cmake.install()
def package_info(self):
if self.options.shared:
self.cpp_info.libs = ['mdbx']
else:
self.cpp_info.libs = ['mdbx-static']

File diff suppressed because it is too large Load Diff

View File

@ -2,118 +2,6 @@ Restrictions & Caveats {#restrictions}
======================
In addition to those listed for some functions.
## Troubleshooting the LCK-file
1. A broken LCK-file can cause sync issues, including appearance of
wrong/inconsistent data for readers. When database opened in the
cooperative read-write mode the LCK-file requires to be mapped to
memory in read-write access. In this case it is always possible for
stray/malfunctioned application could writes thru pointers to
silently corrupt the LCK-file.
Unfortunately, there is no any portable way to prevent such
corruption, since the LCK-file is updated concurrently by
multiple processes in a lock-free manner and any locking is
unwise due to a large overhead.
The "next" version of libmdbx (\ref MithrilDB) will solve this issue.
\note Workaround: Just make all programs using the database close it;
the LCK-file is always reset on first open.
2. Stale reader transactions left behind by an aborted program cause
further writes to grow the database quickly, and stale locks can
block further operation.
MDBX checks for stale readers while opening environment and before
growth the database. But in some cases, this may not be enough.
\note Workaround: Check for stale readers periodically, using the
\ref mdbx_reader_check() function or the mdbx_stat tool.
3. Stale writers will be cleared automatically by MDBX on supported
platforms. But this is platform-specific, especially of
implementation of shared POSIX-mutexes and support for robust
mutexes. For instance there are no known issues on Linux, OSX,
Windows and FreeBSD.
\note Workaround: Otherwise just make all programs using the database
close it; the LCK-file is always reset on first open of the environment.
## Remote filesystems
Do not use MDBX databases on remote filesystems, even between processes
on the same host. This breaks file locks on some platforms, possibly
memory map sync, and certainly sync between programs on different hosts.
On the other hand, MDBX support the exclusive database operation over
a network, and cooperative read-only access to the database placed on
a read-only network shares.
## Child processes
Do not use opened \ref MDBX_env instance(s) in a child processes after `fork()`.
It would be insane to call fork() and any MDBX-functions simultaneously
from multiple threads. The best way is to prevent the presence of open
MDBX-instances during `fork()`.
The \ref MDBX_ENV_CHECKPID build-time option, which is ON by default on
non-Windows platforms (i.e. where `fork()` is available), enables PID
checking at a few critical points. But this does not give any guarantees,
but only allows you to detect such errors a little sooner. Depending on
the platform, you should expect an application crash and/or database
corruption in such cases.
On the other hand, MDBX allow calling \ref mdbx_env_close() in such cases to
release resources, but no more and in general this is a wrong way.
## Read-only mode
There is no pure read-only mode in a normal explicitly way, since
readers need write access to LCK-file to be ones visible for writer.
So MDBX always tries to open/create LCK-file for read-write, but switches
to without-LCK mode on appropriate errors (`EROFS`, `EACCESS`, `EPERM`)
if the read-only mode was requested by the \ref MDBX_RDONLY flag which is
described below.
The "next" version of libmdbx (\ref MithrilDB) will solve this issue for the "many
readers without writer" case.
## One thread - One transaction
A thread can only use one transaction at a time, plus any nested
read-write transactions in the non-writemap mode. Each transaction
belongs to one thread. The \ref MDBX_NOTLS flag changes this for read-only
transactions. See below.
Do not start more than one transaction for a one thread. If you think
about this, it's really strange to do something with two data snapshots
at once, which may be different. MDBX checks and preventing this by
returning corresponding error code (\ref MDBX_TXN_OVERLAPPING, \ref MDBX_BAD_RSLOT,
\ref MDBX_BUSY) unless you using \ref MDBX_NOTLS option on the environment.
Nonetheless, with the `MDBX_NOTLS` option, you must know exactly what you
are doing, otherwise you will get deadlocks or reading an alien data.
## Do not open twice
Do not have open an MDBX database twice in the same process at the same
time. By default MDBX prevent this in most cases by tracking databases
opening and return \ref MDBX_BUSY if anyone LCK-file is already open.
The reason for this is that when the "Open file description" locks (aka
OFD-locks) are not available, MDBX uses POSIX locks on files, and these
locks have issues if one process opens a file multiple times. If a single
process opens the same environment multiple times, closing it once will
remove all the locks held on it, and the other instances will be
vulnerable to corruption from other processes.
For compatibility with LMDB which allows multi-opening, MDBX can be
configured at runtime by `mdbx_setup_debug(MDBX_DBG_LEGACY_MULTIOPEN, ...)`
prior to calling other MDBX functions. In this way MDBX will track
databases opening, detect multi-opening cases and then recover POSIX file
locks as necessary. However, lock recovery can cause unexpected pauses,
such as when another process opened the database in exclusive mode before
the lock was restored - we have to wait until such a process releases the
database, and so on.
## Long-lived read transactions {#long-lived-read}
Avoid long-lived read transactions, especially in the scenarios with a
@ -147,22 +35,40 @@ or debugging of a client application while retaining an active read
transaction. LMDB this results in `MDB_MAP_FULL` error and subsequent write
performance degradation.
MDBX mostly solve "long-lived" readers issue by using the Handle-Slow-Readers
\ref MDBX_hsr_func callback which allows to abort long-lived read transactions,
and using the \ref MDBX_LIFORECLAIM mode which addresses subsequent performance degradation.
The "next" version of libmdbx (\ref MithrilDB) will completely solve this.
MDBX mostly solve "long-lived" readers issue by offering to use a
transaction parking-and-ousting approach by \ref mdbx_txn_park(),
Handle-Slow-Readers \ref MDBX_hsr_func callback which allows to abort
long-lived read transactions, and using the \ref MDBX_LIFORECLAIM mode
which addresses subsequent performance degradation. The "next" version
of libmdbx (aka \ref MithrilDB) will completely solve this.
- Avoid suspending a process with active transactions. These would then be
"long-lived" as above.
Nonetheless, situations that encourage lengthy read transactions while
intensively updating data should be avoided. For example, you should
avoid suspending/blocking processes/threads performing read
transactions, including during debugging, and use transaction parking if
necessary.
- Avoid aborting a process with an active read-only transaction in scenarios
with high rate of write transactions. The transaction becomes "long-lived"
as above until a check for stale readers is performed or the LCK-file is
reset, since the process may not remove it from the lockfile. This does
not apply to write transactions if the system clears stale writers, see
above.
You should also beware of aborting processes that perform reading
transactions. Despite the fact that libmdbx automatically checks and
cleans readers, as an a process aborting (especially with core dump) can
take a long time, and checking readers cannot be performed too often due
to performance degradation.
## Large data items and huge transactions
This issue will be addressed in MithrilDB and one of libmdbx releases,
presumably in 2025. To do this, nonlinear GC recycling will be
implemented, without stopping garbage recycling on the old MVCC snapshot
used by a long read transaction.
After the planned implementation, any long-term reading transaction will
still keep the used MVCC-snapshot (all the database pages forming it)
from being recycled, but it will allow all unused MVCC snapshots to be
recycled, both before and after the readable one. This will eliminate
one of the main architectural flaws inherited from LMDB and caused the
growth of a database in proportion to a volume of data changes made
concurrently with a long-running read transaction.
## Large data items
MDBX allows you to store values up to 1 gigabyte in size, but this is
not the main functionality for a key-value storage, but an additional
@ -180,6 +86,18 @@ and in the absence of a sufficient sequence of free pages, increase the
DB file. Thus, for long values, MDBX provides maximum read performance
at the expense of write performance.
Some aspects related to GC have been refined and improved in 2022 within
the first releases of the 0.12.x series. In particular the search for
free consecutive/adjacent pages through GC has been significantly
speeded, including acceleration using NOEN/SSE2/AVX2/AVX512
instructions.
This issue will be addressed in MithrilDB and refined within one of
0.15.x libmdbx releases, presumably at end of 2025.
### Huge transactions
A similar situation can be with huge transactions, in which a lot of
database pages are retired. The retired pages should be put into GC as a
list of page numbers for future reuse. But in huge transactions, such a
@ -189,7 +107,16 @@ you delete large amounts of information from the database in a single
transaction, MDBX may need to increase the database file to save the
list of pages to be retired.
Both of these issues will be addressed in MithrilDB.
This issue was fixed in 2022 within the first releases of the 0.12.x
series by `Big Foot` feature, which now is enabled by default.
See \ref MDBX_ENABLE_BIGFOOT build-time option.
The `Big Foot` feature which significantly reduces GC overhead for
processing large lists of retired pages from huge transactions. Now
libmdbx avoid creating large chunks of PNLs (page number lists) which
required a long sequences of free pages, aka large/overflow pages. Thus
avoiding searching, allocating and storing such sequences inside GC.
## Space reservation
An MDBX database configuration will often reserve considerable unused
@ -197,4 +124,125 @@ memory address space and maybe file size for future growth. This does
not use actual memory or disk space, but users may need to understand
the difference so they won't be scared off.
\todo To write about the Read/Write Amplification Factors
However, on 64-bit systems with a relative small amount of RAM, such
reservation can deplete system resources (trigger ENOMEM error, etc)
when setting an inadequately large upper DB size using \ref
mdbx_env_set_geometry() or \ref mdbx::env::geometry. So just avoid this.
## Remote filesystems
Do not use MDBX databases on remote filesystems, even between processes
on the same host. This breaks file locks on some platforms, possibly
memory map sync, and certainly sync between programs on different hosts.
On the other hand, MDBX support the exclusive database operation over
a network, and cooperative read-only access to the database placed on
a read-only network shares.
## Child processes
Do not use opened \ref MDBX_env instance(s) in a child processes after `fork()`.
It would be insane to call fork() and any MDBX-functions simultaneously
from multiple threads. The best way is to prevent the presence of open
MDBX-instances during `fork()`.
The \ref MDBX_ENV_CHECKPID build-time option, which is ON by default on
non-Windows platforms (i.e. where `fork()` is available), enables PID
checking at a few critical points. But this does not give any guarantees,
but only allows you to detect such errors a little sooner. Depending on
the platform, you should expect an application crash and/or database
corruption in such cases.
On the other hand, MDBX allow calling \ref mdbx_env_close() in such cases to
release resources, but no more and in general this is a wrong way.
#### Since v0.13.1 and later
Starting from the v0.13.1 release, the \ref mdbx_env_resurrect_after_fork()
is available, which allows you to reuse an already open database
environment in child processes, but strictly without inheriting any
transactions from a parent process.
## Read-only mode
There is no pure read-only mode in a normal explicitly way, since
readers need write access to LCK-file to be ones visible for writer.
So MDBX always tries to open/create LCK-file for read-write, but switches
to without-LCK mode on appropriate errors (`EROFS`, `EACCESS`, `EPERM`)
if the read-only mode was requested by the \ref MDBX_RDONLY flag which is
described below.
## Troubleshooting the LCK-file
1. A broken LCK-file can cause sync issues, including appearance of
wrong/inconsistent data for readers. When database opened in the
cooperative read-write mode the LCK-file requires to be mapped to
memory in read-write access. In this case it is always possible for
stray/malfunctioned application could writes thru pointers to
silently corrupt the LCK-file.
Unfortunately, there is no any portable way to prevent such
corruption, since the LCK-file is updated concurrently by
multiple processes in a lock-free manner and any locking is
unwise due to a large overhead.
\note Workaround: Just make all programs using the database close it;
the LCK-file is always reset on first open.
2. Stale reader transactions left behind by an aborted program cause
further writes to grow the database quickly, and stale locks can
block further operation.
MDBX checks for stale readers while opening environment and before
growth the database. But in some cases, this may not be enough.
\note Workaround: Check for stale readers periodically, using the
\ref mdbx_reader_check() function or the mdbx_stat tool.
3. Stale writers will be cleared automatically by MDBX on supported
platforms. But this is platform-specific, especially of
implementation of shared POSIX-mutexes and support for robust
mutexes. For instance there are no known issues on Linux, OSX,
Windows and FreeBSD.
\note Workaround: Otherwise just make all programs using the database
close it; the LCK-file is always reset on first open of the environment.
## One thread - One transaction
A thread can only use one transaction at a time, plus any nested
read-write transactions in the non-writemap mode. Each transaction
belongs to one thread. The \ref MDBX_NOSTICKYTHREADS flag changes this,
see below.
Do not start more than one transaction for a one thread. If you think
about this, it's really strange to do something with two data snapshots
at once, which may be different. MDBX checks and preventing this by
returning corresponding error code (\ref MDBX_TXN_OVERLAPPING,
\ref MDBX_BAD_RSLOT, \ref MDBX_BUSY) unless you using
\ref MDBX_NOSTICKYTHREADS option on the environment.
Nonetheless, with the `MDBX_NOSTICKYTHREADS` option, you must know
exactly what you are doing, otherwise you will get deadlocks or reading
an alien data.
## Do not open twice
Do not have open an MDBX database twice in the same process at the same
time. By default MDBX prevent this in most cases by tracking databases
opening and return \ref MDBX_BUSY if anyone LCK-file is already open.
The reason for this is that when the "Open file description" locks (aka
OFD-locks) are not available, MDBX uses POSIX locks on files, and these
locks have issues if one process opens a file multiple times. If a single
process opens the same environment multiple times, closing it once will
remove all the locks held on it, and the other instances will be
vulnerable to corruption from other processes.
For compatibility with LMDB which allows multi-opening, MDBX can be
configured at runtime by `mdbx_setup_debug(MDBX_DBG_LEGACY_MULTIOPEN, ...)`
prior to calling other MDBX functions. In this way MDBX will track
databases opening, detect multi-opening cases and then recover POSIX file
locks as necessary. However, lock recovery can cause unexpected pauses,
such as when another process opened the database in exclusive mode before
the lock was restored - we have to wait until such a process releases the
database, and so on.

View File

@ -67,7 +67,7 @@ the end is hit.
To retrieve all keys starting from a specified key value, use \ref MDBX_SET. For
more cursor operations, see the \ref c_api reference.
When using \ref mdbx_cursor_put()\ref , either the function will position the cursor
When using \ref mdbx_cursor_put(), either the function will position the cursor
for you based on the key, or you can use operation \ref MDBX_CURRENT to use the
current position of the cursor. \note Note that key must then match the current
position's key.
@ -129,20 +129,23 @@ no open MDBX-instance(s) during fork(), or at least close it immediately after
necessary) in a child process would be both extreme complicated and so
fragile.
Do not start more than one transaction for a one thread. If you think about
this, it's really strange to do something with two data snapshots at once,
which may be different. MDBX checks and preventing this by returning
corresponding error code (\ref MDBX_TXN_OVERLAPPING, \ref MDBX_BAD_RSLOT, \ref MDBX_BUSY)
unless you using \ref MDBX_NOTLS option on the environment. Nonetheless, with the
\ref MDBX_NOTLS option, you must know exactly what you are doing, otherwise you
will get deadlocks or reading an alien data.
Do not start more than one transaction for a one thread. If you think
about this, it's really strange to do something with two data snapshots
at once, which may be different. MDBX checks and preventing this by
returning corresponding error code (\ref MDBX_TXN_OVERLAPPING,
\ref MDBX_BAD_RSLOT, \ref MDBX_BUSY) unless you using
\ref MDBX_NOSTICKYTHREADS option on the environment. Nonetheless,
with the \ref MDBX_NOSTICKYTHREADS option, you must know exactly what
you are doing, otherwise you will get deadlocks or reading an alien
data.
Also note that a transaction is tied to one thread by default using Thread
Local Storage. If you want to pass read-only transactions across threads,
you can use the \ref MDBX_NOTLS option on the environment. Nevertheless, a write
transaction entirely should only be used in one thread from start to finish.
MDBX checks this in a reasonable manner and return the \ref MDBX_THREAD_MISMATCH
error in rules violation.
Also note that a transaction is tied to one thread by default using
Thread Local Storage. If you want to pass transactions across threads,
you can use the \ref MDBX_NOSTICKYTHREADS option on the environment.
Nevertheless, a write transaction must be committed or aborted in the
same thread which it was started. MDBX checks this in a reasonable
manner and return the \ref MDBX_THREAD_MISMATCH error in rules
violation.
## Transactions, rollbacks etc

View File

@ -1,5 +1,11 @@
The source code is availale on [Gitflic](https://gitflic.ru/project/erthink/libmdbx).
Donations are welcome to ETH `0xD104d8f8B2dC312aaD74899F83EBf3EEBDC1EA3A`.
Всё будет хорошо!
_The Future will (be) [Positive](https://www.ptsecurity.com). Всё будет хорошо._
> Questions, feedback and suggestions are welcome to the [Telegram' group](https://t.me/libmdbx) (archive [1](https://libmdbx.dqdkfa.ru/tg-archive/messages1.html),
> [2](https://libmdbx.dqdkfa.ru/tg-archive/messages2.html), [3](https://libmdbx.dqdkfa.ru/tg-archive/messages3.html), [4](https://libmdbx.dqdkfa.ru/tg-archive/messages4.html),
> [5](https://libmdbx.dqdkfa.ru/tg-archive/messages5.html), [6](https://libmdbx.dqdkfa.ru/tg-archive/messages6.html), [7](https://libmdbx.dqdkfa.ru/tg-archive/messages7.html)).
> See the [ChangeLog](https://gitflic.ru/project/erthink/libmdbx/blob?file=ChangeLog.md) for `NEWS` and latest updates.
\section toc Table of Contents
@ -8,6 +14,7 @@ each of which is divided into several sections.
1. The \ref intro
- \ref characteristics
- \ref improvements
- \ref restrictions
- \ref performance
2. \ref usage
@ -23,8 +30,8 @@ each of which is divided into several sections.
- The \ref mdbx.h++ header file reference
Please do not hesitate to point out errors in the documentation,
including creating [PR](https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/proposing-changes-to-your-work-with-pull-requests) with corrections and improvements.
including creating [merge-request](https://gitflic.ru/project/erthink/libmdbx/merge-request) with corrections and improvements.
---
\section MithrilDB MithrilDB
\section MithrilDB MithrilDB and Future

BIN
docs/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

103
docs/header.html Normal file
View File

@ -0,0 +1,103 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" lang="$langISO">
<head>
<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=11"/>
<meta name="generator" content="Doxygen $doxygenversion"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<link rel="icon" href="favicon.ico">
<link rel="icon" href="img/bear.png" type="image/png">
<link rel="apple-touch-icon" href="img/bear.png">
<meta property="og:type" content="article"/>
<meta property="og:url" content="https://libmdbx.dqdkfa.ru/"/>
<meta name="twitter:title" content="One of the fastest embeddable key-value engine"/>
<meta name="twitter:description" content="MDBX surpasses the legendary LMDB in terms of reliability, features and performance. For now libmdbx is chosen by all modern Ethereum frontiers as a storage engine."/>
<!--BEGIN PROJECT_NAME--><title>$projectname: $title</title><!--END PROJECT_NAME-->
<!--BEGIN !PROJECT_NAME--><title>$title</title><!--END !PROJECT_NAME-->
<!--BEGIN PROJECT_ICON-->
<link rel="icon" href="$relpath^$projecticon" type="image/x-icon" />
<!--END PROJECT_ICON-->
<link href="$relpath^tabs.css" rel="stylesheet" type="text/css"/>
<!--BEGIN DISABLE_INDEX-->
<!--BEGIN FULL_SIDEBAR-->
<script type="text/javascript">var page_layout=1;</script>
<!--END FULL_SIDEBAR-->
<!--END DISABLE_INDEX-->
<script type="text/javascript" src="$relpath^jquery.js"></script>
<script type="text/javascript" src="$relpath^dynsections.js"></script>
<!--BEGIN COPY_CLIPBOARD-->
<script type="text/javascript" src="$relpath^clipboard.js"></script>
<!--END COPY_CLIPBOARD-->
$treeview
$search
$mathjax
$darkmode
<link href="$relpath^$stylesheet" rel="stylesheet" type="text/css" />
$extrastylesheet
</head>
<body>
<!--BEGIN DISABLE_INDEX-->
<!--BEGIN FULL_SIDEBAR-->
<div id="side-nav" class="ui-resizable side-nav-resizable"><!-- do not remove this div, it is closed by doxygen! -->
<!--END FULL_SIDEBAR-->
<!--END DISABLE_INDEX-->
<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
<!--BEGIN TITLEAREA-->
<div id="titlearea">
<table cellspacing="0" cellpadding="0">
<tbody>
<tr id="projectrow">
<!--BEGIN PROJECT_LOGO-->
<td id="projectlogo"><img alt="Logo" src="$relpath^$projectlogo"$logosize/></td>
<!--END PROJECT_LOGO-->
<!--BEGIN PROJECT_NAME-->
<td id="projectalign">
<div id="projectname">$projectname<!--BEGIN PROJECT_NUMBER--><span id="projectnumber">&#160;$projectnumber</span><!--END PROJECT_NUMBER-->
</div>
<!--BEGIN PROJECT_BRIEF--><div id="projectbrief">$projectbrief</div><!--END PROJECT_BRIEF-->
</td>
<!--END PROJECT_NAME-->
<!--BEGIN !PROJECT_NAME-->
<!--BEGIN PROJECT_BRIEF-->
<td>
<div id="projectbrief">$projectbrief</div>
</td>
<!--END PROJECT_BRIEF-->
<!--END !PROJECT_NAME-->
<!--BEGIN DISABLE_INDEX-->
<!--BEGIN SEARCHENGINE-->
<!--BEGIN !FULL_SIDEBAR-->
<td>$searchbox</td>
<!--END !FULL_SIDEBAR-->
<!--END SEARCHENGINE-->
<!--END DISABLE_INDEX-->
</tr>
<!--BEGIN SEARCHENGINE-->
<!--BEGIN FULL_SIDEBAR-->
<tr><td colspan="2">$searchbox</td></tr>
<!--END FULL_SIDEBAR-->
<!--END SEARCHENGINE-->
</tbody>
</table>
</div>
<!--END TITLEAREA-->
<!-- Yandex.Metrika counter -->
<script type="text/javascript" >
(function(m,e,t,r,i,k,a){m[i]=m[i]||function(){(m[i].a=m[i].a||[]).push(arguments)};
m[i].l=1*new Date();
for (var j = 0; j < document.scripts.length; j++) {if (document.scripts[j].src === r) { return; }}
k=e.createElement(t),a=e.getElementsByTagName(t)[0],k.async=1,k.src=r,a.parentNode.insertBefore(k,a)})
(window, document, "script", "https://mc.yandex.ru/metrika/tag.js", "ym");
ym(99261645, "init", {
clickmap:true,
trackLinks:true,
accurateTrackBounce:true,
webvisor:true
});
</script>
<noscript><div><img src="https://mc.yandex.ru/watch/99261645" style="position:absolute; left:-9999px;" alt="" /></div></noscript>
<!-- /Yandex.Metrika counter -->
<!-- end header part -->

27
docs/ld+json Normal file
View File

@ -0,0 +1,27 @@
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "ItemList",
"itemListElement": [{
"@type": "ListItem",
"position": 1,
"name": "Группа в Telegram",
"url": "https://t.me/libmdbx"
},{
"@type": "ListItem",
"position": 2,
"name": "Исходный код",
"url": "https://gitflic.ru/project/erthink/libmdbx"
},{
"@type": "ListItem",
"position": 3,
"name": "C++ API",
"url": "https://libmdbx.dqdkfa.ru/group__cxx__api.html"
},{
"@type": "ListItem",
"position": 4,
"name": "Mirror on Github",
"url": "https://github.com/erthink/libmdbx"
}]
}
</script>

View File

@ -0,0 +1,6 @@
{
"icons": [
{ "src": "favicon.ico", "type": "image/ico", "sizes": "32x32" },
{ "src": "img/bear.png", "type": "image/png", "sizes": "256x256" }
]
}

2
docs/title Normal file
View File

@ -0,0 +1,2 @@
<title>libmdbx: One of the fastest embeddable key-value engine</title>
<meta name="description" content="libmdbx surpasses the legendary LMDB in terms of reliability, features and performance. For now libmdbx is chosen by all modern Ethereum frontiers as a storage engine.">

View File

@ -4,7 +4,7 @@
*/
/*
* Copyright 2015-2021 Leonid Yuriev <leo@yuriev.ru>.
* Copyright 2015-2025 Leonid Yuriev <leo@yuriev.ru>.
* Copyright 2017 Ilya Shipitsin <chipitsine@gmail.com>.
* Copyright 2012-2015 Howard Chu, Symas Corp.
* All rights reserved.
@ -18,7 +18,12 @@
* <http://www.OpenLDAP.org/license.html>.
*/
#if (defined(__MINGW__) || defined(__MINGW32__) || defined(__MINGW64__)) && !defined(__USE_MINGW_ANSI_STDIO)
#define __USE_MINGW_ANSI_STDIO 1
#endif /* MinGW */
#include "mdbx.h"
#include <limits.h>
#include <stdint.h>
#include <stdio.h>
@ -53,33 +58,23 @@ int main(int argc, char *argv[]) {
pagesize_min, pagesize_max, pagesize_default);
printf("\tKey size: minimum %zu, maximum ≈¼ pagesize (%zu bytes for default"
" %zuK pagesize, %zu bytes for %zuK pagesize).\n",
(size_t)0, mdbx_limits_keysize_max(-1, MDBX_DB_DEFAULTS),
pagesize_default / 1024,
mdbx_limits_keysize_max(pagesize_max, MDBX_DB_DEFAULTS),
pagesize_max / 1024);
(size_t)0, mdbx_limits_keysize_max(-1, MDBX_DB_DEFAULTS), pagesize_default / 1024,
mdbx_limits_keysize_max(pagesize_max, MDBX_DB_DEFAULTS), pagesize_max / 1024);
printf("\tValue size: minimum %zu, maximum %zu (0x%08zX) bytes for maps,"
" ≈¼ pagesize for multimaps (%zu bytes for default %zuK pagesize,"
" %zu bytes for %zuK pagesize).\n",
(size_t)0, mdbx_limits_valsize_max(pagesize_min, MDBX_DB_DEFAULTS),
mdbx_limits_valsize_max(pagesize_min, MDBX_DB_DEFAULTS),
mdbx_limits_valsize_max(-1, MDBX_DUPSORT), pagesize_default / 1024,
mdbx_limits_valsize_max(pagesize_max, MDBX_DUPSORT),
pagesize_max / 1024);
mdbx_limits_valsize_max(pagesize_min, MDBX_DB_DEFAULTS), mdbx_limits_valsize_max(-1, MDBX_DUPSORT),
pagesize_default / 1024, mdbx_limits_valsize_max(pagesize_max, MDBX_DUPSORT), pagesize_max / 1024);
printf("\tWrite transaction size: up to %zu (0x%zX) pages (%f %s for default "
"%zuK pagesize, %f %s for %zuK pagesize).\n",
mdbx_limits_txnsize_max(pagesize_min) / pagesize_min,
mdbx_limits_txnsize_max(pagesize_min) / pagesize_min,
mdbx_limits_txnsize_max(-1) / scale_factor, scale_unit,
pagesize_default / 1024,
mdbx_limits_txnsize_max(pagesize_max) / scale_factor, scale_unit,
pagesize_max / 1024);
mdbx_limits_txnsize_max(pagesize_min) / pagesize_min, mdbx_limits_txnsize_max(pagesize_min) / pagesize_min,
mdbx_limits_txnsize_max(-1) / scale_factor, scale_unit, pagesize_default / 1024,
mdbx_limits_txnsize_max(pagesize_max) / scale_factor, scale_unit, pagesize_max / 1024);
printf("\tDatabase size: up to %zu pages (%f %s for default %zuK "
"pagesize, %f %s for %zuK pagesize).\n",
mdbx_limits_dbsize_max(pagesize_min) / pagesize_min,
mdbx_limits_dbsize_max(-1) / scale_factor, scale_unit,
pagesize_default / 1024,
mdbx_limits_dbsize_max(pagesize_max) / scale_factor, scale_unit,
pagesize_max / 1024);
mdbx_limits_dbsize_max(pagesize_min) / pagesize_min, mdbx_limits_dbsize_max(-1) / scale_factor, scale_unit,
pagesize_default / 1024, mdbx_limits_dbsize_max(pagesize_max) / scale_factor, scale_unit, pagesize_max / 1024);
printf("\tMaximum sub-databases: %u.\n", MDBX_MAX_DBI);
printf("-----\n");
@ -88,8 +83,7 @@ int main(int argc, char *argv[]) {
fprintf(stderr, "mdbx_env_create: (%d) %s\n", rc, mdbx_strerror(rc));
goto bailout;
}
rc = mdbx_env_open(env, "./example-db",
MDBX_NOSUBDIR | MDBX_COALESCE | MDBX_LIFORECLAIM, 0664);
rc = mdbx_env_open(env, "./example-db", MDBX_NOSUBDIR | MDBX_LIFORECLAIM, 0664);
if (rc != MDBX_SUCCESS) {
fprintf(stderr, "mdbx_env_open: (%d) %s\n", rc, mdbx_strerror(rc));
goto bailout;
@ -137,9 +131,8 @@ int main(int argc, char *argv[]) {
int found = 0;
while ((rc = mdbx_cursor_get(cursor, &key, &data, MDBX_NEXT)) == 0) {
printf("key: %p %.*s, data: %p %.*s\n", key.iov_base, (int)key.iov_len,
(char *)key.iov_base, data.iov_base, (int)data.iov_len,
(char *)data.iov_base);
printf("key: %p %.*s, data: %p %.*s\n", key.iov_base, (int)key.iov_len, (char *)key.iov_base, data.iov_base,
(int)data.iov_len, (char *)data.iov_base);
found += 1;
}
if (rc != MDBX_NOTFOUND || found == 0) {

View File

@ -4,7 +4,7 @@
*/
/*
* Copyright 2015-2021 Leonid Yuriev <leo@yuriev.ru>.
* Copyright 2015-2025 Leonid Yuriev <leo@yuriev.ru>.
* Copyright 2012-2015 Howard Chu, Symas Corp.
* Copyright 2015,2016 Peter-Service R&D LLC.
* All rights reserved.

3890
mdbx.h

File diff suppressed because it is too large Load Diff

4570
mdbx.h++

File diff suppressed because it is too large Load Diff

View File

@ -1,25 +1,32 @@
From f732fc79456f3b296543ab2625d35eeef2655618 Mon Sep 17 00:00:00 2001
From: Leonid Yuriev <leo@yuriev.ru>
Date: Sun, 24 Oct 2021 20:13:33 +0300
From 349c08cf21b66ecea851340133a1b845c25675f7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=D0=9B=D0=B5=D0=BE=D0=BD=D0=B8=D0=B4=20=D0=AE=D1=80=D1=8C?=
=?UTF-8?q?=D0=B5=D0=B2=20=28Leonid=20Yuriev=29?= <leo@yuriev.ru>
Date: Tue, 22 Apr 2025 14:38:49 +0300
Subject: [PATCH] package/libmdbx: new package (library/database).
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
This patch adds libmdbx v0.11.1:
This patch adds libmdbx:
- libmdbx is one of the fastest compact embeddable key-value ACID database.
- libmdbx has a specific set of properties and capabilities,
focused on creating unique lightweight solutions.
- libmdbx surpasses the legendary LMDB (Lightning Memory-Mapped Database)
in terms of reliability, features and performance.
- https://github.com/erthink/libmdbx
- more information at https://libmdbx.dqdkfa.ru
Signed-off-by: Leonid Yuriev <leo@yuriev.ru>
Signed-off-by: Yann E. MORIN <yann.morin.1998@free.fr>
The 0.13.6 "Бузина" (Elderberry) is stable release of _libmdbx_ branch with new superior features.
The complete ChangeLog: https://gitflic.ru/project/erthink/libmdbx/blob?file=ChangeLog.md
Signed-off-by: Леонид Юрьев (Leonid Yuriev) <leo@yuriev.ru>
---
DEVELOPERS | 3 +++
package/Config.in | 1 +
package/libmdbx/Config.in | 45 ++++++++++++++++++++++++++++++++++++
package/libmdbx/libmdbx.hash | 5 ++++
package/libmdbx/libmdbx.hash | 6 +++++
package/libmdbx/libmdbx.mk | 42 +++++++++++++++++++++++++++++++++
5 files changed, 96 insertions(+)
5 files changed, 97 insertions(+)
create mode 100644 package/libmdbx/Config.in
create mode 100644 package/libmdbx/libmdbx.hash
create mode 100644 package/libmdbx/libmdbx.mk
@ -52,7 +59,7 @@ index 016a99ed1a..a6f95bfaa9 100644
source "package/libodb-mysql/Config.in"
diff --git a/package/libmdbx/Config.in b/package/libmdbx/Config.in
new file mode 100644
index 0000000000..d13f73938f
index 0000000000..a9a4ac45c5
--- /dev/null
+++ b/package/libmdbx/Config.in
@@ -0,0 +1,45 @@
@ -71,7 +78,7 @@ index 0000000000..d13f73938f
+ libmdbx surpasses the legendary LMDB in terms of
+ reliability, features and performance.
+
+ https://github.com/erthink/libmdbx
+ https://libmdbx.dqdkfa.ru
+
+if BR2_PACKAGE_LIBMDBX
+
@ -103,18 +110,19 @@ index 0000000000..d13f73938f
+ !BR2_TOOLCHAIN_GCC_AT_LEAST_4_4
diff --git a/package/libmdbx/libmdbx.hash b/package/libmdbx/libmdbx.hash
new file mode 100644
index 0000000000..c8b50f9ac3
index 0000000000..ae5266716b
--- /dev/null
+++ b/package/libmdbx/libmdbx.hash
@@ -0,0 +1,5 @@
+# Hashes from: https://github.com/erthink/libmdbx/releases/
+sha256 f954ba8c9768914a92c2b46aac0d66bec674dbb4d7b0f01e362ea2921746ddaa libmdbx-amalgamated-0.11.1.tar.gz
@@ -0,0 +1,6 @@
+# Hashes from: https://libmdbx.dqdkfa.ru/release/SHA256SUMS
+sha256 57db987de6f7ccc66a66ae28a7bda9f9fbb48ac5fb9279bcca92fd5de13075d1 libmdbx-amalgamated-0.13.6.tar.xz
+
+# Locally calculated
+sha256 310fe25c858a9515fc8c8d7d1f24a67c9496f84a91e0a0e41ea9975b1371e569 LICENSE
+sha256 0d542e0c8804e39aa7f37eb00da5a762149dc682d7829451287e11b938e94594 LICENSE
+sha256 651f71b46c6bb0046d2122df7f9def9cb24f4dc28c5b11cef059f66565cda30f NOTICE
diff --git a/package/libmdbx/libmdbx.mk b/package/libmdbx/libmdbx.mk
new file mode 100644
index 0000000000..02d00b1a5a
index 0000000000..571757262e
--- /dev/null
+++ b/package/libmdbx/libmdbx.mk
@@ -0,0 +1,42 @@
@ -124,12 +132,12 @@ index 0000000000..02d00b1a5a
+#
+################################################################################
+
+LIBMDBX_VERSION = 0.11.1
+LIBMDBX_SOURCE = libmdbx-amalgamated-$(LIBMDBX_VERSION).tar.gz
+LIBMDBX_SITE = https://github.com/erthink/libmdbx/releases/download/v$(LIBMDBX_VERSION)
+LIBMDBX_VERSION = 0.13.6
+LIBMDBX_SOURCE = libmdbx-amalgamated-$(LIBMDBX_VERSION).tar.xz
+LIBMDBX_SITE = https://libmdbx.dqdkfa.ru/release
+LIBMDBX_SUPPORTS_IN_SOURCE_BUILD = NO
+LIBMDBX_LICENSE = OLDAP-2.8
+LIBMDBX_LICENSE_FILES = LICENSE
+LIBMDBX_LICENSE = Apache-2.0
+LIBMDBX_LICENSE_FILES = LICENSE NOTICE
+LIBMDBX_REDISTRIBUTE = YES
+LIBMDBX_STRIP_COMPONENTS = 0
+LIBMDBX_INSTALL_STAGING = YES
@ -161,5 +169,5 @@ index 0000000000..02d00b1a5a
+
+$(eval $(cmake-package))
--
2.33.1
2.49.0

View File

@ -1,184 +0,0 @@
cmake_minimum_required(VERSION 2.8.7)
set(TARGET mdbx)
project(${TARGET})
set(MDBX_VERSION_MAJOR 0)
set(MDBX_VERSION_MINOR 3)
set(MDBX_VERSION_RELEASE 1)
set(MDBX_VERSION_REVISION 0)
set(MDBX_VERSION_STRING ${MDBX_VERSION_MAJOR}.${MDBX_VERSION_MINOR}.${MDBX_VERSION_RELEASE})
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} -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-linux.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}.${MDBX_VERSION_MINOR}
OUTPUT_NAME ${TARGET}
CLEAN_DIRECT_OUTPUT 1
)
set_target_properties(${TARGET}_STATIC PROPERTIES
VERSION ${MDBX_VERSION_STRING}
SOVERSION ${MDBX_VERSION_MAJOR}.${MDBX_VERSION_MINOR}
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)

View File

@ -1,18 +0,0 @@
#!/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

View File

@ -1,25 +0,0 @@
#!/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

View File

@ -1,25 +1,58 @@
/*
* Copyright 2015-2021 Leonid Yuriev <leo@yuriev.ru>
* and other libmdbx authors: please see AUTHORS file.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted only as authorized by the OpenLDAP
* Public License.
*
* A copy of this license is available in the file LICENSE in the
* top-level directory of the distribution or, alternatively, at
* <http://www.OpenLDAP.org/license.html>. */
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
#define xMDBX_ALLOY 1 /* alloyed build */
#include "internals.h" /* must be included first */
#include "core.c"
#include "osal.c"
#include "version.c"
#if defined(_WIN32) || defined(_WIN64)
#include "lck-windows.c"
#else
#include "api-cold.c"
#include "api-copy.c"
#include "api-cursor.c"
#include "api-dbi.c"
#include "api-env.c"
#include "api-extra.c"
#include "api-key-transform.c"
#include "api-misc.c"
#include "api-opts.c"
#include "api-range-estimate.c"
#include "api-txn-data.c"
#include "api-txn.c"
#include "audit.c"
#include "chk.c"
#include "cogs.c"
#include "coherency.c"
#include "cursor.c"
#include "dbi.c"
#include "dpl.c"
#include "dxb.c"
#include "env.c"
#include "gc-get.c"
#include "gc-put.c"
#include "global.c"
#include "lck-posix.c"
#endif
#include "lck-windows.c"
#include "lck.c"
#include "logging_and_debug.c"
#include "meta.c"
#include "mvcc-readers.c"
#include "node.c"
#include "osal.c"
#include "page-get.c"
#include "page-iov.c"
#include "page-ops.c"
#include "pnl.c"
#include "refund.c"
#include "rkl.c"
#include "spill.c"
#include "table.c"
#include "tls.c"
#include "tree-ops.c"
#include "tree-search.c"
#include "txl.c"
#include "txn-basal.c"
#include "txn-nested.c"
#include "txn-ro.c"
#include "txn.c"
#include "utils.c"
#include "version.c"
#include "walk.c"
#include "windows-import.c"

543
src/api-cold.c Normal file
View File

@ -0,0 +1,543 @@
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
#include "internals.h"
__cold size_t mdbx_default_pagesize(void) {
size_t pagesize = globals.sys_pagesize;
ENSURE(nullptr, is_powerof2(pagesize));
pagesize = (pagesize >= MDBX_MIN_PAGESIZE) ? pagesize : MDBX_MIN_PAGESIZE;
pagesize = (pagesize <= MDBX_MAX_PAGESIZE) ? pagesize : MDBX_MAX_PAGESIZE;
return pagesize;
}
__cold intptr_t mdbx_limits_dbsize_min(intptr_t pagesize) {
if (pagesize < 1)
pagesize = (intptr_t)mdbx_default_pagesize();
else if (unlikely(pagesize < (intptr_t)MDBX_MIN_PAGESIZE || pagesize > (intptr_t)MDBX_MAX_PAGESIZE ||
!is_powerof2((size_t)pagesize)))
return -1;
return MIN_PAGENO * pagesize;
}
__cold intptr_t mdbx_limits_dbsize_max(intptr_t pagesize) {
if (pagesize < 1)
pagesize = (intptr_t)mdbx_default_pagesize();
else if (unlikely(pagesize < (intptr_t)MDBX_MIN_PAGESIZE || pagesize > (intptr_t)MDBX_MAX_PAGESIZE ||
!is_powerof2((size_t)pagesize)))
return -1;
STATIC_ASSERT(MAX_MAPSIZE < INTPTR_MAX);
const uint64_t limit = (1 + (uint64_t)MAX_PAGENO) * pagesize;
return (limit < MAX_MAPSIZE) ? (intptr_t)limit : (intptr_t)MAX_MAPSIZE;
}
__cold intptr_t mdbx_limits_txnsize_max(intptr_t pagesize) {
if (pagesize < 1)
pagesize = (intptr_t)mdbx_default_pagesize();
else if (unlikely(pagesize < (intptr_t)MDBX_MIN_PAGESIZE || pagesize > (intptr_t)MDBX_MAX_PAGESIZE ||
!is_powerof2((size_t)pagesize)))
return -1;
STATIC_ASSERT(MAX_MAPSIZE < INTPTR_MAX);
const uint64_t pgl_limit = pagesize * (uint64_t)(PAGELIST_LIMIT / MDBX_GOLD_RATIO_DBL);
const uint64_t map_limit = (uint64_t)(MAX_MAPSIZE / MDBX_GOLD_RATIO_DBL);
return (pgl_limit < map_limit) ? (intptr_t)pgl_limit : (intptr_t)map_limit;
}
__cold intptr_t mdbx_limits_keysize_max(intptr_t pagesize, MDBX_db_flags_t flags) {
if (pagesize < 1)
pagesize = (intptr_t)mdbx_default_pagesize();
if (unlikely(pagesize < (intptr_t)MDBX_MIN_PAGESIZE || pagesize > (intptr_t)MDBX_MAX_PAGESIZE ||
!is_powerof2((size_t)pagesize)))
return -1;
return keysize_max(pagesize, flags);
}
__cold int mdbx_env_get_maxkeysize_ex(const MDBX_env *env, MDBX_db_flags_t flags) {
if (unlikely(!env || env->signature.weak != env_signature))
return -1;
return (int)mdbx_limits_keysize_max((intptr_t)env->ps, flags);
}
__cold int mdbx_env_get_maxkeysize(const MDBX_env *env) { return mdbx_env_get_maxkeysize_ex(env, MDBX_DUPSORT); }
__cold intptr_t mdbx_limits_keysize_min(MDBX_db_flags_t flags) { return keysize_min(flags); }
__cold intptr_t mdbx_limits_valsize_max(intptr_t pagesize, MDBX_db_flags_t flags) {
if (pagesize < 1)
pagesize = (intptr_t)mdbx_default_pagesize();
if (unlikely(pagesize < (intptr_t)MDBX_MIN_PAGESIZE || pagesize > (intptr_t)MDBX_MAX_PAGESIZE ||
!is_powerof2((size_t)pagesize)))
return -1;
return valsize_max(pagesize, flags);
}
__cold int mdbx_env_get_maxvalsize_ex(const MDBX_env *env, MDBX_db_flags_t flags) {
if (unlikely(!env || env->signature.weak != env_signature))
return -1;
return (int)mdbx_limits_valsize_max((intptr_t)env->ps, flags);
}
__cold intptr_t mdbx_limits_valsize_min(MDBX_db_flags_t flags) { return valsize_min(flags); }
__cold intptr_t mdbx_limits_pairsize4page_max(intptr_t pagesize, MDBX_db_flags_t flags) {
if (pagesize < 1)
pagesize = (intptr_t)mdbx_default_pagesize();
if (unlikely(pagesize < (intptr_t)MDBX_MIN_PAGESIZE || pagesize > (intptr_t)MDBX_MAX_PAGESIZE ||
!is_powerof2((size_t)pagesize)))
return -1;
if (flags & (MDBX_DUPSORT | MDBX_DUPFIXED | MDBX_INTEGERDUP | MDBX_REVERSEDUP))
return BRANCH_NODE_MAX(pagesize) - NODESIZE;
return LEAF_NODE_MAX(pagesize) - NODESIZE;
}
__cold int mdbx_env_get_pairsize4page_max(const MDBX_env *env, MDBX_db_flags_t flags) {
if (unlikely(!env || env->signature.weak != env_signature))
return -1;
return (int)mdbx_limits_pairsize4page_max((intptr_t)env->ps, flags);
}
__cold intptr_t mdbx_limits_valsize4page_max(intptr_t pagesize, MDBX_db_flags_t flags) {
if (pagesize < 1)
pagesize = (intptr_t)mdbx_default_pagesize();
if (unlikely(pagesize < (intptr_t)MDBX_MIN_PAGESIZE || pagesize > (intptr_t)MDBX_MAX_PAGESIZE ||
!is_powerof2((size_t)pagesize)))
return -1;
if (flags & (MDBX_DUPSORT | MDBX_DUPFIXED | MDBX_INTEGERDUP | MDBX_REVERSEDUP))
return valsize_max(pagesize, flags);
return PAGESPACE(pagesize);
}
__cold int mdbx_env_get_valsize4page_max(const MDBX_env *env, MDBX_db_flags_t flags) {
if (unlikely(!env || env->signature.weak != env_signature))
return -1;
return (int)mdbx_limits_valsize4page_max((intptr_t)env->ps, flags);
}
/*----------------------------------------------------------------------------*/
static size_t estimate_rss(size_t database_bytes) {
return database_bytes + database_bytes / 64 + (512 + MDBX_WORDBITS * 16) * MEGABYTE;
}
__cold int mdbx_env_warmup(const MDBX_env *env, const MDBX_txn *txn, MDBX_warmup_flags_t flags,
unsigned timeout_seconds_16dot16) {
if (unlikely(env == nullptr && txn == nullptr))
return LOG_IFERR(MDBX_EINVAL);
if (unlikely(flags > (MDBX_warmup_force | MDBX_warmup_oomsafe | MDBX_warmup_lock | MDBX_warmup_touchlimit |
MDBX_warmup_release)))
return LOG_IFERR(MDBX_EINVAL);
if (txn) {
int err = check_txn(txn, MDBX_TXN_FINISHED | MDBX_TXN_ERROR);
if (unlikely(err != MDBX_SUCCESS))
return LOG_IFERR(err);
}
if (env) {
int err = check_env(env, false);
if (unlikely(err != MDBX_SUCCESS))
return LOG_IFERR(err);
if (txn && unlikely(txn->env != env))
return LOG_IFERR(MDBX_EINVAL);
} else {
env = txn->env;
}
const uint64_t timeout_monotime = (timeout_seconds_16dot16 && (flags & MDBX_warmup_force))
? osal_monotime() + osal_16dot16_to_monotime(timeout_seconds_16dot16)
: 0;
if (flags & MDBX_warmup_release)
munlock_all(env);
pgno_t used_pgno;
if (txn) {
used_pgno = txn->geo.first_unallocated;
} else {
const troika_t troika = meta_tap(env);
used_pgno = meta_recent(env, &troika).ptr_v->geometry.first_unallocated;
}
const size_t used_range = pgno_align2os_bytes(env, used_pgno);
const pgno_t mlock_pgno = bytes2pgno(env, used_range);
int rc = MDBX_SUCCESS;
if (flags & MDBX_warmup_touchlimit) {
const size_t estimated_rss = estimate_rss(used_range);
#if defined(_WIN32) || defined(_WIN64)
SIZE_T current_ws_lower, current_ws_upper;
if (GetProcessWorkingSetSize(GetCurrentProcess(), &current_ws_lower, &current_ws_upper) &&
current_ws_lower < estimated_rss) {
const SIZE_T ws_lower = estimated_rss;
const SIZE_T ws_upper =
(MDBX_WORDBITS == 32 && ws_lower > MEGABYTE * 2048) ? ws_lower : ws_lower + MDBX_WORDBITS * MEGABYTE * 32;
if (!SetProcessWorkingSetSize(GetCurrentProcess(), ws_lower, ws_upper)) {
rc = (int)GetLastError();
WARNING("SetProcessWorkingSetSize(%zu, %zu) error %d", ws_lower, ws_upper, rc);
}
}
#endif /* Windows */
#ifdef RLIMIT_RSS
struct rlimit rss;
if (getrlimit(RLIMIT_RSS, &rss) == 0 && rss.rlim_cur < estimated_rss) {
rss.rlim_cur = estimated_rss;
if (rss.rlim_max < estimated_rss)
rss.rlim_max = estimated_rss;
if (setrlimit(RLIMIT_RSS, &rss)) {
rc = errno;
WARNING("setrlimit(%s, {%zu, %zu}) error %d", "RLIMIT_RSS", (size_t)rss.rlim_cur, (size_t)rss.rlim_max, rc);
}
}
#endif /* RLIMIT_RSS */
#ifdef RLIMIT_MEMLOCK
if (flags & MDBX_warmup_lock) {
struct rlimit memlock;
if (getrlimit(RLIMIT_MEMLOCK, &memlock) == 0 && memlock.rlim_cur < estimated_rss) {
memlock.rlim_cur = estimated_rss;
if (memlock.rlim_max < estimated_rss)
memlock.rlim_max = estimated_rss;
if (setrlimit(RLIMIT_MEMLOCK, &memlock)) {
rc = errno;
WARNING("setrlimit(%s, {%zu, %zu}) error %d", "RLIMIT_MEMLOCK", (size_t)memlock.rlim_cur,
(size_t)memlock.rlim_max, rc);
}
}
}
#endif /* RLIMIT_MEMLOCK */
(void)estimated_rss;
}
#if defined(MLOCK_ONFAULT) && \
((defined(_GNU_SOURCE) && __GLIBC_PREREQ(2, 27)) || (defined(__ANDROID_API__) && __ANDROID_API__ >= 30)) && \
(defined(__linux__) || defined(__gnu_linux__))
if ((flags & MDBX_warmup_lock) != 0 && globals.linux_kernel_version >= 0x04040000 &&
atomic_load32(&env->mlocked_pgno, mo_AcquireRelease) < mlock_pgno) {
if (mlock2(env->dxb_mmap.base, used_range, MLOCK_ONFAULT)) {
rc = errno;
WARNING("mlock2(%zu, %s) error %d", used_range, "MLOCK_ONFAULT", rc);
} else {
update_mlcnt(env, mlock_pgno, true);
rc = MDBX_SUCCESS;
}
if (rc != EINVAL)
flags -= MDBX_warmup_lock;
}
#endif /* MLOCK_ONFAULT */
int err = MDBX_ENOSYS;
err = dxb_set_readahead(env, used_pgno, true, true);
if (err != MDBX_SUCCESS && rc == MDBX_SUCCESS)
rc = err;
if ((flags & MDBX_warmup_force) != 0 && (rc == MDBX_SUCCESS || rc == MDBX_ENOSYS)) {
const volatile uint8_t *ptr = env->dxb_mmap.base;
size_t offset = 0, unused = 42;
#if !(defined(_WIN32) || defined(_WIN64))
if (flags & MDBX_warmup_oomsafe) {
const int null_fd = open("/dev/null", O_WRONLY);
if (unlikely(null_fd < 0))
rc = errno;
else {
struct iovec iov[MDBX_AUXILARY_IOV_MAX];
for (;;) {
unsigned i;
for (i = 0; i < MDBX_AUXILARY_IOV_MAX && offset < used_range; ++i) {
iov[i].iov_base = (void *)(ptr + offset);
iov[i].iov_len = 1;
offset += globals.sys_pagesize;
}
if (unlikely(writev(null_fd, iov, i) < 0)) {
rc = errno;
if (rc == EFAULT)
rc = ENOMEM;
break;
}
if (offset >= used_range) {
rc = MDBX_SUCCESS;
break;
}
if (timeout_seconds_16dot16 && osal_monotime() > timeout_monotime) {
rc = MDBX_RESULT_TRUE;
break;
}
}
close(null_fd);
}
} else
#endif /* Windows */
for (;;) {
unused += ptr[offset];
offset += globals.sys_pagesize;
if (offset >= used_range) {
rc = MDBX_SUCCESS;
break;
}
if (timeout_seconds_16dot16 && osal_monotime() > timeout_monotime) {
rc = MDBX_RESULT_TRUE;
break;
}
}
(void)unused;
}
if ((flags & MDBX_warmup_lock) != 0 && (rc == MDBX_SUCCESS || rc == MDBX_ENOSYS) &&
atomic_load32(&env->mlocked_pgno, mo_AcquireRelease) < mlock_pgno) {
#if defined(_WIN32) || defined(_WIN64)
if (VirtualLock(env->dxb_mmap.base, used_range)) {
update_mlcnt(env, mlock_pgno, true);
rc = MDBX_SUCCESS;
} else {
rc = (int)GetLastError();
WARNING("%s(%zu) error %d", "VirtualLock", used_range, rc);
}
#elif defined(_POSIX_MEMLOCK_RANGE)
if (mlock(env->dxb_mmap.base, used_range) == 0) {
update_mlcnt(env, mlock_pgno, true);
rc = MDBX_SUCCESS;
} else {
rc = errno;
WARNING("%s(%zu) error %d", "mlock", used_range, rc);
}
#else
rc = MDBX_ENOSYS;
#endif
}
return LOG_IFERR(rc);
}
/*----------------------------------------------------------------------------*/
__cold int mdbx_env_get_fd(const MDBX_env *env, mdbx_filehandle_t *arg) {
int rc = check_env(env, true);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
if (unlikely(!arg))
return LOG_IFERR(MDBX_EINVAL);
*arg = env->lazy_fd;
return MDBX_SUCCESS;
}
__cold int mdbx_env_set_flags(MDBX_env *env, MDBX_env_flags_t flags, bool onoff) {
int rc = check_env(env, false);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
if (unlikely(flags & ((env->flags & ENV_ACTIVE) ? ~ENV_CHANGEABLE_FLAGS : ~ENV_USABLE_FLAGS)))
return LOG_IFERR(MDBX_EPERM);
if (unlikely(env->flags & MDBX_RDONLY))
return LOG_IFERR(MDBX_EACCESS);
const bool lock_needed = (env->flags & ENV_ACTIVE) && !env_owned_wrtxn(env);
bool should_unlock = false;
if (lock_needed) {
rc = lck_txn_lock(env, false);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
should_unlock = true;
}
if (onoff)
env->flags = combine_durability_flags(env->flags, flags);
else
env->flags &= ~flags;
if (should_unlock)
lck_txn_unlock(env);
return MDBX_SUCCESS;
}
__cold int mdbx_env_get_flags(const MDBX_env *env, unsigned *flags) {
int rc = check_env(env, false);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
if (unlikely(!flags))
return LOG_IFERR(MDBX_EINVAL);
*flags = env->flags & ENV_USABLE_FLAGS;
return MDBX_SUCCESS;
}
__cold int mdbx_env_set_userctx(MDBX_env *env, void *ctx) {
int rc = check_env(env, false);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
env->userctx = ctx;
return MDBX_SUCCESS;
}
__cold void *mdbx_env_get_userctx(const MDBX_env *env) { return env ? env->userctx : nullptr; }
__cold int mdbx_env_set_assert(MDBX_env *env, MDBX_assert_func *func) {
int rc = check_env(env, false);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
#if MDBX_DEBUG
env->assert_func = func;
return MDBX_SUCCESS;
#else
(void)func;
return LOG_IFERR(MDBX_ENOSYS);
#endif
}
__cold int mdbx_env_set_hsr(MDBX_env *env, MDBX_hsr_func *hsr) {
int rc = check_env(env, false);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
env->hsr_callback = hsr;
return MDBX_SUCCESS;
}
__cold MDBX_hsr_func *mdbx_env_get_hsr(const MDBX_env *env) {
return likely(env && env->signature.weak == env_signature) ? env->hsr_callback : nullptr;
}
#if defined(_WIN32) || defined(_WIN64)
__cold int mdbx_env_get_pathW(const MDBX_env *env, const wchar_t **arg) {
int rc = check_env(env, true);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
if (unlikely(!arg))
return LOG_IFERR(MDBX_EINVAL);
*arg = env->pathname.specified;
return MDBX_SUCCESS;
}
#endif /* Windows */
__cold int mdbx_env_get_path(const MDBX_env *env, const char **arg) {
int rc = check_env(env, true);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
if (unlikely(!arg))
return LOG_IFERR(MDBX_EINVAL);
#if defined(_WIN32) || defined(_WIN64)
if (!env->pathname_char) {
*arg = nullptr;
DWORD flags = /* WC_ERR_INVALID_CHARS */ 0x80;
size_t mb_len =
WideCharToMultiByte(CP_THREAD_ACP, flags, env->pathname.specified, -1, nullptr, 0, nullptr, nullptr);
rc = mb_len ? MDBX_SUCCESS : (int)GetLastError();
if (rc == ERROR_INVALID_FLAGS) {
mb_len = WideCharToMultiByte(CP_THREAD_ACP, flags = 0, env->pathname.specified, -1, nullptr, 0, nullptr, nullptr);
rc = mb_len ? MDBX_SUCCESS : (int)GetLastError();
}
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
char *const mb_pathname = osal_malloc(mb_len);
if (!mb_pathname)
return LOG_IFERR(MDBX_ENOMEM);
if (mb_len != (size_t)WideCharToMultiByte(CP_THREAD_ACP, flags, env->pathname.specified, -1, mb_pathname,
(int)mb_len, nullptr, nullptr)) {
rc = (int)GetLastError();
osal_free(mb_pathname);
return LOG_IFERR(rc);
}
if (env->pathname_char ||
InterlockedCompareExchangePointer((PVOID volatile *)&env->pathname_char, mb_pathname, nullptr))
osal_free(mb_pathname);
}
*arg = env->pathname_char;
#else
*arg = env->pathname.specified;
#endif /* Windows */
return MDBX_SUCCESS;
}
/*------------------------------------------------------------------------------
* Legacy API */
#ifndef LIBMDBX_NO_EXPORTS_LEGACY_API
LIBMDBX_API int mdbx_txn_begin(MDBX_env *env, MDBX_txn *parent, MDBX_txn_flags_t flags, MDBX_txn **ret) {
return __inline_mdbx_txn_begin(env, parent, flags, ret);
}
LIBMDBX_API int mdbx_txn_commit(MDBX_txn *txn) { return __inline_mdbx_txn_commit(txn); }
LIBMDBX_API __cold int mdbx_env_stat(const MDBX_env *env, MDBX_stat *stat, size_t bytes) {
return __inline_mdbx_env_stat(env, stat, bytes);
}
LIBMDBX_API __cold int mdbx_env_info(const MDBX_env *env, MDBX_envinfo *info, size_t bytes) {
return __inline_mdbx_env_info(env, info, bytes);
}
LIBMDBX_API int mdbx_dbi_flags(const MDBX_txn *txn, MDBX_dbi dbi, unsigned *flags) {
return __inline_mdbx_dbi_flags(txn, dbi, flags);
}
LIBMDBX_API __cold int mdbx_env_sync(MDBX_env *env) { return __inline_mdbx_env_sync(env); }
LIBMDBX_API __cold int mdbx_env_sync_poll(MDBX_env *env) { return __inline_mdbx_env_sync_poll(env); }
LIBMDBX_API __cold int mdbx_env_close(MDBX_env *env) { return __inline_mdbx_env_close(env); }
LIBMDBX_API __cold int mdbx_env_set_mapsize(MDBX_env *env, size_t size) {
return __inline_mdbx_env_set_mapsize(env, size);
}
LIBMDBX_API __cold int mdbx_env_set_maxdbs(MDBX_env *env, MDBX_dbi dbs) {
return __inline_mdbx_env_set_maxdbs(env, dbs);
}
LIBMDBX_API __cold int mdbx_env_get_maxdbs(const MDBX_env *env, MDBX_dbi *dbs) {
return __inline_mdbx_env_get_maxdbs(env, dbs);
}
LIBMDBX_API __cold int mdbx_env_set_maxreaders(MDBX_env *env, unsigned readers) {
return __inline_mdbx_env_set_maxreaders(env, readers);
}
LIBMDBX_API __cold int mdbx_env_get_maxreaders(const MDBX_env *env, unsigned *readers) {
return __inline_mdbx_env_get_maxreaders(env, readers);
}
LIBMDBX_API __cold int mdbx_env_set_syncbytes(MDBX_env *env, size_t threshold) {
return __inline_mdbx_env_set_syncbytes(env, threshold);
}
LIBMDBX_API __cold int mdbx_env_get_syncbytes(const MDBX_env *env, size_t *threshold) {
return __inline_mdbx_env_get_syncbytes(env, threshold);
}
LIBMDBX_API __cold int mdbx_env_set_syncperiod(MDBX_env *env, unsigned seconds_16dot16) {
return __inline_mdbx_env_set_syncperiod(env, seconds_16dot16);
}
LIBMDBX_API __cold int mdbx_env_get_syncperiod(const MDBX_env *env, unsigned *seconds_16dot16) {
return __inline_mdbx_env_get_syncperiod(env, seconds_16dot16);
}
LIBMDBX_API __cold uint64_t mdbx_key_from_int64(const int64_t i64) { return __inline_mdbx_key_from_int64(i64); }
LIBMDBX_API __cold uint32_t mdbx_key_from_int32(const int32_t i32) { return __inline_mdbx_key_from_int32(i32); }
LIBMDBX_API __cold intptr_t mdbx_limits_pgsize_min(void) { return __inline_mdbx_limits_pgsize_min(); }
LIBMDBX_API __cold intptr_t mdbx_limits_pgsize_max(void) { return __inline_mdbx_limits_pgsize_max(); }
#endif /* LIBMDBX_NO_EXPORTS_LEGACY_API */

880
src/api-copy.c Normal file
View File

@ -0,0 +1,880 @@
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \note Please refer to the COPYRIGHT file for explanations license change,
/// credits and acknowledgments.
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
#include "internals.h"
typedef struct compacting_context {
MDBX_env *env;
MDBX_txn *txn;
MDBX_copy_flags_t flags;
pgno_t first_unallocated;
osal_condpair_t condpair;
volatile unsigned head;
volatile unsigned tail;
uint8_t *write_buf[2];
size_t write_len[2];
/* Error code. Never cleared if set. Both threads can set nonzero
* to fail the copy. Not mutex-protected, expects atomic int. */
volatile int error;
mdbx_filehandle_t fd;
} ctx_t;
__cold static int compacting_walk_tree(ctx_t *ctx, tree_t *tree);
/* Dedicated writer thread for compacting copy. */
__cold static THREAD_RESULT THREAD_CALL compacting_write_thread(void *arg) {
ctx_t *const ctx = arg;
#if defined(EPIPE) && !(defined(_WIN32) || defined(_WIN64))
sigset_t sigset;
sigemptyset(&sigset);
sigaddset(&sigset, SIGPIPE);
ctx->error = pthread_sigmask(SIG_BLOCK, &sigset, nullptr);
#endif /* EPIPE */
osal_condpair_lock(&ctx->condpair);
while (!ctx->error) {
while (ctx->tail == ctx->head && !ctx->error) {
int err = osal_condpair_wait(&ctx->condpair, true);
if (err != MDBX_SUCCESS) {
ctx->error = err;
goto bailout;
}
}
const unsigned toggle = ctx->tail & 1;
size_t wsize = ctx->write_len[toggle];
if (wsize == 0) {
ctx->tail += 1;
break /* EOF */;
}
ctx->write_len[toggle] = 0;
uint8_t *ptr = ctx->write_buf[toggle];
if (!ctx->error) {
int err = osal_write(ctx->fd, ptr, wsize);
if (err != MDBX_SUCCESS) {
#if defined(EPIPE) && !(defined(_WIN32) || defined(_WIN64))
if (err == EPIPE) {
/* Collect the pending SIGPIPE,
* otherwise at least OS X gives it to the process on thread-exit. */
int unused;
sigwait(&sigset, &unused);
}
#endif /* EPIPE */
ctx->error = err;
goto bailout;
}
}
ctx->tail += 1;
osal_condpair_signal(&ctx->condpair, false);
}
bailout:
osal_condpair_unlock(&ctx->condpair);
return (THREAD_RESULT)0;
}
/* Give buffer and/or MDBX_EOF to writer thread, await unused buffer. */
__cold static int compacting_toggle_write_buffers(ctx_t *ctx) {
osal_condpair_lock(&ctx->condpair);
eASSERT(ctx->env, ctx->head - ctx->tail < 2 || ctx->error);
ctx->head += 1;
osal_condpair_signal(&ctx->condpair, true);
while (!ctx->error && ctx->head - ctx->tail == 2 /* both buffers in use */) {
if (ctx->flags & MDBX_CP_THROTTLE_MVCC)
mdbx_txn_park(ctx->txn, false);
int err = osal_condpair_wait(&ctx->condpair, false);
if (err == MDBX_SUCCESS && (ctx->flags & MDBX_CP_THROTTLE_MVCC) != 0)
err = mdbx_txn_unpark(ctx->txn, false);
if (err != MDBX_SUCCESS)
ctx->error = err;
}
osal_condpair_unlock(&ctx->condpair);
return ctx->error;
}
static int compacting_put_bytes(ctx_t *ctx, const void *src, size_t bytes, pgno_t pgno, pgno_t npages) {
assert(pgno == 0 || bytes > PAGEHDRSZ);
while (bytes > 0) {
const size_t side = ctx->head & 1;
const size_t left = MDBX_ENVCOPY_WRITEBUF - ctx->write_len[side];
if (left < (pgno ? PAGEHDRSZ : 1)) {
int err = compacting_toggle_write_buffers(ctx);
if (unlikely(err != MDBX_SUCCESS))
return err;
continue;
}
const size_t chunk = (bytes < left) ? bytes : left;
void *const dst = ctx->write_buf[side] + ctx->write_len[side];
if (src) {
memcpy(dst, src, chunk);
if (pgno) {
assert(chunk > PAGEHDRSZ);
page_t *mp = dst;
mp->pgno = pgno;
if (mp->txnid == 0)
mp->txnid = ctx->txn->txnid;
if (mp->flags == P_LARGE) {
assert(bytes <= pgno2bytes(ctx->env, npages));
mp->pages = npages;
}
pgno = 0;
}
src = ptr_disp(src, chunk);
} else
memset(dst, 0, chunk);
bytes -= chunk;
ctx->write_len[side] += chunk;
}
return MDBX_SUCCESS;
}
static int compacting_put_page(ctx_t *ctx, const page_t *mp, const size_t head_bytes, const size_t tail_bytes,
const pgno_t npages) {
if (tail_bytes) {
assert(head_bytes + tail_bytes <= ctx->env->ps);
assert(npages == 1 && (page_type(mp) == P_BRANCH || page_type(mp) == P_LEAF));
} else {
assert(head_bytes <= pgno2bytes(ctx->env, npages));
assert((npages == 1 && page_type(mp) == (P_LEAF | P_DUPFIX)) || page_type(mp) == P_LARGE);
}
const pgno_t pgno = ctx->first_unallocated;
ctx->first_unallocated += npages;
int err = compacting_put_bytes(ctx, mp, head_bytes, pgno, npages);
if (unlikely(err != MDBX_SUCCESS))
return err;
err = compacting_put_bytes(ctx, nullptr, pgno2bytes(ctx->env, npages) - (head_bytes + tail_bytes), 0, 0);
if (unlikely(err != MDBX_SUCCESS))
return err;
return compacting_put_bytes(ctx, ptr_disp(mp, ctx->env->ps - tail_bytes), tail_bytes, 0, 0);
}
__cold static int compacting_walk(ctx_t *ctx, MDBX_cursor *mc, pgno_t *const parent_pgno, txnid_t parent_txnid) {
mc->top = 0;
mc->ki[0] = 0;
int rc = page_get(mc, *parent_pgno, &mc->pg[0], parent_txnid);
if (unlikely(rc != MDBX_SUCCESS))
return rc;
rc = tree_search_finalize(mc, nullptr, Z_FIRST);
if (unlikely(rc != MDBX_SUCCESS))
return rc;
/* Make cursor pages writable */
const intptr_t deep_limit = mc->top + 1;
void *const buf = osal_malloc(pgno2bytes(ctx->env, deep_limit + 1));
if (buf == nullptr)
return MDBX_ENOMEM;
void *ptr = buf;
for (intptr_t i = 0; i <= mc->top; i++) {
page_copy(ptr, mc->pg[i], ctx->env->ps);
mc->pg[i] = ptr;
ptr = ptr_disp(ptr, ctx->env->ps);
}
/* This is writable space for a leaf page. Usually not needed. */
page_t *const leaf = ptr;
while (mc->top >= 0) {
page_t *mp = mc->pg[mc->top];
const size_t nkeys = page_numkeys(mp);
if (is_leaf(mp)) {
if (!(mc->flags & z_inner) /* may have nested N_TREE or N_BIG nodes */) {
for (size_t i = 0; i < nkeys; i++) {
node_t *node = page_node(mp, i);
if (node_flags(node) == N_BIG) {
/* Need writable leaf */
if (mp != leaf) {
mc->pg[mc->top] = leaf;
page_copy(leaf, mp, ctx->env->ps);
mp = leaf;
node = page_node(mp, i);
}
const pgr_t lp = page_get_large(mc, node_largedata_pgno(node), mp->txnid);
if (unlikely((rc = lp.err) != MDBX_SUCCESS))
goto bailout;
const size_t datasize = node_ds(node);
const pgno_t npages = largechunk_npages(ctx->env, datasize);
poke_pgno(node_data(node), ctx->first_unallocated);
rc = compacting_put_page(ctx, lp.page, PAGEHDRSZ + datasize, 0, npages);
if (unlikely(rc != MDBX_SUCCESS))
goto bailout;
} else if (node_flags(node) & N_TREE) {
if (!MDBX_DISABLE_VALIDATION && unlikely(node_ds(node) != sizeof(tree_t))) {
ERROR("%s/%d: %s %u", "MDBX_CORRUPTED", MDBX_CORRUPTED, "invalid dupsort sub-tree node size",
(unsigned)node_ds(node));
rc = MDBX_CORRUPTED;
goto bailout;
}
/* Need writable leaf */
if (mp != leaf) {
mc->pg[mc->top] = leaf;
page_copy(leaf, mp, ctx->env->ps);
mp = leaf;
node = page_node(mp, i);
}
tree_t *nested = nullptr;
if (node_flags(node) & N_DUP) {
rc = cursor_dupsort_setup(mc, node, mp);
if (likely(rc == MDBX_SUCCESS)) {
nested = &mc->subcur->nested_tree;
rc = compacting_walk(ctx, &mc->subcur->cursor, &nested->root, mp->txnid);
}
} else {
cASSERT(mc, (mc->flags & z_inner) == 0 && mc->subcur == 0);
cursor_couple_t *couple = container_of(mc, cursor_couple_t, outer);
nested = &couple->inner.nested_tree;
memcpy(nested, node_data(node), sizeof(tree_t));
rc = compacting_walk_tree(ctx, nested);
}
if (unlikely(rc != MDBX_SUCCESS))
goto bailout;
memcpy(node_data(node), nested, sizeof(tree_t));
}
}
}
} else {
mc->ki[mc->top]++;
if (mc->ki[mc->top] < nkeys) {
for (;;) {
const node_t *node = page_node(mp, mc->ki[mc->top]);
rc = page_get(mc, node_pgno(node), &mp, mp->txnid);
if (unlikely(rc != MDBX_SUCCESS))
goto bailout;
mc->top += 1;
if (unlikely(mc->top >= deep_limit)) {
rc = MDBX_CURSOR_FULL;
goto bailout;
}
mc->ki[mc->top] = 0;
if (!is_branch(mp)) {
mc->pg[mc->top] = mp;
break;
}
/* Whenever we advance to a sibling branch page,
* we must proceed all the way down to its first leaf. */
page_copy(mc->pg[mc->top], mp, ctx->env->ps);
}
continue;
}
}
const pgno_t pgno = ctx->first_unallocated;
if (likely(!is_dupfix_leaf(mp))) {
rc = compacting_put_page(ctx, mp, PAGEHDRSZ + mp->lower, ctx->env->ps - (PAGEHDRSZ + mp->upper), 1);
} else {
rc = compacting_put_page(ctx, mp, PAGEHDRSZ + page_numkeys(mp) * mp->dupfix_ksize, 0, 1);
}
if (unlikely(rc != MDBX_SUCCESS))
goto bailout;
if (mc->top) {
/* Update parent if there is one */
node_set_pgno(page_node(mc->pg[mc->top - 1], mc->ki[mc->top - 1]), pgno);
cursor_pop(mc);
} else {
/* Otherwise we're done */
*parent_pgno = pgno;
break;
}
}
bailout:
osal_free(buf);
return rc;
}
__cold static int compacting_walk_tree(ctx_t *ctx, tree_t *tree) {
if (unlikely(tree->root == P_INVALID))
return MDBX_SUCCESS; /* empty db */
cursor_couple_t couple;
memset(&couple, 0, sizeof(couple));
couple.inner.cursor.signature = ~cur_signature_live;
kvx_t kvx = {.clc = {.k = {.lmin = INT_MAX}, .v = {.lmin = INT_MAX}}};
int rc = cursor_init4walk(&couple, ctx->txn, tree, &kvx);
if (unlikely(rc != MDBX_SUCCESS))
return rc;
couple.outer.checking |= z_ignord | z_pagecheck;
couple.inner.cursor.checking |= z_ignord | z_pagecheck;
if (!tree->mod_txnid)
tree->mod_txnid = ctx->txn->txnid;
return compacting_walk(ctx, &couple.outer, &tree->root, tree->mod_txnid);
}
__cold static void compacting_fixup_meta(MDBX_env *env, meta_t *meta) {
eASSERT(env, meta->trees.gc.mod_txnid || meta->trees.gc.root == P_INVALID);
eASSERT(env, meta->trees.main.mod_txnid || meta->trees.main.root == P_INVALID);
/* Calculate filesize taking in account shrink/growing thresholds */
if (meta->geometry.first_unallocated != meta->geometry.now) {
meta->geometry.now = meta->geometry.first_unallocated;
const size_t aligner = pv2pages(meta->geometry.grow_pv ? meta->geometry.grow_pv : meta->geometry.shrink_pv);
if (aligner) {
const pgno_t aligned = pgno_align2os_pgno(env, meta->geometry.first_unallocated + aligner -
meta->geometry.first_unallocated % aligner);
meta->geometry.now = aligned;
}
}
if (meta->geometry.now < meta->geometry.lower)
meta->geometry.now = meta->geometry.lower;
if (meta->geometry.now > meta->geometry.upper)
meta->geometry.now = meta->geometry.upper;
/* Update signature */
assert(meta->geometry.now >= meta->geometry.first_unallocated);
meta_sign_as_steady(meta);
}
/* Make resizable */
__cold static void meta_make_sizeable(meta_t *meta) {
meta->geometry.lower = MIN_PAGENO;
if (meta->geometry.grow_pv == 0) {
const pgno_t step = 1 + (meta->geometry.upper - meta->geometry.lower) / 42;
meta->geometry.grow_pv = pages2pv(step);
}
if (meta->geometry.shrink_pv == 0) {
const pgno_t step = pv2pages(meta->geometry.grow_pv) << 1;
meta->geometry.shrink_pv = pages2pv(step);
}
}
__cold static int copy_with_compacting(MDBX_env *env, MDBX_txn *txn, mdbx_filehandle_t fd, uint8_t *buffer,
const bool dest_is_pipe, const MDBX_copy_flags_t flags) {
const size_t meta_bytes = pgno2bytes(env, NUM_METAS);
uint8_t *const data_buffer = buffer + ceil_powerof2(meta_bytes, globals.sys_pagesize);
meta_t *const meta = meta_init_triplet(env, buffer);
meta_set_txnid(env, meta, txn->txnid);
if (flags & MDBX_CP_FORCE_DYNAMIC_SIZE)
meta_make_sizeable(meta);
/* copy canary sequences if present */
if (txn->canary.v) {
meta->canary = txn->canary;
meta->canary.v = constmeta_txnid(meta);
}
if (txn->dbs[MAIN_DBI].root == P_INVALID) {
/* When the DB is empty, handle it specially to
* fix any breakage like page leaks from ITS#8174. */
meta->trees.main.flags = txn->dbs[MAIN_DBI].flags;
compacting_fixup_meta(env, meta);
if (dest_is_pipe) {
if (flags & MDBX_CP_THROTTLE_MVCC)
mdbx_txn_park(txn, false);
int rc = osal_write(fd, buffer, meta_bytes);
if (likely(rc == MDBX_SUCCESS) && (flags & MDBX_CP_THROTTLE_MVCC) != 0)
rc = mdbx_txn_unpark(txn, false);
if (unlikely(rc != MDBX_SUCCESS))
return rc;
}
} else {
/* Count free pages + GC pages. */
cursor_couple_t couple;
int rc = cursor_init(&couple.outer, txn, FREE_DBI);
if (unlikely(rc != MDBX_SUCCESS))
return rc;
pgno_t gc_npages = txn->dbs[FREE_DBI].branch_pages + txn->dbs[FREE_DBI].leaf_pages + txn->dbs[FREE_DBI].large_pages;
MDBX_val key, data;
rc = outer_first(&couple.outer, &key, &data);
while (rc == MDBX_SUCCESS) {
const pnl_t pnl = data.iov_base;
if (unlikely(data.iov_len % sizeof(pgno_t) || data.iov_len < MDBX_PNL_SIZEOF(pnl))) {
ERROR("%s/%d: %s %zu", "MDBX_CORRUPTED", MDBX_CORRUPTED, "invalid GC-record length", data.iov_len);
return MDBX_CORRUPTED;
}
if (unlikely(!pnl_check(pnl, txn->geo.first_unallocated))) {
ERROR("%s/%d: %s", "MDBX_CORRUPTED", MDBX_CORRUPTED, "invalid GC-record content");
return MDBX_CORRUPTED;
}
gc_npages += MDBX_PNL_GETSIZE(pnl);
rc = outer_next(&couple.outer, &key, &data, MDBX_NEXT);
}
if (unlikely(rc != MDBX_NOTFOUND))
return rc;
meta->geometry.first_unallocated = txn->geo.first_unallocated - gc_npages;
meta->trees.main = txn->dbs[MAIN_DBI];
ctx_t ctx;
memset(&ctx, 0, sizeof(ctx));
rc = osal_condpair_init(&ctx.condpair);
if (unlikely(rc != MDBX_SUCCESS))
return rc;
memset(data_buffer, 0, 2 * (size_t)MDBX_ENVCOPY_WRITEBUF);
ctx.write_buf[0] = data_buffer;
ctx.write_buf[1] = data_buffer + (size_t)MDBX_ENVCOPY_WRITEBUF;
ctx.first_unallocated = NUM_METAS;
ctx.env = env;
ctx.fd = fd;
ctx.txn = txn;
ctx.flags = flags;
osal_thread_t thread;
int thread_err = osal_thread_create(&thread, compacting_write_thread, &ctx);
if (likely(thread_err == MDBX_SUCCESS)) {
if (dest_is_pipe) {
if (!meta->trees.main.mod_txnid)
meta->trees.main.mod_txnid = txn->txnid;
compacting_fixup_meta(env, meta);
if (flags & MDBX_CP_THROTTLE_MVCC)
mdbx_txn_park(txn, false);
rc = osal_write(fd, buffer, meta_bytes);
if (likely(rc == MDBX_SUCCESS) && (flags & MDBX_CP_THROTTLE_MVCC) != 0)
rc = mdbx_txn_unpark(txn, false);
}
if (likely(rc == MDBX_SUCCESS))
rc = compacting_walk_tree(&ctx, &meta->trees.main);
if (ctx.write_len[ctx.head & 1])
/* toggle to flush non-empty buffers */
compacting_toggle_write_buffers(&ctx);
if (likely(rc == MDBX_SUCCESS) && unlikely(meta->geometry.first_unallocated != ctx.first_unallocated)) {
if (ctx.first_unallocated > meta->geometry.first_unallocated) {
ERROR("the source DB %s: post-compactification used pages %" PRIaPGNO " %c expected %" PRIaPGNO,
"has double-used pages or other corruption", ctx.first_unallocated, '>',
meta->geometry.first_unallocated);
rc = MDBX_CORRUPTED; /* corrupted DB */
}
if (ctx.first_unallocated < meta->geometry.first_unallocated) {
WARNING("the source DB %s: post-compactification used pages %" PRIaPGNO " %c expected %" PRIaPGNO,
"has page leak(s)", ctx.first_unallocated, '<', meta->geometry.first_unallocated);
if (dest_is_pipe)
/* the root within already written meta-pages is wrong */
rc = MDBX_CORRUPTED;
}
/* fixup meta */
meta->geometry.first_unallocated = ctx.first_unallocated;
}
/* toggle with empty buffers to exit thread's loop */
eASSERT(env, (ctx.write_len[ctx.head & 1]) == 0);
compacting_toggle_write_buffers(&ctx);
thread_err = osal_thread_join(thread);
eASSERT(env, (ctx.tail == ctx.head && ctx.write_len[ctx.head & 1] == 0) || ctx.error);
osal_condpair_destroy(&ctx.condpair);
}
if (unlikely(thread_err != MDBX_SUCCESS))
return thread_err;
if (unlikely(rc != MDBX_SUCCESS))
return rc;
if (unlikely(ctx.error != MDBX_SUCCESS))
return ctx.error;
if (!dest_is_pipe)
compacting_fixup_meta(env, meta);
}
if (flags & MDBX_CP_THROTTLE_MVCC)
mdbx_txn_park(txn, false);
/* Extend file if required */
if (meta->geometry.now != meta->geometry.first_unallocated) {
const size_t whole_size = pgno2bytes(env, meta->geometry.now);
if (!dest_is_pipe)
return osal_ftruncate(fd, whole_size);
const size_t used_size = pgno2bytes(env, meta->geometry.first_unallocated);
memset(data_buffer, 0, (size_t)MDBX_ENVCOPY_WRITEBUF);
for (size_t offset = used_size; offset < whole_size;) {
const size_t chunk =
((size_t)MDBX_ENVCOPY_WRITEBUF < whole_size - offset) ? (size_t)MDBX_ENVCOPY_WRITEBUF : whole_size - offset;
int rc = osal_write(fd, data_buffer, chunk);
if (unlikely(rc != MDBX_SUCCESS))
return rc;
offset += chunk;
}
}
return MDBX_SUCCESS;
}
//----------------------------------------------------------------------------
__cold static int copy_asis(MDBX_env *env, MDBX_txn *txn, mdbx_filehandle_t fd, uint8_t *buffer,
const bool dest_is_pipe, const MDBX_copy_flags_t flags) {
bool should_unlock = false;
if ((txn->flags & MDBX_TXN_RDONLY) != 0 && (flags & MDBX_CP_RENEW_TXN) != 0) {
/* Try temporarily block writers until we snapshot the meta pages */
int err = lck_txn_lock(env, true);
if (likely(err == MDBX_SUCCESS))
should_unlock = true;
else if (unlikely(err != MDBX_BUSY))
return err;
}
jitter4testing(false);
int rc = MDBX_SUCCESS;
const size_t meta_bytes = pgno2bytes(env, NUM_METAS);
troika_t troika = meta_tap(env);
/* Make a snapshot of meta-pages,
* but writing ones after the data was flushed */
retry_snap_meta:
memcpy(buffer, env->dxb_mmap.base, meta_bytes);
const meta_ptr_t recent = meta_recent(env, &troika);
meta_t *headcopy = /* LY: get pointer to the snapshot copy */
ptr_disp(buffer, ptr_dist(recent.ptr_c, env->dxb_mmap.base));
jitter4testing(false);
if (txn->flags & MDBX_TXN_RDONLY) {
if (recent.txnid != txn->txnid) {
if (flags & MDBX_CP_RENEW_TXN)
rc = mdbx_txn_renew(txn);
else {
rc = MDBX_MVCC_RETARDED;
for (size_t n = 0; n < NUM_METAS; ++n) {
meta_t *const meta = page_meta(ptr_disp(buffer, pgno2bytes(env, n)));
if (troika.txnid[n] == txn->txnid && ((/* is_steady */ (troika.fsm >> n) & 1) || rc != MDBX_SUCCESS)) {
rc = MDBX_SUCCESS;
headcopy = meta;
} else if (troika.txnid[n] > txn->txnid)
meta_set_txnid(env, meta, 0);
}
}
}
if (should_unlock)
lck_txn_unlock(env);
else {
troika_t snap = meta_tap(env);
if (memcmp(&troika, &snap, sizeof(troika_t)) && rc == MDBX_SUCCESS) {
troika = snap;
goto retry_snap_meta;
}
}
}
if (unlikely(rc != MDBX_SUCCESS))
return rc;
if (txn->flags & MDBX_TXN_RDONLY)
eASSERT(env, meta_txnid(headcopy) == txn->txnid);
if (flags & MDBX_CP_FORCE_DYNAMIC_SIZE)
meta_make_sizeable(headcopy);
/* Update signature to steady */
meta_sign_as_steady(headcopy);
/* Copy the data */
const size_t whole_size = pgno_align2os_bytes(env, txn->geo.end_pgno);
const size_t used_size = pgno2bytes(env, txn->geo.first_unallocated);
jitter4testing(false);
if (flags & MDBX_CP_THROTTLE_MVCC)
mdbx_txn_park(txn, false);
if (dest_is_pipe)
rc = osal_write(fd, buffer, meta_bytes);
uint8_t *const data_buffer = buffer + ceil_powerof2(meta_bytes, globals.sys_pagesize);
#if MDBX_USE_COPYFILERANGE
static bool copyfilerange_unavailable;
#if (defined(__linux__) || defined(__gnu_linux__))
if (globals.linux_kernel_version >= 0x05030000 && globals.linux_kernel_version < 0x05130000)
copyfilerange_unavailable = true;
#endif /* linux */
bool not_the_same_filesystem = false;
if (!copyfilerange_unavailable) {
struct statfs statfs_info;
if (fstatfs(fd, &statfs_info) || statfs_info.f_type == /* ECRYPTFS_SUPER_MAGIC */ 0xf15f)
/* avoid use copyfilerange_unavailable() to ecryptfs due bugs */
not_the_same_filesystem = true;
}
#endif /* MDBX_USE_COPYFILERANGE */
for (size_t offset = meta_bytes; rc == MDBX_SUCCESS && offset < used_size;) {
if (flags & MDBX_CP_THROTTLE_MVCC) {
rc = mdbx_txn_unpark(txn, false);
if (unlikely(rc != MDBX_SUCCESS))
break;
}
#if MDBX_USE_SENDFILE
static bool sendfile_unavailable;
if (dest_is_pipe && likely(!sendfile_unavailable)) {
off_t in_offset = offset;
const ssize_t written = sendfile(fd, env->lazy_fd, &in_offset, used_size - offset);
if (likely(written > 0)) {
offset = in_offset;
if (flags & MDBX_CP_THROTTLE_MVCC)
rc = mdbx_txn_park(txn, false);
continue;
}
rc = MDBX_ENODATA;
if (written == 0 || ignore_enosys_and_eagain(rc = errno) != MDBX_RESULT_TRUE)
break;
sendfile_unavailable = true;
}
#endif /* MDBX_USE_SENDFILE */
#if MDBX_USE_COPYFILERANGE
if (!dest_is_pipe && !not_the_same_filesystem && likely(!copyfilerange_unavailable)) {
off_t in_offset = offset, out_offset = offset;
ssize_t bytes_copied = copy_file_range(env->lazy_fd, &in_offset, fd, &out_offset, used_size - offset, 0);
if (likely(bytes_copied > 0)) {
offset = in_offset;
if (flags & MDBX_CP_THROTTLE_MVCC)
rc = mdbx_txn_park(txn, false);
continue;
}
rc = MDBX_ENODATA;
if (bytes_copied == 0)
break;
rc = errno;
if (rc == EXDEV || rc == /* workaround for ecryptfs bug(s),
maybe useful for others FS */
EINVAL)
not_the_same_filesystem = true;
else if (ignore_enosys_and_eagain(rc) == MDBX_RESULT_TRUE)
copyfilerange_unavailable = true;
else
break;
}
#endif /* MDBX_USE_COPYFILERANGE */
/* fallback to portable */
const size_t chunk =
((size_t)MDBX_ENVCOPY_WRITEBUF < used_size - offset) ? (size_t)MDBX_ENVCOPY_WRITEBUF : used_size - offset;
/* copy to avoid EFAULT in case swapped-out */
memcpy(data_buffer, ptr_disp(env->dxb_mmap.base, offset), chunk);
if (flags & MDBX_CP_THROTTLE_MVCC)
mdbx_txn_park(txn, false);
rc = osal_write(fd, data_buffer, chunk);
offset += chunk;
}
/* Extend file if required */
if (likely(rc == MDBX_SUCCESS) && whole_size != used_size) {
if (!dest_is_pipe)
rc = osal_ftruncate(fd, whole_size);
else {
memset(data_buffer, 0, (size_t)MDBX_ENVCOPY_WRITEBUF);
for (size_t offset = used_size; rc == MDBX_SUCCESS && offset < whole_size;) {
const size_t chunk =
((size_t)MDBX_ENVCOPY_WRITEBUF < whole_size - offset) ? (size_t)MDBX_ENVCOPY_WRITEBUF : whole_size - offset;
rc = osal_write(fd, data_buffer, chunk);
offset += chunk;
}
}
}
return rc;
}
//----------------------------------------------------------------------------
__cold static int copy2fd(MDBX_txn *txn, mdbx_filehandle_t fd, MDBX_copy_flags_t flags) {
if (unlikely(txn->flags & MDBX_TXN_DIRTY))
return MDBX_BAD_TXN;
int rc = MDBX_SUCCESS;
if (txn->flags & MDBX_TXN_RDONLY) {
if (flags & MDBX_CP_THROTTLE_MVCC) {
rc = mdbx_txn_park(txn, true);
if (unlikely(rc != MDBX_SUCCESS))
return rc;
}
} else if (unlikely(flags & (MDBX_CP_THROTTLE_MVCC | MDBX_CP_RENEW_TXN)))
return MDBX_EINVAL;
const int dest_is_pipe = osal_is_pipe(fd);
if (MDBX_IS_ERROR(dest_is_pipe))
return dest_is_pipe;
if (!dest_is_pipe) {
rc = osal_fseek(fd, 0);
if (unlikely(rc != MDBX_SUCCESS))
return rc;
}
MDBX_env *const env = txn->env;
const size_t buffer_size =
pgno_align2os_bytes(env, NUM_METAS) +
ceil_powerof2(((flags & MDBX_CP_COMPACT) ? 2 * (size_t)MDBX_ENVCOPY_WRITEBUF : (size_t)MDBX_ENVCOPY_WRITEBUF),
globals.sys_pagesize);
uint8_t *buffer = nullptr;
rc = osal_memalign_alloc(globals.sys_pagesize, buffer_size, (void **)&buffer);
if (unlikely(rc != MDBX_SUCCESS))
return rc;
if (!dest_is_pipe) {
/* Firstly write a stub to meta-pages.
* Now we sure to incomplete copy will not be used. */
memset(buffer, -1, pgno2bytes(env, NUM_METAS));
rc = osal_write(fd, buffer, pgno2bytes(env, NUM_METAS));
}
if (likely(rc == MDBX_SUCCESS))
rc = mdbx_txn_unpark(txn, false);
if (likely(rc == MDBX_SUCCESS)) {
memset(buffer, 0, pgno2bytes(env, NUM_METAS));
rc = ((flags & MDBX_CP_COMPACT) ? copy_with_compacting : copy_asis)(env, txn, fd, buffer, dest_is_pipe, flags);
if (likely(rc == MDBX_SUCCESS))
rc = mdbx_txn_unpark(txn, false);
}
if (txn->flags & MDBX_TXN_RDONLY) {
if (flags & MDBX_CP_THROTTLE_MVCC)
mdbx_txn_park(txn, true);
else if (flags & MDBX_CP_DISPOSE_TXN)
mdbx_txn_reset(txn);
}
if (!dest_is_pipe) {
if (likely(rc == MDBX_SUCCESS) && (flags & MDBX_CP_DONT_FLUSH) == 0)
rc = osal_fsync(fd, MDBX_SYNC_DATA | MDBX_SYNC_SIZE);
/* Write actual meta */
if (likely(rc == MDBX_SUCCESS))
rc = osal_pwrite(fd, buffer, pgno2bytes(env, NUM_METAS), 0);
if (likely(rc == MDBX_SUCCESS) && (flags & MDBX_CP_DONT_FLUSH) == 0)
rc = osal_fsync(fd, MDBX_SYNC_DATA | MDBX_SYNC_IODQ);
}
osal_memalign_free(buffer);
return rc;
}
__cold static int copy2pathname(MDBX_txn *txn, const pathchar_t *dest_path, MDBX_copy_flags_t flags) {
if (unlikely(!dest_path || *dest_path == '\0'))
return MDBX_EINVAL;
/* The destination path must exist, but the destination file must not.
* We don't want the OS to cache the writes, since the source data is
* already in the OS cache. */
mdbx_filehandle_t newfd = INVALID_HANDLE_VALUE;
int rc = osal_openfile(MDBX_OPEN_COPY, txn->env, dest_path, &newfd,
#if defined(_WIN32) || defined(_WIN64)
(mdbx_mode_t)-1
#else
S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP
#endif
);
#if defined(_WIN32) || defined(_WIN64)
/* no locking required since the file opened with ShareMode == 0 */
#else
if (rc == MDBX_SUCCESS) {
MDBX_STRUCT_FLOCK lock_op;
memset(&lock_op, 0, sizeof(lock_op));
lock_op.l_type = F_WRLCK;
lock_op.l_whence = SEEK_SET;
lock_op.l_start = 0;
lock_op.l_len = OFF_T_MAX;
if (MDBX_FCNTL(newfd, MDBX_F_SETLK, &lock_op))
rc = errno;
}
#if defined(LOCK_EX) && (!defined(__ANDROID_API__) || __ANDROID_API__ >= 24)
if (rc == MDBX_SUCCESS && flock(newfd, LOCK_EX | LOCK_NB)) {
const int err_flock = errno, err_fs = osal_check_fs_local(newfd, 0);
if (err_flock != EAGAIN || err_fs != MDBX_EREMOTE) {
ERROR("%s flock(%" MDBX_PRIsPATH ") error %d, remote-fs check status %d", "unexpected", dest_path, err_flock,
err_fs);
rc = err_flock;
} else {
WARNING("%s flock(%" MDBX_PRIsPATH ") error %d, remote-fs check status %d", "ignore", dest_path, err_flock,
err_fs);
}
}
#endif /* LOCK_EX && ANDROID_API >= 24 */
#endif /* Windows / POSIX */
if (rc == MDBX_SUCCESS)
rc = copy2fd(txn, newfd, flags);
if (newfd != INVALID_HANDLE_VALUE) {
int err = osal_closefile(newfd);
if (rc == MDBX_SUCCESS && err != rc)
rc = err;
if (rc != MDBX_SUCCESS)
(void)osal_removefile(dest_path);
}
return rc;
}
//----------------------------------------------------------------------------
__cold int mdbx_txn_copy2fd(MDBX_txn *txn, mdbx_filehandle_t fd, MDBX_copy_flags_t flags) {
int rc = check_txn(txn, MDBX_TXN_BLOCKED);
if (likely(rc == MDBX_SUCCESS))
rc = copy2fd(txn, fd, flags);
if (flags & MDBX_CP_DISPOSE_TXN)
mdbx_txn_abort(txn);
return LOG_IFERR(rc);
}
__cold int mdbx_env_copy2fd(MDBX_env *env, mdbx_filehandle_t fd, MDBX_copy_flags_t flags) {
if (unlikely(flags & (MDBX_CP_DISPOSE_TXN | MDBX_CP_RENEW_TXN)))
return LOG_IFERR(MDBX_EINVAL);
int rc = check_env(env, true);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
MDBX_txn *txn = nullptr;
rc = mdbx_txn_begin(env, nullptr, MDBX_TXN_RDONLY, &txn);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
rc = copy2fd(txn, fd, flags | MDBX_CP_DISPOSE_TXN | MDBX_CP_RENEW_TXN);
mdbx_txn_abort(txn);
return LOG_IFERR(rc);
}
__cold int mdbx_txn_copy2pathname(MDBX_txn *txn, const char *dest_path, MDBX_copy_flags_t flags) {
#if defined(_WIN32) || defined(_WIN64)
wchar_t *dest_pathW = nullptr;
int rc = osal_mb2w(dest_path, &dest_pathW);
if (likely(rc == MDBX_SUCCESS)) {
rc = mdbx_txn_copy2pathnameW(txn, dest_pathW, flags);
osal_free(dest_pathW);
}
return LOG_IFERR(rc);
}
__cold int mdbx_txn_copy2pathnameW(MDBX_txn *txn, const wchar_t *dest_path, MDBX_copy_flags_t flags) {
#endif /* Windows */
int rc = check_txn(txn, MDBX_TXN_BLOCKED);
if (likely(rc == MDBX_SUCCESS))
rc = copy2pathname(txn, dest_path, flags);
if (flags & MDBX_CP_DISPOSE_TXN)
mdbx_txn_abort(txn);
return LOG_IFERR(rc);
}
__cold int mdbx_env_copy(MDBX_env *env, const char *dest_path, MDBX_copy_flags_t flags) {
#if defined(_WIN32) || defined(_WIN64)
wchar_t *dest_pathW = nullptr;
int rc = osal_mb2w(dest_path, &dest_pathW);
if (likely(rc == MDBX_SUCCESS)) {
rc = mdbx_env_copyW(env, dest_pathW, flags);
osal_free(dest_pathW);
}
return LOG_IFERR(rc);
}
__cold int mdbx_env_copyW(MDBX_env *env, const wchar_t *dest_path, MDBX_copy_flags_t flags) {
#endif /* Windows */
if (unlikely(flags & (MDBX_CP_DISPOSE_TXN | MDBX_CP_RENEW_TXN)))
return LOG_IFERR(MDBX_EINVAL);
int rc = check_env(env, true);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
MDBX_txn *txn = nullptr;
rc = mdbx_txn_begin(env, nullptr, MDBX_TXN_RDONLY, &txn);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
rc = copy2pathname(txn, dest_path, flags | MDBX_CP_DISPOSE_TXN | MDBX_CP_RENEW_TXN);
mdbx_txn_abort(txn);
return LOG_IFERR(rc);
}

755
src/api-cursor.c Normal file
View File

@ -0,0 +1,755 @@
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
#include "internals.h"
MDBX_cursor *mdbx_cursor_create(void *context) {
cursor_couple_t *couple = osal_calloc(1, sizeof(cursor_couple_t));
if (unlikely(!couple))
return nullptr;
VALGRIND_MAKE_MEM_UNDEFINED(couple, sizeof(cursor_couple_t));
couple->outer.signature = cur_signature_ready4dispose;
couple->outer.next = &couple->outer;
couple->userctx = context;
cursor_reset(couple);
VALGRIND_MAKE_MEM_DEFINED(&couple->outer.backup, sizeof(couple->outer.backup));
VALGRIND_MAKE_MEM_DEFINED(&couple->outer.tree, sizeof(couple->outer.tree));
VALGRIND_MAKE_MEM_DEFINED(&couple->outer.clc, sizeof(couple->outer.clc));
VALGRIND_MAKE_MEM_DEFINED(&couple->outer.dbi_state, sizeof(couple->outer.dbi_state));
VALGRIND_MAKE_MEM_DEFINED(&couple->outer.subcur, sizeof(couple->outer.subcur));
VALGRIND_MAKE_MEM_DEFINED(&couple->outer.txn, sizeof(couple->outer.txn));
return &couple->outer;
}
int mdbx_cursor_renew(MDBX_txn *txn, MDBX_cursor *mc) {
return likely(mc) ? mdbx_cursor_bind(txn, mc, (kvx_t *)mc->clc - txn->env->kvs) : LOG_IFERR(MDBX_EINVAL);
}
int mdbx_cursor_reset(MDBX_cursor *mc) {
int rc = cursor_check(mc, MDBX_TXN_FINISHED);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
cursor_reset((cursor_couple_t *)mc);
return MDBX_SUCCESS;
}
int mdbx_cursor_bind(MDBX_txn *txn, MDBX_cursor *mc, MDBX_dbi dbi) {
if (unlikely(!mc))
return LOG_IFERR(MDBX_EINVAL);
if (unlikely(mc->signature != cur_signature_ready4dispose && mc->signature != cur_signature_live)) {
int rc = (mc->signature == cur_signature_wait4eot) ? MDBX_EINVAL : MDBX_EBADSIGN;
return LOG_IFERR(rc);
}
int rc = check_txn(txn, MDBX_TXN_FINISHED | MDBX_TXN_HAS_CHILD);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
if (unlikely(dbi == FREE_DBI && !(txn->flags & MDBX_TXN_RDONLY)))
return LOG_IFERR(MDBX_EACCESS);
rc = dbi_check(txn, dbi);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
if (unlikely(mc->backup)) /* Cursor from parent transaction */
LOG_IFERR(MDBX_EINVAL);
if (mc->signature == cur_signature_live) {
if (mc->txn == txn && cursor_dbi(mc) == dbi)
return MDBX_SUCCESS;
rc = mdbx_cursor_unbind(mc);
if (unlikely(rc != MDBX_SUCCESS))
return (rc == MDBX_BAD_TXN) ? MDBX_EINVAL : rc;
}
cASSERT(mc, mc->next == mc);
rc = cursor_init(mc, txn, dbi);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
mc->next = txn->cursors[dbi];
txn->cursors[dbi] = mc;
txn->flags |= txn_may_have_cursors;
return MDBX_SUCCESS;
}
int mdbx_cursor_unbind(MDBX_cursor *mc) {
if (unlikely(!mc))
return LOG_IFERR(MDBX_EINVAL);
if (unlikely(mc->signature != cur_signature_live))
return (mc->signature == cur_signature_ready4dispose) ? MDBX_SUCCESS : LOG_IFERR(MDBX_EBADSIGN);
if (unlikely(mc->backup)) /* Cursor from parent transaction */
/* TODO: реализовать при переходе на двусвязный список курсоров */
return LOG_IFERR(MDBX_EINVAL);
int rc = check_txn(mc->txn, MDBX_TXN_FINISHED | MDBX_TXN_HAS_CHILD);
if (unlikely(rc != MDBX_SUCCESS)) {
for (const MDBX_txn *txn = mc->txn; rc == MDBX_BAD_TXN && check_txn(txn, MDBX_TXN_FINISHED) == MDBX_SUCCESS;
txn = txn->nested)
if (dbi_state(txn, cursor_dbi(mc)) == 0)
/* специальный случай: курсор прикреплён к родительской транзакции, но соответствующий dbi-дескриптор ещё
* не использовался во вложенной транзакции, т.е. курсор ещё не импортирован в дочернюю транзакцию и не имеет
* связанного сохранённого состояния (поэтому mcbackup равен nullptr). */
rc = MDBX_EINVAL;
return LOG_IFERR(rc);
}
if (unlikely(!mc->txn || mc->txn->signature != txn_signature)) {
ERROR("Wrong cursor's transaction %p 0x%x", __Wpedantic_format_voidptr(mc->txn), mc->txn ? mc->txn->signature : 0);
return LOG_IFERR(MDBX_PROBLEM);
}
if (mc->next != mc) {
const size_t dbi = cursor_dbi(mc);
cASSERT(mc, dbi < mc->txn->n_dbi);
cASSERT(mc, &mc->txn->env->kvs[dbi].clc == mc->clc);
if (dbi < mc->txn->n_dbi) {
MDBX_cursor **prev = &mc->txn->cursors[dbi];
while (/* *prev && */ *prev != mc) {
ENSURE(mc->txn->env, (*prev)->signature == cur_signature_live || (*prev)->signature == cur_signature_wait4eot);
prev = &(*prev)->next;
}
cASSERT(mc, *prev == mc);
*prev = mc->next;
}
mc->next = mc;
}
cursor_drown((cursor_couple_t *)mc);
mc->signature = cur_signature_ready4dispose;
return MDBX_SUCCESS;
}
int mdbx_cursor_open(MDBX_txn *txn, MDBX_dbi dbi, MDBX_cursor **ret) {
if (unlikely(!ret))
return LOG_IFERR(MDBX_EINVAL);
*ret = nullptr;
MDBX_cursor *const mc = mdbx_cursor_create(nullptr);
if (unlikely(!mc))
return LOG_IFERR(MDBX_ENOMEM);
int rc = mdbx_cursor_bind(txn, mc, dbi);
if (unlikely(rc != MDBX_SUCCESS)) {
mdbx_cursor_close(mc);
return LOG_IFERR(rc);
}
*ret = mc;
return MDBX_SUCCESS;
}
void mdbx_cursor_close(MDBX_cursor *cursor) {
if (likely(cursor)) {
int err = mdbx_cursor_close2(cursor);
if (unlikely(err != MDBX_SUCCESS))
mdbx_panic("%s:%d error %d (%s) while closing cursor", __func__, __LINE__, err, mdbx_liberr2str(err));
}
}
int mdbx_cursor_close2(MDBX_cursor *mc) {
if (unlikely(!mc))
return LOG_IFERR(MDBX_EINVAL);
if (mc->signature == cur_signature_ready4dispose) {
if (unlikely(mc->txn || mc->backup))
return LOG_IFERR(MDBX_PANIC);
cursor_drown((cursor_couple_t *)mc);
mc->signature = 0;
osal_free(mc);
return MDBX_SUCCESS;
}
if (unlikely(mc->signature != cur_signature_live))
return LOG_IFERR(MDBX_EBADSIGN);
MDBX_txn *const txn = mc->txn;
int rc = check_txn(txn, MDBX_TXN_FINISHED);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
if (mc->backup) {
/* Cursor closed before nested txn ends */
cursor_reset((cursor_couple_t *)mc);
mc->signature = cur_signature_wait4eot;
return MDBX_SUCCESS;
}
if (mc->next != mc) {
const size_t dbi = cursor_dbi(mc);
cASSERT(mc, dbi < mc->txn->n_dbi);
cASSERT(mc, &mc->txn->env->kvs[dbi].clc == mc->clc);
if (likely(dbi < txn->n_dbi)) {
MDBX_cursor **prev = &txn->cursors[dbi];
while (/* *prev && */ *prev != mc) {
ENSURE(txn->env, (*prev)->signature == cur_signature_live || (*prev)->signature == cur_signature_wait4eot);
prev = &(*prev)->next;
}
tASSERT(txn, *prev == mc);
*prev = mc->next;
}
mc->next = mc;
}
cursor_drown((cursor_couple_t *)mc);
mc->signature = 0;
osal_free(mc);
return MDBX_SUCCESS;
}
int mdbx_cursor_copy(const MDBX_cursor *src, MDBX_cursor *dest) {
int rc = cursor_check(src, MDBX_TXN_FINISHED | MDBX_TXN_HAS_CHILD);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
rc = mdbx_cursor_bind(src->txn, dest, cursor_dbi(src));
if (unlikely(rc != MDBX_SUCCESS))
return rc;
assert(dest->tree == src->tree);
assert(cursor_dbi(dest) == cursor_dbi(src));
again:
assert(dest->clc == src->clc);
assert(dest->txn == src->txn);
dest->top_and_flags = src->top_and_flags;
for (intptr_t i = 0; i <= src->top; ++i) {
dest->ki[i] = src->ki[i];
dest->pg[i] = src->pg[i];
}
if (src->subcur) {
dest->subcur->nested_tree = src->subcur->nested_tree;
src = &src->subcur->cursor;
dest = &dest->subcur->cursor;
goto again;
}
return MDBX_SUCCESS;
}
int mdbx_txn_release_all_cursors_ex(const MDBX_txn *txn, bool unbind, size_t *count) {
int rc = check_txn(txn, MDBX_TXN_FINISHED | MDBX_TXN_HAS_CHILD);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
size_t n = 0;
do {
TXN_FOREACH_DBI_FROM(txn, i, MAIN_DBI) {
MDBX_cursor *mc = txn->cursors[i], *next = nullptr;
if (mc) {
txn->cursors[i] = nullptr;
do {
next = mc->next;
if (mc->signature == cur_signature_live) {
mc->signature = cur_signature_wait4eot;
cursor_drown((cursor_couple_t *)mc);
} else
ENSURE(nullptr, mc->signature == cur_signature_wait4eot);
if (mc->backup) {
MDBX_cursor *bk = mc->backup;
mc->next = bk->next;
mc->backup = bk->backup;
bk->backup = nullptr;
bk->signature = 0;
osal_free(bk);
} else {
mc->signature = cur_signature_ready4dispose;
mc->next = mc;
++n;
if (!unbind) {
mc->signature = 0;
osal_free(mc);
}
}
} while ((mc = next) != nullptr);
}
}
txn = txn->parent;
} while (txn);
if (count)
*count = n;
return MDBX_SUCCESS;
}
int mdbx_cursor_compare(const MDBX_cursor *l, const MDBX_cursor *r, bool ignore_multival) {
const int incomparable = INT16_MAX + 1;
if (unlikely(!l))
return r ? -incomparable * 9 : 0;
else if (unlikely(!r))
return incomparable * 9;
if (unlikely(cursor_check_pure(l) != MDBX_SUCCESS))
return (cursor_check_pure(r) == MDBX_SUCCESS) ? -incomparable * 8 : 0;
if (unlikely(cursor_check_pure(r) != MDBX_SUCCESS))
return (cursor_check_pure(l) == MDBX_SUCCESS) ? incomparable * 8 : 0;
if (unlikely(l->clc != r->clc)) {
if (l->txn->env != r->txn->env)
return (l->txn->env > r->txn->env) ? incomparable * 7 : -incomparable * 7;
if (l->txn->txnid != r->txn->txnid)
return (l->txn->txnid > r->txn->txnid) ? incomparable * 6 : -incomparable * 6;
return (l->clc > r->clc) ? incomparable * 5 : -incomparable * 5;
}
assert(cursor_dbi(l) == cursor_dbi(r));
int diff = is_pointed(l) - is_pointed(r);
if (unlikely(diff))
return (diff > 0) ? incomparable * 4 : -incomparable * 4;
if (unlikely(!is_pointed(l)))
return 0;
intptr_t detent = (l->top <= r->top) ? l->top : r->top;
for (intptr_t i = 0; i <= detent; ++i) {
diff = l->ki[i] - r->ki[i];
if (diff)
return diff;
}
if (unlikely(l->top != r->top))
return (l->top > r->top) ? incomparable * 3 : -incomparable * 3;
assert((l->subcur != nullptr) == (r->subcur != nullptr));
if (unlikely((l->subcur != nullptr) != (r->subcur != nullptr)))
return l->subcur ? incomparable * 2 : -incomparable * 2;
if (ignore_multival || !l->subcur)
return 0;
#if MDBX_DEBUG
if (is_pointed(&l->subcur->cursor)) {
const page_t *mp = l->pg[l->top];
const node_t *node = page_node(mp, l->ki[l->top]);
assert(node_flags(node) & N_DUP);
}
if (is_pointed(&r->subcur->cursor)) {
const page_t *mp = r->pg[r->top];
const node_t *node = page_node(mp, r->ki[r->top]);
assert(node_flags(node) & N_DUP);
}
#endif /* MDBX_DEBUG */
l = &l->subcur->cursor;
r = &r->subcur->cursor;
diff = is_pointed(l) - is_pointed(r);
if (unlikely(diff))
return (diff > 0) ? incomparable * 2 : -incomparable * 2;
if (unlikely(!is_pointed(l)))
return 0;
detent = (l->top <= r->top) ? l->top : r->top;
for (intptr_t i = 0; i <= detent; ++i) {
diff = l->ki[i] - r->ki[i];
if (diff)
return diff;
}
if (unlikely(l->top != r->top))
return (l->top > r->top) ? incomparable : -incomparable;
return (l->flags & z_eof_hard) - (r->flags & z_eof_hard);
}
int mdbx_cursor_count_ex(const MDBX_cursor *mc, size_t *count, MDBX_stat *ns, size_t bytes) {
int rc = cursor_check_ro(mc);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
if (ns) {
const size_t size_before_modtxnid = offsetof(MDBX_stat, ms_mod_txnid);
if (unlikely(bytes != sizeof(MDBX_stat)) && bytes != size_before_modtxnid)
return LOG_IFERR(MDBX_EINVAL);
memset(ns, 0, sizeof(*ns));
}
size_t nvals = 0;
if (is_filled(mc)) {
nvals = 1;
if (!inner_hollow(mc)) {
const page_t *mp = mc->pg[mc->top];
const node_t *node = page_node(mp, mc->ki[mc->top]);
cASSERT(mc, node_flags(node) & N_DUP);
const tree_t *nt = &mc->subcur->nested_tree;
nvals = unlikely(nt->items > PTRDIFF_MAX) ? PTRDIFF_MAX : (size_t)nt->items;
if (ns) {
ns->ms_psize = (unsigned)node_ds(node);
if (node_flags(node) & N_TREE) {
ns->ms_psize = mc->txn->env->ps;
ns->ms_depth = nt->height;
ns->ms_branch_pages = nt->branch_pages;
}
cASSERT(mc, nt->large_pages == 0);
ns->ms_leaf_pages = nt->leaf_pages;
ns->ms_entries = nt->items;
if (likely(bytes >= offsetof(MDBX_stat, ms_mod_txnid) + sizeof(ns->ms_mod_txnid)))
ns->ms_mod_txnid = nt->mod_txnid;
}
}
}
if (likely(count))
*count = nvals;
return MDBX_SUCCESS;
}
int mdbx_cursor_count(const MDBX_cursor *mc, size_t *count) {
if (unlikely(count == nullptr))
return LOG_IFERR(MDBX_EINVAL);
return mdbx_cursor_count_ex(mc, count, nullptr, 0);
}
int mdbx_cursor_on_first(const MDBX_cursor *mc) {
int rc = cursor_check_pure(mc);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
for (intptr_t i = 0; i <= mc->top; ++i) {
if (mc->ki[i])
return MDBX_RESULT_FALSE;
}
return MDBX_RESULT_TRUE;
}
int mdbx_cursor_on_first_dup(const MDBX_cursor *mc) {
int rc = cursor_check_pure(mc);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
if (is_filled(mc) && mc->subcur) {
mc = &mc->subcur->cursor;
for (intptr_t i = 0; i <= mc->top; ++i) {
if (mc->ki[i])
return MDBX_RESULT_FALSE;
}
}
return MDBX_RESULT_TRUE;
}
int mdbx_cursor_on_last(const MDBX_cursor *mc) {
int rc = cursor_check_pure(mc);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
for (intptr_t i = 0; i <= mc->top; ++i) {
size_t nkeys = page_numkeys(mc->pg[i]);
if (mc->ki[i] < nkeys - 1)
return MDBX_RESULT_FALSE;
}
return MDBX_RESULT_TRUE;
}
int mdbx_cursor_on_last_dup(const MDBX_cursor *mc) {
int rc = cursor_check_pure(mc);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
if (is_filled(mc) && mc->subcur) {
mc = &mc->subcur->cursor;
for (intptr_t i = 0; i <= mc->top; ++i) {
size_t nkeys = page_numkeys(mc->pg[i]);
if (mc->ki[i] < nkeys - 1)
return MDBX_RESULT_FALSE;
}
}
return MDBX_RESULT_TRUE;
}
int mdbx_cursor_eof(const MDBX_cursor *mc) {
int rc = cursor_check_pure(mc);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
return is_eof(mc) ? MDBX_RESULT_TRUE : MDBX_RESULT_FALSE;
}
int mdbx_cursor_get(MDBX_cursor *mc, MDBX_val *key, MDBX_val *data, MDBX_cursor_op op) {
int rc = cursor_check_ro(mc);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
return LOG_IFERR(cursor_ops(mc, key, data, op));
}
__hot static int scan_confinue(MDBX_cursor *mc, MDBX_predicate_func *predicate, void *context, void *arg, MDBX_val *key,
MDBX_val *value, MDBX_cursor_op turn_op) {
int rc;
switch (turn_op) {
case MDBX_NEXT:
case MDBX_NEXT_NODUP:
for (;;) {
rc = predicate(context, key, value, arg);
if (rc != MDBX_RESULT_FALSE)
return rc;
rc = outer_next(mc, key, value, turn_op);
if (unlikely(rc != MDBX_SUCCESS))
return (rc == MDBX_NOTFOUND) ? MDBX_RESULT_FALSE : rc;
}
case MDBX_PREV:
case MDBX_PREV_NODUP:
for (;;) {
rc = predicate(context, key, value, arg);
if (rc != MDBX_RESULT_FALSE)
return rc;
rc = outer_prev(mc, key, value, turn_op);
if (unlikely(rc != MDBX_SUCCESS))
return (rc == MDBX_NOTFOUND) ? MDBX_RESULT_FALSE : rc;
}
case MDBX_NEXT_DUP:
if (mc->subcur)
for (;;) {
rc = predicate(context, key, value, arg);
if (rc != MDBX_RESULT_FALSE)
return rc;
rc = inner_next(&mc->subcur->cursor, value);
if (unlikely(rc != MDBX_SUCCESS))
return (rc == MDBX_NOTFOUND) ? MDBX_RESULT_FALSE : rc;
}
return MDBX_NOTFOUND;
case MDBX_PREV_DUP:
if (mc->subcur)
for (;;) {
rc = predicate(context, key, value, arg);
if (rc != MDBX_RESULT_FALSE)
return rc;
rc = inner_prev(&mc->subcur->cursor, value);
if (unlikely(rc != MDBX_SUCCESS))
return (rc == MDBX_NOTFOUND) ? MDBX_RESULT_FALSE : rc;
}
return MDBX_NOTFOUND;
default:
for (;;) {
rc = predicate(context, key, value, arg);
if (rc != MDBX_RESULT_FALSE)
return rc;
rc = cursor_ops(mc, key, value, turn_op);
if (unlikely(rc != MDBX_SUCCESS))
return (rc == MDBX_NOTFOUND) ? MDBX_RESULT_FALSE : rc;
}
}
}
int mdbx_cursor_scan(MDBX_cursor *mc, MDBX_predicate_func *predicate, void *context, MDBX_cursor_op start_op,
MDBX_cursor_op turn_op, void *arg) {
if (unlikely(!predicate))
return LOG_IFERR(MDBX_EINVAL);
const unsigned valid_start_mask = 1 << MDBX_FIRST | 1 << MDBX_FIRST_DUP | 1 << MDBX_LAST | 1 << MDBX_LAST_DUP |
1 << MDBX_GET_CURRENT | 1 << MDBX_GET_MULTIPLE;
if (unlikely(start_op > 30 || ((1 << start_op) & valid_start_mask) == 0))
return LOG_IFERR(MDBX_EINVAL);
const unsigned valid_turn_mask = 1 << MDBX_NEXT | 1 << MDBX_NEXT_DUP | 1 << MDBX_NEXT_NODUP | 1 << MDBX_PREV |
1 << MDBX_PREV_DUP | 1 << MDBX_PREV_NODUP | 1 << MDBX_NEXT_MULTIPLE |
1 << MDBX_PREV_MULTIPLE;
if (unlikely(turn_op > 30 || ((1 << turn_op) & valid_turn_mask) == 0))
return LOG_IFERR(MDBX_EINVAL);
MDBX_val key = {nullptr, 0}, value = {nullptr, 0};
int rc = mdbx_cursor_get(mc, &key, &value, start_op);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
return LOG_IFERR(scan_confinue(mc, predicate, context, arg, &key, &value, turn_op));
}
int mdbx_cursor_scan_from(MDBX_cursor *mc, MDBX_predicate_func *predicate, void *context, MDBX_cursor_op from_op,
MDBX_val *key, MDBX_val *value, MDBX_cursor_op turn_op, void *arg) {
if (unlikely(!predicate || !key))
return LOG_IFERR(MDBX_EINVAL);
const unsigned valid_start_mask = 1 << MDBX_GET_BOTH | 1 << MDBX_GET_BOTH_RANGE | 1 << MDBX_SET_KEY |
1 << MDBX_GET_MULTIPLE | 1 << MDBX_SET_LOWERBOUND | 1 << MDBX_SET_UPPERBOUND;
if (unlikely(from_op < MDBX_TO_KEY_LESSER_THAN && ((1 << from_op) & valid_start_mask) == 0))
return LOG_IFERR(MDBX_EINVAL);
const unsigned valid_turn_mask = 1 << MDBX_NEXT | 1 << MDBX_NEXT_DUP | 1 << MDBX_NEXT_NODUP | 1 << MDBX_PREV |
1 << MDBX_PREV_DUP | 1 << MDBX_PREV_NODUP | 1 << MDBX_NEXT_MULTIPLE |
1 << MDBX_PREV_MULTIPLE;
if (unlikely(turn_op > 30 || ((1 << turn_op) & valid_turn_mask) == 0))
return LOG_IFERR(MDBX_EINVAL);
int rc = mdbx_cursor_get(mc, key, value, from_op);
if (unlikely(MDBX_IS_ERROR(rc)))
return LOG_IFERR(rc);
cASSERT(mc, key != nullptr);
MDBX_val stub;
if (!value) {
value = &stub;
rc = cursor_ops(mc, key, value, MDBX_GET_CURRENT);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
}
return LOG_IFERR(scan_confinue(mc, predicate, context, arg, key, value, turn_op));
}
int mdbx_cursor_get_batch(MDBX_cursor *mc, size_t *count, MDBX_val *pairs, size_t limit, MDBX_cursor_op op) {
if (unlikely(!count))
return LOG_IFERR(MDBX_EINVAL);
*count = 0;
if (unlikely(limit < 4 || limit > INTPTR_MAX - 2))
return LOG_IFERR(MDBX_EINVAL);
int rc = cursor_check_ro(mc);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
if (unlikely(mc->subcur))
return LOG_IFERR(MDBX_INCOMPATIBLE) /* must be a non-dupsort table */;
switch (op) {
case MDBX_NEXT:
if (unlikely(is_eof(mc)))
return LOG_IFERR(is_pointed(mc) ? MDBX_NOTFOUND : MDBX_ENODATA);
break;
case MDBX_FIRST:
if (!is_filled(mc)) {
rc = outer_first(mc, nullptr, nullptr);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
}
break;
default:
DEBUG("unhandled/unimplemented cursor operation %u", op);
return LOG_IFERR(MDBX_EINVAL);
}
const page_t *mp = mc->pg[mc->top];
size_t nkeys = page_numkeys(mp);
size_t ki = mc->ki[mc->top];
size_t n = 0;
while (n + 2 <= limit) {
cASSERT(mc, ki < nkeys);
if (unlikely(ki >= nkeys))
goto sibling;
const node_t *leaf = page_node(mp, ki);
pairs[n] = get_key(leaf);
rc = node_read(mc, leaf, &pairs[n + 1], mp);
if (unlikely(rc != MDBX_SUCCESS))
goto bailout;
n += 2;
if (++ki == nkeys) {
sibling:
rc = cursor_sibling_right(mc);
if (rc != MDBX_SUCCESS) {
if (rc == MDBX_NOTFOUND)
rc = MDBX_RESULT_TRUE;
goto bailout;
}
mp = mc->pg[mc->top];
DEBUG("next page is %" PRIaPGNO ", key index %u", mp->pgno, mc->ki[mc->top]);
if (!MDBX_DISABLE_VALIDATION && unlikely(!check_leaf_type(mc, mp))) {
ERROR("unexpected leaf-page #%" PRIaPGNO " type 0x%x seen by cursor", mp->pgno, mp->flags);
rc = MDBX_CORRUPTED;
goto bailout;
}
nkeys = page_numkeys(mp);
ki = 0;
}
}
mc->ki[mc->top] = (indx_t)ki;
bailout:
*count = n;
return LOG_IFERR(rc);
}
/*----------------------------------------------------------------------------*/
int mdbx_cursor_set_userctx(MDBX_cursor *mc, void *ctx) {
int rc = cursor_check(mc, 0);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
cursor_couple_t *couple = container_of(mc, cursor_couple_t, outer);
couple->userctx = ctx;
return MDBX_SUCCESS;
}
void *mdbx_cursor_get_userctx(const MDBX_cursor *mc) {
if (unlikely(!mc))
return nullptr;
if (unlikely(mc->signature != cur_signature_ready4dispose && mc->signature != cur_signature_live))
return nullptr;
cursor_couple_t *couple = container_of(mc, cursor_couple_t, outer);
return couple->userctx;
}
MDBX_txn *mdbx_cursor_txn(const MDBX_cursor *mc) {
if (unlikely(!mc || mc->signature != cur_signature_live))
return nullptr;
MDBX_txn *txn = mc->txn;
if (unlikely(!txn || txn->signature != txn_signature || (txn->flags & MDBX_TXN_FINISHED)))
return nullptr;
return (txn->flags & MDBX_TXN_HAS_CHILD) ? txn->env->txn : txn;
}
MDBX_dbi mdbx_cursor_dbi(const MDBX_cursor *mc) {
if (unlikely(!mc || mc->signature != cur_signature_live))
return UINT_MAX;
return cursor_dbi(mc);
}
/*----------------------------------------------------------------------------*/
int mdbx_cursor_put(MDBX_cursor *mc, const MDBX_val *key, MDBX_val *data, MDBX_put_flags_t flags) {
if (unlikely(key == nullptr || data == nullptr))
return LOG_IFERR(MDBX_EINVAL);
int rc = cursor_check_rw(mc);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
if (unlikely(flags & MDBX_MULTIPLE)) {
rc = cursor_check_multiple(mc, key, data, flags);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
}
if (flags & MDBX_RESERVE) {
if (unlikely(mc->tree->flags & (MDBX_DUPSORT | MDBX_REVERSEDUP | MDBX_INTEGERDUP | MDBX_DUPFIXED)))
return LOG_IFERR(MDBX_INCOMPATIBLE);
data->iov_base = nullptr;
}
return LOG_IFERR(cursor_put_checklen(mc, key, data, flags));
}
int mdbx_cursor_del(MDBX_cursor *mc, MDBX_put_flags_t flags) {
int rc = cursor_check_rw(mc);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
return LOG_IFERR(cursor_del(mc, flags));
}
__cold int mdbx_cursor_ignord(MDBX_cursor *mc) {
int rc = cursor_check(mc, 0);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
mc->checking |= z_ignord;
if (mc->subcur)
mc->subcur->cursor.checking |= z_ignord;
return MDBX_SUCCESS;
}

315
src/api-dbi.c Normal file
View File

@ -0,0 +1,315 @@
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
#include "internals.h"
int mdbx_dbi_open2(MDBX_txn *txn, const MDBX_val *name, MDBX_db_flags_t flags, MDBX_dbi *dbi) {
return LOG_IFERR(dbi_open(txn, name, flags, dbi, nullptr, nullptr));
}
int mdbx_dbi_open_ex2(MDBX_txn *txn, const MDBX_val *name, MDBX_db_flags_t flags, MDBX_dbi *dbi, MDBX_cmp_func *keycmp,
MDBX_cmp_func *datacmp) {
return LOG_IFERR(dbi_open(txn, name, flags, dbi, keycmp, datacmp));
}
static int dbi_open_cstr(MDBX_txn *txn, const char *name_cstr, MDBX_db_flags_t flags, MDBX_dbi *dbi,
MDBX_cmp_func *keycmp, MDBX_cmp_func *datacmp) {
MDBX_val thunk, *name;
if (name_cstr == MDBX_CHK_MAIN || name_cstr == MDBX_CHK_GC || name_cstr == MDBX_CHK_META)
name = (void *)name_cstr;
else {
thunk.iov_len = strlen(name_cstr);
thunk.iov_base = (void *)name_cstr;
name = &thunk;
}
return dbi_open(txn, name, flags, dbi, keycmp, datacmp);
}
int mdbx_dbi_open(MDBX_txn *txn, const char *name, MDBX_db_flags_t flags, MDBX_dbi *dbi) {
return LOG_IFERR(dbi_open_cstr(txn, name, flags, dbi, nullptr, nullptr));
}
int mdbx_dbi_open_ex(MDBX_txn *txn, const char *name, MDBX_db_flags_t flags, MDBX_dbi *dbi, MDBX_cmp_func *keycmp,
MDBX_cmp_func *datacmp) {
return LOG_IFERR(dbi_open_cstr(txn, name, flags, dbi, keycmp, datacmp));
}
__cold int mdbx_drop(MDBX_txn *txn, MDBX_dbi dbi, bool del) {
int rc = check_txn_rw(txn, MDBX_TXN_BLOCKED);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
cursor_couple_t cx;
rc = cursor_init(&cx.outer, txn, dbi);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
if (txn->dbs[dbi].height) {
cx.outer.next = txn->cursors[dbi];
txn->cursors[dbi] = &cx.outer;
rc = tree_drop(&cx.outer, dbi == MAIN_DBI || (cx.outer.tree->flags & MDBX_DUPSORT));
txn->cursors[dbi] = cx.outer.next;
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
}
/* Invalidate the dropped DB's cursors */
for (MDBX_cursor *mc = txn->cursors[dbi]; mc; mc = mc->next)
be_poor(mc);
if (!del || dbi < CORE_DBS) {
/* reset the DB record, mark it dirty */
txn->dbi_state[dbi] |= DBI_DIRTY;
txn->dbs[dbi].height = 0;
txn->dbs[dbi].branch_pages = 0;
txn->dbs[dbi].leaf_pages = 0;
txn->dbs[dbi].large_pages = 0;
txn->dbs[dbi].items = 0;
txn->dbs[dbi].root = P_INVALID;
txn->dbs[dbi].sequence = 0;
/* txn->dbs[dbi].mod_txnid = txn->txnid; */
txn->flags |= MDBX_TXN_DIRTY;
return MDBX_SUCCESS;
}
MDBX_env *const env = txn->env;
MDBX_val name = env->kvs[dbi].name;
rc = cursor_init(&cx.outer, txn, MAIN_DBI);
if (likely(rc == MDBX_SUCCESS)) {
rc = cursor_seek(&cx.outer, &name, nullptr, MDBX_SET).err;
if (likely(rc == MDBX_SUCCESS)) {
cx.outer.next = txn->cursors[MAIN_DBI];
txn->cursors[MAIN_DBI] = &cx.outer;
rc = cursor_del(&cx.outer, N_TREE);
txn->cursors[MAIN_DBI] = cx.outer.next;
if (likely(rc == MDBX_SUCCESS)) {
tASSERT(txn, txn->dbi_state[MAIN_DBI] & DBI_DIRTY);
tASSERT(txn, txn->flags & MDBX_TXN_DIRTY);
txn->dbi_state[dbi] = DBI_LINDO | DBI_OLDEN;
rc = osal_fastmutex_acquire(&env->dbi_lock);
if (likely(rc == MDBX_SUCCESS))
return LOG_IFERR(dbi_close_release(env, dbi));
}
}
}
txn->flags |= MDBX_TXN_ERROR;
return LOG_IFERR(rc);
}
__cold int mdbx_dbi_rename(MDBX_txn *txn, MDBX_dbi dbi, const char *name_cstr) {
MDBX_val thunk, *name;
if (name_cstr == MDBX_CHK_MAIN || name_cstr == MDBX_CHK_GC || name_cstr == MDBX_CHK_META)
name = (void *)name_cstr;
else {
thunk.iov_len = strlen(name_cstr);
thunk.iov_base = (void *)name_cstr;
name = &thunk;
}
return mdbx_dbi_rename2(txn, dbi, name);
}
__cold int mdbx_dbi_rename2(MDBX_txn *txn, MDBX_dbi dbi, const MDBX_val *new_name) {
int rc = check_txn_rw(txn, MDBX_TXN_BLOCKED);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
if (unlikely(new_name == MDBX_CHK_MAIN || new_name->iov_base == MDBX_CHK_MAIN || new_name == MDBX_CHK_GC ||
new_name->iov_base == MDBX_CHK_GC || new_name == MDBX_CHK_META || new_name->iov_base == MDBX_CHK_META))
return LOG_IFERR(MDBX_EINVAL);
if (unlikely(dbi < CORE_DBS))
return LOG_IFERR(MDBX_EINVAL);
rc = dbi_check(txn, dbi);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
rc = osal_fastmutex_acquire(&txn->env->dbi_lock);
if (likely(rc == MDBX_SUCCESS)) {
struct dbi_rename_result pair = dbi_rename_locked(txn, dbi, *new_name);
if (pair.defer)
pair.defer->next = nullptr;
dbi_defer_release(txn->env, pair.defer);
rc = pair.err;
}
return LOG_IFERR(rc);
}
int mdbx_dbi_close(MDBX_env *env, MDBX_dbi dbi) {
int rc = check_env(env, true);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
if (unlikely(dbi < CORE_DBS))
return (dbi == MAIN_DBI) ? MDBX_SUCCESS : LOG_IFERR(MDBX_BAD_DBI);
if (unlikely(dbi >= env->max_dbi))
return LOG_IFERR(MDBX_BAD_DBI);
rc = osal_fastmutex_acquire(&env->dbi_lock);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
if (unlikely(dbi >= env->n_dbi)) {
rc = MDBX_BAD_DBI;
bailout:
osal_fastmutex_release(&env->dbi_lock);
return LOG_IFERR(rc);
}
while (env->basal_txn && (env->dbs_flags[dbi] & DB_VALID) && (env->basal_txn->flags & MDBX_TXN_FINISHED) == 0) {
/* LY: Опасный код, так как env->txn может быть изменено в другом потоке.
* К сожалению тут нет надежного решения и может быть падение при неверном
* использовании API (вызове mdbx_dbi_close конкурентно с завершением
* пишущей транзакции).
*
* Для минимизации вероятности падения сначала проверяем dbi-флаги
* в basal_txn, а уже после в env->txn. Таким образом, падение может быть
* только при коллизии с завершением вложенной транзакции.
*
* Альтернативно можно попробовать выполнять обновление/put записи в
* mainDb соответствующей таблице закрываемого хендла. Семантически это
* верный путь, но проблема в текущем API, в котором исторически dbi-хендл
* живет и закрывается вне транзакции. Причем проблема не только в том,
* что нет указателя на текущую пишущую транзакцию, а в том что
* пользователь точно не ожидает что закрытие хендла приведет к
* скрытой/непрозрачной активности внутри транзакции потенциально
* выполняемой в другом потоке. Другими словами, проблема может быть
* только при неверном использовании API и если пользователь это
* допускает, то точно не будет ожидать скрытых действий внутри
* транзакции, и поэтому этот путь потенциально более опасен. */
const MDBX_txn *const hazard = env->txn;
osal_compiler_barrier();
if ((dbi_state(env->basal_txn, dbi) & (DBI_LINDO | DBI_DIRTY | DBI_CREAT)) > DBI_LINDO) {
rc = MDBX_DANGLING_DBI;
goto bailout;
}
osal_memory_barrier();
if (unlikely(hazard != env->txn))
continue;
if (hazard != env->basal_txn && hazard && (hazard->flags & MDBX_TXN_FINISHED) == 0 &&
hazard->signature == txn_signature &&
(dbi_state(hazard, dbi) & (DBI_LINDO | DBI_DIRTY | DBI_CREAT)) > DBI_LINDO) {
rc = MDBX_DANGLING_DBI;
goto bailout;
}
osal_compiler_barrier();
if (likely(hazard == env->txn))
break;
}
rc = dbi_close_release(env, dbi);
return LOG_IFERR(rc);
}
int mdbx_dbi_flags_ex(const MDBX_txn *txn, MDBX_dbi dbi, unsigned *flags, unsigned *state) {
int rc = check_txn(txn, MDBX_TXN_BLOCKED - MDBX_TXN_ERROR - MDBX_TXN_PARKED);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
rc = dbi_check(txn, dbi);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
if (unlikely(!flags || !state))
return LOG_IFERR(MDBX_EINVAL);
*flags = txn->dbs[dbi].flags & DB_PERSISTENT_FLAGS;
*state = txn->dbi_state[dbi] & (DBI_FRESH | DBI_CREAT | DBI_DIRTY | DBI_STALE);
return MDBX_SUCCESS;
}
static void stat_get(const tree_t *db, MDBX_stat *st, size_t bytes) {
st->ms_depth = db->height;
st->ms_branch_pages = db->branch_pages;
st->ms_leaf_pages = db->leaf_pages;
st->ms_overflow_pages = db->large_pages;
st->ms_entries = db->items;
if (likely(bytes >= offsetof(MDBX_stat, ms_mod_txnid) + sizeof(st->ms_mod_txnid)))
st->ms_mod_txnid = db->mod_txnid;
}
__cold int mdbx_dbi_stat(const MDBX_txn *txn, MDBX_dbi dbi, MDBX_stat *dest, size_t bytes) {
int rc = check_txn(txn, MDBX_TXN_BLOCKED);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
rc = dbi_check(txn, dbi);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
if (unlikely(txn->flags & MDBX_TXN_BLOCKED))
return LOG_IFERR(MDBX_BAD_TXN);
if (unlikely(txn->dbi_state[dbi] & DBI_STALE)) {
rc = tbl_fetch((MDBX_txn *)txn, dbi);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
}
if (unlikely(!dest))
return LOG_IFERR(MDBX_EINVAL);
const size_t size_before_modtxnid = offsetof(MDBX_stat, ms_mod_txnid);
if (unlikely(bytes != sizeof(MDBX_stat)) && bytes != size_before_modtxnid)
return LOG_IFERR(MDBX_EINVAL);
dest->ms_psize = txn->env->ps;
stat_get(&txn->dbs[dbi], dest, bytes);
return MDBX_SUCCESS;
}
__cold int mdbx_enumerate_tables(const MDBX_txn *txn, MDBX_table_enum_func *func, void *ctx) {
if (unlikely(!func))
return LOG_IFERR(MDBX_EINVAL);
int rc = check_txn(txn, MDBX_TXN_BLOCKED);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
cursor_couple_t cx;
rc = cursor_init(&cx.outer, txn, MAIN_DBI);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
cx.outer.next = txn->cursors[MAIN_DBI];
txn->cursors[MAIN_DBI] = &cx.outer;
for (rc = outer_first(&cx.outer, nullptr, nullptr); rc == MDBX_SUCCESS;
rc = outer_next(&cx.outer, nullptr, nullptr, MDBX_NEXT_NODUP)) {
node_t *node = page_node(cx.outer.pg[cx.outer.top], cx.outer.ki[cx.outer.top]);
if (node_flags(node) != N_TREE)
continue;
if (unlikely(node_ds(node) != sizeof(tree_t))) {
ERROR("%s/%d: %s %u", "MDBX_CORRUPTED", MDBX_CORRUPTED, "invalid dupsort sub-tree node size",
(unsigned)node_ds(node));
rc = MDBX_CORRUPTED;
break;
}
tree_t reside;
const tree_t *tree = memcpy(&reside, node_data(node), sizeof(reside));
const MDBX_val name = {node_key(node), node_ks(node)};
const MDBX_env *const env = txn->env;
MDBX_dbi dbi = 0;
for (size_t i = CORE_DBS; i < env->n_dbi; ++i) {
if (i >= txn->n_dbi || !(env->dbs_flags[i] & DB_VALID))
continue;
if (env->kvs[MAIN_DBI].clc.k.cmp(&name, &env->kvs[i].name))
continue;
tree = dbi_dig(txn, i, &reside);
dbi = (MDBX_dbi)i;
break;
}
MDBX_stat stat;
stat_get(tree, &stat, sizeof(stat));
rc = func(ctx, txn, &name, tree->flags, &stat, dbi);
if (rc != MDBX_SUCCESS)
goto bailout;
}
rc = (rc == MDBX_NOTFOUND) ? MDBX_SUCCESS : rc;
bailout:
txn->cursors[MAIN_DBI] = cx.outer.next;
return LOG_IFERR(rc);
}

1407
src/api-env.c Normal file

File diff suppressed because it is too large Load Diff

165
src/api-extra.c Normal file
View File

@ -0,0 +1,165 @@
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
#include "internals.h"
/*------------------------------------------------------------------------------
* Readers API */
__cold int mdbx_reader_list(const MDBX_env *env, MDBX_reader_list_func *func, void *ctx) {
int rc = check_env(env, true);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
if (unlikely(!func))
return LOG_IFERR(MDBX_EINVAL);
rc = MDBX_RESULT_TRUE;
int serial = 0;
lck_t *const lck = env->lck_mmap.lck;
if (likely(lck)) {
const size_t snap_nreaders = atomic_load32(&lck->rdt_length, mo_AcquireRelease);
for (size_t i = 0; i < snap_nreaders; i++) {
const reader_slot_t *r = lck->rdt + i;
retry_reader:;
const uint32_t pid = atomic_load32(&r->pid, mo_AcquireRelease);
if (!pid)
continue;
txnid_t txnid = safe64_read(&r->txnid);
const uint64_t tid = atomic_load64(&r->tid, mo_Relaxed);
const pgno_t pages_used = atomic_load32(&r->snapshot_pages_used, mo_Relaxed);
const uint64_t reader_pages_retired = atomic_load64(&r->snapshot_pages_retired, mo_Relaxed);
if (unlikely(txnid != safe64_read(&r->txnid) || pid != atomic_load32(&r->pid, mo_AcquireRelease) ||
tid != atomic_load64(&r->tid, mo_Relaxed) ||
pages_used != atomic_load32(&r->snapshot_pages_used, mo_Relaxed) ||
reader_pages_retired != atomic_load64(&r->snapshot_pages_retired, mo_Relaxed)))
goto retry_reader;
eASSERT(env, txnid > 0);
if (txnid >= SAFE64_INVALID_THRESHOLD)
txnid = 0;
size_t bytes_used = 0;
size_t bytes_retained = 0;
uint64_t lag = 0;
if (txnid) {
troika_t troika = meta_tap(env);
retry_header:;
const meta_ptr_t head = meta_recent(env, &troika);
const uint64_t head_pages_retired = unaligned_peek_u64_volatile(4, head.ptr_v->pages_retired);
if (unlikely(meta_should_retry(env, &troika) ||
head_pages_retired != unaligned_peek_u64_volatile(4, head.ptr_v->pages_retired)))
goto retry_header;
lag = (head.txnid - txnid) / xMDBX_TXNID_STEP;
bytes_used = pgno2bytes(env, pages_used);
bytes_retained = (head_pages_retired > reader_pages_retired)
? pgno2bytes(env, (pgno_t)(head_pages_retired - reader_pages_retired))
: 0;
}
rc = func(ctx, ++serial, (unsigned)i, pid, (mdbx_tid_t)((intptr_t)tid), txnid, lag, bytes_used, bytes_retained);
if (unlikely(rc != MDBX_SUCCESS))
break;
}
}
return LOG_IFERR(rc);
}
__cold int mdbx_reader_check(MDBX_env *env, int *dead) {
if (dead)
*dead = 0;
return LOG_IFERR(mvcc_cleanup_dead(env, false, dead));
}
__cold int mdbx_thread_register(const MDBX_env *env) {
int rc = check_env(env, true);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
if (unlikely(!env->lck_mmap.lck))
return LOG_IFERR((env->flags & MDBX_EXCLUSIVE) ? MDBX_EINVAL : MDBX_EPERM);
if (unlikely((env->flags & ENV_TXKEY) == 0)) {
eASSERT(env, env->flags & MDBX_NOSTICKYTHREADS);
return LOG_IFERR(MDBX_EINVAL) /* MDBX_NOSTICKYTHREADS mode */;
}
eASSERT(env, (env->flags & (MDBX_NOSTICKYTHREADS | ENV_TXKEY)) == ENV_TXKEY);
reader_slot_t *r = thread_rthc_get(env->me_txkey);
if (unlikely(r != nullptr)) {
eASSERT(env, r->pid.weak == env->pid);
eASSERT(env, r->tid.weak == osal_thread_self());
if (unlikely(r->pid.weak != env->pid))
return LOG_IFERR(MDBX_BAD_RSLOT);
return MDBX_RESULT_TRUE /* already registered */;
}
return LOG_IFERR(mvcc_bind_slot((MDBX_env *)env).err);
}
__cold int mdbx_thread_unregister(const MDBX_env *env) {
int rc = check_env(env, true);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
if (unlikely(!env->lck_mmap.lck))
return MDBX_RESULT_TRUE;
if (unlikely((env->flags & ENV_TXKEY) == 0)) {
eASSERT(env, env->flags & MDBX_NOSTICKYTHREADS);
return MDBX_RESULT_TRUE /* MDBX_NOSTICKYTHREADS mode */;
}
eASSERT(env, (env->flags & (MDBX_NOSTICKYTHREADS | ENV_TXKEY)) == ENV_TXKEY);
reader_slot_t *r = thread_rthc_get(env->me_txkey);
if (unlikely(r == nullptr))
return MDBX_RESULT_TRUE /* not registered */;
eASSERT(env, r->pid.weak == env->pid);
if (unlikely(r->pid.weak != env->pid || r->tid.weak != osal_thread_self()))
return LOG_IFERR(MDBX_BAD_RSLOT);
eASSERT(env, r->txnid.weak >= SAFE64_INVALID_THRESHOLD);
if (unlikely(r->txnid.weak < SAFE64_INVALID_THRESHOLD))
return LOG_IFERR(MDBX_BUSY) /* transaction is still active */;
atomic_store32(&r->pid, 0, mo_Relaxed);
atomic_store32(&env->lck->rdt_refresh_flag, true, mo_AcquireRelease);
thread_rthc_set(env->me_txkey, nullptr);
return MDBX_SUCCESS;
}
/*------------------------------------------------------------------------------
* Locking API */
int mdbx_txn_lock(MDBX_env *env, bool dont_wait) {
int rc = check_env(env, true);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
if (unlikely(env->flags & MDBX_RDONLY))
return LOG_IFERR(MDBX_EACCESS);
if (dont_wait && unlikely(env->basal_txn->owner || (env->basal_txn->flags & MDBX_TXN_FINISHED) == 0))
return LOG_IFERR(MDBX_BUSY);
return LOG_IFERR(lck_txn_lock(env, dont_wait));
}
int mdbx_txn_unlock(MDBX_env *env) {
int rc = check_env(env, true);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
if (unlikely(env->flags & MDBX_RDONLY))
return LOG_IFERR(MDBX_EACCESS);
#if MDBX_TXN_CHECKOWNER
if (unlikely(env->basal_txn->owner != osal_thread_self()))
return LOG_IFERR(MDBX_THREAD_MISMATCH);
#endif /* MDBX_TXN_CHECKOWNER */
if (unlikely((env->basal_txn->flags & MDBX_TXN_FINISHED) == 0))
return LOG_IFERR(MDBX_BUSY);
lck_txn_unlock(env);
return MDBX_SUCCESS;
}

197
src/api-key-transform.c Normal file
View File

@ -0,0 +1,197 @@
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
#include "internals.h"
static inline double key2double(const int64_t key) {
union {
uint64_t u;
double f;
} casting;
casting.u = (key < 0) ? key + UINT64_C(0x8000000000000000) : UINT64_C(0xffffFFFFffffFFFF) - key;
return casting.f;
}
static inline uint64_t double2key(const double *const ptr) {
STATIC_ASSERT(sizeof(double) == sizeof(int64_t));
const int64_t i = *(const int64_t *)ptr;
const uint64_t u = (i < 0) ? UINT64_C(0xffffFFFFffffFFFF) - i : i + UINT64_C(0x8000000000000000);
if (ASSERT_ENABLED()) {
const double f = key2double(u);
assert(memcmp(&f, ptr, sizeof(double)) == 0);
}
return u;
}
static inline float key2float(const int32_t key) {
union {
uint32_t u;
float f;
} casting;
casting.u = (key < 0) ? key + UINT32_C(0x80000000) : UINT32_C(0xffffFFFF) - key;
return casting.f;
}
static inline uint32_t float2key(const float *const ptr) {
STATIC_ASSERT(sizeof(float) == sizeof(int32_t));
const int32_t i = *(const int32_t *)ptr;
const uint32_t u = (i < 0) ? UINT32_C(0xffffFFFF) - i : i + UINT32_C(0x80000000);
if (ASSERT_ENABLED()) {
const float f = key2float(u);
assert(memcmp(&f, ptr, sizeof(float)) == 0);
}
return u;
}
uint64_t mdbx_key_from_double(const double ieee754_64bit) { return double2key(&ieee754_64bit); }
uint64_t mdbx_key_from_ptrdouble(const double *const ieee754_64bit) { return double2key(ieee754_64bit); }
uint32_t mdbx_key_from_float(const float ieee754_32bit) { return float2key(&ieee754_32bit); }
uint32_t mdbx_key_from_ptrfloat(const float *const ieee754_32bit) { return float2key(ieee754_32bit); }
#define IEEE754_DOUBLE_MANTISSA_SIZE 52
#define IEEE754_DOUBLE_EXPONENTA_BIAS 0x3FF
#define IEEE754_DOUBLE_EXPONENTA_MAX 0x7FF
#define IEEE754_DOUBLE_IMPLICIT_LEAD UINT64_C(0x0010000000000000)
#define IEEE754_DOUBLE_MANTISSA_MASK UINT64_C(0x000FFFFFFFFFFFFF)
#define IEEE754_DOUBLE_MANTISSA_AMAX UINT64_C(0x001FFFFFFFFFFFFF)
static inline int clz64(uint64_t value) {
#if __GNUC_PREREQ(4, 1) || __has_builtin(__builtin_clzl)
if (sizeof(value) == sizeof(int))
return __builtin_clz(value);
if (sizeof(value) == sizeof(long))
return __builtin_clzl(value);
#if (defined(__SIZEOF_LONG_LONG__) && __SIZEOF_LONG_LONG__ == 8) || __has_builtin(__builtin_clzll)
return __builtin_clzll(value);
#endif /* have(long long) && long long == uint64_t */
#endif /* GNU C */
#if defined(_MSC_VER)
unsigned long index;
#if defined(_M_AMD64) || defined(_M_ARM64) || defined(_M_X64)
_BitScanReverse64(&index, value);
return 63 - index;
#else
if (value > UINT32_MAX) {
_BitScanReverse(&index, (uint32_t)(value >> 32));
return 31 - index;
}
_BitScanReverse(&index, (uint32_t)value);
return 63 - index;
#endif
#endif /* MSVC */
value |= value >> 1;
value |= value >> 2;
value |= value >> 4;
value |= value >> 8;
value |= value >> 16;
value |= value >> 32;
static const uint8_t debruijn_clz64[64] = {63, 16, 62, 7, 15, 36, 61, 3, 6, 14, 22, 26, 35, 47, 60, 2,
9, 5, 28, 11, 13, 21, 42, 19, 25, 31, 34, 40, 46, 52, 59, 1,
17, 8, 37, 4, 23, 27, 48, 10, 29, 12, 43, 20, 32, 41, 53, 18,
38, 24, 49, 30, 44, 33, 54, 39, 50, 45, 55, 51, 56, 57, 58, 0};
return debruijn_clz64[value * UINT64_C(0x03F79D71B4CB0A89) >> 58];
}
static inline uint64_t round_mantissa(const uint64_t u64, int shift) {
assert(shift < 0 && u64 > 0);
shift = -shift;
const unsigned half = 1 << (shift - 1);
const unsigned lsb = 1 & (unsigned)(u64 >> shift);
const unsigned tie2even = 1 ^ lsb;
return (u64 + half - tie2even) >> shift;
}
uint64_t mdbx_key_from_jsonInteger(const int64_t json_integer) {
const uint64_t bias = UINT64_C(0x8000000000000000);
if (json_integer > 0) {
const uint64_t u64 = json_integer;
int shift = clz64(u64) - (64 - IEEE754_DOUBLE_MANTISSA_SIZE - 1);
uint64_t mantissa = u64 << shift;
if (unlikely(shift < 0)) {
mantissa = round_mantissa(u64, shift);
if (mantissa > IEEE754_DOUBLE_MANTISSA_AMAX)
mantissa = round_mantissa(u64, --shift);
}
assert(mantissa >= IEEE754_DOUBLE_IMPLICIT_LEAD && mantissa <= IEEE754_DOUBLE_MANTISSA_AMAX);
const uint64_t exponent = (uint64_t)IEEE754_DOUBLE_EXPONENTA_BIAS + IEEE754_DOUBLE_MANTISSA_SIZE - shift;
assert(exponent > 0 && exponent <= IEEE754_DOUBLE_EXPONENTA_MAX);
const uint64_t key = bias + (exponent << IEEE754_DOUBLE_MANTISSA_SIZE) + (mantissa - IEEE754_DOUBLE_IMPLICIT_LEAD);
#if !defined(_MSC_VER) || defined(_DEBUG) /* Workaround for MSVC error LNK2019: unresolved external \
symbol __except1 referenced in function __ftol3_except */
assert(key == mdbx_key_from_double((double)json_integer));
#endif /* Workaround for MSVC */
return key;
}
if (json_integer < 0) {
const uint64_t u64 = -json_integer;
int shift = clz64(u64) - (64 - IEEE754_DOUBLE_MANTISSA_SIZE - 1);
uint64_t mantissa = u64 << shift;
if (unlikely(shift < 0)) {
mantissa = round_mantissa(u64, shift);
if (mantissa > IEEE754_DOUBLE_MANTISSA_AMAX)
mantissa = round_mantissa(u64, --shift);
}
assert(mantissa >= IEEE754_DOUBLE_IMPLICIT_LEAD && mantissa <= IEEE754_DOUBLE_MANTISSA_AMAX);
const uint64_t exponent = (uint64_t)IEEE754_DOUBLE_EXPONENTA_BIAS + IEEE754_DOUBLE_MANTISSA_SIZE - shift;
assert(exponent > 0 && exponent <= IEEE754_DOUBLE_EXPONENTA_MAX);
const uint64_t key =
bias - 1 - (exponent << IEEE754_DOUBLE_MANTISSA_SIZE) - (mantissa - IEEE754_DOUBLE_IMPLICIT_LEAD);
#if !defined(_MSC_VER) || defined(_DEBUG) /* Workaround for MSVC error LNK2019: unresolved external \
symbol __except1 referenced in function __ftol3_except */
assert(key == mdbx_key_from_double((double)json_integer));
#endif /* Workaround for MSVC */
return key;
}
return bias;
}
int64_t mdbx_jsonInteger_from_key(const MDBX_val v) {
assert(v.iov_len == 8);
const uint64_t key = unaligned_peek_u64(2, v.iov_base);
const uint64_t bias = UINT64_C(0x8000000000000000);
const uint64_t covalent = (key > bias) ? key - bias : bias - key - 1;
const int shift = IEEE754_DOUBLE_EXPONENTA_BIAS + 63 -
(IEEE754_DOUBLE_EXPONENTA_MAX & (int)(covalent >> IEEE754_DOUBLE_MANTISSA_SIZE));
if (unlikely(shift < 1))
return (key < bias) ? INT64_MIN : INT64_MAX;
if (unlikely(shift > 63))
return 0;
const uint64_t unscaled = ((covalent & IEEE754_DOUBLE_MANTISSA_MASK) << (63 - IEEE754_DOUBLE_MANTISSA_SIZE)) + bias;
const int64_t absolute = unscaled >> shift;
const int64_t value = (key < bias) ? -absolute : absolute;
assert(key == mdbx_key_from_jsonInteger(value) ||
(mdbx_key_from_jsonInteger(value - 1) < key && key < mdbx_key_from_jsonInteger(value + 1)));
return value;
}
double mdbx_double_from_key(const MDBX_val v) {
assert(v.iov_len == 8);
return key2double(unaligned_peek_u64(2, v.iov_base));
}
float mdbx_float_from_key(const MDBX_val v) {
assert(v.iov_len == 4);
return key2float(unaligned_peek_u32(2, v.iov_base));
}
int32_t mdbx_int32_from_key(const MDBX_val v) {
assert(v.iov_len == 4);
return (int32_t)(unaligned_peek_u32(2, v.iov_base) - UINT32_C(0x80000000));
}
int64_t mdbx_int64_from_key(const MDBX_val v) {
assert(v.iov_len == 8);
return (int64_t)(unaligned_peek_u64(2, v.iov_base) - UINT64_C(0x8000000000000000));
}

286
src/api-misc.c Normal file
View File

@ -0,0 +1,286 @@
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
#include "internals.h"
__cold int mdbx_is_readahead_reasonable(size_t volume, intptr_t redundancy) {
if (volume <= 1024 * 1024 * 4ul)
return MDBX_RESULT_TRUE;
intptr_t pagesize, total_ram_pages;
int err = mdbx_get_sysraminfo(&pagesize, &total_ram_pages, nullptr);
if (unlikely(err != MDBX_SUCCESS))
return LOG_IFERR(err);
const int log2page = log2n_powerof2(pagesize);
const intptr_t volume_pages = (volume + pagesize - 1) >> log2page;
const intptr_t redundancy_pages = (redundancy < 0) ? -(intptr_t)((-redundancy + pagesize - 1) >> log2page)
: (intptr_t)(redundancy + pagesize - 1) >> log2page;
if (volume_pages >= total_ram_pages || volume_pages + redundancy_pages >= total_ram_pages)
return MDBX_RESULT_FALSE;
intptr_t avail_ram_pages;
err = mdbx_get_sysraminfo(nullptr, nullptr, &avail_ram_pages);
if (unlikely(err != MDBX_SUCCESS))
return LOG_IFERR(err);
return (volume_pages + redundancy_pages >= avail_ram_pages) ? MDBX_RESULT_FALSE : MDBX_RESULT_TRUE;
}
int mdbx_dbi_sequence(MDBX_txn *txn, MDBX_dbi dbi, uint64_t *result, uint64_t increment) {
int rc = check_txn(txn, MDBX_TXN_BLOCKED);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
rc = dbi_check(txn, dbi);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
if (unlikely(txn->dbi_state[dbi] & DBI_STALE)) {
rc = tbl_fetch(txn, dbi);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
}
tree_t *dbs = &txn->dbs[dbi];
if (likely(result))
*result = dbs->sequence;
if (likely(increment > 0)) {
if (unlikely(dbi == FREE_DBI || (txn->flags & MDBX_TXN_RDONLY) != 0))
return MDBX_EACCESS;
uint64_t new = dbs->sequence + increment;
if (unlikely(new < increment))
return MDBX_RESULT_TRUE;
tASSERT(txn, new > dbs->sequence);
if ((txn->dbi_state[dbi] & DBI_DIRTY) == 0) {
txn->flags |= MDBX_TXN_DIRTY;
txn->dbi_state[dbi] |= DBI_DIRTY;
if (unlikely(dbi == MAIN_DBI) && txn->dbs[MAIN_DBI].root != P_INVALID) {
/* LY: Временная подпорка для coherency_check(), которую в перспективе
* следует заменить вместе с переделкой установки mod_txnid.
*
* Суть проблемы:
* - coherency_check() в качестве одного из критериев "когерентности"
* проверяет условие meta.maindb.mod_txnid == maindb.root->txnid;
* - при обновлении maindb.sequence высталяется DBI_DIRTY, что приведет
* к обновлению meta.maindb.mod_txnid = current_txnid;
* - однако, если в само дерево maindb обновление не вносились и оно
* не пустое, то корневая страницы останеться с прежним txnid и из-за
* этого ложно сработает coherency_check().
*
* Временное (текущее) решение: Принудительно обновляем корневую
* страницу в описанной выше ситуации. Это устраняет проблему, но и
* не создает рисков регресса.
*
* FIXME: Итоговое решение, которое предстоит реализовать:
* - изменить семантику установки/обновления mod_txnid, привязав его
* строго к изменению b-tree, но не атрибутов;
* - обновлять mod_txnid при фиксации вложенных транзакций;
* - для dbi-хендлов пользовательских table (видимо) можно оставить
* DBI_DIRTY в качестве признака необходимости обновления записи
* table в MainDB, при этом взводить DBI_DIRTY вместе с обновлением
* mod_txnid, в том числе при обновлении sequence.
* - для MAIN_DBI при обновлении sequence не следует взводить DBI_DIRTY
* и/или обновлять mod_txnid, а только взводить MDBX_TXN_DIRTY.
* - альтернативно, можно перераспределить флажки-признаки dbi_state,
* чтобы различать состояние dirty-tree и dirty-attributes. */
cursor_couple_t cx;
rc = cursor_init(&cx.outer, txn, MAIN_DBI);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
rc = tree_search(&cx.outer, nullptr, Z_MODIFY | Z_ROOTONLY);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
}
}
dbs->sequence = new;
}
return MDBX_SUCCESS;
}
int mdbx_cmp(const MDBX_txn *txn, MDBX_dbi dbi, const MDBX_val *a, const MDBX_val *b) {
eASSERT(nullptr, txn->signature == txn_signature);
tASSERT(txn, (dbi_state(txn, dbi) & DBI_VALID) && !dbi_changed(txn, dbi));
tASSERT(txn, dbi < txn->env->n_dbi && (txn->env->dbs_flags[dbi] & DB_VALID) != 0);
return txn->env->kvs[dbi].clc.k.cmp(a, b);
}
int mdbx_dcmp(const MDBX_txn *txn, MDBX_dbi dbi, const MDBX_val *a, const MDBX_val *b) {
eASSERT(nullptr, txn->signature == txn_signature);
tASSERT(txn, (dbi_state(txn, dbi) & DBI_VALID) && !dbi_changed(txn, dbi));
tASSERT(txn, dbi < txn->env->n_dbi && (txn->env->dbs_flags[dbi] & DB_VALID));
return txn->env->kvs[dbi].clc.v.cmp(a, b);
}
__cold MDBX_cmp_func *mdbx_get_keycmp(MDBX_db_flags_t flags) { return builtin_keycmp(flags); }
__cold MDBX_cmp_func *mdbx_get_datacmp(MDBX_db_flags_t flags) { return builtin_datacmp(flags); }
/*----------------------------------------------------------------------------*/
__cold const char *mdbx_liberr2str(int errnum) {
/* Table of descriptions for MDBX errors */
static const char *const tbl[] = {
"MDBX_KEYEXIST: Key/data pair already exists",
"MDBX_NOTFOUND: No matching key/data pair found",
"MDBX_PAGE_NOTFOUND: Requested page not found",
"MDBX_CORRUPTED: Database is corrupted",
"MDBX_PANIC: Environment had fatal error",
"MDBX_VERSION_MISMATCH: DB version mismatch libmdbx",
"MDBX_INVALID: File is not an MDBX file",
"MDBX_MAP_FULL: Environment mapsize limit reached",
"MDBX_DBS_FULL: Too many DBI-handles (maxdbs reached)",
"MDBX_READERS_FULL: Too many readers (maxreaders reached)",
nullptr /* MDBX_TLS_FULL (-30789): unused in MDBX */,
"MDBX_TXN_FULL: Transaction has too many dirty pages,"
" i.e transaction is too big",
"MDBX_CURSOR_FULL: Cursor stack limit reachedn - this usually indicates"
" corruption, i.e branch-pages loop",
"MDBX_PAGE_FULL: Internal error - Page has no more space",
"MDBX_UNABLE_EXTEND_MAPSIZE: Database engine was unable to extend"
" mapping, e.g. since address space is unavailable or busy,"
" or Operation system not supported such operations",
"MDBX_INCOMPATIBLE: Environment or database is not compatible"
" with the requested operation or the specified flags",
"MDBX_BAD_RSLOT: Invalid reuse of reader locktable slot,"
" e.g. read-transaction already run for current thread",
"MDBX_BAD_TXN: Transaction is not valid for requested operation,"
" e.g. had errored and be must aborted, has a child, or is invalid",
"MDBX_BAD_VALSIZE: Invalid size or alignment of key or data"
" for target database, either invalid table name",
"MDBX_BAD_DBI: The specified DBI-handle is invalid"
" or changed by another thread/transaction",
"MDBX_PROBLEM: Unexpected internal error, transaction should be aborted",
"MDBX_BUSY: Another write transaction is running,"
" or environment is already used while opening with MDBX_EXCLUSIVE flag",
};
if (errnum >= MDBX_KEYEXIST && errnum <= MDBX_BUSY) {
int i = errnum - MDBX_KEYEXIST;
return tbl[i];
}
switch (errnum) {
case MDBX_SUCCESS:
return "MDBX_SUCCESS: Successful";
case MDBX_EMULTIVAL:
return "MDBX_EMULTIVAL: The specified key has"
" more than one associated value";
case MDBX_EBADSIGN:
return "MDBX_EBADSIGN: Wrong signature of a runtime object(s),"
" e.g. memory corruption or double-free";
case MDBX_WANNA_RECOVERY:
return "MDBX_WANNA_RECOVERY: Database should be recovered,"
" but this could NOT be done automatically for now"
" since it opened in read-only mode";
case MDBX_EKEYMISMATCH:
return "MDBX_EKEYMISMATCH: The given key value is mismatched to the"
" current cursor position";
case MDBX_TOO_LARGE:
return "MDBX_TOO_LARGE: Database is too large for current system,"
" e.g. could NOT be mapped into RAM";
case MDBX_THREAD_MISMATCH:
return "MDBX_THREAD_MISMATCH: A thread has attempted to use a not"
" owned object, e.g. a transaction that started by another thread";
case MDBX_TXN_OVERLAPPING:
return "MDBX_TXN_OVERLAPPING: Overlapping read and write transactions for"
" the current thread";
case MDBX_DUPLICATED_CLK:
return "MDBX_DUPLICATED_CLK: Alternative/Duplicate LCK-file is exists,"
" please keep one and remove unused other";
case MDBX_DANGLING_DBI:
return "MDBX_DANGLING_DBI: Some cursors and/or other resources should be"
" closed before table or corresponding DBI-handle could be (re)used";
case MDBX_OUSTED:
return "MDBX_OUSTED: The parked read transaction was outed for the sake"
" of recycling old MVCC snapshots";
case MDBX_MVCC_RETARDED:
return "MDBX_MVCC_RETARDED: MVCC snapshot used by parked transaction was bygone";
default:
return nullptr;
}
}
__cold const char *mdbx_strerror_r(int errnum, char *buf, size_t buflen) {
const char *msg = mdbx_liberr2str(errnum);
if (!msg && buflen > 0 && buflen < INT_MAX) {
#if defined(_WIN32) || defined(_WIN64)
DWORD size = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr, errnum,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buf, (DWORD)buflen, nullptr);
while (size && buf[size - 1] <= ' ')
--size;
buf[size] = 0;
return size ? buf : "FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM) failed";
#elif defined(_GNU_SOURCE) && defined(__GLIBC__)
/* GNU-specific */
if (errnum > 0)
msg = strerror_r(errnum, buf, buflen);
#elif (_POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600)
/* XSI-compliant */
if (errnum > 0 && strerror_r(errnum, buf, buflen) == 0)
msg = buf;
#else
if (errnum > 0) {
msg = strerror(errnum);
if (msg) {
strncpy(buf, msg, buflen);
msg = buf;
}
}
#endif
if (!msg) {
(void)snprintf(buf, buflen, "error %d", errnum);
msg = buf;
}
buf[buflen - 1] = '\0';
}
return msg;
}
__cold const char *mdbx_strerror(int errnum) {
#if defined(_WIN32) || defined(_WIN64)
static char buf[1024];
return mdbx_strerror_r(errnum, buf, sizeof(buf));
#else
const char *msg = mdbx_liberr2str(errnum);
if (!msg) {
if (errnum > 0)
msg = strerror(errnum);
if (!msg) {
static char buf[32];
(void)snprintf(buf, sizeof(buf) - 1, "error %d", errnum);
msg = buf;
}
}
return msg;
#endif
}
#if defined(_WIN32) || defined(_WIN64) /* Bit of madness for Windows */
const char *mdbx_strerror_r_ANSI2OEM(int errnum, char *buf, size_t buflen) {
const char *msg = mdbx_liberr2str(errnum);
if (!msg && buflen > 0 && buflen < INT_MAX) {
DWORD size = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr, errnum,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buf, (DWORD)buflen, nullptr);
while (size && buf[size - 1] <= ' ')
--size;
buf[size] = 0;
if (!size)
msg = "FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM) failed";
else if (!CharToOemBuffA(buf, buf, size))
msg = "CharToOemBuffA() failed";
else
msg = buf;
}
return msg;
}
const char *mdbx_strerror_ANSI2OEM(int errnum) {
static char buf[1024];
return mdbx_strerror_r_ANSI2OEM(errnum, buf, sizeof(buf));
}
#endif /* Bit of madness for Windows */

578
src/api-opts.c Normal file
View File

@ -0,0 +1,578 @@
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
#include "internals.h"
static pgno_t env_max_pgno(const MDBX_env *env) {
return env->ps ? bytes2pgno(env, env->geo_in_bytes.upper ? env->geo_in_bytes.upper : MAX_MAPSIZE) : PAGELIST_LIMIT;
}
__cold pgno_t default_dp_limit(const MDBX_env *env) {
/* auto-setup dp_limit by "The42" ;-) */
intptr_t total_ram_pages, avail_ram_pages;
int err = mdbx_get_sysraminfo(nullptr, &total_ram_pages, &avail_ram_pages);
pgno_t dp_limit = 1024;
if (unlikely(err != MDBX_SUCCESS))
ERROR("mdbx_get_sysraminfo(), rc %d", err);
else {
size_t estimate = (size_t)(total_ram_pages + avail_ram_pages) / 42;
if (env->ps) {
if (env->ps > globals.sys_pagesize)
estimate /= env->ps / globals.sys_pagesize;
else if (env->ps < globals.sys_pagesize)
estimate *= globals.sys_pagesize / env->ps;
}
dp_limit = (pgno_t)estimate;
}
dp_limit = (dp_limit < PAGELIST_LIMIT) ? dp_limit : PAGELIST_LIMIT;
const pgno_t max_pgno = env_max_pgno(env);
if (dp_limit > max_pgno - NUM_METAS)
dp_limit = max_pgno - NUM_METAS;
dp_limit = (dp_limit > CURSOR_STACK_SIZE * 4) ? dp_limit : CURSOR_STACK_SIZE * 4;
return dp_limit;
}
__cold static pgno_t default_rp_augment_limit(const MDBX_env *env) {
const size_t timeframe = /* 16 секунд */ 16 << 16;
const size_t remain_1sec =
(env->options.gc_time_limit < timeframe) ? timeframe - (size_t)env->options.gc_time_limit : 0;
const size_t minimum = (env->maxgc_large1page * 2 > MDBX_PNL_INITIAL) ? env->maxgc_large1page * 2 : MDBX_PNL_INITIAL;
const size_t one_third = env->geo_in_bytes.now / 3 >> env->ps2ln;
const size_t augment_limit =
(one_third > minimum) ? minimum + (one_third - minimum) / timeframe * remain_1sec : minimum;
eASSERT(env, augment_limit < PAGELIST_LIMIT);
return pnl_bytes2size(pnl_size2bytes(augment_limit));
}
static bool default_prefault_write(const MDBX_env *env) {
return !MDBX_MMAP_INCOHERENT_FILE_WRITE && !env->incore &&
(env->flags & (MDBX_WRITEMAP | MDBX_RDONLY)) == MDBX_WRITEMAP;
}
static bool default_prefer_waf_insteadof_balance(const MDBX_env *env) {
(void)env;
return false;
}
static uint16_t default_subpage_limit(const MDBX_env *env) {
(void)env;
return 65535 /* 100% */;
}
static uint16_t default_subpage_room_threshold(const MDBX_env *env) {
(void)env;
return 0 /* 0% */;
}
static uint16_t default_subpage_reserve_prereq(const MDBX_env *env) {
(void)env;
return 27525 /* 42% */;
}
static uint16_t default_subpage_reserve_limit(const MDBX_env *env) {
(void)env;
return 2753 /* 4.2% */;
}
static uint16_t default_merge_threshold_16dot16_percent(const MDBX_env *env) {
(void)env;
return 65536 / 4 /* 25% */;
}
static pgno_t default_dp_reserve_limit(const MDBX_env *env) {
(void)env;
return MDBX_PNL_INITIAL;
}
static pgno_t default_dp_initial(const MDBX_env *env) {
(void)env;
return MDBX_PNL_INITIAL;
}
static uint8_t default_spill_max_denominator(const MDBX_env *env) {
(void)env;
return 8;
}
static uint8_t default_spill_min_denominator(const MDBX_env *env) {
(void)env;
return 8;
}
static uint8_t default_spill_parent4child_denominator(const MDBX_env *env) {
(void)env;
return 0;
}
static uint8_t default_dp_loose_limit(const MDBX_env *env) {
(void)env;
return 64;
}
void env_options_init(MDBX_env *env) {
env->options.rp_augment_limit = default_rp_augment_limit(env);
env->options.dp_reserve_limit = default_dp_reserve_limit(env);
env->options.dp_initial = default_dp_initial(env);
env->options.dp_limit = default_dp_limit(env);
env->options.spill_max_denominator = default_spill_max_denominator(env);
env->options.spill_min_denominator = default_spill_min_denominator(env);
env->options.spill_parent4child_denominator = default_spill_parent4child_denominator(env);
env->options.dp_loose_limit = default_dp_loose_limit(env);
env->options.merge_threshold_16dot16_percent = default_merge_threshold_16dot16_percent(env);
if (default_prefer_waf_insteadof_balance(env))
env->options.prefer_waf_insteadof_balance = true;
#if !(defined(_WIN32) || defined(_WIN64))
env->options.writethrough_threshold =
#if defined(__linux__) || defined(__gnu_linux__)
globals.running_on_WSL1 ? MAX_PAGENO :
#endif /* Linux */
MDBX_WRITETHROUGH_THRESHOLD_DEFAULT;
#endif /* Windows */
env->options.subpage.limit = default_subpage_limit(env);
env->options.subpage.room_threshold = default_subpage_room_threshold(env);
env->options.subpage.reserve_prereq = default_subpage_reserve_prereq(env);
env->options.subpage.reserve_limit = default_subpage_reserve_limit(env);
}
void env_options_adjust_dp_limit(MDBX_env *env) {
if (!env->options.flags.non_auto.dp_limit)
env->options.dp_limit = default_dp_limit(env);
else {
const pgno_t max_pgno = env_max_pgno(env);
if (env->options.dp_limit > max_pgno - NUM_METAS)
env->options.dp_limit = max_pgno - NUM_METAS;
if (env->options.dp_limit < CURSOR_STACK_SIZE * 4)
env->options.dp_limit = CURSOR_STACK_SIZE * 4;
}
#ifdef MDBX_DEBUG_DPL_LIMIT
env->options.dp_limit = MDBX_DEBUG_DPL_LIMIT;
#endif /* MDBX_DEBUG_DPL_LIMIT */
if (env->options.dp_initial > env->options.dp_limit && env->options.dp_initial > default_dp_initial(env))
env->options.dp_initial = env->options.dp_limit;
env->options.need_dp_limit_adjust = false;
}
void env_options_adjust_defaults(MDBX_env *env) {
if (!env->options.flags.non_auto.rp_augment_limit)
env->options.rp_augment_limit = default_rp_augment_limit(env);
if (!env->options.flags.non_auto.prefault_write)
env->options.prefault_write = default_prefault_write(env);
env->options.need_dp_limit_adjust = true;
if (!env->txn)
env_options_adjust_dp_limit(env);
const size_t basis = env->geo_in_bytes.now;
/* TODO: use options? */
const unsigned factor = 9;
size_t threshold = (basis < ((size_t)65536 << factor)) ? 65536 /* minimal threshold */
: (basis > (MEGABYTE * 4 << factor)) ? MEGABYTE * 4 /* maximal threshold */
: basis >> factor;
threshold =
(threshold < env->geo_in_bytes.shrink || !env->geo_in_bytes.shrink) ? threshold : env->geo_in_bytes.shrink;
env->madv_threshold = bytes2pgno(env, bytes_align2os_bytes(env, threshold));
}
//------------------------------------------------------------------------------
__cold int mdbx_env_set_option(MDBX_env *env, const MDBX_option_t option, uint64_t value) {
int err = check_env(env, false);
if (unlikely(err != MDBX_SUCCESS))
return LOG_IFERR(err);
const bool lock_needed = ((env->flags & ENV_ACTIVE) && env->basal_txn && !env_owned_wrtxn(env));
bool should_unlock = false;
switch (option) {
case MDBX_opt_sync_bytes:
if (value == /* default */ UINT64_MAX)
value = MAX_WRITE;
if (unlikely(env->flags & MDBX_RDONLY))
return LOG_IFERR(MDBX_EACCESS);
if (unlikely(!(env->flags & ENV_ACTIVE)))
return LOG_IFERR(MDBX_EPERM);
if (unlikely(value > SIZE_MAX - 65536))
return LOG_IFERR(MDBX_EINVAL);
value = bytes2pgno(env, (size_t)value + env->ps - 1);
if ((uint32_t)value != atomic_load32(&env->lck->autosync_threshold, mo_AcquireRelease) &&
atomic_store32(&env->lck->autosync_threshold, (uint32_t)value, mo_Relaxed)
/* Дергаем sync(force=off) только если задано новое не-нулевое значение
* и мы вне транзакции */
&& lock_needed) {
err = env_sync(env, false, false);
if (err == /* нечего сбрасывать на диск */ MDBX_RESULT_TRUE)
err = MDBX_SUCCESS;
}
break;
case MDBX_opt_sync_period:
if (value == /* default */ UINT64_MAX)
value = 2780315 /* 42.42424 секунды */;
if (unlikely(env->flags & MDBX_RDONLY))
return LOG_IFERR(MDBX_EACCESS);
if (unlikely(!(env->flags & ENV_ACTIVE)))
return LOG_IFERR(MDBX_EPERM);
if (unlikely(value > UINT32_MAX))
return LOG_IFERR(MDBX_EINVAL);
value = osal_16dot16_to_monotime((uint32_t)value);
if (value != atomic_load64(&env->lck->autosync_period, mo_AcquireRelease) &&
atomic_store64(&env->lck->autosync_period, value, mo_Relaxed)
/* Дергаем sync(force=off) только если задано новое не-нулевое значение
* и мы вне транзакции */
&& lock_needed) {
err = env_sync(env, false, false);
if (err == /* нечего сбрасывать на диск */ MDBX_RESULT_TRUE)
err = MDBX_SUCCESS;
}
break;
case MDBX_opt_max_db:
if (value == /* default */ UINT64_MAX)
value = 42;
if (unlikely(value > MDBX_MAX_DBI))
return LOG_IFERR(MDBX_EINVAL);
if (unlikely(env->dxb_mmap.base))
return LOG_IFERR(MDBX_EPERM);
env->max_dbi = (unsigned)value + CORE_DBS;
break;
case MDBX_opt_max_readers:
if (value == /* default */ UINT64_MAX)
value = MDBX_READERS_LIMIT;
if (unlikely(value < 1 || value > MDBX_READERS_LIMIT))
return LOG_IFERR(MDBX_EINVAL);
if (unlikely(env->dxb_mmap.base))
return LOG_IFERR(MDBX_EPERM);
env->max_readers = (unsigned)value;
break;
case MDBX_opt_dp_reserve_limit:
if (value == /* default */ UINT64_MAX)
value = default_dp_reserve_limit(env);
if (unlikely(value > INT_MAX))
return LOG_IFERR(MDBX_EINVAL);
if (env->options.dp_reserve_limit != (unsigned)value) {
if (lock_needed) {
err = lck_txn_lock(env, false);
if (unlikely(err != MDBX_SUCCESS))
return LOG_IFERR(err);
should_unlock = true;
}
env->options.dp_reserve_limit = (unsigned)value;
while (env->shadow_reserve_len > env->options.dp_reserve_limit) {
eASSERT(env, env->shadow_reserve != nullptr);
page_t *dp = env->shadow_reserve;
MDBX_ASAN_UNPOISON_MEMORY_REGION(dp, env->ps);
VALGRIND_MAKE_MEM_DEFINED(&page_next(dp), sizeof(page_t *));
env->shadow_reserve = page_next(dp);
void *const ptr = ptr_disp(dp, -(ptrdiff_t)sizeof(size_t));
osal_free(ptr);
env->shadow_reserve_len -= 1;
}
}
break;
case MDBX_opt_rp_augment_limit:
if (value == /* default */ UINT64_MAX) {
env->options.flags.non_auto.rp_augment_limit = 0;
env->options.rp_augment_limit = default_rp_augment_limit(env);
} else if (unlikely(value > PAGELIST_LIMIT))
return LOG_IFERR(MDBX_EINVAL);
else {
env->options.flags.non_auto.rp_augment_limit = 1;
env->options.rp_augment_limit = (unsigned)value;
}
break;
case MDBX_opt_gc_time_limit:
if (value == /* default */ UINT64_MAX)
value = 0;
if (unlikely(value > UINT32_MAX))
return LOG_IFERR(MDBX_EINVAL);
if (unlikely(env->flags & MDBX_RDONLY))
return LOG_IFERR(MDBX_EACCESS);
value = osal_16dot16_to_monotime((uint32_t)value);
if (value != env->options.gc_time_limit) {
if (env->txn && lock_needed)
return LOG_IFERR(MDBX_EPERM);
env->options.gc_time_limit = value;
if (!env->options.flags.non_auto.rp_augment_limit)
env->options.rp_augment_limit = default_rp_augment_limit(env);
}
break;
case MDBX_opt_txn_dp_limit:
case MDBX_opt_txn_dp_initial:
if (value != /* default */ UINT64_MAX && unlikely(value > PAGELIST_LIMIT || value < CURSOR_STACK_SIZE * 4))
return LOG_IFERR(MDBX_EINVAL);
if (unlikely(env->flags & MDBX_RDONLY))
return LOG_IFERR(MDBX_EACCESS);
if (lock_needed) {
err = lck_txn_lock(env, false);
if (unlikely(err != MDBX_SUCCESS))
return LOG_IFERR(err);
should_unlock = true;
}
if (env->txn)
err = MDBX_EPERM /* unable change during transaction */;
else {
const pgno_t max_pgno = env_max_pgno(env);
if (option == MDBX_opt_txn_dp_initial) {
if (value == /* default */ UINT64_MAX)
env->options.dp_initial = default_dp_initial(env);
else {
env->options.dp_initial = (pgno_t)value;
if (env->options.dp_initial > max_pgno)
env->options.dp_initial = (max_pgno > CURSOR_STACK_SIZE * 4) ? max_pgno : CURSOR_STACK_SIZE * 4;
}
}
if (option == MDBX_opt_txn_dp_limit) {
if (value == /* default */ UINT64_MAX) {
env->options.flags.non_auto.dp_limit = 0;
} else {
env->options.flags.non_auto.dp_limit = 1;
env->options.dp_limit = (pgno_t)value;
}
env_options_adjust_dp_limit(env);
}
}
break;
case MDBX_opt_spill_max_denominator:
if (value == /* default */ UINT64_MAX)
value = default_spill_max_denominator(env);
if (unlikely(value > 255))
return LOG_IFERR(MDBX_EINVAL);
env->options.spill_max_denominator = (uint8_t)value;
break;
case MDBX_opt_spill_min_denominator:
if (value == /* default */ UINT64_MAX)
value = default_spill_min_denominator(env);
if (unlikely(value > 255))
return LOG_IFERR(MDBX_EINVAL);
env->options.spill_min_denominator = (uint8_t)value;
break;
case MDBX_opt_spill_parent4child_denominator:
if (value == /* default */ UINT64_MAX)
value = default_spill_parent4child_denominator(env);
if (unlikely(value > 255))
return LOG_IFERR(MDBX_EINVAL);
env->options.spill_parent4child_denominator = (uint8_t)value;
break;
case MDBX_opt_loose_limit:
if (value == /* default */ UINT64_MAX)
value = default_dp_loose_limit(env);
if (unlikely(value > 255))
return LOG_IFERR(MDBX_EINVAL);
env->options.dp_loose_limit = (uint8_t)value;
break;
case MDBX_opt_merge_threshold_16dot16_percent:
if (value == /* default */ UINT64_MAX)
value = default_merge_threshold_16dot16_percent(env);
if (unlikely(value < 8192 || value > 32768))
return LOG_IFERR(MDBX_EINVAL);
env->options.merge_threshold_16dot16_percent = (unsigned)value;
recalculate_merge_thresholds(env);
break;
case MDBX_opt_writethrough_threshold:
#if defined(_WIN32) || defined(_WIN64)
/* позволяем "установить" значение по-умолчанию и совпадающее
* с поведением соответствующим текущей установке MDBX_NOMETASYNC */
if (value == /* default */ UINT64_MAX && value != ((env->flags & MDBX_NOMETASYNC) ? 0 : UINT_MAX))
err = MDBX_EINVAL;
#else
if (value == /* default */ UINT64_MAX)
value = MDBX_WRITETHROUGH_THRESHOLD_DEFAULT;
if (value != (unsigned)value)
err = MDBX_EINVAL;
else
env->options.writethrough_threshold = (unsigned)value;
#endif
break;
case MDBX_opt_prefault_write_enable:
if (value == /* default */ UINT64_MAX) {
env->options.prefault_write = default_prefault_write(env);
env->options.flags.non_auto.prefault_write = false;
} else if (value > 1)
err = MDBX_EINVAL;
else {
env->options.prefault_write = value != 0;
env->options.flags.non_auto.prefault_write = true;
}
break;
case MDBX_opt_prefer_waf_insteadof_balance:
if (value == /* default */ UINT64_MAX)
env->options.prefer_waf_insteadof_balance = default_prefer_waf_insteadof_balance(env);
else if (value > 1)
err = MDBX_EINVAL;
else
env->options.prefer_waf_insteadof_balance = value != 0;
break;
case MDBX_opt_subpage_limit:
if (value == /* default */ UINT64_MAX) {
env->options.subpage.limit = default_subpage_limit(env);
recalculate_subpage_thresholds(env);
} else if (value > 65535)
err = MDBX_EINVAL;
else {
env->options.subpage.limit = (uint16_t)value;
recalculate_subpage_thresholds(env);
}
break;
case MDBX_opt_subpage_room_threshold:
if (value == /* default */ UINT64_MAX) {
env->options.subpage.room_threshold = default_subpage_room_threshold(env);
recalculate_subpage_thresholds(env);
} else if (value > 65535)
err = MDBX_EINVAL;
else {
env->options.subpage.room_threshold = (uint16_t)value;
recalculate_subpage_thresholds(env);
}
break;
case MDBX_opt_subpage_reserve_prereq:
if (value == /* default */ UINT64_MAX) {
env->options.subpage.reserve_prereq = default_subpage_reserve_prereq(env);
recalculate_subpage_thresholds(env);
} else if (value > 65535)
err = MDBX_EINVAL;
else {
env->options.subpage.reserve_prereq = (uint16_t)value;
recalculate_subpage_thresholds(env);
}
break;
case MDBX_opt_subpage_reserve_limit:
if (value == /* default */ UINT64_MAX) {
env->options.subpage.reserve_limit = default_subpage_reserve_limit(env);
recalculate_subpage_thresholds(env);
} else if (value > 65535)
err = MDBX_EINVAL;
else {
env->options.subpage.reserve_limit = (uint16_t)value;
recalculate_subpage_thresholds(env);
}
break;
default:
return LOG_IFERR(MDBX_EINVAL);
}
if (should_unlock)
lck_txn_unlock(env);
return LOG_IFERR(err);
}
__cold int mdbx_env_get_option(const MDBX_env *env, const MDBX_option_t option, uint64_t *pvalue) {
int err = check_env(env, false);
if (unlikely(err != MDBX_SUCCESS))
return LOG_IFERR(err);
if (unlikely(!pvalue))
return LOG_IFERR(MDBX_EINVAL);
switch (option) {
case MDBX_opt_sync_bytes:
if (unlikely(!(env->flags & ENV_ACTIVE)))
return LOG_IFERR(MDBX_EPERM);
*pvalue = pgno2bytes(env, atomic_load32(&env->lck->autosync_threshold, mo_Relaxed));
break;
case MDBX_opt_sync_period:
if (unlikely(!(env->flags & ENV_ACTIVE)))
return LOG_IFERR(MDBX_EPERM);
*pvalue = osal_monotime_to_16dot16(atomic_load64(&env->lck->autosync_period, mo_Relaxed));
break;
case MDBX_opt_max_db:
*pvalue = env->max_dbi - CORE_DBS;
break;
case MDBX_opt_max_readers:
*pvalue = env->max_readers;
break;
case MDBX_opt_dp_reserve_limit:
*pvalue = env->options.dp_reserve_limit;
break;
case MDBX_opt_rp_augment_limit:
*pvalue = env->options.rp_augment_limit;
break;
case MDBX_opt_gc_time_limit:
*pvalue = osal_monotime_to_16dot16(env->options.gc_time_limit);
break;
case MDBX_opt_txn_dp_limit:
*pvalue = env->options.dp_limit;
break;
case MDBX_opt_txn_dp_initial:
*pvalue = env->options.dp_initial;
break;
case MDBX_opt_spill_max_denominator:
*pvalue = env->options.spill_max_denominator;
break;
case MDBX_opt_spill_min_denominator:
*pvalue = env->options.spill_min_denominator;
break;
case MDBX_opt_spill_parent4child_denominator:
*pvalue = env->options.spill_parent4child_denominator;
break;
case MDBX_opt_loose_limit:
*pvalue = env->options.dp_loose_limit;
break;
case MDBX_opt_merge_threshold_16dot16_percent:
*pvalue = env->options.merge_threshold_16dot16_percent;
break;
case MDBX_opt_writethrough_threshold:
#if defined(_WIN32) || defined(_WIN64)
*pvalue = (env->flags & MDBX_NOMETASYNC) ? 0 : INT_MAX;
#else
*pvalue = env->options.writethrough_threshold;
#endif
break;
case MDBX_opt_prefault_write_enable:
*pvalue = env->options.prefault_write;
break;
case MDBX_opt_prefer_waf_insteadof_balance:
*pvalue = env->options.prefer_waf_insteadof_balance;
break;
case MDBX_opt_subpage_limit:
*pvalue = env->options.subpage.limit;
break;
case MDBX_opt_subpage_room_threshold:
*pvalue = env->options.subpage.room_threshold;
break;
case MDBX_opt_subpage_reserve_prereq:
*pvalue = env->options.subpage.reserve_prereq;
break;
case MDBX_opt_subpage_reserve_limit:
*pvalue = env->options.subpage.reserve_limit;
break;
default:
return LOG_IFERR(MDBX_EINVAL);
}
return MDBX_SUCCESS;
}

365
src/api-range-estimate.c Normal file
View File

@ -0,0 +1,365 @@
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
#include "internals.h"
typedef struct diff_result {
ptrdiff_t diff;
intptr_t level;
ptrdiff_t root_nkeys;
} diff_t;
/* calculates: r = x - y */
__hot static int cursor_diff(const MDBX_cursor *const __restrict x, const MDBX_cursor *const __restrict y,
diff_t *const __restrict r) {
r->diff = 0;
r->level = 0;
r->root_nkeys = 0;
int rc = check_txn(x->txn, MDBX_TXN_BLOCKED);
if (unlikely(rc != MDBX_SUCCESS))
return rc;
if (unlikely(x->txn != y->txn))
return MDBX_BAD_TXN;
if (unlikely(y->dbi_state != x->dbi_state))
return MDBX_EINVAL;
const intptr_t depth = (x->top < y->top) ? x->top : y->top;
if (unlikely(depth < 0))
return MDBX_ENODATA;
r->root_nkeys = page_numkeys(x->pg[0]);
intptr_t nkeys = r->root_nkeys;
for (;;) {
if (unlikely(y->pg[r->level] != x->pg[r->level])) {
ERROR("Mismatch cursors's pages at %zu level", r->level);
return MDBX_PROBLEM;
}
r->diff = x->ki[r->level] - y->ki[r->level];
if (r->diff)
break;
r->level += 1;
if (r->level > depth) {
r->diff = CMP2INT(x->flags & z_eof_hard, y->flags & z_eof_hard);
return MDBX_SUCCESS;
}
nkeys = page_numkeys(x->pg[r->level]);
}
while (unlikely(r->diff == 1) && likely(r->level < depth)) {
r->level += 1;
/* DB'PAGEs: 0------------------>MAX
*
* CURSORs: y < x
* STACK[i ]: |
* STACK[+1]: ...y++N|0++x...
*/
nkeys = page_numkeys(y->pg[r->level]);
r->diff = (nkeys - y->ki[r->level]) + x->ki[r->level];
assert(r->diff > 0);
}
while (unlikely(r->diff == -1) && likely(r->level < depth)) {
r->level += 1;
/* DB'PAGEs: 0------------------>MAX
*
* CURSORs: x < y
* STACK[i ]: |
* STACK[+1]: ...x--N|0--y...
*/
nkeys = page_numkeys(x->pg[r->level]);
r->diff = -(nkeys - x->ki[r->level]) - y->ki[r->level];
assert(r->diff < 0);
}
return MDBX_SUCCESS;
}
__hot static ptrdiff_t estimate(const tree_t *tree, diff_t *const __restrict dr) {
/* root: branch-page => scale = leaf-factor * branch-factor^(N-1)
* level-1: branch-page(s) => scale = leaf-factor * branch-factor^2
* level-2: branch-page(s) => scale = leaf-factor * branch-factor
* level-N: branch-page(s) => scale = leaf-factor
* leaf-level: leaf-page(s) => scale = 1
*/
ptrdiff_t btree_power = (ptrdiff_t)tree->height - 2 - (ptrdiff_t)dr->level;
if (btree_power < 0)
return dr->diff;
ptrdiff_t estimated = (ptrdiff_t)tree->items * dr->diff / (ptrdiff_t)tree->leaf_pages;
if (btree_power == 0)
return estimated;
if (tree->height < 4) {
assert(dr->level == 0 && btree_power == 1);
return (ptrdiff_t)tree->items * dr->diff / (ptrdiff_t)dr->root_nkeys;
}
/* average_branchpage_fillfactor = total(branch_entries) / branch_pages
total(branch_entries) = leaf_pages + branch_pages - 1 (root page) */
const size_t log2_fixedpoint = sizeof(size_t) - 1;
const size_t half = UINT64_C(1) << (log2_fixedpoint - 1);
const size_t factor = ((tree->leaf_pages + tree->branch_pages - 1) << log2_fixedpoint) / tree->branch_pages;
while (1) {
switch ((size_t)btree_power) {
default: {
const size_t square = (factor * factor + half) >> log2_fixedpoint;
const size_t quad = (square * square + half) >> log2_fixedpoint;
do {
estimated = estimated * quad + half;
estimated >>= log2_fixedpoint;
btree_power -= 4;
} while (btree_power >= 4);
continue;
}
case 3:
estimated = estimated * factor + half;
estimated >>= log2_fixedpoint;
__fallthrough /* fall through */;
case 2:
estimated = estimated * factor + half;
estimated >>= log2_fixedpoint;
__fallthrough /* fall through */;
case 1:
estimated = estimated * factor + half;
estimated >>= log2_fixedpoint;
__fallthrough /* fall through */;
case 0:
if (unlikely(estimated > (ptrdiff_t)tree->items))
return (ptrdiff_t)tree->items;
if (unlikely(estimated < -(ptrdiff_t)tree->items))
return -(ptrdiff_t)tree->items;
return estimated;
}
}
}
/*------------------------------------------------------------------------------
* Range-Estimation API */
__hot int mdbx_estimate_distance(const MDBX_cursor *first, const MDBX_cursor *last, ptrdiff_t *distance_items) {
if (unlikely(!distance_items))
return LOG_IFERR(MDBX_EINVAL);
int rc = cursor_check_pure(first);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
rc = cursor_check_pure(last);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
*distance_items = 0;
diff_t dr;
rc = cursor_diff(last, first, &dr);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
cASSERT(first, dr.diff || inner_pointed(first) == inner_pointed(last));
if (unlikely(dr.diff == 0) && inner_pointed(first)) {
first = &first->subcur->cursor;
last = &last->subcur->cursor;
rc = cursor_diff(first, last, &dr);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
}
if (likely(dr.diff != 0))
*distance_items = estimate(first->tree, &dr);
return MDBX_SUCCESS;
}
__hot int mdbx_estimate_move(const MDBX_cursor *cursor, MDBX_val *key, MDBX_val *data, MDBX_cursor_op move_op,
ptrdiff_t *distance_items) {
if (unlikely(!distance_items || move_op == MDBX_GET_CURRENT || move_op == MDBX_GET_MULTIPLE))
return LOG_IFERR(MDBX_EINVAL);
int rc = cursor_check_ro(cursor);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
if (unlikely(!is_pointed(cursor)))
return LOG_IFERR(MDBX_ENODATA);
cursor_couple_t next;
rc = cursor_init(&next.outer, cursor->txn, cursor_dbi(cursor));
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
cursor_cpstk(cursor, &next.outer);
if (cursor->tree->flags & MDBX_DUPSORT) {
subcur_t *mx = &container_of(cursor, cursor_couple_t, outer)->inner;
cursor_cpstk(&mx->cursor, &next.inner.cursor);
}
MDBX_val stub_data;
if (data == nullptr) {
const unsigned mask = 1 << MDBX_GET_BOTH | 1 << MDBX_GET_BOTH_RANGE | 1 << MDBX_SET_KEY;
if (unlikely(mask & (1 << move_op)))
return LOG_IFERR(MDBX_EINVAL);
stub_data.iov_base = nullptr;
stub_data.iov_len = 0;
data = &stub_data;
}
MDBX_val stub_key;
if (key == nullptr) {
const unsigned mask =
1 << MDBX_GET_BOTH | 1 << MDBX_GET_BOTH_RANGE | 1 << MDBX_SET_KEY | 1 << MDBX_SET | 1 << MDBX_SET_RANGE;
if (unlikely(mask & (1 << move_op)))
return LOG_IFERR(MDBX_EINVAL);
stub_key.iov_base = nullptr;
stub_key.iov_len = 0;
key = &stub_key;
}
next.outer.signature = cur_signature_live;
rc = cursor_ops(&next.outer, key, data, move_op);
if (unlikely(rc != MDBX_SUCCESS && (rc != MDBX_NOTFOUND || !is_pointed(&next.outer))))
return LOG_IFERR(rc);
if (move_op == MDBX_LAST) {
next.outer.flags |= z_eof_hard;
next.inner.cursor.flags |= z_eof_hard;
}
return mdbx_estimate_distance(cursor, &next.outer, distance_items);
}
__hot int mdbx_estimate_range(const MDBX_txn *txn, MDBX_dbi dbi, const MDBX_val *begin_key, const MDBX_val *begin_data,
const MDBX_val *end_key, const MDBX_val *end_data, ptrdiff_t *size_items) {
if (unlikely(!size_items))
return LOG_IFERR(MDBX_EINVAL);
if (unlikely(begin_data && (begin_key == nullptr || begin_key == MDBX_EPSILON)))
return LOG_IFERR(MDBX_EINVAL);
if (unlikely(end_data && (end_key == nullptr || end_key == MDBX_EPSILON)))
return LOG_IFERR(MDBX_EINVAL);
if (unlikely(begin_key == MDBX_EPSILON && end_key == MDBX_EPSILON))
return LOG_IFERR(MDBX_EINVAL);
int rc = check_txn(txn, MDBX_TXN_BLOCKED);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
cursor_couple_t begin;
/* LY: first, initialize cursor to refresh a DB in case it have DB_STALE */
rc = cursor_init(&begin.outer, txn, dbi);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
if (unlikely(begin.outer.tree->items == 0)) {
*size_items = 0;
return MDBX_SUCCESS;
}
if (!begin_key) {
if (unlikely(!end_key)) {
/* LY: FIRST..LAST case */
*size_items = (ptrdiff_t)begin.outer.tree->items;
return MDBX_SUCCESS;
}
rc = outer_first(&begin.outer, nullptr, nullptr);
if (unlikely(end_key == MDBX_EPSILON)) {
/* LY: FIRST..+epsilon case */
return LOG_IFERR((rc == MDBX_SUCCESS) ? mdbx_cursor_count(&begin.outer, (size_t *)size_items) : rc);
}
} else {
if (unlikely(begin_key == MDBX_EPSILON)) {
if (end_key == nullptr) {
/* LY: -epsilon..LAST case */
rc = outer_last(&begin.outer, nullptr, nullptr);
return LOG_IFERR((rc == MDBX_SUCCESS) ? mdbx_cursor_count(&begin.outer, (size_t *)size_items) : rc);
}
/* LY: -epsilon..value case */
assert(end_key != MDBX_EPSILON);
begin_key = end_key;
} else if (unlikely(end_key == MDBX_EPSILON)) {
/* LY: value..+epsilon case */
assert(begin_key != MDBX_EPSILON);
end_key = begin_key;
}
if (end_key && !begin_data && !end_data &&
(begin_key == end_key || begin.outer.clc->k.cmp(begin_key, end_key) == 0)) {
/* LY: single key case */
rc = cursor_seek(&begin.outer, (MDBX_val *)begin_key, nullptr, MDBX_SET).err;
if (unlikely(rc != MDBX_SUCCESS)) {
*size_items = 0;
return LOG_IFERR((rc == MDBX_NOTFOUND) ? MDBX_SUCCESS : rc);
}
*size_items = 1;
if (inner_pointed(&begin.outer))
*size_items = (sizeof(*size_items) >= sizeof(begin.inner.nested_tree.items) ||
begin.inner.nested_tree.items <= PTRDIFF_MAX)
? (size_t)begin.inner.nested_tree.items
: PTRDIFF_MAX;
return MDBX_SUCCESS;
} else {
MDBX_val proxy_key = *begin_key;
MDBX_val proxy_data = {nullptr, 0};
if (begin_data)
proxy_data = *begin_data;
rc = LOG_IFERR(cursor_seek(&begin.outer, &proxy_key, &proxy_data, MDBX_SET_LOWERBOUND).err);
}
}
if (unlikely(rc != MDBX_SUCCESS)) {
if (rc != MDBX_NOTFOUND || !is_pointed(&begin.outer))
return LOG_IFERR(rc);
}
cursor_couple_t end;
rc = cursor_init(&end.outer, txn, dbi);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
if (!end_key) {
rc = outer_last(&end.outer, nullptr, nullptr);
end.outer.flags |= z_eof_hard;
end.inner.cursor.flags |= z_eof_hard;
} else {
MDBX_val proxy_key = *end_key;
MDBX_val proxy_data = {nullptr, 0};
if (end_data)
proxy_data = *end_data;
rc = cursor_seek(&end.outer, &proxy_key, &proxy_data, MDBX_SET_LOWERBOUND).err;
}
if (unlikely(rc != MDBX_SUCCESS)) {
if (rc != MDBX_NOTFOUND || !is_pointed(&end.outer))
return LOG_IFERR(rc);
}
rc = mdbx_estimate_distance(&begin.outer, &end.outer, size_items);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
assert(*size_items >= -(ptrdiff_t)begin.outer.tree->items && *size_items <= (ptrdiff_t)begin.outer.tree->items);
#if 0 /* LY: Was decided to returns as-is (i.e. negative) the estimation \
* results for an inverted ranges. */
/* Commit 8ddfd1f34ad7cf7a3c4aa75d2e248ca7e639ed63
Change-Id: If59eccf7311123ab6384c4b93f9b1fed5a0a10d1 */
if (*size_items < 0) {
/* LY: inverted range case */
*size_items += (ptrdiff_t)begin.outer.tree->items;
} else if (*size_items == 0 && begin_key && end_key) {
int cmp = begin.outer.kvx->cmp(&origin_begin_key, &origin_end_key);
if (cmp == 0 && cursor_pointed(begin.inner.cursor.flags) &&
begin_data && end_data)
cmp = begin.outer.kvx->v.cmp(&origin_begin_data, &origin_end_data);
if (cmp > 0) {
/* LY: inverted range case with empty scope */
*size_items = (ptrdiff_t)begin.outer.tree->items;
}
}
assert(*size_items >= 0 &&
*size_items <= (ptrdiff_t)begin.outer.tree->items);
#endif
return MDBX_SUCCESS;
}

454
src/api-txn-data.c Normal file
View File

@ -0,0 +1,454 @@
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
#include "internals.h"
__cold int mdbx_dbi_dupsort_depthmask(const MDBX_txn *txn, MDBX_dbi dbi, uint32_t *mask) {
if (unlikely(!mask))
return LOG_IFERR(MDBX_EINVAL);
*mask = 0;
int rc = check_txn(txn, MDBX_TXN_BLOCKED);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
cursor_couple_t cx;
rc = cursor_init(&cx.outer, txn, dbi);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
if ((cx.outer.tree->flags & MDBX_DUPSORT) == 0)
return MDBX_RESULT_TRUE;
MDBX_val key, data;
rc = outer_first(&cx.outer, &key, &data);
while (rc == MDBX_SUCCESS) {
const node_t *node = page_node(cx.outer.pg[cx.outer.top], cx.outer.ki[cx.outer.top]);
const tree_t *db = node_data(node);
const unsigned flags = node_flags(node);
switch (flags) {
case N_BIG:
case 0:
/* single-value entry, deep = 0 */
*mask |= 1 << 0;
break;
case N_DUP:
/* single sub-page, deep = 1 */
*mask |= 1 << 1;
break;
case N_DUP | N_TREE:
/* sub-tree */
*mask |= 1 << UNALIGNED_PEEK_16(db, tree_t, height);
break;
default:
ERROR("%s/%d: %s %u", "MDBX_CORRUPTED", MDBX_CORRUPTED, "invalid node-size", flags);
return LOG_IFERR(MDBX_CORRUPTED);
}
rc = outer_next(&cx.outer, &key, &data, MDBX_NEXT_NODUP);
}
return LOG_IFERR((rc == MDBX_NOTFOUND) ? MDBX_SUCCESS : rc);
}
int mdbx_canary_get(const MDBX_txn *txn, MDBX_canary *canary) {
if (unlikely(canary == nullptr))
return LOG_IFERR(MDBX_EINVAL);
int rc = check_txn(txn, MDBX_TXN_BLOCKED - MDBX_TXN_PARKED);
if (unlikely(rc != MDBX_SUCCESS)) {
memset(canary, 0, sizeof(*canary));
return LOG_IFERR(rc);
}
*canary = txn->canary;
return MDBX_SUCCESS;
}
int mdbx_get(const MDBX_txn *txn, MDBX_dbi dbi, const MDBX_val *key, MDBX_val *data) {
DKBUF_DEBUG;
DEBUG("===> get db %u key [%s]", dbi, DKEY_DEBUG(key));
if (unlikely(!key || !data))
return LOG_IFERR(MDBX_EINVAL);
int rc = check_txn(txn, MDBX_TXN_BLOCKED);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
cursor_couple_t cx;
rc = cursor_init(&cx.outer, txn, dbi);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
return LOG_IFERR(cursor_seek(&cx.outer, (MDBX_val *)key, data, MDBX_SET).err);
}
int mdbx_get_equal_or_great(const MDBX_txn *txn, MDBX_dbi dbi, MDBX_val *key, MDBX_val *data) {
if (unlikely(!key || !data))
return LOG_IFERR(MDBX_EINVAL);
int rc = check_txn(txn, MDBX_TXN_BLOCKED);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
cursor_couple_t cx;
rc = cursor_init(&cx.outer, txn, dbi);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
return LOG_IFERR(cursor_ops(&cx.outer, key, data, MDBX_SET_LOWERBOUND));
}
int mdbx_get_ex(const MDBX_txn *txn, MDBX_dbi dbi, MDBX_val *key, MDBX_val *data, size_t *values_count) {
DKBUF_DEBUG;
DEBUG("===> get db %u key [%s]", dbi, DKEY_DEBUG(key));
if (unlikely(!key || !data))
return LOG_IFERR(MDBX_EINVAL);
int rc = check_txn(txn, MDBX_TXN_BLOCKED);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
cursor_couple_t cx;
rc = cursor_init(&cx.outer, txn, dbi);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
rc = cursor_seek(&cx.outer, key, data, MDBX_SET_KEY).err;
if (unlikely(rc != MDBX_SUCCESS)) {
if (values_count)
*values_count = 0;
return LOG_IFERR(rc);
}
if (values_count) {
*values_count = 1;
if (inner_pointed(&cx.outer))
*values_count =
(sizeof(*values_count) >= sizeof(cx.inner.nested_tree.items) || cx.inner.nested_tree.items <= PTRDIFF_MAX)
? (size_t)cx.inner.nested_tree.items
: PTRDIFF_MAX;
}
return MDBX_SUCCESS;
}
/*----------------------------------------------------------------------------*/
int mdbx_canary_put(MDBX_txn *txn, const MDBX_canary *canary) {
int rc = check_txn_rw(txn, MDBX_TXN_BLOCKED);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
if (likely(canary)) {
if (txn->canary.x == canary->x && txn->canary.y == canary->y && txn->canary.z == canary->z)
return MDBX_SUCCESS;
txn->canary.x = canary->x;
txn->canary.y = canary->y;
txn->canary.z = canary->z;
}
txn->canary.v = txn->txnid;
txn->flags |= MDBX_TXN_DIRTY;
return MDBX_SUCCESS;
}
/* Функция сообщает находится ли указанный адрес в "грязной" странице у
* заданной пишущей транзакции. В конечном счете это позволяет избавиться от
* лишнего копирования данных из НЕ-грязных страниц.
*
* "Грязные" страницы - это те, которые уже были изменены в ходе пишущей
* транзакции. Соответственно, какие-либо дальнейшие изменения могут привести
* к перезаписи таких страниц. Поэтому все функции, выполняющие изменения, в
* качестве аргументов НЕ должны получать указатели на данные в таких
* страницах. В свою очередь "НЕ грязные" страницы перед модификацией будут
* скопированы.
*
* Другими словами, данные из "грязных" страниц должны быть либо скопированы
* перед передачей в качестве аргументов для дальнейших модификаций, либо
* отвергнуты на стадии проверки корректности аргументов.
*
* Таким образом, функция позволяет как избавится от лишнего копирования,
* так и выполнить более полную проверку аргументов.
*
* ВАЖНО: Передаваемый указатель должен указывать на начало данных. Только
* так гарантируется что актуальный заголовок страницы будет физически
* расположен в той-же странице памяти, в том числе для многостраничных
* P_LARGE страниц с длинными данными. */
int mdbx_is_dirty(const MDBX_txn *txn, const void *ptr) {
int rc = check_txn(txn, MDBX_TXN_BLOCKED - MDBX_TXN_PARKED);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
const MDBX_env *env = txn->env;
const ptrdiff_t offset = ptr_dist(ptr, env->dxb_mmap.base);
if (offset >= 0) {
const pgno_t pgno = bytes2pgno(env, offset);
if (likely(pgno < txn->geo.first_unallocated)) {
const page_t *page = pgno2page(env, pgno);
if (unlikely(page->pgno != pgno || (page->flags & P_ILL_BITS) != 0)) {
/* The ptr pointed into middle of a large page,
* not to the beginning of a data. */
return LOG_IFERR(MDBX_EINVAL);
}
return ((txn->flags & MDBX_TXN_RDONLY) || !is_modifable(txn, page)) ? MDBX_RESULT_FALSE : MDBX_RESULT_TRUE;
}
if ((size_t)offset < env->dxb_mmap.limit) {
/* Указатель адресует что-то в пределах mmap, но за границей
* распределенных страниц. Такое может случится если mdbx_is_dirty()
* вызывается после операции, в ходе которой грязная страница была
* возвращена в нераспределенное пространство. */
return (txn->flags & MDBX_TXN_RDONLY) ? LOG_IFERR(MDBX_EINVAL) : MDBX_RESULT_TRUE;
}
}
/* Страница вне используемого mmap-диапазона, т.е. либо в функцию был
* передан некорректный адрес, либо адрес в теневой странице, которая была
* выделена посредством malloc().
*
* Для режима MDBX_WRITE_MAP режима страница однозначно "не грязная",
* а для режимов без MDBX_WRITE_MAP однозначно "не чистая". */
return (txn->flags & (MDBX_WRITEMAP | MDBX_TXN_RDONLY)) ? LOG_IFERR(MDBX_EINVAL) : MDBX_RESULT_TRUE;
}
int mdbx_del(MDBX_txn *txn, MDBX_dbi dbi, const MDBX_val *key, const MDBX_val *data) {
if (unlikely(!key))
return LOG_IFERR(MDBX_EINVAL);
if (unlikely(dbi <= FREE_DBI))
return LOG_IFERR(MDBX_BAD_DBI);
int rc = check_txn_rw(txn, MDBX_TXN_BLOCKED);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
cursor_couple_t cx;
rc = cursor_init(&cx.outer, txn, dbi);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
MDBX_val proxy;
MDBX_cursor_op op = MDBX_SET;
unsigned flags = MDBX_ALLDUPS;
if (data) {
proxy = *data;
data = &proxy;
op = MDBX_GET_BOTH;
flags = 0;
}
rc = cursor_seek(&cx.outer, (MDBX_val *)key, (MDBX_val *)data, op).err;
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
cx.outer.next = txn->cursors[dbi];
txn->cursors[dbi] = &cx.outer;
rc = cursor_del(&cx.outer, flags);
txn->cursors[dbi] = cx.outer.next;
return LOG_IFERR(rc);
}
int mdbx_put(MDBX_txn *txn, MDBX_dbi dbi, const MDBX_val *key, MDBX_val *data, MDBX_put_flags_t flags) {
if (unlikely(!key || !data))
return LOG_IFERR(MDBX_EINVAL);
if (unlikely(dbi <= FREE_DBI))
return LOG_IFERR(MDBX_BAD_DBI);
if (unlikely(flags & ~(MDBX_NOOVERWRITE | MDBX_NODUPDATA | MDBX_ALLDUPS | MDBX_ALLDUPS | MDBX_RESERVE | MDBX_APPEND |
MDBX_APPENDDUP | MDBX_CURRENT | MDBX_MULTIPLE)))
return LOG_IFERR(MDBX_EINVAL);
int rc = check_txn_rw(txn, MDBX_TXN_BLOCKED);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
cursor_couple_t cx;
rc = cursor_init(&cx.outer, txn, dbi);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
if (unlikely(flags & MDBX_MULTIPLE)) {
rc = cursor_check_multiple(&cx.outer, key, data, flags);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
}
if (flags & MDBX_RESERVE) {
if (unlikely(cx.outer.tree->flags & (MDBX_DUPSORT | MDBX_REVERSEDUP | MDBX_INTEGERDUP | MDBX_DUPFIXED)))
return LOG_IFERR(MDBX_INCOMPATIBLE);
data->iov_base = nullptr;
}
cx.outer.next = txn->cursors[dbi];
txn->cursors[dbi] = &cx.outer;
/* LY: support for update (explicit overwrite) */
if (flags & MDBX_CURRENT) {
rc = cursor_seek(&cx.outer, (MDBX_val *)key, nullptr, MDBX_SET).err;
if (likely(rc == MDBX_SUCCESS) && (txn->dbs[dbi].flags & MDBX_DUPSORT) && (flags & MDBX_ALLDUPS) == 0) {
/* LY: allows update (explicit overwrite) only for unique keys */
node_t *node = page_node(cx.outer.pg[cx.outer.top], cx.outer.ki[cx.outer.top]);
if (node_flags(node) & N_DUP) {
tASSERT(txn, inner_pointed(&cx.outer) && cx.outer.subcur->nested_tree.items > 1);
rc = MDBX_EMULTIVAL;
if ((flags & MDBX_NOOVERWRITE) == 0) {
flags -= MDBX_CURRENT;
rc = cursor_del(&cx.outer, MDBX_ALLDUPS);
}
}
}
}
if (likely(rc == MDBX_SUCCESS))
rc = cursor_put_checklen(&cx.outer, key, data, flags);
txn->cursors[dbi] = cx.outer.next;
return LOG_IFERR(rc);
}
//------------------------------------------------------------------------------
/* Позволяет обновить или удалить существующую запись с получением
* в old_data предыдущего значения данных. При этом если new_data равен
* нулю, то выполняется удаление, иначе обновление/вставка.
*
* Текущее значение может находиться в уже измененной (грязной) странице.
* В этом случае страница будет перезаписана при обновлении, а само старое
* значение утрачено. Поэтому исходно в old_data должен быть передан
* дополнительный буфер для копирования старого значения.
* Если переданный буфер слишком мал, то функция вернет -1, установив
* old_data->iov_len в соответствующее значение.
*
* Для не-уникальных ключей также возможен второй сценарий использования,
* когда посредством old_data из записей с одинаковым ключом для
* удаления/обновления выбирается конкретная. Для выбора этого сценария
* во flags следует одновременно указать MDBX_CURRENT и MDBX_NOOVERWRITE.
* Именно эта комбинация выбрана, так как она лишена смысла, и этим позволяет
* идентифицировать запрос такого сценария.
*
* Функция может быть замещена соответствующими операциями с курсорами
* после двух доработок (TODO):
* - внешняя аллокация курсоров, в том числе на стеке (без malloc).
* - получения dirty-статуса страницы по адресу (знать о MUTABLE/WRITEABLE).
*/
int mdbx_replace_ex(MDBX_txn *txn, MDBX_dbi dbi, const MDBX_val *key, MDBX_val *new_data, MDBX_val *old_data,
MDBX_put_flags_t flags, MDBX_preserve_func preserver, void *preserver_context) {
if (unlikely(!key || !old_data || old_data == new_data))
return LOG_IFERR(MDBX_EINVAL);
if (unlikely(old_data->iov_base == nullptr && old_data->iov_len))
return LOG_IFERR(MDBX_EINVAL);
if (unlikely(new_data == nullptr && (flags & (MDBX_CURRENT | MDBX_RESERVE)) != MDBX_CURRENT))
return LOG_IFERR(MDBX_EINVAL);
if (unlikely(dbi <= FREE_DBI))
return LOG_IFERR(MDBX_BAD_DBI);
if (unlikely(flags & ~(MDBX_NOOVERWRITE | MDBX_NODUPDATA | MDBX_ALLDUPS | MDBX_RESERVE | MDBX_APPEND |
MDBX_APPENDDUP | MDBX_CURRENT)))
return LOG_IFERR(MDBX_EINVAL);
int rc = check_txn_rw(txn, MDBX_TXN_BLOCKED);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
cursor_couple_t cx;
rc = cursor_init(&cx.outer, txn, dbi);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
cx.outer.next = txn->cursors[dbi];
txn->cursors[dbi] = &cx.outer;
MDBX_val present_key = *key;
if (F_ISSET(flags, MDBX_CURRENT | MDBX_NOOVERWRITE)) {
/* в old_data значение для выбора конкретного дубликата */
if (unlikely(!(txn->dbs[dbi].flags & MDBX_DUPSORT))) {
rc = MDBX_EINVAL;
goto bailout;
}
/* убираем лишний бит, он был признаком запрошенного режима */
flags -= MDBX_NOOVERWRITE;
rc = cursor_seek(&cx.outer, &present_key, old_data, MDBX_GET_BOTH).err;
if (rc != MDBX_SUCCESS)
goto bailout;
} else {
/* в old_data буфер для сохранения предыдущего значения */
if (unlikely(new_data && old_data->iov_base == new_data->iov_base))
return LOG_IFERR(MDBX_EINVAL);
MDBX_val present_data;
rc = cursor_seek(&cx.outer, &present_key, &present_data, MDBX_SET_KEY).err;
if (unlikely(rc != MDBX_SUCCESS)) {
old_data->iov_base = nullptr;
old_data->iov_len = 0;
if (rc != MDBX_NOTFOUND || (flags & MDBX_CURRENT))
goto bailout;
} else if (flags & MDBX_NOOVERWRITE) {
rc = MDBX_KEYEXIST;
*old_data = present_data;
goto bailout;
} else {
page_t *page = cx.outer.pg[cx.outer.top];
if (txn->dbs[dbi].flags & MDBX_DUPSORT) {
if (flags & MDBX_CURRENT) {
/* disallow update/delete for multi-values */
node_t *node = page_node(page, cx.outer.ki[cx.outer.top]);
if (node_flags(node) & N_DUP) {
tASSERT(txn, inner_pointed(&cx.outer) && cx.outer.subcur->nested_tree.items > 1);
if (cx.outer.subcur->nested_tree.items > 1) {
rc = MDBX_EMULTIVAL;
goto bailout;
}
}
/* В LMDB флажок MDBX_CURRENT здесь приведет
* к замене данных без учета MDBX_DUPSORT сортировки,
* но здесь это в любом случае допустимо, так как мы
* проверили что для ключа есть только одно значение. */
}
}
if (is_modifable(txn, page)) {
if (new_data && eq_fast(&present_data, new_data)) {
/* если данные совпадают, то ничего делать не надо */
*old_data = *new_data;
goto bailout;
}
rc = preserver ? preserver(preserver_context, old_data, present_data.iov_base, present_data.iov_len)
: MDBX_SUCCESS;
if (unlikely(rc != MDBX_SUCCESS))
goto bailout;
} else {
*old_data = present_data;
}
flags |= MDBX_CURRENT;
}
}
if (likely(new_data))
rc = cursor_put_checklen(&cx.outer, key, new_data, flags);
else
rc = cursor_del(&cx.outer, flags & MDBX_ALLDUPS);
bailout:
txn->cursors[dbi] = cx.outer.next;
return LOG_IFERR(rc);
}
static int default_value_preserver(void *context, MDBX_val *target, const void *src, size_t bytes) {
(void)context;
if (unlikely(target->iov_len < bytes)) {
target->iov_base = nullptr;
target->iov_len = bytes;
return MDBX_RESULT_TRUE;
}
memcpy(target->iov_base, src, target->iov_len = bytes);
return MDBX_SUCCESS;
}
int mdbx_replace(MDBX_txn *txn, MDBX_dbi dbi, const MDBX_val *key, MDBX_val *new_data, MDBX_val *old_data,
MDBX_put_flags_t flags) {
return mdbx_replace_ex(txn, dbi, key, new_data, old_data, flags, default_value_preserver, nullptr);
}

540
src/api-txn.c Normal file
View File

@ -0,0 +1,540 @@
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
#include "internals.h"
#ifdef __SANITIZE_THREAD__
/* LY: avoid tsan-trap by txn, mm_last_pg and geo.first_unallocated */
__attribute__((__no_sanitize_thread__, __noinline__))
#endif
int mdbx_txn_straggler(const MDBX_txn *txn, int *percent)
{
int rc = check_txn(txn, MDBX_TXN_BLOCKED - MDBX_TXN_PARKED);
if (likely(rc == MDBX_SUCCESS))
rc = check_env(txn->env, true);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR((rc > 0) ? -rc : rc);
if (unlikely((txn->flags & MDBX_TXN_RDONLY) == 0)) {
if (percent)
*percent = (int)((txn->geo.first_unallocated * UINT64_C(100) + txn->geo.end_pgno / 2) / txn->geo.end_pgno);
return 0;
}
txnid_t lag;
troika_t troika = meta_tap(txn->env);
do {
const meta_ptr_t head = meta_recent(txn->env, &troika);
if (percent) {
const pgno_t maxpg = head.ptr_v->geometry.now;
*percent = (int)((head.ptr_v->geometry.first_unallocated * UINT64_C(100) + maxpg / 2) / maxpg);
}
lag = (head.txnid - txn->txnid) / xMDBX_TXNID_STEP;
} while (unlikely(meta_should_retry(txn->env, &troika)));
return (lag > INT_MAX) ? INT_MAX : (int)lag;
}
MDBX_env *mdbx_txn_env(const MDBX_txn *txn) {
if (unlikely(!txn || txn->signature != txn_signature || txn->env->signature.weak != env_signature))
return nullptr;
return txn->env;
}
uint64_t mdbx_txn_id(const MDBX_txn *txn) {
if (unlikely(!txn || txn->signature != txn_signature))
return 0;
return txn->txnid;
}
MDBX_txn_flags_t mdbx_txn_flags(const MDBX_txn *txn) {
STATIC_ASSERT(
(MDBX_TXN_INVALID & (MDBX_TXN_FINISHED | MDBX_TXN_ERROR | MDBX_TXN_DIRTY | MDBX_TXN_SPILLS | MDBX_TXN_HAS_CHILD |
txn_gc_drained | txn_shrink_allowed | txn_rw_begin_flags | txn_ro_begin_flags)) == 0);
if (unlikely(!txn || txn->signature != txn_signature))
return MDBX_TXN_INVALID;
assert(0 == (int)(txn->flags & MDBX_TXN_INVALID));
MDBX_txn_flags_t flags = txn->flags;
if (F_ISSET(flags, MDBX_TXN_PARKED | MDBX_TXN_RDONLY) && txn->ro.slot &&
safe64_read(&txn->ro.slot->tid) == MDBX_TID_TXN_OUSTED)
flags |= MDBX_TXN_OUSTED;
return flags;
}
int mdbx_txn_reset(MDBX_txn *txn) {
int rc = check_txn(txn, 0);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
rc = check_env(txn->env, false);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
/* This call is only valid for read-only txns */
if (unlikely((txn->flags & MDBX_TXN_RDONLY) == 0))
return LOG_IFERR(MDBX_EINVAL);
/* LY: don't close DBI-handles */
rc = txn_end(txn, TXN_END_RESET | TXN_END_UPDATE);
if (rc == MDBX_SUCCESS) {
tASSERT(txn, txn->signature == txn_signature);
tASSERT(txn, txn->owner == 0);
}
return LOG_IFERR(rc);
}
int mdbx_txn_break(MDBX_txn *txn) {
do {
int rc = check_txn(txn, 0);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
txn->flags |= MDBX_TXN_ERROR;
txn = txn->nested;
} while (txn);
return MDBX_SUCCESS;
}
int mdbx_txn_abort(MDBX_txn *txn) {
int rc = check_txn(txn, 0);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
rc = check_env(txn->env, true);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
#if MDBX_TXN_CHECKOWNER
if ((txn->flags & (MDBX_TXN_RDONLY | MDBX_NOSTICKYTHREADS)) == MDBX_NOSTICKYTHREADS &&
unlikely(txn->owner != osal_thread_self())) {
mdbx_txn_break(txn);
return LOG_IFERR(MDBX_THREAD_MISMATCH);
}
#endif /* MDBX_TXN_CHECKOWNER */
return LOG_IFERR(txn_abort(txn));
}
int mdbx_txn_park(MDBX_txn *txn, bool autounpark) {
STATIC_ASSERT(MDBX_TXN_BLOCKED > MDBX_TXN_ERROR);
int rc = check_txn(txn, MDBX_TXN_BLOCKED - MDBX_TXN_ERROR);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
rc = check_env(txn->env, true);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
if (unlikely((txn->flags & MDBX_TXN_RDONLY) == 0))
return LOG_IFERR(MDBX_TXN_INVALID);
if (unlikely((txn->flags & MDBX_TXN_ERROR))) {
rc = txn_end(txn, TXN_END_RESET | TXN_END_UPDATE);
return LOG_IFERR(rc ? rc : MDBX_OUSTED);
}
return LOG_IFERR(txn_ro_park(txn, autounpark));
}
int mdbx_txn_unpark(MDBX_txn *txn, bool restart_if_ousted) {
STATIC_ASSERT(MDBX_TXN_BLOCKED > MDBX_TXN_PARKED + MDBX_TXN_ERROR);
int rc = check_txn(txn, MDBX_TXN_BLOCKED - MDBX_TXN_PARKED - MDBX_TXN_ERROR);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
rc = check_env(txn->env, true);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
if (unlikely(!F_ISSET(txn->flags, MDBX_TXN_RDONLY | MDBX_TXN_PARKED)))
return MDBX_SUCCESS;
rc = txn_ro_unpark(txn);
if (likely(rc != MDBX_OUSTED) || !restart_if_ousted)
return LOG_IFERR(rc);
tASSERT(txn, txn->flags & MDBX_TXN_FINISHED);
rc = txn_renew(txn, MDBX_TXN_RDONLY);
return (rc == MDBX_SUCCESS) ? MDBX_RESULT_TRUE : LOG_IFERR(rc);
}
int mdbx_txn_renew(MDBX_txn *txn) {
int rc = check_txn(txn, 0);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
rc = check_env(txn->env, true);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
if (unlikely((txn->flags & MDBX_TXN_RDONLY) == 0))
return LOG_IFERR(MDBX_EINVAL);
if (unlikely(txn->owner != 0 || !(txn->flags & MDBX_TXN_FINISHED))) {
rc = mdbx_txn_reset(txn);
if (unlikely(rc != MDBX_SUCCESS))
return rc;
}
rc = txn_renew(txn, MDBX_TXN_RDONLY);
if (rc == MDBX_SUCCESS) {
tASSERT(txn, txn->owner == (txn->flags & MDBX_NOSTICKYTHREADS) ? 0 : osal_thread_self());
DEBUG("renew txn %" PRIaTXN "%c %p on env %p, root page %" PRIaPGNO "/%" PRIaPGNO, txn->txnid,
(txn->flags & MDBX_TXN_RDONLY) ? 'r' : 'w', (void *)txn, (void *)txn->env, txn->dbs[MAIN_DBI].root,
txn->dbs[FREE_DBI].root);
}
return LOG_IFERR(rc);
}
int mdbx_txn_set_userctx(MDBX_txn *txn, void *ctx) {
int rc = check_txn(txn, 0);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
txn->userctx = ctx;
return MDBX_SUCCESS;
}
void *mdbx_txn_get_userctx(const MDBX_txn *txn) { return check_txn(txn, MDBX_TXN_FINISHED) ? nullptr : txn->userctx; }
int mdbx_txn_begin_ex(MDBX_env *env, MDBX_txn *parent, MDBX_txn_flags_t flags, MDBX_txn **ret, void *context) {
if (unlikely(!ret))
return LOG_IFERR(MDBX_EINVAL);
*ret = nullptr;
if (unlikely((flags & ~txn_rw_begin_flags) && (parent || (flags & ~txn_ro_begin_flags))))
return LOG_IFERR(MDBX_EINVAL);
int rc = check_env(env, true);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
if (unlikely(env->flags & MDBX_RDONLY & ~flags)) /* write txn in RDONLY env */
return LOG_IFERR(MDBX_EACCESS);
/* Reuse preallocated write txn. However, do not touch it until
* txn_renew() succeeds, since it currently may be active. */
MDBX_txn *txn = nullptr;
if (parent) {
/* Nested transactions: Max 1 child, write txns only, no writemap */
rc = check_txn(parent, MDBX_TXN_BLOCKED - MDBX_TXN_PARKED);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
if (unlikely(parent->flags & (MDBX_TXN_RDONLY | MDBX_WRITEMAP))) {
rc = MDBX_BAD_TXN;
if ((parent->flags & MDBX_TXN_RDONLY) == 0) {
ERROR("%s mode is incompatible with nested transactions", "MDBX_WRITEMAP");
rc = MDBX_INCOMPATIBLE;
}
return LOG_IFERR(rc);
}
if (unlikely(parent->env != env))
return LOG_IFERR(MDBX_BAD_TXN);
flags |= parent->flags & (txn_rw_begin_flags | MDBX_TXN_SPILLS | MDBX_NOSTICKYTHREADS | MDBX_WRITEMAP);
rc = txn_nested_create(parent, flags);
txn = parent->nested;
if (unlikely(rc != MDBX_SUCCESS)) {
int err = txn_end(txn, TXN_END_FAIL_BEGIN_NESTED);
return err ? err : rc;
}
if (AUDIT_ENABLED() && ASSERT_ENABLED()) {
txn->signature = txn_signature;
tASSERT(txn, audit_ex(txn, 0, false) == 0);
}
} else {
txn = env->basal_txn;
if (flags & MDBX_TXN_RDONLY) {
txn = txn_alloc(flags, env);
if (unlikely(!txn))
return LOG_IFERR(MDBX_ENOMEM);
}
rc = txn_renew(txn, flags);
if (unlikely(rc != MDBX_SUCCESS)) {
if (txn != env->basal_txn)
osal_free(txn);
return LOG_IFERR(rc);
}
}
if (flags & (MDBX_TXN_RDONLY_PREPARE - MDBX_TXN_RDONLY))
eASSERT(env, txn->flags == (MDBX_TXN_RDONLY | MDBX_TXN_FINISHED));
else if (flags & MDBX_TXN_RDONLY)
eASSERT(env, (txn->flags & ~(MDBX_NOSTICKYTHREADS | MDBX_TXN_RDONLY | MDBX_WRITEMAP |
/* Win32: SRWL flag */ txn_shrink_allowed)) == 0);
else {
eASSERT(env, (txn->flags & ~(MDBX_NOSTICKYTHREADS | MDBX_WRITEMAP | txn_shrink_allowed | txn_may_have_cursors |
MDBX_NOMETASYNC | MDBX_SAFE_NOSYNC | MDBX_TXN_SPILLS)) == 0);
assert(!txn->wr.spilled.list && !txn->wr.spilled.least_removed);
}
txn->signature = txn_signature;
txn->userctx = context;
*ret = txn;
DEBUG("begin txn %" PRIaTXN "%c %p on env %p, root page %" PRIaPGNO "/%" PRIaPGNO, txn->txnid,
(flags & MDBX_TXN_RDONLY) ? 'r' : 'w', (void *)txn, (void *)env, txn->dbs[MAIN_DBI].root,
txn->dbs[FREE_DBI].root);
return MDBX_SUCCESS;
}
static void latency_gcprof(MDBX_commit_latency *latency, const MDBX_txn *txn) {
MDBX_env *const env = txn->env;
if (latency && likely(env->lck) && MDBX_ENABLE_PROFGC) {
pgop_stat_t *const ptr = &env->lck->pgops;
latency->gc_prof.work_counter = ptr->gc_prof.work.spe_counter;
latency->gc_prof.work_rtime_monotonic = osal_monotime_to_16dot16(ptr->gc_prof.work.rtime_monotonic);
latency->gc_prof.work_xtime_cpu = osal_monotime_to_16dot16(ptr->gc_prof.work.xtime_cpu);
latency->gc_prof.work_rsteps = ptr->gc_prof.work.rsteps;
latency->gc_prof.work_xpages = ptr->gc_prof.work.xpages;
latency->gc_prof.work_majflt = ptr->gc_prof.work.majflt;
latency->gc_prof.self_counter = ptr->gc_prof.self.spe_counter;
latency->gc_prof.self_rtime_monotonic = osal_monotime_to_16dot16(ptr->gc_prof.self.rtime_monotonic);
latency->gc_prof.self_xtime_cpu = osal_monotime_to_16dot16(ptr->gc_prof.self.xtime_cpu);
latency->gc_prof.self_rsteps = ptr->gc_prof.self.rsteps;
latency->gc_prof.self_xpages = ptr->gc_prof.self.xpages;
latency->gc_prof.self_majflt = ptr->gc_prof.self.majflt;
latency->gc_prof.wloops = ptr->gc_prof.wloops;
latency->gc_prof.coalescences = ptr->gc_prof.coalescences;
latency->gc_prof.wipes = ptr->gc_prof.wipes;
latency->gc_prof.flushes = ptr->gc_prof.flushes;
latency->gc_prof.kicks = ptr->gc_prof.kicks;
latency->gc_prof.pnl_merge_work.time = osal_monotime_to_16dot16(ptr->gc_prof.work.pnl_merge.time);
latency->gc_prof.pnl_merge_work.calls = ptr->gc_prof.work.pnl_merge.calls;
latency->gc_prof.pnl_merge_work.volume = ptr->gc_prof.work.pnl_merge.volume;
latency->gc_prof.pnl_merge_self.time = osal_monotime_to_16dot16(ptr->gc_prof.self.pnl_merge.time);
latency->gc_prof.pnl_merge_self.calls = ptr->gc_prof.self.pnl_merge.calls;
latency->gc_prof.pnl_merge_self.volume = ptr->gc_prof.self.pnl_merge.volume;
if (txn == env->basal_txn)
memset(&ptr->gc_prof, 0, sizeof(ptr->gc_prof));
}
}
static void latency_init(MDBX_commit_latency *latency, struct commit_timestamp *ts) {
ts->start = 0;
ts->gc_cpu = 0;
if (latency) {
ts->start = osal_monotime();
memset(latency, 0, sizeof(*latency));
}
ts->prep = ts->gc = ts->audit = ts->write = ts->sync = ts->start;
}
static void latency_done(MDBX_commit_latency *latency, struct commit_timestamp *ts) {
if (latency) {
latency->preparation = (ts->prep > ts->start) ? osal_monotime_to_16dot16(ts->prep - ts->start) : 0;
latency->gc_wallclock = (ts->gc > ts->prep) ? osal_monotime_to_16dot16(ts->gc - ts->prep) : 0;
latency->gc_cputime = ts->gc_cpu ? osal_monotime_to_16dot16(ts->gc_cpu) : 0;
latency->audit = (ts->audit > ts->gc) ? osal_monotime_to_16dot16(ts->audit - ts->gc) : 0;
latency->write = (ts->write > ts->audit) ? osal_monotime_to_16dot16(ts->write - ts->audit) : 0;
latency->sync = (ts->sync > ts->write) ? osal_monotime_to_16dot16(ts->sync - ts->write) : 0;
const uint64_t ts_end = osal_monotime();
latency->ending = (ts_end > ts->sync) ? osal_monotime_to_16dot16(ts_end - ts->sync) : 0;
latency->whole = osal_monotime_to_16dot16_noUnderflow(ts_end - ts->start);
}
}
int mdbx_txn_commit_ex(MDBX_txn *txn, MDBX_commit_latency *latency) {
STATIC_ASSERT(MDBX_TXN_FINISHED == MDBX_TXN_BLOCKED - MDBX_TXN_HAS_CHILD - MDBX_TXN_ERROR - MDBX_TXN_PARKED);
struct commit_timestamp ts;
latency_init(latency, &ts);
int rc = check_txn(txn, MDBX_TXN_FINISHED);
if (unlikely(rc != MDBX_SUCCESS)) {
if (rc == MDBX_BAD_TXN && F_ISSET(txn->flags, MDBX_TXN_FINISHED | MDBX_TXN_RDONLY)) {
rc = MDBX_RESULT_TRUE;
goto fail;
}
return LOG_IFERR(rc);
}
MDBX_env *const env = txn->env;
if (MDBX_ENV_CHECKPID && unlikely(env->pid != osal_getpid())) {
env->flags |= ENV_FATAL_ERROR;
rc = MDBX_PANIC;
return LOG_IFERR(rc);
}
if (txn->flags & MDBX_TXN_RDONLY) {
if (unlikely(txn->parent || (txn->flags & MDBX_TXN_HAS_CHILD) || txn == env->txn || txn == env->basal_txn)) {
ERROR("attempt to commit %s txn %p", "strange read-only", (void *)txn);
return MDBX_PROBLEM;
}
latency_gcprof(latency, txn);
rc = (txn->flags & MDBX_TXN_ERROR) ? MDBX_RESULT_TRUE : MDBX_SUCCESS;
txn_end(txn, TXN_END_PURE_COMMIT | TXN_END_UPDATE | TXN_END_SLOT | TXN_END_FREE);
goto done;
}
#if MDBX_TXN_CHECKOWNER
if ((txn->flags & MDBX_NOSTICKYTHREADS) && txn == env->basal_txn && unlikely(txn->owner != osal_thread_self())) {
txn->flags |= MDBX_TXN_ERROR;
rc = MDBX_THREAD_MISMATCH;
return LOG_IFERR(rc);
}
#endif /* MDBX_TXN_CHECKOWNER */
if (unlikely(txn->flags & MDBX_TXN_ERROR)) {
rc = MDBX_RESULT_TRUE;
fail:
latency_gcprof(latency, txn);
int err = txn_abort(txn);
if (unlikely(err != MDBX_SUCCESS))
rc = err;
goto done;
}
if (txn->nested) {
rc = mdbx_txn_commit_ex(txn->nested, nullptr);
tASSERT(txn, txn->nested == nullptr);
if (unlikely(rc != MDBX_SUCCESS))
goto fail;
}
if (unlikely(txn != env->txn)) {
ERROR("attempt to commit %s txn %p", "unknown", (void *)txn);
return MDBX_EINVAL;
}
if (txn->parent) {
if (unlikely(txn->parent->nested != txn || txn->parent->env != env)) {
ERROR("attempt to commit %s txn %p", "strange nested", (void *)txn);
return MDBX_PROBLEM;
}
latency_gcprof(latency, txn);
rc = txn_nested_join(txn, latency ? &ts : nullptr);
goto done;
}
rc = txn_basal_commit(txn, latency ? &ts : nullptr);
latency_gcprof(latency, txn);
int end = TXN_END_COMMITTED | TXN_END_UPDATE;
if (unlikely(rc != MDBX_SUCCESS)) {
end = TXN_END_ABORT;
if (rc == MDBX_RESULT_TRUE) {
end = TXN_END_PURE_COMMIT | TXN_END_UPDATE;
rc = MDBX_NOSUCCESS_PURE_COMMIT ? MDBX_RESULT_TRUE : MDBX_SUCCESS;
}
}
int err = txn_end(txn, end);
if (unlikely(err != MDBX_SUCCESS))
rc = err;
done:
latency_done(latency, &ts);
return LOG_IFERR(rc);
}
int mdbx_txn_info(const MDBX_txn *txn, MDBX_txn_info *info, bool scan_rlt) {
int rc = check_txn(txn, MDBX_TXN_FINISHED);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
if (unlikely(!info))
return LOG_IFERR(MDBX_EINVAL);
MDBX_env *const env = txn->env;
#if MDBX_ENV_CHECKPID
if (unlikely(env->pid != osal_getpid())) {
env->flags |= ENV_FATAL_ERROR;
return LOG_IFERR(MDBX_PANIC);
}
#endif /* MDBX_ENV_CHECKPID */
info->txn_id = txn->txnid;
info->txn_space_used = pgno2bytes(env, txn->geo.first_unallocated);
if (txn->flags & MDBX_TXN_RDONLY) {
meta_ptr_t head;
uint64_t head_retired;
troika_t troika = meta_tap(env);
do {
/* fetch info from volatile head */
head = meta_recent(env, &troika);
head_retired = unaligned_peek_u64_volatile(4, head.ptr_v->pages_retired);
info->txn_space_limit_soft = pgno2bytes(env, head.ptr_v->geometry.now);
info->txn_space_limit_hard = pgno2bytes(env, head.ptr_v->geometry.upper);
info->txn_space_leftover = pgno2bytes(env, head.ptr_v->geometry.now - head.ptr_v->geometry.first_unallocated);
} while (unlikely(meta_should_retry(env, &troika)));
info->txn_reader_lag = head.txnid - info->txn_id;
info->txn_space_dirty = info->txn_space_retired = 0;
uint64_t reader_snapshot_pages_retired = 0;
if (txn->ro.slot &&
((txn->flags & MDBX_TXN_PARKED) == 0 || safe64_read(&txn->ro.slot->tid) != MDBX_TID_TXN_OUSTED) &&
head_retired >
(reader_snapshot_pages_retired = atomic_load64(&txn->ro.slot->snapshot_pages_retired, mo_Relaxed))) {
info->txn_space_dirty = info->txn_space_retired =
pgno2bytes(env, (pgno_t)(head_retired - reader_snapshot_pages_retired));
size_t retired_next_reader = 0;
lck_t *const lck = env->lck_mmap.lck;
if (scan_rlt && info->txn_reader_lag > 1 && lck) {
/* find next more recent reader */
txnid_t next_reader = head.txnid;
const size_t snap_nreaders = atomic_load32(&lck->rdt_length, mo_AcquireRelease);
for (size_t i = 0; i < snap_nreaders; ++i) {
retry:
if (atomic_load32(&lck->rdt[i].pid, mo_AcquireRelease)) {
jitter4testing(true);
const uint64_t snap_tid = safe64_read(&lck->rdt[i].tid);
const txnid_t snap_txnid = safe64_read(&lck->rdt[i].txnid);
const uint64_t snap_retired = atomic_load64(&lck->rdt[i].snapshot_pages_retired, mo_AcquireRelease);
if (unlikely(snap_retired != atomic_load64(&lck->rdt[i].snapshot_pages_retired, mo_Relaxed)) ||
snap_txnid != safe64_read(&lck->rdt[i].txnid) || snap_tid != safe64_read(&lck->rdt[i].tid))
goto retry;
if (snap_txnid <= txn->txnid) {
retired_next_reader = 0;
break;
}
if (snap_txnid < next_reader && snap_tid >= MDBX_TID_TXN_OUSTED) {
next_reader = snap_txnid;
retired_next_reader = pgno2bytes(
env, (pgno_t)(snap_retired - atomic_load64(&txn->ro.slot->snapshot_pages_retired, mo_Relaxed)));
}
}
}
}
info->txn_space_dirty = retired_next_reader;
}
} else {
info->txn_space_limit_soft = pgno2bytes(env, txn->geo.now);
info->txn_space_limit_hard = pgno2bytes(env, txn->geo.upper);
info->txn_space_retired =
pgno2bytes(env, txn->nested ? (size_t)txn->wr.retired_pages : MDBX_PNL_GETSIZE(txn->wr.retired_pages));
info->txn_space_leftover = pgno2bytes(env, txn->wr.dirtyroom);
info->txn_space_dirty =
pgno2bytes(env, txn->wr.dirtylist ? txn->wr.dirtylist->pages_including_loose
: (txn->wr.writemap_dirty_npages + txn->wr.writemap_spilled_npages));
info->txn_reader_lag = INT64_MAX;
lck_t *const lck = env->lck_mmap.lck;
if (scan_rlt && lck) {
txnid_t oldest_reading = txn->txnid;
const size_t snap_nreaders = atomic_load32(&lck->rdt_length, mo_AcquireRelease);
if (snap_nreaders) {
txn_gc_detent(txn);
oldest_reading = txn->env->gc.detent;
if (oldest_reading == txn->wr.troika.txnid[txn->wr.troika.recent]) {
/* Если самый старый используемый снимок является предыдущим, т. е. непосредственно предшествующим текущей
* транзакции, то просматриваем таблицу читателей чтобы выяснить действительно ли снимок используется
* читателями. */
oldest_reading = txn->txnid;
for (size_t i = 0; i < snap_nreaders; ++i) {
if (atomic_load32(&lck->rdt[i].pid, mo_Relaxed) && txn->env->gc.detent == safe64_read(&lck->rdt[i].txnid)) {
oldest_reading = txn->env->gc.detent;
break;
}
}
}
}
info->txn_reader_lag = txn->txnid - oldest_reading;
}
}
return MDBX_SUCCESS;
}

364
src/atomics-ops.h Normal file
View File

@ -0,0 +1,364 @@
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
#pragma once
#include "essentials.h"
#ifndef __cplusplus
#ifdef MDBX_HAVE_C11ATOMICS
#define osal_memory_fence(order, write) atomic_thread_fence((write) ? mo_c11_store(order) : mo_c11_load(order))
#else /* MDBX_HAVE_C11ATOMICS */
#define osal_memory_fence(order, write) \
do { \
osal_compiler_barrier(); \
if (write && order > (MDBX_CPU_WRITEBACK_INCOHERENT ? mo_Relaxed : mo_AcquireRelease)) \
osal_memory_barrier(); \
} while (0)
#endif /* MDBX_HAVE_C11ATOMICS */
#if defined(MDBX_HAVE_C11ATOMICS) && defined(__LCC__)
#define atomic_store32(p, value, order) \
({ \
const uint32_t value_to_store = (value); \
atomic_store_explicit(MDBX_c11a_rw(uint32_t, p), value_to_store, mo_c11_store(order)); \
value_to_store; \
})
#define atomic_load32(p, order) atomic_load_explicit(MDBX_c11a_ro(uint32_t, p), mo_c11_load(order))
#define atomic_store64(p, value, order) \
({ \
const uint64_t value_to_store = (value); \
atomic_store_explicit(MDBX_c11a_rw(uint64_t, p), value_to_store, mo_c11_store(order)); \
value_to_store; \
})
#define atomic_load64(p, order) atomic_load_explicit(MDBX_c11a_ro(uint64_t, p), mo_c11_load(order))
#endif /* LCC && MDBX_HAVE_C11ATOMICS */
#ifndef atomic_store32
MDBX_MAYBE_UNUSED static __always_inline uint32_t atomic_store32(mdbx_atomic_uint32_t *p, const uint32_t value,
enum mdbx_memory_order order) {
STATIC_ASSERT(sizeof(mdbx_atomic_uint32_t) == 4);
#ifdef MDBX_HAVE_C11ATOMICS
assert(atomic_is_lock_free(MDBX_c11a_rw(uint32_t, p)));
atomic_store_explicit(MDBX_c11a_rw(uint32_t, p), value, mo_c11_store(order));
#else /* MDBX_HAVE_C11ATOMICS */
if (order != mo_Relaxed)
osal_compiler_barrier();
p->weak = value;
osal_memory_fence(order, true);
#endif /* MDBX_HAVE_C11ATOMICS */
return value;
}
#endif /* atomic_store32 */
#ifndef atomic_load32
MDBX_MAYBE_UNUSED static __always_inline uint32_t atomic_load32(const volatile mdbx_atomic_uint32_t *p,
enum mdbx_memory_order order) {
STATIC_ASSERT(sizeof(mdbx_atomic_uint32_t) == 4);
#ifdef MDBX_HAVE_C11ATOMICS
assert(atomic_is_lock_free(MDBX_c11a_ro(uint32_t, p)));
return atomic_load_explicit(MDBX_c11a_ro(uint32_t, p), mo_c11_load(order));
#else /* MDBX_HAVE_C11ATOMICS */
osal_memory_fence(order, false);
const uint32_t value = p->weak;
if (order != mo_Relaxed)
osal_compiler_barrier();
return value;
#endif /* MDBX_HAVE_C11ATOMICS */
}
#endif /* atomic_load32 */
/*------------------------------------------------------------------------------
* safe read/write volatile 64-bit fields on 32-bit architectures. */
/* LY: for testing non-atomic 64-bit txnid on 32-bit arches.
* #define xMDBX_TXNID_STEP (UINT32_MAX / 3) */
#ifndef xMDBX_TXNID_STEP
#if MDBX_64BIT_CAS
#define xMDBX_TXNID_STEP 1u
#else
#define xMDBX_TXNID_STEP 2u
#endif
#endif /* xMDBX_TXNID_STEP */
#ifndef atomic_store64
MDBX_MAYBE_UNUSED static __always_inline uint64_t atomic_store64(mdbx_atomic_uint64_t *p, const uint64_t value,
enum mdbx_memory_order order) {
STATIC_ASSERT(sizeof(mdbx_atomic_uint64_t) == 8);
#if MDBX_64BIT_ATOMIC
#if __GNUC_PREREQ(11, 0)
STATIC_ASSERT(__alignof__(mdbx_atomic_uint64_t) >= sizeof(uint64_t));
#endif /* GNU C >= 11 */
#ifdef MDBX_HAVE_C11ATOMICS
assert(atomic_is_lock_free(MDBX_c11a_rw(uint64_t, p)));
atomic_store_explicit(MDBX_c11a_rw(uint64_t, p), value, mo_c11_store(order));
#else /* MDBX_HAVE_C11ATOMICS */
if (order != mo_Relaxed)
osal_compiler_barrier();
p->weak = value;
osal_memory_fence(order, true);
#endif /* MDBX_HAVE_C11ATOMICS */
#else /* !MDBX_64BIT_ATOMIC */
osal_compiler_barrier();
atomic_store32(&p->low, (uint32_t)value, mo_Relaxed);
jitter4testing(true);
atomic_store32(&p->high, (uint32_t)(value >> 32), order);
jitter4testing(true);
#endif /* !MDBX_64BIT_ATOMIC */
return value;
}
#endif /* atomic_store64 */
#ifndef atomic_load64
MDBX_MAYBE_UNUSED static
#if MDBX_64BIT_ATOMIC
__always_inline
#endif /* MDBX_64BIT_ATOMIC */
uint64_t
atomic_load64(const volatile mdbx_atomic_uint64_t *p, enum mdbx_memory_order order) {
STATIC_ASSERT(sizeof(mdbx_atomic_uint64_t) == 8);
#if MDBX_64BIT_ATOMIC
#ifdef MDBX_HAVE_C11ATOMICS
assert(atomic_is_lock_free(MDBX_c11a_ro(uint64_t, p)));
return atomic_load_explicit(MDBX_c11a_ro(uint64_t, p), mo_c11_load(order));
#else /* MDBX_HAVE_C11ATOMICS */
osal_memory_fence(order, false);
const uint64_t value = p->weak;
if (order != mo_Relaxed)
osal_compiler_barrier();
return value;
#endif /* MDBX_HAVE_C11ATOMICS */
#else /* !MDBX_64BIT_ATOMIC */
osal_compiler_barrier();
uint64_t value = (uint64_t)atomic_load32(&p->high, order) << 32;
jitter4testing(true);
value |= atomic_load32(&p->low, (order == mo_Relaxed) ? mo_Relaxed : mo_AcquireRelease);
jitter4testing(true);
for (;;) {
osal_compiler_barrier();
uint64_t again = (uint64_t)atomic_load32(&p->high, order) << 32;
jitter4testing(true);
again |= atomic_load32(&p->low, (order == mo_Relaxed) ? mo_Relaxed : mo_AcquireRelease);
jitter4testing(true);
if (likely(value == again))
return value;
value = again;
}
#endif /* !MDBX_64BIT_ATOMIC */
}
#endif /* atomic_load64 */
MDBX_MAYBE_UNUSED static __always_inline void atomic_yield(void) {
#if defined(_WIN32) || defined(_WIN64)
YieldProcessor();
#elif defined(__ia32__) || defined(__e2k__)
__builtin_ia32_pause();
#elif defined(__ia64__)
#if defined(__HP_cc__) || defined(__HP_aCC__)
_Asm_hint(_HINT_PAUSE);
#else
__asm__ __volatile__("hint @pause");
#endif
#elif defined(__aarch64__) || (defined(__ARM_ARCH) && __ARM_ARCH > 6) || defined(__ARM_ARCH_6K__)
#ifdef __CC_ARM
__yield();
#else
__asm__ __volatile__("yield");
#endif
#elif (defined(__mips64) || defined(__mips64__)) && defined(__mips_isa_rev) && __mips_isa_rev >= 2
__asm__ __volatile__("pause");
#elif defined(__mips) || defined(__mips__) || defined(__mips64) || defined(__mips64__) || defined(_M_MRX000) || \
defined(_MIPS_) || defined(__MWERKS__) || defined(__sgi)
__asm__ __volatile__(".word 0x00000140");
#elif defined(__linux__) || defined(__gnu_linux__) || defined(_UNIX03_SOURCE)
sched_yield();
#elif (defined(_GNU_SOURCE) && __GLIBC_PREREQ(2, 1)) || defined(_OPEN_THREADS)
pthread_yield();
#endif
}
#if MDBX_64BIT_CAS
MDBX_MAYBE_UNUSED static __always_inline bool atomic_cas64(mdbx_atomic_uint64_t *p, uint64_t c, uint64_t v) {
#ifdef MDBX_HAVE_C11ATOMICS
STATIC_ASSERT(sizeof(long long) >= sizeof(uint64_t));
assert(atomic_is_lock_free(MDBX_c11a_rw(uint64_t, p)));
return atomic_compare_exchange_strong(MDBX_c11a_rw(uint64_t, p), &c, v);
#elif defined(__GNUC__) || defined(__clang__)
return __sync_bool_compare_and_swap(&p->weak, c, v);
#elif defined(_MSC_VER)
return c == (uint64_t)_InterlockedCompareExchange64((volatile __int64 *)&p->weak, v, c);
#elif defined(__APPLE__)
return OSAtomicCompareAndSwap64Barrier(c, v, &p->weak);
#else
#error FIXME: Unsupported compiler
#endif
}
#endif /* MDBX_64BIT_CAS */
MDBX_MAYBE_UNUSED static __always_inline bool atomic_cas32(mdbx_atomic_uint32_t *p, uint32_t c, uint32_t v) {
#ifdef MDBX_HAVE_C11ATOMICS
STATIC_ASSERT(sizeof(int) >= sizeof(uint32_t));
assert(atomic_is_lock_free(MDBX_c11a_rw(uint32_t, p)));
return atomic_compare_exchange_strong(MDBX_c11a_rw(uint32_t, p), &c, v);
#elif defined(__GNUC__) || defined(__clang__)
return __sync_bool_compare_and_swap(&p->weak, c, v);
#elif defined(_MSC_VER)
STATIC_ASSERT(sizeof(volatile long) == sizeof(volatile uint32_t));
return c == (uint32_t)_InterlockedCompareExchange((volatile long *)&p->weak, v, c);
#elif defined(__APPLE__)
return OSAtomicCompareAndSwap32Barrier(c, v, &p->weak);
#else
#error FIXME: Unsupported compiler
#endif
}
MDBX_MAYBE_UNUSED static __always_inline uint32_t atomic_add32(mdbx_atomic_uint32_t *p, uint32_t v) {
#ifdef MDBX_HAVE_C11ATOMICS
STATIC_ASSERT(sizeof(int) >= sizeof(uint32_t));
assert(atomic_is_lock_free(MDBX_c11a_rw(uint32_t, p)));
return atomic_fetch_add(MDBX_c11a_rw(uint32_t, p), v);
#elif defined(__GNUC__) || defined(__clang__)
return __sync_fetch_and_add(&p->weak, v);
#elif defined(_MSC_VER)
STATIC_ASSERT(sizeof(volatile long) == sizeof(volatile uint32_t));
return (uint32_t)_InterlockedExchangeAdd((volatile long *)&p->weak, v);
#elif defined(__APPLE__)
return OSAtomicAdd32Barrier(v, &p->weak);
#else
#error FIXME: Unsupported compiler
#endif
}
#define atomic_sub32(p, v) atomic_add32(p, 0 - (v))
MDBX_MAYBE_UNUSED static __always_inline uint64_t safe64_txnid_next(uint64_t txnid) {
txnid += xMDBX_TXNID_STEP;
#if !MDBX_64BIT_CAS
/* avoid overflow of low-part in safe64_reset() */
txnid += (UINT32_MAX == (uint32_t)txnid);
#endif
return txnid;
}
/* Atomically make target value >= SAFE64_INVALID_THRESHOLD */
MDBX_MAYBE_UNUSED static __always_inline void safe64_reset(mdbx_atomic_uint64_t *p, bool single_writer) {
if (single_writer) {
#if MDBX_64BIT_ATOMIC && MDBX_WORDBITS >= 64
atomic_store64(p, UINT64_MAX, mo_AcquireRelease);
#else
atomic_store32(&p->high, UINT32_MAX, mo_AcquireRelease);
#endif /* MDBX_64BIT_ATOMIC && MDBX_WORDBITS >= 64 */
} else {
#if MDBX_64BIT_CAS && MDBX_64BIT_ATOMIC
/* atomically make value >= SAFE64_INVALID_THRESHOLD by 64-bit operation */
atomic_store64(p, UINT64_MAX, mo_AcquireRelease);
#elif MDBX_64BIT_CAS
/* atomically make value >= SAFE64_INVALID_THRESHOLD by 32-bit operation */
atomic_store32(&p->high, UINT32_MAX, mo_AcquireRelease);
#else
/* it is safe to increment low-part to avoid ABA, since xMDBX_TXNID_STEP > 1
* and overflow was preserved in safe64_txnid_next() */
STATIC_ASSERT(xMDBX_TXNID_STEP > 1);
atomic_add32(&p->low, 1) /* avoid ABA in safe64_reset_compare() */;
atomic_store32(&p->high, UINT32_MAX, mo_AcquireRelease);
atomic_add32(&p->low, 1) /* avoid ABA in safe64_reset_compare() */;
#endif /* MDBX_64BIT_CAS && MDBX_64BIT_ATOMIC */
}
assert(p->weak >= SAFE64_INVALID_THRESHOLD);
jitter4testing(true);
}
MDBX_MAYBE_UNUSED static __always_inline bool safe64_reset_compare(mdbx_atomic_uint64_t *p, uint64_t compare) {
/* LY: This function is used to reset `txnid` from hsr-handler in case
* the asynchronously cancellation of read transaction. Therefore,
* there may be a collision between the cleanup performed here and
* asynchronous termination and restarting of the read transaction
* in another process/thread. In general we MUST NOT reset the `txnid`
* if a new transaction was started (i.e. if `txnid` was changed). */
#if MDBX_64BIT_CAS
bool rc = atomic_cas64(p, compare, UINT64_MAX);
#else
/* LY: There is no gold ratio here since shared mutex is too costly,
* in such way we must acquire/release it for every update of txnid,
* i.e. twice for each read transaction). */
bool rc = false;
if (likely(atomic_load32(&p->low, mo_AcquireRelease) == (uint32_t)compare &&
atomic_cas32(&p->high, (uint32_t)(compare >> 32), UINT32_MAX))) {
if (unlikely(atomic_load32(&p->low, mo_AcquireRelease) != (uint32_t)compare))
atomic_cas32(&p->high, UINT32_MAX, (uint32_t)(compare >> 32));
else
rc = true;
}
#endif /* MDBX_64BIT_CAS */
jitter4testing(true);
return rc;
}
MDBX_MAYBE_UNUSED static __always_inline void safe64_write(mdbx_atomic_uint64_t *p, const uint64_t v) {
assert(p->weak >= SAFE64_INVALID_THRESHOLD);
#if MDBX_64BIT_ATOMIC && MDBX_64BIT_CAS
atomic_store64(p, v, mo_AcquireRelease);
#else /* MDBX_64BIT_ATOMIC */
osal_compiler_barrier();
/* update low-part but still value >= SAFE64_INVALID_THRESHOLD */
atomic_store32(&p->low, (uint32_t)v, mo_Relaxed);
assert(p->weak >= SAFE64_INVALID_THRESHOLD);
jitter4testing(true);
/* update high-part from SAFE64_INVALID_THRESHOLD to actual value */
atomic_store32(&p->high, (uint32_t)(v >> 32), mo_AcquireRelease);
#endif /* MDBX_64BIT_ATOMIC */
assert(p->weak == v);
jitter4testing(true);
}
MDBX_MAYBE_UNUSED static __always_inline uint64_t safe64_read(const mdbx_atomic_uint64_t *p) {
jitter4testing(true);
uint64_t v;
do
v = atomic_load64(p, mo_AcquireRelease);
while (!MDBX_64BIT_ATOMIC && unlikely(v != p->weak));
return v;
}
#if 0 /* unused for now */
MDBX_MAYBE_UNUSED static __always_inline bool safe64_is_valid(uint64_t v) {
#if MDBX_WORDBITS >= 64
return v < SAFE64_INVALID_THRESHOLD;
#else
return (v >> 32) != UINT32_MAX;
#endif /* MDBX_WORDBITS */
}
MDBX_MAYBE_UNUSED static __always_inline bool
safe64_is_valid_ptr(const mdbx_atomic_uint64_t *p) {
#if MDBX_64BIT_ATOMIC
return atomic_load64(p, mo_AcquireRelease) < SAFE64_INVALID_THRESHOLD;
#else
return atomic_load32(&p->high, mo_AcquireRelease) != UINT32_MAX;
#endif /* MDBX_64BIT_ATOMIC */
}
#endif /* unused for now */
/* non-atomic write with safety for reading a half-updated value */
MDBX_MAYBE_UNUSED static __always_inline void safe64_update(mdbx_atomic_uint64_t *p, const uint64_t v) {
#if MDBX_64BIT_ATOMIC
atomic_store64(p, v, mo_Relaxed);
#else
safe64_reset(p, true);
safe64_write(p, v);
#endif /* MDBX_64BIT_ATOMIC */
}
/* non-atomic increment with safety for reading a half-updated value */
MDBX_MAYBE_UNUSED static
#if MDBX_64BIT_ATOMIC
__always_inline
#endif /* MDBX_64BIT_ATOMIC */
void
safe64_inc(mdbx_atomic_uint64_t *p, const uint64_t v) {
assert(v > 0);
safe64_update(p, safe64_read(p) + v);
}
#endif /* !__cplusplus */

97
src/atomics-types.h Normal file
View File

@ -0,0 +1,97 @@
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
#pragma once
#include "essentials.h"
#ifndef MDBX_64BIT_ATOMIC
#error "The MDBX_64BIT_ATOMIC must be defined before"
#endif /* MDBX_64BIT_ATOMIC */
#ifndef MDBX_64BIT_CAS
#error "The MDBX_64BIT_CAS must be defined before"
#endif /* MDBX_64BIT_CAS */
#if defined(__cplusplus) && !defined(__STDC_NO_ATOMICS__) && __has_include(<cstdatomic>)
#include <cstdatomic>
#define MDBX_HAVE_C11ATOMICS
#elif !defined(__cplusplus) && (__STDC_VERSION__ >= 201112L || __has_extension(c_atomic)) && \
!defined(__STDC_NO_ATOMICS__) && \
(__GNUC_PREREQ(4, 9) || __CLANG_PREREQ(3, 8) || !(defined(__GNUC__) || defined(__clang__)))
#include <stdatomic.h>
#define MDBX_HAVE_C11ATOMICS
#elif defined(__GNUC__) || defined(__clang__)
#elif defined(_MSC_VER)
#pragma warning(disable : 4163) /* 'xyz': not available as an intrinsic */
#pragma warning(disable : 4133) /* 'function': incompatible types - from \
'size_t' to 'LONGLONG' */
#pragma warning(disable : 4244) /* 'return': conversion from 'LONGLONG' to \
'std::size_t', possible loss of data */
#pragma warning(disable : 4267) /* 'function': conversion from 'size_t' to \
'long', possible loss of data */
#pragma intrinsic(_InterlockedExchangeAdd, _InterlockedCompareExchange)
#pragma intrinsic(_InterlockedExchangeAdd64, _InterlockedCompareExchange64)
#elif defined(__APPLE__)
#include <libkern/OSAtomic.h>
#else
#error FIXME atomic-ops
#endif
typedef enum mdbx_memory_order {
mo_Relaxed,
mo_AcquireRelease
/* , mo_SequentialConsistency */
} mdbx_memory_order_t;
typedef union {
volatile uint32_t weak;
#ifdef MDBX_HAVE_C11ATOMICS
volatile _Atomic uint32_t c11a;
#endif /* MDBX_HAVE_C11ATOMICS */
} mdbx_atomic_uint32_t;
typedef union {
volatile uint64_t weak;
#if defined(MDBX_HAVE_C11ATOMICS) && (MDBX_64BIT_CAS || MDBX_64BIT_ATOMIC)
volatile _Atomic uint64_t c11a;
#endif
#if !defined(MDBX_HAVE_C11ATOMICS) || !MDBX_64BIT_CAS || !MDBX_64BIT_ATOMIC
__anonymous_struct_extension__ struct {
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
mdbx_atomic_uint32_t low, high;
#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
mdbx_atomic_uint32_t high, low;
#else
#error "FIXME: Unsupported byte order"
#endif /* __BYTE_ORDER__ */
};
#endif
} mdbx_atomic_uint64_t;
#ifdef MDBX_HAVE_C11ATOMICS
/* Crutches for C11 atomic compiler's bugs */
#if defined(__e2k__) && defined(__LCC__) && __LCC__ < /* FIXME */ 127
#define MDBX_c11a_ro(type, ptr) (&(ptr)->weak)
#define MDBX_c11a_rw(type, ptr) (&(ptr)->weak)
#elif defined(__clang__) && __clang__ < 8
#define MDBX_c11a_ro(type, ptr) ((volatile _Atomic(type) *)&(ptr)->c11a)
#define MDBX_c11a_rw(type, ptr) (&(ptr)->c11a)
#else
#define MDBX_c11a_ro(type, ptr) (&(ptr)->c11a)
#define MDBX_c11a_rw(type, ptr) (&(ptr)->c11a)
#endif /* Crutches for C11 atomic compiler's bugs */
#define mo_c11_store(fence) \
(((fence) == mo_Relaxed) ? memory_order_relaxed \
: ((fence) == mo_AcquireRelease) ? memory_order_release \
: memory_order_seq_cst)
#define mo_c11_load(fence) \
(((fence) == mo_Relaxed) ? memory_order_relaxed \
: ((fence) == mo_AcquireRelease) ? memory_order_acquire \
: memory_order_seq_cst)
#endif /* MDBX_HAVE_C11ATOMICS */
#define SAFE64_INVALID_THRESHOLD UINT64_C(0xffffFFFF00000000)

107
src/audit.c Normal file
View File

@ -0,0 +1,107 @@
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
#include "internals.h"
struct audit_ctx {
size_t used;
uint8_t *const done_bitmap;
};
static int audit_dbi(void *ctx, const MDBX_txn *txn, const MDBX_val *name, MDBX_db_flags_t flags,
const struct MDBX_stat *stat, MDBX_dbi dbi) {
struct audit_ctx *audit_ctx = ctx;
(void)name;
(void)txn;
(void)flags;
audit_ctx->used += (size_t)stat->ms_branch_pages + (size_t)stat->ms_leaf_pages + (size_t)stat->ms_overflow_pages;
if (dbi)
audit_ctx->done_bitmap[dbi / CHAR_BIT] |= 1 << dbi % CHAR_BIT;
return MDBX_SUCCESS;
}
static size_t audit_db_used(const tree_t *db) {
return db ? (size_t)db->branch_pages + (size_t)db->leaf_pages + (size_t)db->large_pages : 0;
}
__cold static int audit_ex_locked(MDBX_txn *txn, const size_t retired_stored, const bool dont_filter_gc) {
const MDBX_env *const env = txn->env;
tASSERT(txn, (txn->flags & MDBX_TXN_RDONLY) == 0);
const size_t pending = txn->wr.loose_count + MDBX_PNL_GETSIZE(txn->wr.repnl) +
(MDBX_PNL_GETSIZE(txn->wr.retired_pages) - retired_stored);
cursor_couple_t cx;
int rc = cursor_init(&cx.outer, txn, FREE_DBI);
if (unlikely(rc != MDBX_SUCCESS))
return rc;
size_t gc = 0;
MDBX_val key, data;
rc = outer_first(&cx.outer, &key, &data);
while (rc == MDBX_SUCCESS) {
if (unlikely(key.iov_len != sizeof(txnid_t))) {
ERROR("%s/%d: %s %u", "MDBX_CORRUPTED", MDBX_CORRUPTED, "invalid GC-key size", (unsigned)key.iov_len);
return MDBX_CORRUPTED;
}
const txnid_t id = unaligned_peek_u64(4, key.iov_base);
const size_t len = *(pgno_t *)data.iov_base;
const bool acc = dont_filter_gc || !gc_is_reclaimed(txn, id);
TRACE("%s id %" PRIaTXN " len %zu", acc ? "acc" : "skip", id, len);
if (acc)
gc += len;
rc = outer_next(&cx.outer, &key, &data, MDBX_NEXT);
}
tASSERT(txn, rc == MDBX_NOTFOUND);
const size_t done_bitmap_size = (txn->n_dbi + CHAR_BIT - 1) / CHAR_BIT;
if (txn->parent) {
tASSERT(txn, txn->n_dbi == txn->parent->n_dbi && txn->n_dbi == txn->env->txn->n_dbi);
#if MDBX_ENABLE_DBI_SPARSE
tASSERT(txn, txn->dbi_sparse == txn->parent->dbi_sparse && txn->dbi_sparse == txn->env->txn->dbi_sparse);
#endif /* MDBX_ENABLE_DBI_SPARSE */
}
struct audit_ctx ctx = {0, alloca(done_bitmap_size)};
memset(ctx.done_bitmap, 0, done_bitmap_size);
ctx.used =
NUM_METAS + audit_db_used(dbi_dig(txn, FREE_DBI, nullptr)) + audit_db_used(dbi_dig(txn, MAIN_DBI, nullptr));
rc = mdbx_enumerate_tables(txn, audit_dbi, &ctx);
tASSERT(txn, rc == MDBX_SUCCESS);
for (size_t dbi = CORE_DBS; dbi < txn->n_dbi; ++dbi) {
if (ctx.done_bitmap[dbi / CHAR_BIT] & (1 << dbi % CHAR_BIT))
continue;
const tree_t *db = dbi_dig(txn, dbi, nullptr);
if (db)
ctx.used += audit_db_used(db);
else if (dbi_state(txn, dbi))
WARNING("audit %s@%" PRIaTXN ": unable account dbi %zd / \"%.*s\", state 0x%02x", txn->parent ? "nested-" : "",
txn->txnid, dbi, (int)env->kvs[dbi].name.iov_len, (const char *)env->kvs[dbi].name.iov_base,
dbi_state(txn, dbi));
}
if (pending + gc + ctx.used == txn->geo.first_unallocated)
return MDBX_SUCCESS;
if ((txn->flags & MDBX_TXN_RDONLY) == 0)
ERROR("audit @%" PRIaTXN ": %zu(pending) = %zu(loose) + "
"%zu(reclaimed) + %zu(retired-pending) - %zu(retired-stored)",
txn->txnid, pending, txn->wr.loose_count, MDBX_PNL_GETSIZE(txn->wr.repnl),
txn->wr.retired_pages ? MDBX_PNL_GETSIZE(txn->wr.retired_pages) : 0, retired_stored);
ERROR("audit @%" PRIaTXN ": %zu(pending) + %zu"
"(gc) + %zu(count) = %zu(total) <> %zu"
"(allocated)",
txn->txnid, pending, gc, ctx.used, pending + gc + ctx.used, (size_t)txn->geo.first_unallocated);
return MDBX_PROBLEM;
}
__cold int audit_ex(MDBX_txn *txn, size_t retired_stored, bool dont_filter_gc) {
MDBX_env *const env = txn->env;
int rc = osal_fastmutex_acquire(&env->dbi_lock);
if (likely(rc == MDBX_SUCCESS)) {
rc = audit_ex_locked(txn, retired_stored, dont_filter_gc);
ENSURE(txn->env, osal_fastmutex_release(&env->dbi_lock) == MDBX_SUCCESS);
}
return rc;
}

View File

@ -1,27 +1,27 @@
N | MASK | ENV | TXN | DB | PUT | DBI | NODE | PAGE | MRESIZE |
--|---------|-----------|--------------|----------|-----------|------------|---------|----------|---------|
0 |0000 0001|ALLOC_CACHE|TXN_FINISHED | | |DBI_DIRTY |F_BIGDATA|P_BRANCH | |
1 |0000 0002|ALLOC_GC |TXN_ERROR |REVERSEKEY|F_SUBDATA |DBI_STALE |F_SUBDATA|P_LEAF | |
2 |0000 0004|ALLOC_NEW |TXN_DIRTY |DUPSORT | |DBI_FRESH |F_DUPDATA|P_OVERFLOW| |
3 |0000 0008|ALLOC_SLOT |TXN_SPILLS |INTEGERKEY| |DBI_CREAT | |P_META | |
4 |0000 0010| |TXN_HAS_CHILD |DUPFIXED |NOOVERWRITE|DBI_VALID | |P_BAD | |
5 |0000 0020| | |INTEGERDUP|NODUPDATA |DBI_USRVALID| |P_LEAF2 | |
6 |0000 0040| | |REVERSEDUP|CURRENT |DBI_DUPDATA | |P_SUBP | |
7 |0000 0080| | | |ALLDUPS |DBI_AUDITED | | | |
8 |0000 0100| _MAY_MOVE | | | | | | | <= |
0 |0000 0001|ALLOC_RSRV |TXN_FINISHED | | |DBI_DIRTY |N_BIG |P_BRANCH | |
1 |0000 0002|ALLOC_UNIMP|TXN_ERROR |REVERSEKEY|N_TREE |DBI_STALE |N_TREE |P_LEAF | |
2 |0000 0004|ALLOC_COLSC|TXN_DIRTY |DUPSORT | |DBI_FRESH |N_DUP |P_LARGE | |
3 |0000 0008|ALLOC_SSCAN|TXN_SPILLS |INTEGERKEY| |DBI_CREAT | |P_META | |
4 |0000 0010|ALLOC_FIFO |TXN_HAS_CHILD |DUPFIXED |NOOVERWRITE|DBI_VALID | |P_BAD | |
5 |0000 0020| |TXN_PARKED |INTEGERDUP|NODUPDATA | | |P_DUPFIX | |
6 |0000 0040| |TXN_AUTOUNPARK|REVERSEDUP|CURRENT |DBI_OLDEN | |P_SUBP | |
7 |0000 0080| |TXN_DRAINED_GC|DB_VALID |ALLDUPS |DBI_LINDO | | | |
8 |0000 0100| _MAY_MOVE |TXN_CURSORS | | | | | | <= |
9 |0000 0200| _MAY_UNMAP| | | | | | | <= |
10|0000 0400| | | | | | | | |
11|0000 0800| | | | | | | | |
12|0000 1000| | | | | | | | |
13|0000 2000| | | | | | |P_SPILLED | |
13|0000 2000|VALIDATION | | | | | |P_SPILLED | |
14|0000 4000|NOSUBDIR | | | | | |P_LOOSE | |
15|0000 8000| | |DB_VALID |NOSPILL | | |P_FROZEN | |
15|0000 8000| | | | | | |P_FROZEN | |
16|0001 0000|SAFE_NOSYNC|TXN_NOSYNC | |RESERVE | |RESERVE | | |
17|0002 0000|RDONLY |TXN_RDONLY | |APPEND | |APPEND | | <= |
18|0004 0000|NOMETASYNC |TXN_NOMETASYNC|CREATE |APPENDDUP | | | | |
19|0008 0000|WRITEMAP |<= | |MULTIPLE | | | | <= |
20|0010 0000|UTTERLY | | | | | | | <= |
21|0020 0000|NOTLS |<= | | | | | | |
21|0020 0000|NOSTICKYTHR|<= | | | | | | |
22|0040 0000|EXCLUSIVE | | | | | | | |
23|0080 0000|NORDAHEAD | | | | | | | |
24|0100 0000|NOMEMINIT |TXN_PREPARE | | | | | | |

1825
src/chk.c Normal file

File diff suppressed because it is too large Load Diff

309
src/cogs.c Normal file
View File

@ -0,0 +1,309 @@
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
#include "internals.h"
/*------------------------------------------------------------------------------
* Pack/Unpack 16-bit values for Grow step & Shrink threshold */
MDBX_NOTHROW_CONST_FUNCTION static inline pgno_t me2v(size_t m, size_t e) {
assert(m < 2048 && e < 8);
return (pgno_t)(32768 + ((m + 1) << (e + 8)));
}
MDBX_NOTHROW_CONST_FUNCTION static inline uint16_t v2me(size_t v, size_t e) {
assert(v > (e ? me2v(2047, e - 1) : 32768));
assert(v <= me2v(2047, e));
size_t m = (v - 32768 + ((size_t)1 << (e + 8)) - 1) >> (e + 8);
m -= m > 0;
assert(m < 2048 && e < 8);
// f e d c b a 9 8 7 6 5 4 3 2 1 0
// 1 e e e m m m m m m m m m m m 1
const uint16_t pv = (uint16_t)(0x8001 + (e << 12) + (m << 1));
assert(pv != 65535);
return pv;
}
/* Convert 16-bit packed (exponential quantized) value to number of pages */
pgno_t pv2pages(uint16_t pv) {
if ((pv & 0x8001) != 0x8001)
return pv;
if (pv == 65535)
return 65536;
// f e d c b a 9 8 7 6 5 4 3 2 1 0
// 1 e e e m m m m m m m m m m m 1
return me2v((pv >> 1) & 2047, (pv >> 12) & 7);
}
/* Convert number of pages to 16-bit packed (exponential quantized) value */
uint16_t pages2pv(size_t pages) {
if (pages < 32769 || (pages < 65536 && (pages & 1) == 0))
return (uint16_t)pages;
if (pages <= me2v(2047, 0))
return v2me(pages, 0);
if (pages <= me2v(2047, 1))
return v2me(pages, 1);
if (pages <= me2v(2047, 2))
return v2me(pages, 2);
if (pages <= me2v(2047, 3))
return v2me(pages, 3);
if (pages <= me2v(2047, 4))
return v2me(pages, 4);
if (pages <= me2v(2047, 5))
return v2me(pages, 5);
if (pages <= me2v(2047, 6))
return v2me(pages, 6);
return (pages < me2v(2046, 7)) ? v2me(pages, 7) : 65533;
}
__cold bool pv2pages_verify(void) {
bool ok = true, dump_translation = false;
for (size_t i = 0; i < 65536; ++i) {
size_t pages = pv2pages(i);
size_t x = pages2pv(pages);
size_t xp = pv2pages(x);
if (pages != xp) {
ERROR("%zu => %zu => %zu => %zu\n", i, pages, x, xp);
ok = false;
} else if (dump_translation && !(x == i || (x % 2 == 0 && x < 65536))) {
DEBUG("%zu => %zu => %zu => %zu\n", i, pages, x, xp);
}
}
return ok;
}
/*----------------------------------------------------------------------------*/
MDBX_NOTHROW_PURE_FUNCTION size_t bytes_align2os_bytes(const MDBX_env *env, size_t bytes) {
return ceil_powerof2(bytes, (env->ps > globals.sys_pagesize) ? env->ps : globals.sys_pagesize);
}
MDBX_NOTHROW_PURE_FUNCTION size_t pgno_align2os_bytes(const MDBX_env *env, size_t pgno) {
return ceil_powerof2(pgno2bytes(env, pgno), globals.sys_pagesize);
}
MDBX_NOTHROW_PURE_FUNCTION pgno_t pgno_align2os_pgno(const MDBX_env *env, size_t pgno) {
return bytes2pgno(env, pgno_align2os_bytes(env, pgno));
}
/*----------------------------------------------------------------------------*/
MDBX_NOTHROW_PURE_FUNCTION static __always_inline int cmp_int_inline(const size_t expected_alignment, const MDBX_val *a,
const MDBX_val *b) {
if (likely(a->iov_len == b->iov_len)) {
if (sizeof(size_t) > 7 && likely(a->iov_len == 8))
return CMP2INT(unaligned_peek_u64(expected_alignment, a->iov_base),
unaligned_peek_u64(expected_alignment, b->iov_base));
if (likely(a->iov_len == 4))
return CMP2INT(unaligned_peek_u32(expected_alignment, a->iov_base),
unaligned_peek_u32(expected_alignment, b->iov_base));
if (sizeof(size_t) < 8 && likely(a->iov_len == 8))
return CMP2INT(unaligned_peek_u64(expected_alignment, a->iov_base),
unaligned_peek_u64(expected_alignment, b->iov_base));
}
ERROR("mismatch and/or invalid size %p.%zu/%p.%zu for INTEGERKEY/INTEGERDUP", a->iov_base, a->iov_len, b->iov_base,
b->iov_len);
return 0;
}
MDBX_NOTHROW_PURE_FUNCTION __hot int cmp_int_unaligned(const MDBX_val *a, const MDBX_val *b) {
return cmp_int_inline(1, a, b);
}
#ifndef cmp_int_align2
/* Compare two items pointing at 2-byte aligned unsigned int's. */
MDBX_NOTHROW_PURE_FUNCTION __hot int cmp_int_align2(const MDBX_val *a, const MDBX_val *b) {
return cmp_int_inline(2, a, b);
}
#endif /* cmp_int_align2 */
#ifndef cmp_int_align4
/* Compare two items pointing at 4-byte aligned unsigned int's. */
MDBX_NOTHROW_PURE_FUNCTION __hot int cmp_int_align4(const MDBX_val *a, const MDBX_val *b) {
return cmp_int_inline(4, a, b);
}
#endif /* cmp_int_align4 */
/* Compare two items lexically */
MDBX_NOTHROW_PURE_FUNCTION __hot int cmp_lexical(const MDBX_val *a, const MDBX_val *b) {
if (a->iov_len == b->iov_len)
return a->iov_len ? memcmp(a->iov_base, b->iov_base, a->iov_len) : 0;
const int diff_len = (a->iov_len < b->iov_len) ? -1 : 1;
const size_t shortest = (a->iov_len < b->iov_len) ? a->iov_len : b->iov_len;
int diff_data = shortest ? memcmp(a->iov_base, b->iov_base, shortest) : 0;
return likely(diff_data) ? diff_data : diff_len;
}
MDBX_NOTHROW_PURE_FUNCTION static __always_inline unsigned tail3le(const uint8_t *p, size_t l) {
STATIC_ASSERT(sizeof(unsigned) > 2);
// 1: 0 0 0
// 2: 0 1 1
// 3: 0 1 2
return p[0] | p[l >> 1] << 8 | p[l - 1] << 16;
}
/* Compare two items in reverse byte order */
MDBX_NOTHROW_PURE_FUNCTION __hot int cmp_reverse(const MDBX_val *a, const MDBX_val *b) {
size_t left = (a->iov_len < b->iov_len) ? a->iov_len : b->iov_len;
if (likely(left)) {
const uint8_t *pa = ptr_disp(a->iov_base, a->iov_len);
const uint8_t *pb = ptr_disp(b->iov_base, b->iov_len);
while (left >= sizeof(size_t)) {
pa -= sizeof(size_t);
pb -= sizeof(size_t);
left -= sizeof(size_t);
STATIC_ASSERT(sizeof(size_t) == 4 || sizeof(size_t) == 8);
if (sizeof(size_t) == 4) {
uint32_t xa = unaligned_peek_u32(1, pa);
uint32_t xb = unaligned_peek_u32(1, pb);
#if __BYTE_ORDER__ != __ORDER_LITTLE_ENDIAN__
xa = osal_bswap32(xa);
xb = osal_bswap32(xb);
#endif /* __BYTE_ORDER__ != __ORDER_BIG_ENDIAN__ */
if (xa != xb)
return (xa < xb) ? -1 : 1;
} else {
uint64_t xa = unaligned_peek_u64(1, pa);
uint64_t xb = unaligned_peek_u64(1, pb);
#if __BYTE_ORDER__ != __ORDER_LITTLE_ENDIAN__
xa = osal_bswap64(xa);
xb = osal_bswap64(xb);
#endif /* __BYTE_ORDER__ != __ORDER_BIG_ENDIAN__ */
if (xa != xb)
return (xa < xb) ? -1 : 1;
}
}
if (sizeof(size_t) == 8 && left >= 4) {
pa -= 4;
pb -= 4;
left -= 4;
uint32_t xa = unaligned_peek_u32(1, pa);
uint32_t xb = unaligned_peek_u32(1, pb);
#if __BYTE_ORDER__ != __ORDER_LITTLE_ENDIAN__
xa = osal_bswap32(xa);
xb = osal_bswap32(xb);
#endif /* __BYTE_ORDER__ != __ORDER_BIG_ENDIAN__ */
if (xa != xb)
return (xa < xb) ? -1 : 1;
}
if (left) {
unsigned xa = tail3le(pa - left, left);
unsigned xb = tail3le(pb - left, left);
if (xa != xb)
return (xa < xb) ? -1 : 1;
}
}
return CMP2INT(a->iov_len, b->iov_len);
}
/* Fast non-lexically comparator */
MDBX_NOTHROW_PURE_FUNCTION __hot int cmp_lenfast(const MDBX_val *a, const MDBX_val *b) {
int diff = CMP2INT(a->iov_len, b->iov_len);
return (likely(diff) || a->iov_len == 0) ? diff : memcmp(a->iov_base, b->iov_base, a->iov_len);
}
MDBX_NOTHROW_PURE_FUNCTION __hot bool eq_fast_slowpath(const uint8_t *a, const uint8_t *b, size_t l) {
if (likely(l > 3)) {
if (MDBX_UNALIGNED_OK >= 4 && likely(l < 9))
return ((unaligned_peek_u32(1, a) - unaligned_peek_u32(1, b)) |
(unaligned_peek_u32(1, a + l - 4) - unaligned_peek_u32(1, b + l - 4))) == 0;
if (MDBX_UNALIGNED_OK >= 8 && sizeof(size_t) > 7 && likely(l < 17))
return ((unaligned_peek_u64(1, a) - unaligned_peek_u64(1, b)) |
(unaligned_peek_u64(1, a + l - 8) - unaligned_peek_u64(1, b + l - 8))) == 0;
return memcmp(a, b, l) == 0;
}
if (likely(l))
return tail3le(a, l) == tail3le(b, l);
return true;
}
int cmp_equal_or_greater(const MDBX_val *a, const MDBX_val *b) { return eq_fast(a, b) ? 0 : 1; }
int cmp_equal_or_wrong(const MDBX_val *a, const MDBX_val *b) { return eq_fast(a, b) ? 0 : -1; }
/*----------------------------------------------------------------------------*/
__cold void update_mlcnt(const MDBX_env *env, const pgno_t new_aligned_mlocked_pgno, const bool lock_not_release) {
for (;;) {
const pgno_t mlock_pgno_before = atomic_load32(&env->mlocked_pgno, mo_AcquireRelease);
eASSERT(env, pgno_align2os_pgno(env, mlock_pgno_before) == mlock_pgno_before);
eASSERT(env, pgno_align2os_pgno(env, new_aligned_mlocked_pgno) == new_aligned_mlocked_pgno);
if (lock_not_release ? (mlock_pgno_before >= new_aligned_mlocked_pgno)
: (mlock_pgno_before <= new_aligned_mlocked_pgno))
break;
if (likely(atomic_cas32(&((MDBX_env *)env)->mlocked_pgno, mlock_pgno_before, new_aligned_mlocked_pgno)))
for (;;) {
mdbx_atomic_uint32_t *const mlcnt = env->lck->mlcnt;
const int32_t snap_locked = atomic_load32(mlcnt + 0, mo_Relaxed);
const int32_t snap_unlocked = atomic_load32(mlcnt + 1, mo_Relaxed);
if (mlock_pgno_before == 0 && (snap_locked - snap_unlocked) < INT_MAX) {
eASSERT(env, lock_not_release);
if (unlikely(!atomic_cas32(mlcnt + 0, snap_locked, snap_locked + 1)))
continue;
}
if (new_aligned_mlocked_pgno == 0 && (snap_locked - snap_unlocked) > 0) {
eASSERT(env, !lock_not_release);
if (unlikely(!atomic_cas32(mlcnt + 1, snap_unlocked, snap_unlocked + 1)))
continue;
}
NOTICE("%s-pages %u..%u, mlocked-process(es) %u -> %u", lock_not_release ? "lock" : "unlock",
lock_not_release ? mlock_pgno_before : new_aligned_mlocked_pgno,
lock_not_release ? new_aligned_mlocked_pgno : mlock_pgno_before, snap_locked - snap_unlocked,
atomic_load32(mlcnt + 0, mo_Relaxed) - atomic_load32(mlcnt + 1, mo_Relaxed));
return;
}
}
}
__cold void munlock_after(const MDBX_env *env, const pgno_t aligned_pgno, const size_t end_bytes) {
if (atomic_load32(&env->mlocked_pgno, mo_AcquireRelease) > aligned_pgno) {
int err = MDBX_ENOSYS;
const size_t munlock_begin = pgno2bytes(env, aligned_pgno);
const size_t munlock_size = end_bytes - munlock_begin;
eASSERT(env, end_bytes % globals.sys_pagesize == 0 && munlock_begin % globals.sys_pagesize == 0 &&
munlock_size % globals.sys_pagesize == 0);
#if defined(_WIN32) || defined(_WIN64)
err = VirtualUnlock(ptr_disp(env->dxb_mmap.base, munlock_begin), munlock_size) ? MDBX_SUCCESS : (int)GetLastError();
if (err == ERROR_NOT_LOCKED)
err = MDBX_SUCCESS;
#elif defined(_POSIX_MEMLOCK_RANGE)
err = munlock(ptr_disp(env->dxb_mmap.base, munlock_begin), munlock_size) ? errno : MDBX_SUCCESS;
#endif
if (likely(err == MDBX_SUCCESS))
update_mlcnt(env, aligned_pgno, false);
else {
#if defined(_WIN32) || defined(_WIN64)
WARNING("VirtualUnlock(%zu, %zu) error %d", munlock_begin, munlock_size, err);
#else
WARNING("munlock(%zu, %zu) error %d", munlock_begin, munlock_size, err);
#endif
}
}
}
__cold void munlock_all(const MDBX_env *env) {
munlock_after(env, 0, bytes_align2os_bytes(env, env->dxb_mmap.current));
}
/*----------------------------------------------------------------------------*/
uint32_t combine_durability_flags(const uint32_t a, const uint32_t b) {
uint32_t r = a | b;
/* avoid false MDBX_UTTERLY_NOSYNC */
if (F_ISSET(r, MDBX_UTTERLY_NOSYNC) && !F_ISSET(a, MDBX_UTTERLY_NOSYNC) && !F_ISSET(b, MDBX_UTTERLY_NOSYNC))
r = (r - MDBX_UTTERLY_NOSYNC) | MDBX_SAFE_NOSYNC;
/* convert DEPRECATED_MAPASYNC to MDBX_SAFE_NOSYNC */
if ((r & (MDBX_WRITEMAP | DEPRECATED_MAPASYNC)) == (MDBX_WRITEMAP | DEPRECATED_MAPASYNC) &&
!F_ISSET(r, MDBX_UTTERLY_NOSYNC))
r = (r - DEPRECATED_MAPASYNC) | MDBX_SAFE_NOSYNC;
/* force MDBX_NOMETASYNC if NOSYNC enabled */
if (r & (MDBX_SAFE_NOSYNC | MDBX_UTTERLY_NOSYNC))
r |= MDBX_NOMETASYNC;
assert(!(F_ISSET(r, MDBX_UTTERLY_NOSYNC) && !F_ISSET(a, MDBX_UTTERLY_NOSYNC) && !F_ISSET(b, MDBX_UTTERLY_NOSYNC)));
return r;
}

507
src/cogs.h Normal file
View File

@ -0,0 +1,507 @@
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
#pragma once
#include "essentials.h"
MDBX_NOTHROW_CONST_FUNCTION MDBX_INTERNAL pgno_t pv2pages(uint16_t pv);
MDBX_NOTHROW_CONST_FUNCTION MDBX_INTERNAL uint16_t pages2pv(size_t pages);
MDBX_MAYBE_UNUSED MDBX_INTERNAL bool pv2pages_verify(void);
/*------------------------------------------------------------------------------
* Nodes, Keys & Values length limitation factors:
*
* BRANCH_NODE_MAX
* Branch-page must contain at least two nodes, within each a key and a child
* page number. But page can't be split if it contains less that 4 keys,
* i.e. a page should not overflow before adding the fourth key. Therefore,
* at least 3 branch-node should fit in the single branch-page. Further, the
* first node of a branch-page doesn't contain a key, i.e. the first node
* is always require space just for itself. Thus:
* PAGESPACE = pagesize - page_hdr_len;
* BRANCH_NODE_MAX = even_floor(
* (PAGESPACE - sizeof(indx_t) - NODESIZE) / (3 - 1) - sizeof(indx_t));
* KEYLEN_MAX = BRANCH_NODE_MAX - node_hdr_len;
*
* LEAF_NODE_MAX
* Leaf-node must fit into single leaf-page, where a value could be placed on
* a large/overflow page. However, may require to insert a nearly page-sized
* node between two large nodes are already fill-up a page. In this case the
* page must be split to two if some pair of nodes fits on one page, or
* otherwise the page should be split to the THREE with a single node
* per each of ones. Such 1-into-3 page splitting is costly and complex since
* requires TWO insertion into the parent page, that could lead to split it
* and so on up to the root. Therefore double-splitting is avoided here and
* the maximum node size is half of a leaf page space:
* LEAF_NODE_MAX = even_floor(PAGESPACE / 2 - sizeof(indx_t));
* DATALEN_NO_OVERFLOW = LEAF_NODE_MAX - NODESIZE - KEYLEN_MAX;
*
* - Table-node must fit into one leaf-page:
* TABLE_NAME_MAX = LEAF_NODE_MAX - node_hdr_len - sizeof(tree_t);
*
* - Dupsort values itself are a keys in a dupsort-table and couldn't be longer
* than the KEYLEN_MAX. But dupsort node must not great than LEAF_NODE_MAX,
* since dupsort value couldn't be placed on a large/overflow page:
* DUPSORT_DATALEN_MAX = min(KEYLEN_MAX,
* max(DATALEN_NO_OVERFLOW, sizeof(tree_t));
*/
#define PAGESPACE(pagesize) ((pagesize) - PAGEHDRSZ)
#define BRANCH_NODE_MAX(pagesize) \
(EVEN_FLOOR((PAGESPACE(pagesize) - sizeof(indx_t) - NODESIZE) / (3 - 1) - sizeof(indx_t)))
#define LEAF_NODE_MAX(pagesize) (EVEN_FLOOR(PAGESPACE(pagesize) / 2) - sizeof(indx_t))
#define MAX_GC1OVPAGE(pagesize) (PAGESPACE(pagesize) / sizeof(pgno_t) - 1)
MDBX_NOTHROW_CONST_FUNCTION static inline size_t keysize_max(size_t pagesize, MDBX_db_flags_t flags) {
assert(pagesize >= MDBX_MIN_PAGESIZE && pagesize <= MDBX_MAX_PAGESIZE && is_powerof2(pagesize));
STATIC_ASSERT(BRANCH_NODE_MAX(MDBX_MIN_PAGESIZE) - NODESIZE >= 8);
if (flags & MDBX_INTEGERKEY)
return 8 /* sizeof(uint64_t) */;
const intptr_t max_branch_key = BRANCH_NODE_MAX(pagesize) - NODESIZE;
STATIC_ASSERT(LEAF_NODE_MAX(MDBX_MIN_PAGESIZE) - NODESIZE -
/* sizeof(uint64) as a key */ 8 >
sizeof(tree_t));
if (flags & (MDBX_DUPSORT | MDBX_DUPFIXED | MDBX_REVERSEDUP | MDBX_INTEGERDUP)) {
const intptr_t max_dupsort_leaf_key = LEAF_NODE_MAX(pagesize) - NODESIZE - sizeof(tree_t);
return (max_branch_key < max_dupsort_leaf_key) ? max_branch_key : max_dupsort_leaf_key;
}
return max_branch_key;
}
MDBX_NOTHROW_CONST_FUNCTION static inline size_t env_keysize_max(const MDBX_env *env, MDBX_db_flags_t flags) {
size_t size_max;
if (flags & MDBX_INTEGERKEY)
size_max = 8 /* sizeof(uint64_t) */;
else {
const intptr_t max_branch_key = env->branch_nodemax - NODESIZE;
STATIC_ASSERT(LEAF_NODE_MAX(MDBX_MIN_PAGESIZE) - NODESIZE -
/* sizeof(uint64) as a key */ 8 >
sizeof(tree_t));
if (flags & (MDBX_DUPSORT | MDBX_DUPFIXED | MDBX_REVERSEDUP | MDBX_INTEGERDUP)) {
const intptr_t max_dupsort_leaf_key = env->leaf_nodemax - NODESIZE - sizeof(tree_t);
size_max = (max_branch_key < max_dupsort_leaf_key) ? max_branch_key : max_dupsort_leaf_key;
} else
size_max = max_branch_key;
}
eASSERT(env, size_max == keysize_max(env->ps, flags));
return size_max;
}
MDBX_NOTHROW_CONST_FUNCTION static inline size_t keysize_min(MDBX_db_flags_t flags) {
return (flags & MDBX_INTEGERKEY) ? 4 /* sizeof(uint32_t) */ : 0;
}
MDBX_NOTHROW_CONST_FUNCTION static inline size_t valsize_min(MDBX_db_flags_t flags) {
if (flags & MDBX_INTEGERDUP)
return 4 /* sizeof(uint32_t) */;
else if (flags & MDBX_DUPFIXED)
return sizeof(indx_t);
else
return 0;
}
MDBX_NOTHROW_CONST_FUNCTION static inline size_t valsize_max(size_t pagesize, MDBX_db_flags_t flags) {
assert(pagesize >= MDBX_MIN_PAGESIZE && pagesize <= MDBX_MAX_PAGESIZE && is_powerof2(pagesize));
if (flags & MDBX_INTEGERDUP)
return 8 /* sizeof(uint64_t) */;
if (flags & (MDBX_DUPSORT | MDBX_DUPFIXED | MDBX_REVERSEDUP))
return keysize_max(pagesize, 0);
const unsigned page_ln2 = log2n_powerof2(pagesize);
const size_t hard = 0x7FF00000ul;
const size_t hard_pages = hard >> page_ln2;
STATIC_ASSERT(PAGELIST_LIMIT <= MAX_PAGENO);
const size_t pages_limit = PAGELIST_LIMIT / 4;
const size_t limit = (hard_pages < pages_limit) ? hard : (pages_limit << page_ln2);
return (limit < MAX_MAPSIZE / 2) ? limit : MAX_MAPSIZE / 2;
}
MDBX_NOTHROW_CONST_FUNCTION static inline size_t env_valsize_max(const MDBX_env *env, MDBX_db_flags_t flags) {
size_t size_max;
if (flags & MDBX_INTEGERDUP)
size_max = 8 /* sizeof(uint64_t) */;
else if (flags & (MDBX_DUPSORT | MDBX_DUPFIXED | MDBX_REVERSEDUP))
size_max = env_keysize_max(env, 0);
else {
const size_t hard = 0x7FF00000ul;
const size_t hard_pages = hard >> env->ps2ln;
STATIC_ASSERT(PAGELIST_LIMIT <= MAX_PAGENO);
const size_t pages_limit = PAGELIST_LIMIT / 4;
const size_t limit = (hard_pages < pages_limit) ? hard : (pages_limit << env->ps2ln);
size_max = (limit < MAX_MAPSIZE / 2) ? limit : MAX_MAPSIZE / 2;
}
eASSERT(env, size_max == valsize_max(env->ps, flags));
return size_max;
}
/*----------------------------------------------------------------------------*/
MDBX_NOTHROW_PURE_FUNCTION static inline size_t leaf_size(const MDBX_env *env, const MDBX_val *key,
const MDBX_val *data) {
size_t node_bytes = node_size(key, data);
if (node_bytes > env->leaf_nodemax)
/* put on large/overflow page */
node_bytes = node_size_len(key->iov_len, 0) + sizeof(pgno_t);
return node_bytes + sizeof(indx_t);
}
MDBX_NOTHROW_PURE_FUNCTION static inline size_t branch_size(const MDBX_env *env, const MDBX_val *key) {
/* Size of a node in a branch page with a given key.
* This is just the node header plus the key, there is no data. */
size_t node_bytes = node_size(key, nullptr);
if (unlikely(node_bytes > env->branch_nodemax)) {
/* put on large/overflow page, not implemented */
mdbx_panic("node_size(key) %zu > %u branch_nodemax", node_bytes, env->branch_nodemax);
node_bytes = node_size(key, nullptr) + sizeof(pgno_t);
}
return node_bytes + sizeof(indx_t);
}
MDBX_NOTHROW_CONST_FUNCTION static inline uint16_t flags_db2sub(uint16_t db_flags) {
uint16_t sub_flags = db_flags & MDBX_DUPFIXED;
/* MDBX_INTEGERDUP => MDBX_INTEGERKEY */
#define SHIFT_INTEGERDUP_TO_INTEGERKEY 2
STATIC_ASSERT((MDBX_INTEGERDUP >> SHIFT_INTEGERDUP_TO_INTEGERKEY) == MDBX_INTEGERKEY);
sub_flags |= (db_flags & MDBX_INTEGERDUP) >> SHIFT_INTEGERDUP_TO_INTEGERKEY;
/* MDBX_REVERSEDUP => MDBX_REVERSEKEY */
#define SHIFT_REVERSEDUP_TO_REVERSEKEY 5
STATIC_ASSERT((MDBX_REVERSEDUP >> SHIFT_REVERSEDUP_TO_REVERSEKEY) == MDBX_REVERSEKEY);
sub_flags |= (db_flags & MDBX_REVERSEDUP) >> SHIFT_REVERSEDUP_TO_REVERSEKEY;
return sub_flags;
}
static inline bool check_table_flags(unsigned flags) {
switch (flags & ~(MDBX_REVERSEKEY | MDBX_INTEGERKEY)) {
default:
NOTICE("invalid db-flags 0x%x", flags);
return false;
case MDBX_DUPSORT:
case MDBX_DUPSORT | MDBX_REVERSEDUP:
case MDBX_DUPSORT | MDBX_DUPFIXED:
case MDBX_DUPSORT | MDBX_DUPFIXED | MDBX_REVERSEDUP:
case MDBX_DUPSORT | MDBX_DUPFIXED | MDBX_INTEGERDUP:
case MDBX_DUPSORT | MDBX_DUPFIXED | MDBX_INTEGERDUP | MDBX_REVERSEDUP:
case MDBX_DB_DEFAULTS:
return (flags & (MDBX_REVERSEKEY | MDBX_INTEGERKEY)) != (MDBX_REVERSEKEY | MDBX_INTEGERKEY);
}
}
static inline int tbl_setup_ifneed(const MDBX_env *env, volatile kvx_t *const kvx, const tree_t *const db) {
return likely(kvx->clc.v.lmax) ? MDBX_SUCCESS : tbl_setup(env, kvx, db);
}
/*----------------------------------------------------------------------------*/
MDBX_NOTHROW_PURE_FUNCTION static inline size_t pgno2bytes(const MDBX_env *env, size_t pgno) {
eASSERT(env, (1u << env->ps2ln) == env->ps);
return ((size_t)pgno) << env->ps2ln;
}
MDBX_NOTHROW_PURE_FUNCTION static inline page_t *pgno2page(const MDBX_env *env, size_t pgno) {
return ptr_disp(env->dxb_mmap.base, pgno2bytes(env, pgno));
}
MDBX_NOTHROW_PURE_FUNCTION static inline pgno_t bytes2pgno(const MDBX_env *env, size_t bytes) {
eASSERT(env, (env->ps >> env->ps2ln) == 1);
return (pgno_t)(bytes >> env->ps2ln);
}
MDBX_NOTHROW_PURE_FUNCTION MDBX_INTERNAL size_t bytes_align2os_bytes(const MDBX_env *env, size_t bytes);
MDBX_NOTHROW_PURE_FUNCTION MDBX_INTERNAL size_t pgno_align2os_bytes(const MDBX_env *env, size_t pgno);
MDBX_NOTHROW_PURE_FUNCTION MDBX_INTERNAL pgno_t pgno_align2os_pgno(const MDBX_env *env, size_t pgno);
MDBX_NOTHROW_PURE_FUNCTION static inline pgno_t largechunk_npages(const MDBX_env *env, size_t bytes) {
return bytes2pgno(env, PAGEHDRSZ - 1 + bytes) + 1;
}
MDBX_NOTHROW_PURE_FUNCTION static inline MDBX_val get_key(const node_t *node) {
MDBX_val key;
key.iov_len = node_ks(node);
key.iov_base = node_key(node);
return key;
}
static inline void get_key_optional(const node_t *node, MDBX_val *keyptr /* __may_null */) {
if (keyptr)
*keyptr = get_key(node);
}
MDBX_NOTHROW_PURE_FUNCTION static inline void *page_data(const page_t *mp) { return ptr_disp(mp, PAGEHDRSZ); }
MDBX_NOTHROW_PURE_FUNCTION static inline const page_t *data_page(const void *data) {
return container_of(data, page_t, entries);
}
MDBX_NOTHROW_PURE_FUNCTION static inline meta_t *page_meta(page_t *mp) { return (meta_t *)page_data(mp); }
MDBX_NOTHROW_PURE_FUNCTION static inline size_t page_numkeys(const page_t *mp) {
assert(mp->lower <= mp->upper);
return mp->lower >> 1;
}
MDBX_NOTHROW_PURE_FUNCTION static inline size_t page_room(const page_t *mp) {
assert(mp->lower <= mp->upper);
return mp->upper - mp->lower;
}
MDBX_NOTHROW_PURE_FUNCTION static inline size_t page_space(const MDBX_env *env) {
STATIC_ASSERT(PAGEHDRSZ % 2 == 0);
return env->ps - PAGEHDRSZ;
}
MDBX_NOTHROW_PURE_FUNCTION static inline size_t page_used(const MDBX_env *env, const page_t *mp) {
return page_space(env) - page_room(mp);
}
/* The percentage of space used in the page, in a percents. */
MDBX_MAYBE_UNUSED MDBX_NOTHROW_PURE_FUNCTION static inline unsigned page_fill_percentum_x10(const MDBX_env *env,
const page_t *mp) {
const size_t space = page_space(env);
return (unsigned)((page_used(env, mp) * 1000 + space / 2) / space);
}
MDBX_NOTHROW_PURE_FUNCTION static inline node_t *page_node(const page_t *mp, size_t i) {
assert(page_type_compat(mp) == P_LEAF || page_type(mp) == P_BRANCH);
assert(page_numkeys(mp) > i);
assert(mp->entries[i] % 2 == 0);
return ptr_disp(mp, mp->entries[i] + PAGEHDRSZ);
}
MDBX_NOTHROW_PURE_FUNCTION static inline void *page_dupfix_ptr(const page_t *mp, size_t i, size_t keysize) {
assert(page_type_compat(mp) == (P_LEAF | P_DUPFIX) && i == (indx_t)i && mp->dupfix_ksize == keysize);
(void)keysize;
return ptr_disp(mp, PAGEHDRSZ + mp->dupfix_ksize * (indx_t)i);
}
MDBX_NOTHROW_PURE_FUNCTION static inline MDBX_val page_dupfix_key(const page_t *mp, size_t i, size_t keysize) {
MDBX_val r;
r.iov_base = page_dupfix_ptr(mp, i, keysize);
r.iov_len = mp->dupfix_ksize;
return r;
}
/*----------------------------------------------------------------------------*/
MDBX_NOTHROW_PURE_FUNCTION MDBX_INTERNAL int cmp_int_unaligned(const MDBX_val *a, const MDBX_val *b);
#if MDBX_UNALIGNED_OK < 2 || (MDBX_DEBUG || MDBX_FORCE_ASSERTIONS || !defined(NDEBUG))
MDBX_NOTHROW_PURE_FUNCTION MDBX_INTERNAL int
/* Compare two items pointing at 2-byte aligned unsigned int's. */
cmp_int_align2(const MDBX_val *a, const MDBX_val *b);
#else
#define cmp_int_align2 cmp_int_unaligned
#endif /* !MDBX_UNALIGNED_OK || debug */
#if MDBX_UNALIGNED_OK < 4 || (MDBX_DEBUG || MDBX_FORCE_ASSERTIONS || !defined(NDEBUG))
MDBX_NOTHROW_PURE_FUNCTION MDBX_INTERNAL int
/* Compare two items pointing at 4-byte aligned unsigned int's. */
cmp_int_align4(const MDBX_val *a, const MDBX_val *b);
#else
#define cmp_int_align4 cmp_int_unaligned
#endif /* !MDBX_UNALIGNED_OK || debug */
/* Compare two items lexically */
MDBX_NOTHROW_PURE_FUNCTION MDBX_INTERNAL int cmp_lexical(const MDBX_val *a, const MDBX_val *b);
/* Compare two items in reverse byte order */
MDBX_NOTHROW_PURE_FUNCTION MDBX_INTERNAL int cmp_reverse(const MDBX_val *a, const MDBX_val *b);
/* Fast non-lexically comparator */
MDBX_NOTHROW_PURE_FUNCTION MDBX_INTERNAL int cmp_lenfast(const MDBX_val *a, const MDBX_val *b);
MDBX_NOTHROW_PURE_FUNCTION MDBX_INTERNAL bool eq_fast_slowpath(const uint8_t *a, const uint8_t *b, size_t l);
MDBX_NOTHROW_PURE_FUNCTION static inline bool eq_fast(const MDBX_val *a, const MDBX_val *b) {
return unlikely(a->iov_len == b->iov_len) && eq_fast_slowpath(a->iov_base, b->iov_base, a->iov_len);
}
MDBX_NOTHROW_PURE_FUNCTION MDBX_INTERNAL int cmp_equal_or_greater(const MDBX_val *a, const MDBX_val *b);
MDBX_NOTHROW_PURE_FUNCTION MDBX_INTERNAL int cmp_equal_or_wrong(const MDBX_val *a, const MDBX_val *b);
static inline MDBX_cmp_func *builtin_keycmp(MDBX_db_flags_t flags) {
return (flags & MDBX_REVERSEKEY) ? cmp_reverse : (flags & MDBX_INTEGERKEY) ? cmp_int_align2 : cmp_lexical;
}
static inline MDBX_cmp_func *builtin_datacmp(MDBX_db_flags_t flags) {
return !(flags & MDBX_DUPSORT)
? cmp_lenfast
: ((flags & MDBX_INTEGERDUP) ? cmp_int_unaligned
: ((flags & MDBX_REVERSEDUP) ? cmp_reverse : cmp_lexical));
}
/*----------------------------------------------------------------------------*/
MDBX_INTERNAL uint32_t combine_durability_flags(const uint32_t a, const uint32_t b);
MDBX_CONST_FUNCTION static inline lck_t *lckless_stub(const MDBX_env *env) {
uintptr_t stub = (uintptr_t)&env->lckless_placeholder;
/* align to avoid false-positive alarm from UndefinedBehaviorSanitizer */
stub = (stub + MDBX_CACHELINE_SIZE - 1) & ~(MDBX_CACHELINE_SIZE - 1);
return (lck_t *)stub;
}
#if !(defined(_WIN32) || defined(_WIN64))
MDBX_CONST_FUNCTION static inline int ignore_enosys(int err) {
#ifdef ENOSYS
if (err == ENOSYS)
return MDBX_RESULT_TRUE;
#endif /* ENOSYS */
#ifdef ENOIMPL
if (err == ENOIMPL)
return MDBX_RESULT_TRUE;
#endif /* ENOIMPL */
#ifdef ENOTSUP
if (err == ENOTSUP)
return MDBX_RESULT_TRUE;
#endif /* ENOTSUP */
#ifdef ENOSUPP
if (err == ENOSUPP)
return MDBX_RESULT_TRUE;
#endif /* ENOSUPP */
#ifdef EOPNOTSUPP
if (err == EOPNOTSUPP)
return MDBX_RESULT_TRUE;
#endif /* EOPNOTSUPP */
return err;
}
MDBX_MAYBE_UNUSED MDBX_CONST_FUNCTION static inline int ignore_enosys_and_eagain(int err) {
return (err == EAGAIN) ? MDBX_RESULT_TRUE : ignore_enosys(err);
}
MDBX_MAYBE_UNUSED MDBX_CONST_FUNCTION static inline int ignore_enosys_and_einval(int err) {
return (err == EINVAL) ? MDBX_RESULT_TRUE : ignore_enosys(err);
}
#endif /* defined(_WIN32) || defined(_WIN64) */
static inline int check_env(const MDBX_env *env, const bool wanna_active) {
if (unlikely(!env))
return MDBX_EINVAL;
if (unlikely(env->signature.weak != env_signature))
return MDBX_EBADSIGN;
if (unlikely(env->flags & ENV_FATAL_ERROR))
return MDBX_PANIC;
if (wanna_active) {
#if MDBX_ENV_CHECKPID
if (unlikely(env->pid != osal_getpid()) && env->pid) {
((MDBX_env *)env)->flags |= ENV_FATAL_ERROR;
return MDBX_PANIC;
}
#endif /* MDBX_ENV_CHECKPID */
if (unlikely((env->flags & ENV_ACTIVE) == 0))
return MDBX_EPERM;
eASSERT(env, env->dxb_mmap.base != nullptr);
}
return MDBX_SUCCESS;
}
static __always_inline int check_txn(const MDBX_txn *txn, int bad_bits) {
if (unlikely(!txn))
return MDBX_EINVAL;
if (unlikely(txn->signature != txn_signature))
return MDBX_EBADSIGN;
if (bad_bits) {
if (unlikely(!txn->env->dxb_mmap.base))
return MDBX_EPERM;
if (unlikely(txn->flags & bad_bits)) {
if ((bad_bits & MDBX_TXN_RDONLY) && unlikely(txn->flags & MDBX_TXN_RDONLY))
return MDBX_EACCESS;
if ((bad_bits & MDBX_TXN_PARKED) == 0)
return MDBX_BAD_TXN;
return txn_check_badbits_parked(txn, bad_bits);
}
}
tASSERT(txn, (txn->flags & MDBX_TXN_FINISHED) ||
(txn->flags & MDBX_NOSTICKYTHREADS) == (txn->env->flags & MDBX_NOSTICKYTHREADS));
#if MDBX_TXN_CHECKOWNER
if ((txn->flags & (MDBX_NOSTICKYTHREADS | MDBX_TXN_FINISHED)) != MDBX_NOSTICKYTHREADS &&
!(bad_bits /* abort/reset/txn-break */ == 0 &&
((txn->flags & (MDBX_TXN_RDONLY | MDBX_TXN_FINISHED)) == (MDBX_TXN_RDONLY | MDBX_TXN_FINISHED))) &&
unlikely(txn->owner != osal_thread_self()))
return txn->owner ? MDBX_THREAD_MISMATCH : MDBX_BAD_TXN;
#endif /* MDBX_TXN_CHECKOWNER */
return MDBX_SUCCESS;
}
static inline int check_txn_rw(const MDBX_txn *txn, int bad_bits) {
return check_txn(txn, (bad_bits | MDBX_TXN_RDONLY) & ~MDBX_TXN_PARKED);
}
/*----------------------------------------------------------------------------*/
MDBX_INTERNAL void mincore_clean_cache(const MDBX_env *const env);
MDBX_INTERNAL void update_mlcnt(const MDBX_env *env, const pgno_t new_aligned_mlocked_pgno,
const bool lock_not_release);
MDBX_INTERNAL void munlock_after(const MDBX_env *env, const pgno_t aligned_pgno, const size_t end_bytes);
MDBX_INTERNAL void munlock_all(const MDBX_env *env);
/*----------------------------------------------------------------------------*/
/* Cache coherence and mmap invalidation */
#ifndef MDBX_CPU_WRITEBACK_INCOHERENT
#error "The MDBX_CPU_WRITEBACK_INCOHERENT must be defined before"
#elif MDBX_CPU_WRITEBACK_INCOHERENT
#define osal_flush_incoherent_cpu_writeback() osal_memory_barrier()
#else
#define osal_flush_incoherent_cpu_writeback() osal_compiler_barrier()
#endif /* MDBX_CPU_WRITEBACK_INCOHERENT */
MDBX_MAYBE_UNUSED static inline void osal_flush_incoherent_mmap(const void *addr, size_t nbytes,
const intptr_t pagesize) {
#ifndef MDBX_MMAP_INCOHERENT_FILE_WRITE
#error "The MDBX_MMAP_INCOHERENT_FILE_WRITE must be defined before"
#elif MDBX_MMAP_INCOHERENT_FILE_WRITE
char *const begin = (char *)(-pagesize & (intptr_t)addr);
char *const end = (char *)(-pagesize & (intptr_t)((char *)addr + nbytes + pagesize - 1));
int err = msync(begin, end - begin, MS_SYNC | MS_INVALIDATE) ? errno : 0;
eASSERT(nullptr, err == 0);
(void)err;
#else
(void)pagesize;
#endif /* MDBX_MMAP_INCOHERENT_FILE_WRITE */
#ifndef MDBX_MMAP_INCOHERENT_CPU_CACHE
#error "The MDBX_MMAP_INCOHERENT_CPU_CACHE must be defined before"
#elif MDBX_MMAP_INCOHERENT_CPU_CACHE
#ifdef DCACHE
/* MIPS has cache coherency issues.
* Note: for any nbytes >= on-chip cache size, entire is flushed. */
cacheflush((void *)addr, nbytes, DCACHE);
#else
#error "Oops, cacheflush() not available"
#endif /* DCACHE */
#endif /* MDBX_MMAP_INCOHERENT_CPU_CACHE */
#if !MDBX_MMAP_INCOHERENT_FILE_WRITE && !MDBX_MMAP_INCOHERENT_CPU_CACHE
(void)addr;
(void)nbytes;
#endif
}

170
src/coherency.c Normal file
View File

@ -0,0 +1,170 @@
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
#include "internals.h"
/* check against https://libmdbx.dqdkfa.ru/dead-github/issues/269 */
static bool coherency_check(const MDBX_env *env, const txnid_t txnid, const volatile tree_t *trees,
const volatile meta_t *meta, bool report) {
const txnid_t freedb_mod_txnid = trees[FREE_DBI].mod_txnid;
const txnid_t maindb_mod_txnid = trees[MAIN_DBI].mod_txnid;
const pgno_t last_pgno = meta->geometry.now;
const pgno_t freedb_root_pgno = trees[FREE_DBI].root;
const page_t *freedb_root =
(env->dxb_mmap.base && freedb_root_pgno < last_pgno) ? pgno2page(env, freedb_root_pgno) : nullptr;
const pgno_t maindb_root_pgno = trees[MAIN_DBI].root;
const page_t *maindb_root =
(env->dxb_mmap.base && maindb_root_pgno < last_pgno) ? pgno2page(env, maindb_root_pgno) : nullptr;
const uint64_t magic_and_version = unaligned_peek_u64_volatile(4, &meta->magic_and_version);
bool ok = true;
if (freedb_root_pgno != P_INVALID && unlikely(freedb_root_pgno >= last_pgno)) {
if (report)
WARNING("catch invalid %s-db root %" PRIaPGNO " for meta_txnid %" PRIaTXN " %s", "free", freedb_root_pgno, txnid,
(env->stuck_meta < 0) ? "(workaround for incoherent flaw of unified page/buffer cache)"
: "(wagering meta)");
ok = false;
}
if (maindb_root_pgno != P_INVALID && unlikely(maindb_root_pgno >= last_pgno)) {
if (report)
WARNING("catch invalid %s-db root %" PRIaPGNO " for meta_txnid %" PRIaTXN " %s", "main", maindb_root_pgno, txnid,
(env->stuck_meta < 0) ? "(workaround for incoherent flaw of unified page/buffer cache)"
: "(wagering meta)");
ok = false;
}
if (unlikely(txnid < freedb_mod_txnid ||
(!freedb_mod_txnid && freedb_root && likely(magic_and_version == MDBX_DATA_MAGIC)))) {
if (report)
WARNING(
"catch invalid %s-db.mod_txnid %" PRIaTXN " for meta_txnid %" PRIaTXN " %s", "free", freedb_mod_txnid, txnid,
(env->stuck_meta < 0) ? "(workaround for incoherent flaw of unified page/buffer cache)" : "(wagering meta)");
ok = false;
}
if (unlikely(txnid < maindb_mod_txnid ||
(!maindb_mod_txnid && maindb_root && likely(magic_and_version == MDBX_DATA_MAGIC)))) {
if (report)
WARNING(
"catch invalid %s-db.mod_txnid %" PRIaTXN " for meta_txnid %" PRIaTXN " %s", "main", maindb_mod_txnid, txnid,
(env->stuck_meta < 0) ? "(workaround for incoherent flaw of unified page/buffer cache)" : "(wagering meta)");
ok = false;
}
/* Проверяем отметки внутри корневых страниц только если сами страницы
* в пределах текущего отображения. Иначе возможны SIGSEGV до переноса
* вызова coherency_check_head() после dxb_resize() внутри txn_renew(). */
if (likely(freedb_root && freedb_mod_txnid &&
(size_t)ptr_dist(env->dxb_mmap.base, freedb_root) < env->dxb_mmap.limit)) {
VALGRIND_MAKE_MEM_DEFINED(freedb_root, sizeof(freedb_root->txnid));
MDBX_ASAN_UNPOISON_MEMORY_REGION(freedb_root, sizeof(freedb_root->txnid));
const txnid_t root_txnid = freedb_root->txnid;
if (unlikely(root_txnid != freedb_mod_txnid)) {
if (report)
WARNING("catch invalid root_page %" PRIaPGNO " mod_txnid %" PRIaTXN " for %s-db.mod_txnid %" PRIaTXN " %s",
freedb_root_pgno, root_txnid, "free", freedb_mod_txnid,
(env->stuck_meta < 0) ? "(workaround for incoherent flaw of "
"unified page/buffer cache)"
: "(wagering meta)");
ok = false;
}
}
if (likely(maindb_root && maindb_mod_txnid &&
(size_t)ptr_dist(env->dxb_mmap.base, maindb_root) < env->dxb_mmap.limit)) {
VALGRIND_MAKE_MEM_DEFINED(maindb_root, sizeof(maindb_root->txnid));
MDBX_ASAN_UNPOISON_MEMORY_REGION(maindb_root, sizeof(maindb_root->txnid));
const txnid_t root_txnid = maindb_root->txnid;
if (unlikely(root_txnid != maindb_mod_txnid)) {
if (report)
WARNING("catch invalid root_page %" PRIaPGNO " mod_txnid %" PRIaTXN " for %s-db.mod_txnid %" PRIaTXN " %s",
maindb_root_pgno, root_txnid, "main", maindb_mod_txnid,
(env->stuck_meta < 0) ? "(workaround for incoherent flaw of "
"unified page/buffer cache)"
: "(wagering meta)");
ok = false;
}
}
if (unlikely(!ok) && report)
env->lck->pgops.incoherence.weak =
(env->lck->pgops.incoherence.weak >= INT32_MAX) ? INT32_MAX : env->lck->pgops.incoherence.weak + 1;
return ok;
}
__cold int coherency_timeout(uint64_t *timestamp, intptr_t pgno, const MDBX_env *env) {
if (likely(timestamp && *timestamp == 0))
*timestamp = osal_monotime();
else if (unlikely(!timestamp || osal_monotime() - *timestamp > osal_16dot16_to_monotime(65536 / 10))) {
if (pgno >= 0 && pgno != env->stuck_meta)
ERROR("bailout waiting for %" PRIuSIZE " page arrival %s", pgno,
"(workaround for incoherent flaw of unified page/buffer cache)");
else if (env->stuck_meta < 0)
ERROR("bailout waiting for valid snapshot (%s)", "workaround for incoherent flaw of unified page/buffer cache");
return MDBX_PROBLEM;
}
osal_memory_fence(mo_AcquireRelease, true);
#if defined(_WIN32) || defined(_WIN64)
SwitchToThread();
#elif defined(__linux__) || defined(__gnu_linux__) || defined(_UNIX03_SOURCE)
sched_yield();
#elif (defined(_GNU_SOURCE) && __GLIBC_PREREQ(2, 1)) || defined(_OPEN_THREADS)
pthread_yield();
#else
usleep(42);
#endif
return MDBX_RESULT_TRUE;
}
/* check with timeout as the workaround
* for https://libmdbx.dqdkfa.ru/dead-github/issues/269 */
__hot int coherency_fetch_head(MDBX_txn *txn, const meta_ptr_t head, uint64_t *timestamp) {
/* Copy the DB info and flags */
txn->txnid = head.txnid;
txn->geo = head.ptr_c->geometry;
memcpy(txn->dbs, &head.ptr_c->trees, sizeof(head.ptr_c->trees));
STATIC_ASSERT(sizeof(head.ptr_c->trees) == CORE_DBS * sizeof(tree_t));
VALGRIND_MAKE_MEM_UNDEFINED(txn->dbs + CORE_DBS, txn->env->max_dbi - CORE_DBS);
txn->canary = head.ptr_c->canary;
if (unlikely(!coherency_check(txn->env, head.txnid, txn->dbs, head.ptr_v, *timestamp == 0) ||
txn->txnid != meta_txnid(head.ptr_v)))
return coherency_timeout(timestamp, -1, txn->env);
if (unlikely(txn->dbs[FREE_DBI].flags != MDBX_INTEGERKEY)) {
if ((txn->dbs[FREE_DBI].flags & DB_PERSISTENT_FLAGS) != MDBX_INTEGERKEY ||
unaligned_peek_u64(4, &head.ptr_c->magic_and_version) == MDBX_DATA_MAGIC) {
ERROR("unexpected/invalid db-flags 0x%x for %s", txn->dbs[FREE_DBI].flags, "GC/FreeDB");
return MDBX_INCOMPATIBLE;
}
txn->dbs[FREE_DBI].flags &= DB_PERSISTENT_FLAGS;
}
tASSERT(txn, txn->dbs[FREE_DBI].flags == MDBX_INTEGERKEY);
tASSERT(txn, check_table_flags(txn->dbs[MAIN_DBI].flags));
return MDBX_SUCCESS;
}
int coherency_check_written(const MDBX_env *env, const txnid_t txnid, const volatile meta_t *meta, const intptr_t pgno,
uint64_t *timestamp) {
const bool report = !(timestamp && *timestamp);
const txnid_t head_txnid = meta_txnid(meta);
if (likely(head_txnid >= MIN_TXNID && head_txnid >= txnid)) {
if (likely(coherency_check(env, head_txnid, &meta->trees.gc, meta, report))) {
eASSERT(env, meta->trees.gc.flags == MDBX_INTEGERKEY);
eASSERT(env, check_table_flags(meta->trees.main.flags));
return MDBX_SUCCESS;
}
} else if (report) {
env->lck->pgops.incoherence.weak =
(env->lck->pgops.incoherence.weak >= INT32_MAX) ? INT32_MAX : env->lck->pgops.incoherence.weak + 1;
WARNING("catch %s txnid %" PRIaTXN " for meta_%" PRIaPGNO " %s",
(head_txnid < MIN_TXNID) ? "invalid" : "unexpected", head_txnid,
bytes2pgno(env, ptr_dist(meta, env->dxb_mmap.base)),
"(workaround for incoherent flaw of unified page/buffer cache)");
}
return coherency_timeout(timestamp, pgno, env);
}
bool coherency_check_meta(const MDBX_env *env, const volatile meta_t *meta, bool report) {
uint64_t timestamp = 0;
return coherency_check_written(env, 0, meta, -1, report ? &timestamp : nullptr) == MDBX_SUCCESS;
}

View File

@ -5,12 +5,15 @@
/* clang-format off */
#cmakedefine LTO_ENABLED
#cmakedefine MDBX_USE_VALGRIND
#cmakedefine ENABLE_MEMCHECK
#cmakedefine ENABLE_GPROF
#cmakedefine ENABLE_GCOV
#cmakedefine ENABLE_ASAN
#cmakedefine ENABLE_UBSAN
#cmakedefine01 MDBX_FORCE_ASSERTIONS
#if !defined(MDBX_BUILD_TEST) && !defined(MDBX_BUILD_CXX)
#cmakedefine01 MDBX_BUILD_CXX
#endif
/* Common */
#cmakedefine01 MDBX_TXN_CHECKOWNER
@ -26,20 +29,39 @@
#ifndef MDBX_TRUST_RTC_AUTO
#cmakedefine01 MDBX_TRUST_RTC
#endif
#cmakedefine01 MDBX_DISABLE_PAGECHECKS
#cmakedefine01 MDBX_DISABLE_VALIDATION
#cmakedefine01 MDBX_AVOID_MSYNC
#cmakedefine01 MDBX_ENABLE_REFUND
#cmakedefine01 MDBX_ENABLE_BIGFOOT
#cmakedefine01 MDBX_ENABLE_PGOP_STAT
#cmakedefine01 MDBX_ENABLE_PROFGC
#cmakedefine01 MDBX_ENABLE_DBI_SPARSE
#cmakedefine01 MDBX_ENABLE_DBI_LOCKFREE
/* Windows */
#if defined(MDBX_BUILD_TEST) || !defined(MDBX_BUILD_CXX) || MDBX_BUILD_CXX
#define MDBX_WITHOUT_MSVC_CRT 0
#else
#cmakedefine01 MDBX_WITHOUT_MSVC_CRT
#endif /* MDBX_WITHOUT_MSVC_CRT */
/* MacOS & iOS */
#cmakedefine01 MDBX_OSX_SPEED_INSTEADOF_DURABILITY
#cmakedefine01 MDBX_APPLE_SPEED_INSTEADOF_DURABILITY
/* POSIX */
#cmakedefine01 MDBX_DISABLE_GNU_SOURCE
#cmakedefine MDBX_USE_OFDLOCKS_AUTO
#ifndef MDBX_USE_OFDLOCKS_AUTO
#cmakedefine01 MDBX_USE_OFDLOCKS
#endif
#endif /* MDBX_USE_OFDLOCKS */
#cmakedefine MDBX_MMAP_NEEDS_JOLT_AUTO
#ifndef MDBX_MMAP_NEEDS_JOLT_AUTO
#cmakedefine01 MDBX_MMAP_NEEDS_JOLT
#endif /* MDBX_MMAP_NEEDS_JOLT */
#cmakedefine01 MDBX_USE_MINCORE
/* Build Info */
#ifndef MDBX_BUILD_TIMESTAMP
@ -57,6 +79,9 @@
#ifndef MDBX_BUILD_FLAGS
#cmakedefine MDBX_BUILD_FLAGS "@MDBX_BUILD_FLAGS@"
#endif
#ifndef MDBX_BUILD_METADATA
#cmakedefine MDBX_BUILD_METADATA "@MDBX_BUILD_METADATA@"
#endif
#cmakedefine MDBX_BUILD_SOURCERY @MDBX_BUILD_SOURCERY@
/* *INDENT-ON* */

22693
src/core.c

File diff suppressed because it is too large Load Diff

2396
src/cursor.c Normal file

File diff suppressed because it is too large Load Diff

391
src/cursor.h Normal file
View File

@ -0,0 +1,391 @@
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
#pragma once
#include "essentials.h"
/* Состояние курсора.
*
* плохой/poor:
* - неустановленный курсор с незаполненым стеком;
* - следует пропускать во всех циклах отслеживания/корректировки
* позиций курсоров;
* - допускаются только операции предполагающие установку абсолютной позиции;
* - в остальных случаях возвращается ENODATA.
*
* У таких курсоров top = -1 и flags < 0, что позволяет дешево проверять и
* пропускать такие курсоры в циклах отслеживания/корректировки по условию
* probe_cursor->top < this_cursor->top.
*
* пустой/hollow:
* - частично инициализированный курсор, но без доступной пользователю позиции,
* поэтому нельзя выполнить какую-либо операцию без абсолютного (не
* относительного) позиционирования;
* - ki[top] может быть некорректным, в том числе >= page_numkeys(pg[top]).
*
* У таких курсоров top >= 0, но flags < 0 (есть флажок z_hollow).
*
* установленный/pointed:
* - полностью инициализированный курсор с конкретной позицией с данными;
* - можно прочитать текущую строку, удалить её, либо выполнить
* относительное перемещение;
* - может иметь флажки z_after_delete, z_eof_hard и z_eof_soft;
* - наличие z_eof_soft означает что курсор перемещен за пределы данных,
* поэтому нелья прочитать текущие данные, либо удалить их.
*
* У таких курсоров top >= 0 и flags >= 0 (нет флажка z_hollow).
*
* наполненный данными/filled:
* - это установленный/pointed курсор без флагов z_eof_soft;
* - за курсором есть даные, возможны CRUD операции в текущей позиции.
*
* У таких курсоров top >= 0 и (unsigned)flags < z_eof_soft.
*
* Изменения состояния.
*
* - Сбрасывается состояние курсора посредством top_and_flags |= z_poor_mark,
* что равносильно top = -1 вместе с flags |= z_poor_mark;
* - При позиционировании курсора сначала устанавливается top, а flags
* только в самом конце при отсутстви ошибок.
* - Повторное позиционирование first/last может начинаться
* с установки/обнуления только top без сброса flags, что позволяет работать
* быстрому пути внутри tree_search_finalize().
*
* - Заморочки с концом данных:
* - mdbx_cursor_get(NEXT) выполняет две операции (перемещение и чтение),
* поэтому перемещение на последнюю строку строку всегда успешно,
* а ошибка возвращается только при последующем next().
* Однако, из-за этой двойственности семантика ситуации возврата ошибки
* из mdbx_cursor_get(NEXT) допускает разночтение/неопределенность, ибо
* не понятно к чему относится ошибка:
* - Если к чтению данных, то курсор перемещен и стоит после последней
* строки. Соответственно, чтение в текущей позиции запрещено,
* а при выполнении prev() курсор вернется на последнюю строку;
* - Если же ошибка относится к перемещению, то курсор не перемещен и
* остается на последней строке. Соответственно, чтение в текущей
* позиции допустимо, а при выполнении prev() курсор встанет
* на пред-последнюю строку.
* - Пикантность в том, что пользователи (так или иначе) полагаются
* на оба варианта поведения, при этом конечно ожидают что после
* ошибки MDBX_NEXT функция mdbx_cursor_eof() будет возвращать true.
* - далее добавляется схожая ситуация с MDBX_GET_RANGE, MDBX_LOWERBOUND,
* MDBX_GET_BOTH_RANGE и MDBX_UPPERBOUND. Тут при неуспехе поиска курсор
* может/должен стоять после последней строки.
* - далее добавляется MDBX_LAST. Тут курсор должен стоять на последней
* строке и допускать чтение в текузщей позиции,
* но mdbx_cursor_eof() должен возвращать true.
*
* Решение = делаем два флажка z_eof_soft и z_eof_hard:
* - Когда установлен только z_eof_soft,
* функция mdbx_cursor_eof() возвращает true, но допускается
* чтение данных в текущей позиции, а prev() передвигает курсор
* на пред-последнюю строку.
* - Когда установлен z_eof_hard, чтение данных в текущей позиции
* не допускается, и mdbx_cursor_eof() также возвращает true,
* а prev() устанавливает курсора на последюю строку. */
enum cursor_state {
/* Это вложенный курсор для вложенного дерева/страницы и является
inner-элементом struct cursor_couple. */
z_inner = 0x01,
/* Происходит подготовка к обновлению GC,
поэтому можно брать страницы из GC даже для FREE_DBI. */
z_gcu_preparation = 0x02,
/* Курсор только-что создан, поэтому допускается авто-установка
в начало/конец, вместо возврата ошибки. */
z_fresh = 0x04,
/* Предыдущей операцией было удаление, поэтому курсор уже физически указывает
на следующий элемент и соответствующая операция перемещения должна
игнорироваться. */
z_after_delete = 0x08,
/* */
z_disable_tree_search_fastpath = 0x10,
/* Курсор логически в конце данных, но физически на последней строке,
* ki[top] == page_numkeys(pg[top]) - 1 и читать данные в текущей позиции. */
z_eof_soft = 0x20,
/* Курсор логически за концом данных, поэтому следующий переход "назад"
должен игнорироваться и/или приводить к установке на последнюю строку.
В текущем же состоянии нельзя делать CRUD операции. */
z_eof_hard = 0x40,
/* За курсором нет данных, логически его позиция не определена,
нельзя делать CRUD операции в текущей позиции.
Относительное перемещение запрещено. */
z_hollow = -128 /* 0x80 */,
/* Маски для сброса/установки состояния. */
z_clear_mask = z_inner | z_gcu_preparation,
z_poor_mark = z_eof_hard | z_hollow | z_disable_tree_search_fastpath,
z_fresh_mark = z_poor_mark | z_fresh
};
MDBX_MAYBE_UNUSED MDBX_NOTHROW_PURE_FUNCTION static inline bool is_inner(const MDBX_cursor *mc) {
return (mc->flags & z_inner) != 0;
}
MDBX_MAYBE_UNUSED MDBX_NOTHROW_PURE_FUNCTION static inline bool is_poor(const MDBX_cursor *mc) {
const bool r = mc->top < 0;
cASSERT(mc, r == (mc->top_and_flags < 0));
if (r && mc->subcur)
cASSERT(mc, mc->subcur->cursor.flags < 0 && mc->subcur->cursor.top < 0);
return r;
}
MDBX_MAYBE_UNUSED MDBX_NOTHROW_PURE_FUNCTION static inline bool is_pointed(const MDBX_cursor *mc) {
const bool r = mc->top >= 0;
cASSERT(mc, r == (mc->top_and_flags >= 0));
if (!r && mc->subcur)
cASSERT(mc, is_poor(&mc->subcur->cursor));
return r;
}
MDBX_MAYBE_UNUSED MDBX_NOTHROW_PURE_FUNCTION static inline bool is_hollow(const MDBX_cursor *mc) {
const bool r = mc->flags < 0;
if (!r) {
cASSERT(mc, mc->top >= 0);
cASSERT(mc, (mc->flags & z_eof_hard) || mc->ki[mc->top] < page_numkeys(mc->pg[mc->top]));
} else if (mc->subcur)
cASSERT(mc, is_poor(&mc->subcur->cursor) || (is_pointed(mc) && mc->subcur->cursor.flags < 0));
return r;
}
MDBX_MAYBE_UNUSED MDBX_NOTHROW_PURE_FUNCTION static inline bool is_eof(const MDBX_cursor *mc) {
const bool r = z_eof_soft <= (uint8_t)mc->flags;
return r;
}
MDBX_MAYBE_UNUSED MDBX_NOTHROW_PURE_FUNCTION static inline bool is_filled(const MDBX_cursor *mc) {
const bool r = z_eof_hard > (uint8_t)mc->flags;
return r;
}
MDBX_MAYBE_UNUSED MDBX_NOTHROW_PURE_FUNCTION static inline bool inner_filled(const MDBX_cursor *mc) {
return mc->subcur && is_filled(&mc->subcur->cursor);
}
MDBX_MAYBE_UNUSED MDBX_NOTHROW_PURE_FUNCTION static inline bool inner_pointed(const MDBX_cursor *mc) {
return mc->subcur && is_pointed(&mc->subcur->cursor);
}
MDBX_MAYBE_UNUSED MDBX_NOTHROW_PURE_FUNCTION static inline bool inner_hollow(const MDBX_cursor *mc) {
const bool r = !mc->subcur || is_hollow(&mc->subcur->cursor);
#if MDBX_DEBUG || MDBX_FORCE_ASSERTIONS
if (!r) {
cASSERT(mc, is_filled(mc));
const page_t *mp = mc->pg[mc->top];
const node_t *node = page_node(mp, mc->ki[mc->top]);
cASSERT(mc, node_flags(node) & N_DUP);
}
#endif /* MDBX_DEBUG || MDBX_FORCE_ASSERTIONS */
return r;
}
MDBX_MAYBE_UNUSED static inline void inner_gone(MDBX_cursor *mc) {
if (mc->subcur) {
TRACE("reset inner cursor %p", __Wpedantic_format_voidptr(&mc->subcur->cursor));
mc->subcur->nested_tree.root = 0;
mc->subcur->cursor.top_and_flags = z_inner | z_poor_mark;
}
}
MDBX_MAYBE_UNUSED static inline void be_poor(MDBX_cursor *mc) {
const bool inner = is_inner(mc);
if (inner) {
mc->tree->root = 0;
mc->top_and_flags = z_inner | z_poor_mark;
} else {
mc->top_and_flags |= z_poor_mark;
inner_gone(mc);
}
cASSERT(mc, is_poor(mc) && !is_pointed(mc) && !is_filled(mc));
cASSERT(mc, inner == is_inner(mc));
}
MDBX_MAYBE_UNUSED static inline void be_filled(MDBX_cursor *mc) {
cASSERT(mc, mc->top >= 0);
cASSERT(mc, mc->ki[mc->top] < page_numkeys(mc->pg[mc->top]));
const bool inner = is_inner(mc);
mc->flags &= z_clear_mask;
cASSERT(mc, is_filled(mc));
cASSERT(mc, inner == is_inner(mc));
}
MDBX_MAYBE_UNUSED static inline bool is_related(const MDBX_cursor *base, const MDBX_cursor *scan) {
cASSERT(base, base->top >= 0);
return base->top <= scan->top && base != scan;
}
/* Флаги контроля/проверки курсора. */
enum cursor_checking {
z_branch = 0x01 /* same as P_BRANCH for check_leaf_type() */,
z_leaf = 0x02 /* same as P_LEAF for check_leaf_type() */,
z_largepage = 0x04 /* same as P_LARGE for check_leaf_type() */,
z_updating = 0x08 /* update/rebalance pending */,
z_ignord = 0x10 /* don't check keys ordering */,
z_dupfix = 0x20 /* same as P_DUPFIX for check_leaf_type() */,
z_retiring = 0x40 /* refs to child pages may be invalid */,
z_pagecheck = 0x80 /* perform page checking, see MDBX_VALIDATION */
};
MDBX_INTERNAL int __must_check_result cursor_validate(const MDBX_cursor *mc);
MDBX_MAYBE_UNUSED MDBX_NOTHROW_PURE_FUNCTION static inline size_t cursor_dbi(const MDBX_cursor *mc) {
cASSERT(mc, mc->txn && mc->txn->signature == txn_signature);
size_t dbi = mc->dbi_state - mc->txn->dbi_state;
cASSERT(mc, dbi < mc->txn->env->n_dbi);
return dbi;
}
MDBX_MAYBE_UNUSED MDBX_NOTHROW_PURE_FUNCTION static inline bool cursor_dbi_changed(const MDBX_cursor *mc) {
return dbi_changed(mc->txn, cursor_dbi(mc));
}
MDBX_MAYBE_UNUSED MDBX_NOTHROW_PURE_FUNCTION static inline uint8_t *cursor_dbi_state(const MDBX_cursor *mc) {
return mc->dbi_state;
}
MDBX_MAYBE_UNUSED MDBX_NOTHROW_PURE_FUNCTION static inline bool cursor_is_gc(const MDBX_cursor *mc) {
return mc->dbi_state == mc->txn->dbi_state + FREE_DBI;
}
MDBX_MAYBE_UNUSED MDBX_NOTHROW_PURE_FUNCTION static inline bool cursor_is_main(const MDBX_cursor *mc) {
return mc->dbi_state == mc->txn->dbi_state + MAIN_DBI;
}
MDBX_MAYBE_UNUSED MDBX_NOTHROW_PURE_FUNCTION static inline bool cursor_is_core(const MDBX_cursor *mc) {
return mc->dbi_state < mc->txn->dbi_state + CORE_DBS;
}
MDBX_MAYBE_UNUSED static inline int cursor_dbi_dbg(const MDBX_cursor *mc) {
/* Debugging output value of a cursor's DBI: Negative for a sub-cursor. */
const int dbi = cursor_dbi(mc);
return (mc->flags & z_inner) ? -dbi : dbi;
}
MDBX_MAYBE_UNUSED static inline int __must_check_result cursor_push(MDBX_cursor *mc, page_t *mp, indx_t ki) {
TRACE("pushing page %" PRIaPGNO " on db %d cursor %p", mp->pgno, cursor_dbi_dbg(mc), __Wpedantic_format_voidptr(mc));
if (unlikely(mc->top >= CURSOR_STACK_SIZE - 1)) {
be_poor(mc);
mc->txn->flags |= MDBX_TXN_ERROR;
return MDBX_CURSOR_FULL;
}
mc->top += 1;
mc->pg[mc->top] = mp;
mc->ki[mc->top] = ki;
return MDBX_SUCCESS;
}
MDBX_MAYBE_UNUSED static inline void cursor_pop(MDBX_cursor *mc) {
TRACE("popped page %" PRIaPGNO " off db %d cursor %p", mc->pg[mc->top]->pgno, cursor_dbi_dbg(mc),
__Wpedantic_format_voidptr(mc));
cASSERT(mc, mc->top >= 0);
mc->top -= 1;
}
MDBX_NOTHROW_PURE_FUNCTION static inline bool check_leaf_type(const MDBX_cursor *mc, const page_t *mp) {
return (((page_type(mp) ^ mc->checking) & (z_branch | z_leaf | z_largepage | z_dupfix)) == 0);
}
MDBX_INTERNAL int cursor_check(const MDBX_cursor *mc, int txn_bad_bits);
/* без необходимости доступа к данным, без активации припаркованных транзакций. */
static inline int cursor_check_pure(const MDBX_cursor *mc) {
return cursor_check(mc, MDBX_TXN_BLOCKED - MDBX_TXN_PARKED);
}
/* для чтения данных, с активацией припаркованных транзакций. */
static inline int cursor_check_ro(const MDBX_cursor *mc) { return cursor_check(mc, MDBX_TXN_BLOCKED); }
/* для записи данных. */
static inline int cursor_check_rw(const MDBX_cursor *mc) {
return cursor_check(mc, (MDBX_TXN_BLOCKED - MDBX_TXN_PARKED) | MDBX_TXN_RDONLY);
}
MDBX_INTERNAL MDBX_cursor *cursor_eot(MDBX_cursor *cursor, MDBX_txn *txn);
MDBX_INTERNAL int cursor_shadow(MDBX_cursor *cursor, MDBX_txn *nested, const size_t dbi);
MDBX_INTERNAL MDBX_cursor *cursor_cpstk(const MDBX_cursor *csrc, MDBX_cursor *cdst);
MDBX_INTERNAL int __must_check_result cursor_ops(MDBX_cursor *mc, MDBX_val *key, MDBX_val *data,
const MDBX_cursor_op op);
MDBX_INTERNAL int __must_check_result cursor_check_multiple(MDBX_cursor *mc, const MDBX_val *key, MDBX_val *data,
unsigned flags);
MDBX_INTERNAL int __must_check_result cursor_put_checklen(MDBX_cursor *mc, const MDBX_val *key, MDBX_val *data,
unsigned flags);
MDBX_INTERNAL int __must_check_result cursor_put(MDBX_cursor *mc, const MDBX_val *key, MDBX_val *data, unsigned flags);
MDBX_INTERNAL int __must_check_result cursor_validate_updating(MDBX_cursor *mc);
MDBX_INTERNAL int __must_check_result cursor_del(MDBX_cursor *mc, unsigned flags);
MDBX_INTERNAL int __must_check_result cursor_sibling_left(MDBX_cursor *mc);
MDBX_INTERNAL int __must_check_result cursor_sibling_right(MDBX_cursor *mc);
typedef struct cursor_set_result {
int err;
bool exact;
} csr_t;
MDBX_INTERNAL csr_t cursor_seek(MDBX_cursor *mc, MDBX_val *key, MDBX_val *data, MDBX_cursor_op op);
MDBX_INTERNAL int __must_check_result inner_first(MDBX_cursor *__restrict mc, MDBX_val *__restrict data);
MDBX_INTERNAL int __must_check_result inner_last(MDBX_cursor *__restrict mc, MDBX_val *__restrict data);
MDBX_INTERNAL int __must_check_result outer_first(MDBX_cursor *__restrict mc, MDBX_val *__restrict key,
MDBX_val *__restrict data);
MDBX_INTERNAL int __must_check_result outer_last(MDBX_cursor *__restrict mc, MDBX_val *__restrict key,
MDBX_val *__restrict data);
MDBX_INTERNAL int __must_check_result inner_next(MDBX_cursor *__restrict mc, MDBX_val *__restrict data);
MDBX_INTERNAL int __must_check_result inner_prev(MDBX_cursor *__restrict mc, MDBX_val *__restrict data);
MDBX_INTERNAL int __must_check_result outer_next(MDBX_cursor *__restrict mc, MDBX_val *__restrict key,
MDBX_val *__restrict data, MDBX_cursor_op op);
MDBX_INTERNAL int __must_check_result outer_prev(MDBX_cursor *__restrict mc, MDBX_val *__restrict key,
MDBX_val *__restrict data, MDBX_cursor_op op);
MDBX_INTERNAL int cursor_init4walk(cursor_couple_t *couple, const MDBX_txn *const txn, tree_t *const tree,
kvx_t *const kvx);
MDBX_INTERNAL int __must_check_result cursor_init(MDBX_cursor *mc, const MDBX_txn *txn, size_t dbi);
MDBX_INTERNAL int __must_check_result cursor_dupsort_setup(MDBX_cursor *mc, const node_t *node, const page_t *mp);
MDBX_INTERNAL int __must_check_result cursor_touch(MDBX_cursor *const mc, const MDBX_val *key, const MDBX_val *data);
/*----------------------------------------------------------------------------*/
/* Update sub-page pointer, if any, in mc->subcur.
* Needed when the node which contains the sub-page may have moved.
* Called with mp = mc->pg[mc->top], ki = mc->ki[mc->top]. */
MDBX_MAYBE_UNUSED static inline void cursor_inner_refresh(const MDBX_cursor *mc, const page_t *mp, unsigned ki) {
cASSERT(mc, is_leaf(mp));
const node_t *node = page_node(mp, ki);
if ((node_flags(node) & (N_DUP | N_TREE)) == N_DUP)
mc->subcur->cursor.pg[0] = node_data(node);
}
MDBX_MAYBE_UNUSED MDBX_INTERNAL bool cursor_is_tracked(const MDBX_cursor *mc);
static inline void cursor_reset(cursor_couple_t *couple) {
couple->outer.top_and_flags = z_fresh_mark;
couple->inner.cursor.top_and_flags = z_fresh_mark | z_inner;
}
static inline void cursor_drown(cursor_couple_t *couple) {
couple->outer.top_and_flags = z_poor_mark;
couple->inner.cursor.top_and_flags = z_poor_mark | z_inner;
couple->outer.txn = nullptr;
couple->inner.cursor.txn = nullptr;
couple->outer.tree = nullptr;
/* сохраняем clc-указатель, так он используется для вычисления dbi в mdbx_cursor_renew(). */
couple->outer.dbi_state = nullptr;
couple->inner.cursor.dbi_state = nullptr;
}

692
src/dbi.c Normal file
View File

@ -0,0 +1,692 @@
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
#include "internals.h"
#if MDBX_ENABLE_DBI_SPARSE
size_t dbi_bitmap_ctz_fallback(const MDBX_txn *txn, intptr_t bmi) {
tASSERT(txn, bmi > 0);
bmi &= -bmi;
if (sizeof(txn->dbi_sparse[0]) > 4) {
static const uint8_t debruijn_ctz64[64] = {0, 1, 2, 53, 3, 7, 54, 27, 4, 38, 41, 8, 34, 55, 48, 28,
62, 5, 39, 46, 44, 42, 22, 9, 24, 35, 59, 56, 49, 18, 29, 11,
63, 52, 6, 26, 37, 40, 33, 47, 61, 45, 43, 21, 23, 58, 17, 10,
51, 25, 36, 32, 60, 20, 57, 16, 50, 31, 19, 15, 30, 14, 13, 12};
return debruijn_ctz64[(UINT64_C(0x022FDD63CC95386D) * (uint64_t)bmi) >> 58];
} else {
static const uint8_t debruijn_ctz32[32] = {0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8,
31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9};
return debruijn_ctz32[(UINT32_C(0x077CB531) * (uint32_t)bmi) >> 27];
}
}
#endif /* MDBX_ENABLE_DBI_SPARSE */
struct dbi_snap_result dbi_snap(const MDBX_env *env, const size_t dbi) {
eASSERT(env, dbi < env->n_dbi);
struct dbi_snap_result r;
uint32_t snap = atomic_load32(&env->dbi_seqs[dbi], mo_AcquireRelease);
do {
r.sequence = snap;
r.flags = env->dbs_flags[dbi];
snap = atomic_load32(&env->dbi_seqs[dbi], mo_AcquireRelease);
} while (unlikely(snap != r.sequence));
return r;
}
__noinline int dbi_import(MDBX_txn *txn, const size_t dbi) {
const MDBX_env *const env = txn->env;
if (dbi >= env->n_dbi || !env->dbs_flags[dbi])
return MDBX_BAD_DBI;
#if MDBX_ENABLE_DBI_SPARSE
const size_t bitmap_chunk = CHAR_BIT * sizeof(txn->dbi_sparse[0]);
const size_t bitmap_indx = dbi / bitmap_chunk;
const size_t bitmap_mask = (size_t)1 << dbi % bitmap_chunk;
if (dbi >= txn->n_dbi) {
for (size_t i = (txn->n_dbi + bitmap_chunk - 1) / bitmap_chunk; bitmap_indx >= i; ++i)
txn->dbi_sparse[i] = 0;
eASSERT(env, (txn->dbi_sparse[bitmap_indx] & bitmap_mask) == 0);
MDBX_txn *scan = txn;
do {
eASSERT(env, scan->dbi_sparse == txn->dbi_sparse);
eASSERT(env, scan->n_dbi < dbi + 1);
scan->n_dbi = (unsigned)dbi + 1;
scan->dbi_state[dbi] = 0;
scan = scan->parent;
} while (scan /* && scan->dbi_sparse == txn->dbi_sparse */);
txn->dbi_sparse[bitmap_indx] |= bitmap_mask;
goto lindo;
}
if ((txn->dbi_sparse[bitmap_indx] & bitmap_mask) == 0) {
MDBX_txn *scan = txn;
do {
eASSERT(env, scan->dbi_sparse == txn->dbi_sparse);
eASSERT(env, scan->n_dbi == txn->n_dbi);
scan->dbi_state[dbi] = 0;
scan = scan->parent;
} while (scan /* && scan->dbi_sparse == txn->dbi_sparse */);
txn->dbi_sparse[bitmap_indx] |= bitmap_mask;
goto lindo;
}
#else
if (dbi >= txn->n_dbi) {
size_t i = txn->n_dbi;
do
txn->dbi_state[i] = 0;
while (dbi >= ++i);
txn->n_dbi = i;
goto lindo;
}
#endif /* MDBX_ENABLE_DBI_SPARSE */
if (!txn->dbi_state[dbi]) {
lindo:
/* dbi-слот еще не инициализирован в транзакции, а хендл не использовался */
txn->cursors[dbi] = nullptr;
MDBX_txn *const parent = txn->parent;
if (parent) {
/* вложенная пишущая транзакция */
int rc = dbi_check(parent, dbi);
/* копируем состояние dbi-хендла очищая new-флаги. */
eASSERT(env, txn->dbi_seqs == parent->dbi_seqs);
txn->dbi_state[dbi] = parent->dbi_state[dbi] & ~(DBI_FRESH | DBI_CREAT | DBI_DIRTY);
if (likely(rc == MDBX_SUCCESS)) {
txn->dbs[dbi] = parent->dbs[dbi];
rc = txn_shadow_cursors(parent, dbi);
}
return rc;
}
txn->dbi_seqs[dbi] = 0;
txn->dbi_state[dbi] = DBI_LINDO;
} else {
eASSERT(env, txn->dbi_seqs[dbi] != env->dbi_seqs[dbi].weak);
if (unlikely((txn->dbi_state[dbi] & (DBI_VALID | DBI_OLDEN)) || txn->cursors[dbi])) {
/* хендл уже использовался в транзакции, но был закрыт или переоткрыт,
* либо при явном пере-открытии хендла есть висячие курсоры */
eASSERT(env, (txn->dbi_state[dbi] & DBI_STALE) == 0);
txn->dbi_seqs[dbi] = env->dbi_seqs[dbi].weak;
txn->dbi_state[dbi] = DBI_OLDEN | DBI_LINDO;
return txn->cursors[dbi] ? MDBX_DANGLING_DBI : MDBX_BAD_DBI;
}
}
/* хендл не использовался в транзакции, либо явно пере-отрывается при
* отсутствии висячих курсоров */
eASSERT(env, (txn->dbi_state[dbi] & DBI_LINDO) && !txn->cursors[dbi]);
/* читаем актуальные флаги и sequence */
struct dbi_snap_result snap = dbi_snap(env, dbi);
txn->dbi_seqs[dbi] = snap.sequence;
if (snap.flags & DB_VALID) {
txn->dbs[dbi].flags = snap.flags & DB_PERSISTENT_FLAGS;
txn->dbi_state[dbi] = DBI_LINDO | DBI_VALID | DBI_STALE;
return MDBX_SUCCESS;
}
return MDBX_BAD_DBI;
}
int dbi_defer_release(MDBX_env *const env, defer_free_item_t *const chain) {
size_t length = 0;
defer_free_item_t *obsolete_chain = nullptr;
#if MDBX_ENABLE_DBI_LOCKFREE
const uint64_t now = osal_monotime();
defer_free_item_t **scan = &env->defer_free;
if (env->defer_free) {
const uint64_t threshold_1second = osal_16dot16_to_monotime(1 * 65536);
do {
defer_free_item_t *item = *scan;
if (now - item->timestamp < threshold_1second) {
scan = &item->next;
length += 1;
} else {
*scan = item->next;
item->next = obsolete_chain;
obsolete_chain = item;
}
} while (*scan);
}
eASSERT(env, *scan == nullptr);
if (chain) {
defer_free_item_t *item = chain;
do {
item->timestamp = now;
item = item->next;
} while (item);
*scan = chain;
}
#else /* MDBX_ENABLE_DBI_LOCKFREE */
obsolete_chain = chain;
#endif /* MDBX_ENABLE_DBI_LOCKFREE */
ENSURE(env, osal_fastmutex_release(&env->dbi_lock) == MDBX_SUCCESS);
if (length > 42) {
#if defined(_WIN32) || defined(_WIN64)
SwitchToThread();
#else
sched_yield();
#endif /* Windows */
}
while (obsolete_chain) {
defer_free_item_t *item = obsolete_chain;
obsolete_chain = obsolete_chain->next;
osal_free(item);
}
return chain ? MDBX_SUCCESS : MDBX_BAD_DBI;
}
/* Export or close DBI handles opened in this txn. */
int dbi_update(MDBX_txn *txn, bool keep) {
MDBX_env *const env = txn->env;
tASSERT(txn, !txn->parent && txn == env->basal_txn);
bool locked = false;
defer_free_item_t *defer_chain = nullptr;
TXN_FOREACH_DBI_USER(txn, dbi) {
if (likely((txn->dbi_state[dbi] & DBI_CREAT) == 0))
continue;
if (!locked) {
int err = osal_fastmutex_acquire(&env->dbi_lock);
if (unlikely(err != MDBX_SUCCESS))
return err;
locked = true;
if (dbi >= env->n_dbi)
/* хендл был закрыт из другого потока пока захватывали блокировку */
continue;
}
tASSERT(txn, dbi < env->n_dbi);
if (keep) {
env->dbs_flags[dbi] = txn->dbs[dbi].flags | DB_VALID;
} else {
uint32_t seq = dbi_seq_next(env, dbi);
defer_free_item_t *item = env->kvs[dbi].name.iov_base;
if (item) {
env->dbs_flags[dbi] = 0;
env->kvs[dbi].name.iov_len = 0;
env->kvs[dbi].name.iov_base = nullptr;
atomic_store32(&env->dbi_seqs[dbi], seq, mo_AcquireRelease);
osal_flush_incoherent_cpu_writeback();
item->next = defer_chain;
defer_chain = item;
} else {
eASSERT(env, env->kvs[dbi].name.iov_len == 0);
eASSERT(env, env->dbs_flags[dbi] == 0);
}
}
}
if (locked) {
size_t i = env->n_dbi;
eASSERT(env, env->n_dbi >= CORE_DBS);
while ((env->dbs_flags[i - 1] & DB_VALID) == 0) {
--i;
eASSERT(env, i >= CORE_DBS);
eASSERT(env, !env->dbs_flags[i] && !env->kvs[i].name.iov_len && !env->kvs[i].name.iov_base);
}
env->n_dbi = (unsigned)i;
dbi_defer_release(env, defer_chain);
}
return MDBX_SUCCESS;
}
int dbi_bind(MDBX_txn *txn, const size_t dbi, unsigned user_flags, MDBX_cmp_func *keycmp, MDBX_cmp_func *datacmp) {
const MDBX_env *const env = txn->env;
eASSERT(env, dbi < txn->n_dbi && dbi < env->n_dbi);
eASSERT(env, dbi_state(txn, dbi) & DBI_LINDO);
eASSERT(env, env->dbs_flags[dbi] != DB_POISON);
if ((env->dbs_flags[dbi] & DB_VALID) == 0) {
eASSERT(env, !env->kvs[dbi].clc.k.cmp && !env->kvs[dbi].clc.v.cmp && !env->kvs[dbi].name.iov_len &&
!env->kvs[dbi].name.iov_base && !env->kvs[dbi].clc.k.lmax && !env->kvs[dbi].clc.k.lmin &&
!env->kvs[dbi].clc.v.lmax && !env->kvs[dbi].clc.v.lmin);
} else {
eASSERT(env, !(txn->dbi_state[dbi] & DBI_VALID) || (txn->dbs[dbi].flags | DB_VALID) == env->dbs_flags[dbi]);
eASSERT(env, env->kvs[dbi].name.iov_base || dbi < CORE_DBS);
}
/* Если dbi уже использовался, то корректными считаем четыре варианта:
* 1) user_flags равны MDBX_DB_ACCEDE
* = предполагаем что пользователь открывает существующую table,
* при этом код проверки не позволит установить другие компараторы.
* 2) user_flags нулевые, а оба компаратора пустые/нулевые или равны текущим
* = предполагаем что пользователь открывает существующую table
* старым способом с нулевыми с флагами по-умолчанию.
* 3) user_flags совпадают, а компараторы не заданы или те же
* = предполагаем что пользователь открывает table указывая все параметры;
* 4) user_flags отличаются, но table пустая и задан флаг MDBX_CREATE
* = предполагаем что пользователь пересоздает table;
*/
if ((user_flags & ~MDBX_CREATE) != (unsigned)(env->dbs_flags[dbi] & DB_PERSISTENT_FLAGS)) {
/* flags are differs, check other conditions */
if ((!user_flags && (!keycmp || keycmp == env->kvs[dbi].clc.k.cmp) &&
(!datacmp || datacmp == env->kvs[dbi].clc.v.cmp)) ||
user_flags == MDBX_DB_ACCEDE) {
user_flags = env->dbs_flags[dbi] & DB_PERSISTENT_FLAGS;
} else if ((user_flags & MDBX_CREATE) == 0)
return /* FIXME: return extended info */ MDBX_INCOMPATIBLE;
else {
if (txn->dbi_state[dbi] & DBI_STALE) {
eASSERT(env, env->dbs_flags[dbi] & DB_VALID);
int err = tbl_fetch(txn, dbi);
if (unlikely(err == MDBX_SUCCESS))
return err;
}
eASSERT(env, ((env->dbs_flags[dbi] ^ txn->dbs[dbi].flags) & DB_PERSISTENT_FLAGS) == 0);
eASSERT(env, (txn->dbi_state[dbi] & (DBI_LINDO | DBI_VALID | DBI_STALE)) == (DBI_LINDO | DBI_VALID));
if (unlikely(txn->dbs[dbi].leaf_pages))
return /* FIXME: return extended info */ MDBX_INCOMPATIBLE;
/* Пересоздаём table если там пусто */
if (unlikely(txn->cursors[dbi]))
return MDBX_DANGLING_DBI;
env->dbs_flags[dbi] = DB_POISON;
atomic_store32(&env->dbi_seqs[dbi], dbi_seq_next(env, dbi), mo_AcquireRelease);
const uint32_t seq = dbi_seq_next(env, dbi);
const uint16_t db_flags = user_flags & DB_PERSISTENT_FLAGS;
eASSERT(env, txn->dbs[dbi].height == 0 && txn->dbs[dbi].items == 0 && txn->dbs[dbi].root == P_INVALID);
env->kvs[dbi].clc.k.cmp = keycmp ? keycmp : builtin_keycmp(user_flags);
env->kvs[dbi].clc.v.cmp = datacmp ? datacmp : builtin_datacmp(user_flags);
txn->dbs[dbi].flags = db_flags;
txn->dbs[dbi].dupfix_size = 0;
if (unlikely(tbl_setup(env, &env->kvs[dbi], &txn->dbs[dbi]))) {
txn->dbi_state[dbi] = DBI_LINDO;
txn->flags |= MDBX_TXN_ERROR;
return MDBX_PROBLEM;
}
env->dbs_flags[dbi] = db_flags | DB_VALID;
atomic_store32(&env->dbi_seqs[dbi], seq, mo_AcquireRelease);
txn->dbi_seqs[dbi] = seq;
txn->dbi_state[dbi] = DBI_LINDO | DBI_VALID | DBI_CREAT | DBI_DIRTY;
txn->flags |= MDBX_TXN_DIRTY;
}
}
if (!keycmp)
keycmp = (env->dbs_flags[dbi] & DB_VALID) ? env->kvs[dbi].clc.k.cmp : builtin_keycmp(user_flags);
if (env->kvs[dbi].clc.k.cmp != keycmp) {
if (env->dbs_flags[dbi] & DB_VALID)
return MDBX_EINVAL;
env->kvs[dbi].clc.k.cmp = keycmp;
}
if (!datacmp)
datacmp = (env->dbs_flags[dbi] & DB_VALID) ? env->kvs[dbi].clc.v.cmp : builtin_datacmp(user_flags);
if (env->kvs[dbi].clc.v.cmp != datacmp) {
if (env->dbs_flags[dbi] & DB_VALID)
return MDBX_EINVAL;
env->kvs[dbi].clc.v.cmp = datacmp;
}
return MDBX_SUCCESS;
}
static inline size_t dbi_namelen(const MDBX_val name) {
return (name.iov_len > sizeof(defer_free_item_t)) ? name.iov_len : sizeof(defer_free_item_t);
}
static int dbi_open_locked(MDBX_txn *txn, unsigned user_flags, MDBX_dbi *dbi, MDBX_cmp_func *keycmp,
MDBX_cmp_func *datacmp, MDBX_val name) {
MDBX_env *const env = txn->env;
/* Cannot mix named table(s) with DUPSORT flags */
tASSERT(txn, (txn->dbi_state[MAIN_DBI] & (DBI_LINDO | DBI_VALID | DBI_STALE)) == (DBI_LINDO | DBI_VALID));
if (unlikely(txn->dbs[MAIN_DBI].flags & MDBX_DUPSORT)) {
if (unlikely((user_flags & MDBX_CREATE) == 0))
return MDBX_NOTFOUND;
if (unlikely(txn->dbs[MAIN_DBI].leaf_pages))
/* В MainDB есть записи, либо она уже использовалась. */
return MDBX_INCOMPATIBLE;
/* Пересоздаём MainDB когда там пусто. */
tASSERT(txn,
txn->dbs[MAIN_DBI].height == 0 && txn->dbs[MAIN_DBI].items == 0 && txn->dbs[MAIN_DBI].root == P_INVALID);
if (unlikely(txn->cursors[MAIN_DBI]))
return MDBX_DANGLING_DBI;
env->dbs_flags[MAIN_DBI] = DB_POISON;
atomic_store32(&env->dbi_seqs[MAIN_DBI], dbi_seq_next(env, MAIN_DBI), mo_AcquireRelease);
const uint32_t seq = dbi_seq_next(env, MAIN_DBI);
const uint16_t main_flags = txn->dbs[MAIN_DBI].flags & (MDBX_REVERSEKEY | MDBX_INTEGERKEY);
env->kvs[MAIN_DBI].clc.k.cmp = builtin_keycmp(main_flags);
env->kvs[MAIN_DBI].clc.v.cmp = builtin_datacmp(main_flags);
txn->dbs[MAIN_DBI].flags = main_flags;
txn->dbs[MAIN_DBI].dupfix_size = 0;
int err = tbl_setup(env, &env->kvs[MAIN_DBI], &txn->dbs[MAIN_DBI]);
if (unlikely(err != MDBX_SUCCESS)) {
txn->dbi_state[MAIN_DBI] = DBI_LINDO;
txn->flags |= MDBX_TXN_ERROR;
env->flags |= ENV_FATAL_ERROR;
return err;
}
env->dbs_flags[MAIN_DBI] = main_flags | DB_VALID;
txn->dbi_seqs[MAIN_DBI] = atomic_store32(&env->dbi_seqs[MAIN_DBI], seq, mo_AcquireRelease);
txn->dbi_state[MAIN_DBI] |= DBI_DIRTY;
txn->flags |= MDBX_TXN_DIRTY;
}
tASSERT(txn, env->kvs[MAIN_DBI].clc.k.cmp);
/* Is the DB already open? */
size_t slot = env->n_dbi;
for (size_t scan = CORE_DBS; scan < env->n_dbi; ++scan) {
if ((env->dbs_flags[scan] & DB_VALID) == 0) {
/* Remember this free slot */
slot = (slot < scan) ? slot : scan;
continue;
}
if (!env->kvs[MAIN_DBI].clc.k.cmp(&name, &env->kvs[scan].name)) {
slot = scan;
int err = dbi_check(txn, slot);
if (err == MDBX_BAD_DBI && txn->dbi_state[slot] == (DBI_OLDEN | DBI_LINDO)) {
/* хендл использовался, стал невалидным,
* но теперь явно пере-открывается в этой транзакци */
eASSERT(env, !txn->cursors[slot]);
txn->dbi_state[slot] = DBI_LINDO;
err = dbi_check(txn, slot);
}
if (err == MDBX_SUCCESS) {
err = dbi_bind(txn, slot, user_flags, keycmp, datacmp);
if (likely(err == MDBX_SUCCESS)) {
goto done;
}
}
return err;
}
}
/* Fail, if no free slot and max hit */
if (unlikely(slot >= env->max_dbi))
return MDBX_DBS_FULL;
if (env->n_dbi == slot)
eASSERT(env, !env->dbs_flags[slot] && !env->kvs[slot].name.iov_len && !env->kvs[slot].name.iov_base);
env->dbs_flags[slot] = DB_POISON;
atomic_store32(&env->dbi_seqs[slot], dbi_seq_next(env, slot), mo_AcquireRelease);
memset(&env->kvs[slot], 0, sizeof(env->kvs[slot]));
if (env->n_dbi == slot)
env->n_dbi = (unsigned)slot + 1;
eASSERT(env, slot < env->n_dbi);
int err = dbi_check(txn, slot);
eASSERT(env, err == MDBX_BAD_DBI);
if (err != MDBX_BAD_DBI)
return MDBX_PROBLEM;
/* Find the DB info */
MDBX_val body;
cursor_couple_t cx;
int rc = cursor_init(&cx.outer, txn, MAIN_DBI);
if (unlikely(rc != MDBX_SUCCESS))
return rc;
rc = cursor_seek(&cx.outer, &name, &body, MDBX_SET).err;
if (unlikely(rc != MDBX_SUCCESS)) {
if (rc != MDBX_NOTFOUND || !(user_flags & MDBX_CREATE))
return rc;
} else {
/* make sure this is actually a table */
node_t *node = page_node(cx.outer.pg[cx.outer.top], cx.outer.ki[cx.outer.top]);
if (unlikely((node_flags(node) & (N_DUP | N_TREE)) != N_TREE))
return MDBX_INCOMPATIBLE;
if (!MDBX_DISABLE_VALIDATION && unlikely(body.iov_len != sizeof(tree_t))) {
ERROR("%s/%d: %s %zu", "MDBX_CORRUPTED", MDBX_CORRUPTED, "invalid table node size", body.iov_len);
return MDBX_CORRUPTED;
}
memcpy(&txn->dbs[slot], body.iov_base, sizeof(tree_t));
}
/* Done here so we cannot fail after creating a new DB */
defer_free_item_t *const clone = osal_malloc(dbi_namelen(name));
if (unlikely(!clone))
return MDBX_ENOMEM;
memcpy(clone, name.iov_base, name.iov_len);
name.iov_base = clone;
uint8_t dbi_state = DBI_LINDO | DBI_VALID | DBI_FRESH;
if (unlikely(rc)) {
/* MDBX_NOTFOUND and MDBX_CREATE: Create new DB */
tASSERT(txn, rc == MDBX_NOTFOUND);
body.iov_base = memset(&txn->dbs[slot], 0, body.iov_len = sizeof(tree_t));
txn->dbs[slot].root = P_INVALID;
txn->dbs[slot].mod_txnid = txn->txnid;
txn->dbs[slot].flags = user_flags & DB_PERSISTENT_FLAGS;
cx.outer.next = txn->cursors[MAIN_DBI];
txn->cursors[MAIN_DBI] = &cx.outer;
rc = cursor_put_checklen(&cx.outer, &name, &body, N_TREE | MDBX_NOOVERWRITE);
txn->cursors[MAIN_DBI] = cx.outer.next;
if (unlikely(rc != MDBX_SUCCESS))
goto bailout;
dbi_state |= DBI_DIRTY | DBI_CREAT;
txn->flags |= MDBX_TXN_DIRTY;
tASSERT(txn, (txn->dbi_state[MAIN_DBI] & DBI_DIRTY) != 0);
}
/* Got info, register DBI in this txn */
const uint32_t seq = dbi_seq_next(env, slot);
eASSERT(env, env->dbs_flags[slot] == DB_POISON && !txn->cursors[slot] &&
(txn->dbi_state[slot] & (DBI_LINDO | DBI_VALID)) == DBI_LINDO);
txn->dbi_state[slot] = dbi_state;
memcpy(&txn->dbs[slot], body.iov_base, sizeof(txn->dbs[slot]));
env->dbs_flags[slot] = txn->dbs[slot].flags;
rc = dbi_bind(txn, slot, user_flags, keycmp, datacmp);
if (unlikely(rc != MDBX_SUCCESS))
goto bailout;
env->kvs[slot].name = name;
env->dbs_flags[slot] = txn->dbs[slot].flags | DB_VALID;
txn->dbi_seqs[slot] = atomic_store32(&env->dbi_seqs[slot], seq, mo_AcquireRelease);
done:
*dbi = (MDBX_dbi)slot;
tASSERT(txn, slot < txn->n_dbi && (env->dbs_flags[slot] & DB_VALID) != 0);
eASSERT(env, dbi_check(txn, slot) == MDBX_SUCCESS);
return MDBX_SUCCESS;
bailout:
eASSERT(env, !txn->cursors[slot] && !env->kvs[slot].name.iov_len && !env->kvs[slot].name.iov_base);
txn->dbi_state[slot] &= DBI_LINDO | DBI_OLDEN;
env->dbs_flags[slot] = 0;
osal_free(clone);
if (slot + 1 == env->n_dbi)
txn->n_dbi = env->n_dbi = (unsigned)slot;
return rc;
}
int dbi_open(MDBX_txn *txn, const MDBX_val *const name, unsigned user_flags, MDBX_dbi *dbi, MDBX_cmp_func *keycmp,
MDBX_cmp_func *datacmp) {
if (unlikely(!dbi))
return MDBX_EINVAL;
*dbi = 0;
if (user_flags != MDBX_ACCEDE && unlikely(!check_table_flags(user_flags & ~MDBX_CREATE)))
return MDBX_EINVAL;
int rc = check_txn(txn, MDBX_TXN_BLOCKED);
if (unlikely(rc != MDBX_SUCCESS))
return rc;
if ((user_flags & MDBX_CREATE) && unlikely(txn->flags & MDBX_TXN_RDONLY))
return MDBX_EACCESS;
/* main table? */
if (unlikely(name == MDBX_CHK_MAIN || name->iov_base == MDBX_CHK_MAIN)) {
rc = dbi_bind(txn, MAIN_DBI, user_flags, keycmp, datacmp);
if (likely(rc == MDBX_SUCCESS))
*dbi = MAIN_DBI;
return rc;
}
if (unlikely(name == MDBX_CHK_GC || name->iov_base == MDBX_CHK_GC)) {
rc = dbi_bind(txn, FREE_DBI, user_flags, keycmp, datacmp);
if (likely(rc == MDBX_SUCCESS))
*dbi = FREE_DBI;
return rc;
}
if (unlikely(name == MDBX_CHK_META || name->iov_base == MDBX_CHK_META))
return MDBX_EINVAL;
if (unlikely(name->iov_len > txn->env->leaf_nodemax - NODESIZE - sizeof(tree_t)))
return MDBX_EINVAL;
#if MDBX_ENABLE_DBI_LOCKFREE
/* Is the DB already open? */
const MDBX_env *const env = txn->env;
size_t free_slot = env->n_dbi;
for (size_t i = CORE_DBS; i < env->n_dbi; ++i) {
retry:
if ((env->dbs_flags[i] & DB_VALID) == 0) {
free_slot = i;
continue;
}
const uint32_t snap_seq = atomic_load32(&env->dbi_seqs[i], mo_AcquireRelease);
const uint16_t snap_flags = env->dbs_flags[i];
const MDBX_val snap_name = env->kvs[i].name;
if (user_flags != MDBX_ACCEDE &&
(((user_flags ^ snap_flags) & DB_PERSISTENT_FLAGS) || (keycmp && keycmp != env->kvs[i].clc.k.cmp) ||
(datacmp && datacmp != env->kvs[i].clc.v.cmp)))
continue;
const uint32_t main_seq = atomic_load32(&env->dbi_seqs[MAIN_DBI], mo_AcquireRelease);
MDBX_cmp_func *const snap_cmp = env->kvs[MAIN_DBI].clc.k.cmp;
if (unlikely(!(snap_flags & DB_VALID) || !snap_name.iov_base || !snap_name.iov_len || !snap_cmp))
continue;
const bool name_match = snap_cmp(&snap_name, name) == 0;
osal_flush_incoherent_cpu_writeback();
if (unlikely(snap_seq != atomic_load32(&env->dbi_seqs[i], mo_AcquireRelease) ||
main_seq != atomic_load32(&env->dbi_seqs[MAIN_DBI], mo_AcquireRelease) ||
snap_flags != env->dbs_flags[i] || snap_name.iov_base != env->kvs[i].name.iov_base ||
snap_name.iov_len != env->kvs[i].name.iov_len))
goto retry;
if (name_match) {
rc = dbi_check(txn, i);
if (rc == MDBX_BAD_DBI && txn->dbi_state[i] == (DBI_OLDEN | DBI_LINDO)) {
/* хендл использовался, стал невалидным,
* но теперь явно пере-открывается в этой транзакци */
eASSERT(env, !txn->cursors[i]);
txn->dbi_state[i] = DBI_LINDO;
rc = dbi_check(txn, i);
}
if (likely(rc == MDBX_SUCCESS)) {
rc = dbi_bind(txn, i, user_flags, keycmp, datacmp);
if (likely(rc == MDBX_SUCCESS))
*dbi = (MDBX_dbi)i;
}
return rc;
}
}
/* Fail, if no free slot and max hit */
if (unlikely(free_slot >= env->max_dbi))
return MDBX_DBS_FULL;
#endif /* MDBX_ENABLE_DBI_LOCKFREE */
rc = osal_fastmutex_acquire(&txn->env->dbi_lock);
if (likely(rc == MDBX_SUCCESS)) {
rc = dbi_open_locked(txn, user_flags, dbi, keycmp, datacmp, *name);
ENSURE(txn->env, osal_fastmutex_release(&txn->env->dbi_lock) == MDBX_SUCCESS);
}
return rc;
}
__cold struct dbi_rename_result dbi_rename_locked(MDBX_txn *txn, MDBX_dbi dbi, MDBX_val new_name) {
struct dbi_rename_result pair;
pair.defer = nullptr;
pair.err = dbi_check(txn, dbi);
if (unlikely(pair.err != MDBX_SUCCESS))
return pair;
MDBX_env *const env = txn->env;
MDBX_val old_name = env->kvs[dbi].name;
if (env->kvs[MAIN_DBI].clc.k.cmp(&new_name, &old_name) == 0 && MDBX_DEBUG == 0)
return pair;
cursor_couple_t cx;
pair.err = cursor_init(&cx.outer, txn, MAIN_DBI);
if (unlikely(pair.err != MDBX_SUCCESS))
return pair;
pair.err = cursor_seek(&cx.outer, &new_name, nullptr, MDBX_SET).err;
if (unlikely(pair.err != MDBX_NOTFOUND)) {
pair.err = (pair.err == MDBX_SUCCESS) ? MDBX_KEYEXIST : pair.err;
return pair;
}
pair.defer = osal_malloc(dbi_namelen(new_name));
if (unlikely(!pair.defer)) {
pair.err = MDBX_ENOMEM;
return pair;
}
new_name.iov_base = memcpy(pair.defer, new_name.iov_base, new_name.iov_len);
cx.outer.next = txn->cursors[MAIN_DBI];
txn->cursors[MAIN_DBI] = &cx.outer;
MDBX_val data = {&txn->dbs[dbi], sizeof(tree_t)};
pair.err = cursor_put_checklen(&cx.outer, &new_name, &data, N_TREE | MDBX_NOOVERWRITE);
if (likely(pair.err == MDBX_SUCCESS)) {
pair.err = cursor_seek(&cx.outer, &old_name, nullptr, MDBX_SET).err;
if (likely(pair.err == MDBX_SUCCESS))
pair.err = cursor_del(&cx.outer, N_TREE);
if (likely(pair.err == MDBX_SUCCESS)) {
pair.defer = env->kvs[dbi].name.iov_base;
env->kvs[dbi].name = new_name;
} else
txn->flags |= MDBX_TXN_ERROR;
}
txn->cursors[MAIN_DBI] = cx.outer.next;
return pair;
}
static defer_free_item_t *dbi_close_locked(MDBX_env *env, MDBX_dbi dbi) {
eASSERT(env, dbi >= CORE_DBS);
if (unlikely(dbi >= env->n_dbi))
return nullptr;
const uint32_t seq = dbi_seq_next(env, dbi);
defer_free_item_t *defer_item = env->kvs[dbi].name.iov_base;
if (likely(defer_item)) {
env->dbs_flags[dbi] = 0;
env->kvs[dbi].name.iov_len = 0;
env->kvs[dbi].name.iov_base = nullptr;
atomic_store32(&env->dbi_seqs[dbi], seq, mo_AcquireRelease);
osal_flush_incoherent_cpu_writeback();
defer_item->next = nullptr;
if (env->n_dbi == dbi + 1) {
size_t i = env->n_dbi;
do {
--i;
eASSERT(env, i >= CORE_DBS);
eASSERT(env, !env->dbs_flags[i] && !env->kvs[i].name.iov_len && !env->kvs[i].name.iov_base);
} while (i > CORE_DBS && !env->kvs[i - 1].name.iov_base);
env->n_dbi = (unsigned)i;
}
}
return defer_item;
}
__cold const tree_t *dbi_dig(const MDBX_txn *txn, const size_t dbi, tree_t *fallback) {
const MDBX_txn *dig = txn;
do {
tASSERT(txn, txn->n_dbi == dig->n_dbi);
const uint8_t state = dbi_state(dig, dbi);
if (state & DBI_LINDO)
switch (state & (DBI_VALID | DBI_STALE | DBI_OLDEN)) {
case DBI_VALID:
case DBI_OLDEN:
return dig->dbs + dbi;
case 0:
return fallback;
case DBI_VALID | DBI_STALE:
case DBI_OLDEN | DBI_STALE:
break;
default:
tASSERT(txn, !!"unexpected dig->dbi_state[dbi]");
}
dig = dig->parent;
} while (dig);
return fallback;
}
int dbi_close_release(MDBX_env *env, MDBX_dbi dbi) { return dbi_defer_release(env, dbi_close_locked(env, dbi)); }

147
src/dbi.h Normal file
View File

@ -0,0 +1,147 @@
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
#pragma once
#include "essentials.h"
#if MDBX_ENABLE_DBI_SPARSE
MDBX_NOTHROW_CONST_FUNCTION MDBX_MAYBE_UNUSED MDBX_INTERNAL size_t dbi_bitmap_ctz_fallback(const MDBX_txn *txn,
intptr_t bmi);
static inline size_t dbi_bitmap_ctz(const MDBX_txn *txn, intptr_t bmi) {
tASSERT(txn, bmi > 0);
STATIC_ASSERT(sizeof(bmi) >= sizeof(txn->dbi_sparse[0]));
#if __GNUC_PREREQ(4, 1) || __has_builtin(__builtin_ctzl)
if (sizeof(txn->dbi_sparse[0]) <= sizeof(int))
return __builtin_ctz((int)bmi);
if (sizeof(txn->dbi_sparse[0]) == sizeof(long))
return __builtin_ctzl((long)bmi);
#if (defined(__SIZEOF_LONG_LONG__) && __SIZEOF_LONG_LONG__ == 8) || __has_builtin(__builtin_ctzll)
return __builtin_ctzll(bmi);
#endif /* have(long long) && long long == uint64_t */
#endif /* GNU C */
#if defined(_MSC_VER)
unsigned long index;
if (sizeof(txn->dbi_sparse[0]) > 4) {
#if defined(_M_AMD64) || defined(_M_ARM64) || defined(_M_X64)
_BitScanForward64(&index, bmi);
return index;
#else
if (bmi > UINT32_MAX) {
_BitScanForward(&index, (uint32_t)((uint64_t)bmi >> 32));
return index;
}
#endif
}
_BitScanForward(&index, (uint32_t)bmi);
return index;
#endif /* MSVC */
return dbi_bitmap_ctz_fallback(txn, bmi);
}
static inline bool dbi_foreach_step(const MDBX_txn *const txn, size_t *bitmap_item, size_t *dbi) {
const size_t bitmap_chunk = CHAR_BIT * sizeof(txn->dbi_sparse[0]);
if (*bitmap_item & 1) {
*bitmap_item >>= 1;
return txn->dbi_state[*dbi] != 0;
}
if (*bitmap_item) {
size_t bitmap_skip = dbi_bitmap_ctz(txn, *bitmap_item);
*bitmap_item >>= bitmap_skip;
*dbi += bitmap_skip - 1;
} else {
*dbi = (*dbi - 1) | (bitmap_chunk - 1);
*bitmap_item = txn->dbi_sparse[(1 + *dbi) / bitmap_chunk];
if (*bitmap_item == 0)
*dbi += bitmap_chunk;
}
return false;
}
/* LY: Макрос целенаправленно сделан с одним циклом, чтобы сохранить возможность
* использования оператора break */
#define TXN_FOREACH_DBI_FROM(TXN, I, FROM) \
for (size_t bitmap_item = TXN->dbi_sparse[0] >> FROM, I = FROM; I < TXN->n_dbi; ++I) \
if (dbi_foreach_step(TXN, &bitmap_item, &I))
#else
#define TXN_FOREACH_DBI_FROM(TXN, I, FROM) \
for (size_t I = FROM; I < TXN->n_dbi; ++I) \
if (TXN->dbi_state[I])
#endif /* MDBX_ENABLE_DBI_SPARSE */
#define TXN_FOREACH_DBI_ALL(TXN, I) TXN_FOREACH_DBI_FROM(TXN, I, 0)
#define TXN_FOREACH_DBI_USER(TXN, I) TXN_FOREACH_DBI_FROM(TXN, I, CORE_DBS)
MDBX_INTERNAL int dbi_import(MDBX_txn *txn, const size_t dbi);
struct dbi_snap_result {
uint32_t sequence;
unsigned flags;
};
MDBX_INTERNAL struct dbi_snap_result dbi_snap(const MDBX_env *env, const size_t dbi);
MDBX_INTERNAL int dbi_update(MDBX_txn *txn, bool keep);
static inline uint8_t dbi_state(const MDBX_txn *txn, const size_t dbi) {
STATIC_ASSERT((int)DBI_DIRTY == MDBX_DBI_DIRTY && (int)DBI_STALE == MDBX_DBI_STALE &&
(int)DBI_FRESH == MDBX_DBI_FRESH && (int)DBI_CREAT == MDBX_DBI_CREAT);
#if MDBX_ENABLE_DBI_SPARSE
const size_t bitmap_chunk = CHAR_BIT * sizeof(txn->dbi_sparse[0]);
const size_t bitmap_indx = dbi / bitmap_chunk;
const size_t bitmap_mask = (size_t)1 << dbi % bitmap_chunk;
return likely(dbi < txn->n_dbi && (txn->dbi_sparse[bitmap_indx] & bitmap_mask) != 0) ? txn->dbi_state[dbi] : 0;
#else
return likely(dbi < txn->n_dbi) ? txn->dbi_state[dbi] : 0;
#endif /* MDBX_ENABLE_DBI_SPARSE */
}
static inline bool dbi_changed(const MDBX_txn *txn, const size_t dbi) {
const MDBX_env *const env = txn->env;
eASSERT(env, dbi_state(txn, dbi) & DBI_LINDO);
const uint32_t snap_seq = atomic_load32(&env->dbi_seqs[dbi], mo_AcquireRelease);
return unlikely(snap_seq != txn->dbi_seqs[dbi]);
}
static inline int dbi_check(const MDBX_txn *txn, const size_t dbi) {
const uint8_t state = dbi_state(txn, dbi);
if (likely((state & DBI_LINDO) != 0 && !dbi_changed(txn, dbi)))
return (state & DBI_VALID) ? MDBX_SUCCESS : MDBX_BAD_DBI;
/* Медленный путь: ленивая до-инициализацяи и импорт */
return dbi_import((MDBX_txn *)txn, dbi);
}
static inline uint32_t dbi_seq_next(const MDBX_env *const env, size_t dbi) {
uint32_t v = atomic_load32(&env->dbi_seqs[dbi], mo_AcquireRelease) + 1;
return v ? v : 1;
}
MDBX_INTERNAL int dbi_open(MDBX_txn *txn, const MDBX_val *const name, unsigned user_flags, MDBX_dbi *dbi,
MDBX_cmp_func *keycmp, MDBX_cmp_func *datacmp);
MDBX_INTERNAL int dbi_bind(MDBX_txn *txn, const size_t dbi, unsigned user_flags, MDBX_cmp_func *keycmp,
MDBX_cmp_func *datacmp);
typedef struct defer_free_item {
struct defer_free_item *next;
uint64_t timestamp;
} defer_free_item_t;
MDBX_INTERNAL int dbi_defer_release(MDBX_env *const env, defer_free_item_t *const chain);
MDBX_INTERNAL int dbi_close_release(MDBX_env *env, MDBX_dbi dbi);
MDBX_INTERNAL const tree_t *dbi_dig(const MDBX_txn *txn, const size_t dbi, tree_t *fallback);
struct dbi_rename_result {
defer_free_item_t *defer;
int err;
};
MDBX_INTERNAL struct dbi_rename_result dbi_rename_locked(MDBX_txn *txn, MDBX_dbi dbi, MDBX_val new_name);

View File

@ -1,36 +1,36 @@
#pragma push_macro("mdbx_trace")
#pragma push_macro("mdbx_debug")
#pragma push_macro("mdbx_verbose")
#pragma push_macro("mdbx_notice")
#pragma push_macro("mdbx_warning")
#pragma push_macro("mdbx_error")
#pragma push_macro("mdbx_assert")
#if defined(__GNUC__) && !defined(__LCC__)
#undef mdbx_trace
#define mdbx_trace(fmt, ...) \
mdbx_debug_log(MDBX_LOG_TRACE, __func__, __LINE__, fmt "\n", __VA_ARGS__)
#pragma push_macro("TRACE")
#pragma push_macro("DEBUG")
#pragma push_macro("VERBOSE")
#pragma push_macro("NOTICE")
#pragma push_macro("WARNING")
#pragma push_macro("ERROR")
#pragma push_macro("eASSERT")
#undef mdbx_debug
#define mdbx_debug(fmt, ...) \
mdbx_debug_log(MDBX_LOG_DEBUG, __func__, __LINE__, fmt "\n", __VA_ARGS__)
#undef TRACE
#define TRACE(fmt, ...) debug_log(MDBX_LOG_TRACE, __func__, __LINE__, fmt "\n", __VA_ARGS__)
#undef mdbx_verbose
#define mdbx_verbose(fmt, ...) \
mdbx_debug_log(MDBX_LOG_VERBOSE, __func__, __LINE__, fmt "\n", __VA_ARGS__)
#undef DEBUG
#define DEBUG(fmt, ...) debug_log(MDBX_LOG_DEBUG, __func__, __LINE__, fmt "\n", __VA_ARGS__)
#undef mdbx_notice
#define mdbx_notice(fmt, ...) \
mdbx_debug_log(MDBX_LOG_NOTICE, __func__, __LINE__, fmt "\n", __VA_ARGS__)
#undef VERBOSE
#define VERBOSE(fmt, ...) debug_log(MDBX_LOG_VERBOSE, __func__, __LINE__, fmt "\n", __VA_ARGS__)
#undef mdbx_warning
#define mdbx_warning(fmt, ...) \
mdbx_debug_log(MDBX_LOG_WARN, __func__, __LINE__, fmt "\n", __VA_ARGS__)
#undef NOTICE
#define NOTICE(fmt, ...) debug_log(MDBX_LOG_NOTICE, __func__, __LINE__, fmt "\n", __VA_ARGS__)
#undef mdbx_error
#define mdbx_error(fmt, ...) \
mdbx_debug_log(MDBX_LOG_ERROR, __func__, __LINE__, fmt "\n", __VA_ARGS__)
#undef WARNING
#define WARNING(fmt, ...) debug_log(MDBX_LOG_WARN, __func__, __LINE__, fmt "\n", __VA_ARGS__)
#undef mdbx_assert
#define mdbx_assert(env, expr) mdbx_ensure(env, expr)
#undef ERROR
#define ERROR(fmt, ...) debug_log(MDBX_LOG_ERROR, __func__, __LINE__, fmt "\n", __VA_ARGS__)
#pragma GCC optimize("-O0")
#undef eASSERT
#define eASSERT(env, expr) ENSURE(env, expr)
#if !defined(__clang__)
#pragma GCC optimize("-Og")
#endif
#endif /* GCC only */

View File

@ -1,9 +1,15 @@
#pragma pop_macro("mdbx_trace")
#pragma pop_macro("mdbx_debug")
#pragma pop_macro("mdbx_verbose")
#pragma pop_macro("mdbx_notice")
#pragma pop_macro("mdbx_warning")
#pragma pop_macro("mdbx_error")
#pragma pop_macro("mdbx_assert")
#if defined(__GNUC__) && !defined(__LCC__)
#pragma pop_macro("TRACE")
#pragma pop_macro("DEBUG")
#pragma pop_macro("VERBOSE")
#pragma pop_macro("NOTICE")
#pragma pop_macro("WARNING")
#pragma pop_macro("ERROR")
#pragma pop_macro("eASSERT")
#if !defined(__clang__)
#pragma GCC reset_options
#endif
#endif /* GCC only */

View File

@ -1,347 +0,0 @@
/*
* Copyright 2015-2021 Leonid Yuriev <leo@yuriev.ru>
* and other libmdbx authors: please see AUTHORS file.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted only as authorized by the OpenLDAP
* Public License.
*
* A copy of this license is available in the file LICENSE in the
* top-level directory of the distribution or, alternatively, at
* <http://www.OpenLDAP.org/license.html>.
*/
#pragma once
/* *INDENT-OFF* */
/* clang-format off */
#ifndef __GNUC_PREREQ
# if defined(__GNUC__) && defined(__GNUC_MINOR__)
# define __GNUC_PREREQ(maj, min) \
((__GNUC__ << 16) + __GNUC_MINOR__ >= ((maj) << 16) + (min))
# else
# define __GNUC_PREREQ(maj, min) (0)
# endif
#endif /* __GNUC_PREREQ */
#ifndef __CLANG_PREREQ
# ifdef __clang__
# define __CLANG_PREREQ(maj,min) \
((__clang_major__ << 16) + __clang_minor__ >= ((maj) << 16) + (min))
# else
# define __CLANG_PREREQ(maj,min) (0)
# endif
#endif /* __CLANG_PREREQ */
#ifndef __GLIBC_PREREQ
# if defined(__GLIBC__) && defined(__GLIBC_MINOR__)
# define __GLIBC_PREREQ(maj, min) \
((__GLIBC__ << 16) + __GLIBC_MINOR__ >= ((maj) << 16) + (min))
# else
# define __GLIBC_PREREQ(maj, min) (0)
# endif
#endif /* __GLIBC_PREREQ */
#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
#if __has_feature(address_sanitizer)
# define __SANITIZE_ADDRESS__ 1
#endif
/*----------------------------------------------------------------------------*/
#ifndef __extern_C
# ifdef __cplusplus
# define __extern_C extern "C"
# else
# define __extern_C
# endif
#endif /* __extern_C */
#if !defined(nullptr) && !defined(__cplusplus) || (__cplusplus < 201103L && !defined(_MSC_VER))
# define nullptr NULL
#endif
/*----------------------------------------------------------------------------*/
#ifndef __always_inline
# if defined(__GNUC__) || __has_attribute(__always_inline__)
# define __always_inline __inline __attribute__((__always_inline__))
# elif defined(_MSC_VER)
# define __always_inline __forceinline
# else
# define __always_inline
# endif
#endif /* __always_inline */
#ifndef __noinline
# if defined(__GNUC__) || __has_attribute(__noinline__)
# define __noinline __attribute__((__noinline__))
# elif defined(_MSC_VER)
# define __noinline __declspec(noinline)
# else
# define __noinline
# endif
#endif /* __noinline */
#ifndef __must_check_result
# if defined(__GNUC__) || __has_attribute(__warn_unused_result__)
# define __must_check_result __attribute__((__warn_unused_result__))
# else
# define __must_check_result
# endif
#endif /* __must_check_result */
#if !defined(__noop) && !defined(_MSC_VER)
# define __noop(...) do {} while(0)
#endif /* __noop */
#ifndef __fallthrough
# if defined(__cplusplus) && (__has_cpp_attribute(fallthrough) && \
(!defined(__clang__) || __clang__ > 4)) || __cplusplus >= 201703L
# define __fallthrough [[fallthrough]]
# elif __GNUC_PREREQ(8, 0) && defined(__cplusplus) && __cplusplus >= 201103L
# define __fallthrough [[fallthrough]]
# elif __GNUC_PREREQ(7, 0) && \
(!defined(__LCC__) || (__LCC__ == 124 && __LCC_MINOR__ >= 12) || \
(__LCC__ == 125 && __LCC_MINOR__ >= 5) || (__LCC__ >= 126))
# define __fallthrough __attribute__((__fallthrough__))
# elif defined(__clang__) && defined(__cplusplus) && __cplusplus >= 201103L &&\
__has_feature(cxx_attributes) && __has_warning("-Wimplicit-fallthrough")
# define __fallthrough [[clang::fallthrough]]
# else
# define __fallthrough
# endif
#endif /* __fallthrough */
#ifndef __unreachable
# if __GNUC_PREREQ(4,5) || __has_builtin(__builtin_unreachable)
# define __unreachable() __builtin_unreachable()
# elif defined(_MSC_VER)
# define __unreachable() __assume(0)
# else
# define __unreachable() __noop()
# endif
#endif /* __unreachable */
#ifndef __prefetch
# if defined(__GNUC__) || defined(__clang__) || __has_builtin(__builtin_prefetch)
# define __prefetch(ptr) __builtin_prefetch(ptr)
# else
# define __prefetch(ptr) __noop(ptr)
# endif
#endif /* __prefetch */
#ifndef __nothrow
# if defined(__cplusplus)
# if __cplusplus < 201703L
# define __nothrow throw()
# else
# define __nothrow noexcept(true)
# endif /* __cplusplus */
# elif defined(__GNUC__) || __has_attribute(__nothrow__)
# define __nothrow __attribute__((__nothrow__))
# elif defined(_MSC_VER) && defined(__cplusplus)
# define __nothrow __declspec(nothrow)
# else
# define __nothrow
# endif
#endif /* __nothrow */
#ifndef __hidden
# if defined(__GNUC__) || __has_attribute(__visibility__)
# define __hidden __attribute__((__visibility__("hidden")))
# else
# define __hidden
# endif
#endif /* __hidden */
#ifndef __optimize
# if defined(__OPTIMIZE__)
# if (defined(__GNUC__) && !defined(__clang__)) || __has_attribute(__optimize__)
# define __optimize(ops) __attribute__((__optimize__(ops)))
# else
# define __optimize(ops)
# endif
# else
# define __optimize(ops)
# endif
#endif /* __optimize */
#ifndef __hot
# if defined(__OPTIMIZE__)
# if defined(__e2k__)
# define __hot __attribute__((__hot__)) __optimize(3)
# elif defined(__clang__) && !__has_attribute(__hot_) \
&& __has_attribute(__section__) && (defined(__linux__) || defined(__gnu_linux__))
/* just put frequently used functions in separate section */
# define __hot __attribute__((__section__("text.hot"))) __optimize("O3")
# elif defined(__GNUC__) || __has_attribute(__hot__)
# define __hot __attribute__((__hot__)) __optimize("O3")
# else
# define __hot __optimize("O3")
# endif
# else
# define __hot
# endif
#endif /* __hot */
#ifndef __cold
# if defined(__OPTIMIZE__)
# if defined(__e2k__)
# define __cold __attribute__((__cold__)) __optimize(1)
# elif defined(__clang__) && !__has_attribute(cold) \
&& __has_attribute(__section__) && (defined(__linux__) || defined(__gnu_linux__))
/* just put infrequently used functions in separate section */
# define __cold __attribute__((__section__("text.unlikely"))) __optimize("Os")
# elif defined(__GNUC__) || __has_attribute(cold)
# define __cold __attribute__((__cold__)) __optimize("Os")
# else
# define __cold __optimize("Os")
# endif
# else
# define __cold
# endif
#endif /* __cold */
#ifndef __flatten
# if defined(__OPTIMIZE__) && (defined(__GNUC__) || __has_attribute(__flatten__))
# define __flatten __attribute__((__flatten__))
# else
# define __flatten
# endif
#endif /* __flatten */
#ifndef likely
# if (defined(__GNUC__) || __has_builtin(__builtin_expect)) && !defined(__COVERITY__)
# define likely(cond) __builtin_expect(!!(cond), 1)
# else
# define likely(x) (!!(x))
# endif
#endif /* likely */
#ifndef unlikely
# if (defined(__GNUC__) || __has_builtin(__builtin_expect)) && !defined(__COVERITY__)
# define unlikely(cond) __builtin_expect(!!(cond), 0)
# else
# define unlikely(x) (!!(x))
# endif
#endif /* unlikely */
#ifndef __anonymous_struct_extension__
# if defined(__GNUC__)
# define __anonymous_struct_extension__ __extension__
# else
# define __anonymous_struct_extension__
# endif
#endif /* __anonymous_struct_extension__ */
#ifndef __Wpedantic_format_voidptr
MDBX_MAYBE_UNUSED MDBX_PURE_FUNCTION static __inline const void*
__Wpedantic_format_voidptr(const void* ptr) {return ptr;}
# define __Wpedantic_format_voidptr(ARG) __Wpedantic_format_voidptr(ARG)
#endif /* __Wpedantic_format_voidptr */
/*----------------------------------------------------------------------------*/
#if defined(MDBX_USE_VALGRIND)
# include <valgrind/memcheck.h>
# ifndef VALGRIND_DISABLE_ADDR_ERROR_REPORTING_IN_RANGE
/* LY: available since Valgrind 3.10 */
# define VALGRIND_DISABLE_ADDR_ERROR_REPORTING_IN_RANGE(a,s)
# define VALGRIND_ENABLE_ADDR_ERROR_REPORTING_IN_RANGE(a,s)
# endif
#elif !defined(RUNNING_ON_VALGRIND)
# define VALGRIND_CREATE_MEMPOOL(h,r,z)
# define VALGRIND_DESTROY_MEMPOOL(h)
# define VALGRIND_MEMPOOL_TRIM(h,a,s)
# define VALGRIND_MEMPOOL_ALLOC(h,a,s)
# define VALGRIND_MEMPOOL_FREE(h,a)
# define VALGRIND_MEMPOOL_CHANGE(h,a,b,s)
# define VALGRIND_MAKE_MEM_NOACCESS(a,s)
# define VALGRIND_MAKE_MEM_DEFINED(a,s)
# define VALGRIND_MAKE_MEM_UNDEFINED(a,s)
# define VALGRIND_DISABLE_ADDR_ERROR_REPORTING_IN_RANGE(a,s)
# define VALGRIND_ENABLE_ADDR_ERROR_REPORTING_IN_RANGE(a,s)
# define VALGRIND_CHECK_MEM_IS_ADDRESSABLE(a,s) (0)
# define VALGRIND_CHECK_MEM_IS_DEFINED(a,s) (0)
# define RUNNING_ON_VALGRIND (0)
#endif /* MDBX_USE_VALGRIND */
#ifdef __SANITIZE_ADDRESS__
# include <sanitizer/asan_interface.h>
#elif !defined(ASAN_POISON_MEMORY_REGION)
# define ASAN_POISON_MEMORY_REGION(addr, size) \
((void)(addr), (void)(size))
# define ASAN_UNPOISON_MEMORY_REGION(addr, size) \
((void)(addr), (void)(size))
#endif /* __SANITIZE_ADDRESS__ */
/*----------------------------------------------------------------------------*/
#ifndef ARRAY_LENGTH
# ifdef __cplusplus
template <typename T, size_t N>
char (&__ArraySizeHelper(T (&array)[N]))[N];
# define ARRAY_LENGTH(array) (sizeof(::__ArraySizeHelper(array)))
# else
# define ARRAY_LENGTH(array) (sizeof(array) / sizeof(array[0]))
# endif
#endif /* ARRAY_LENGTH */
#ifndef ARRAY_END
# define ARRAY_END(array) (&array[ARRAY_LENGTH(array)])
#endif /* ARRAY_END */
#define CONCAT(a,b) a##b
#define XCONCAT(a,b) CONCAT(a,b)
#ifndef offsetof
# define offsetof(type, member) __builtin_offsetof(type, member)
#endif /* offsetof */
#ifndef container_of
# define container_of(ptr, type, member) \
((type *)((char *)(ptr) - offsetof(type, member)))
#endif /* container_of */
#define MDBX_TETRAD(a, b, c, d) \
((uint32_t)(a) << 24 | (uint32_t)(b) << 16 | (uint32_t)(c) << 8 | (d))
#define MDBX_STRING_TETRAD(str) MDBX_TETRAD(str[0], str[1], str[2], str[3])
#define FIXME "FIXME: " __FILE__ ", " MDBX_STRINGIFY(__LINE__)
#ifndef STATIC_ASSERT_MSG
# if defined(static_assert)
# define STATIC_ASSERT_MSG(expr, msg) static_assert(expr, msg)
# elif defined(_STATIC_ASSERT)
# define STATIC_ASSERT_MSG(expr, msg) _STATIC_ASSERT(expr)
# elif defined(_MSC_VER)
# include <crtdbg.h>
# define STATIC_ASSERT_MSG(expr, msg) _STATIC_ASSERT(expr)
# elif (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L) \
|| __has_feature(c_static_assert)
# define STATIC_ASSERT_MSG(expr, msg) _Static_assert(expr, msg)
# else
# define STATIC_ASSERT_MSG(expr, msg) switch (0) {case 0:case (expr):;}
# endif
#endif /* STATIC_ASSERT */
#ifndef STATIC_ASSERT
# define STATIC_ASSERT(expr) STATIC_ASSERT_MSG(expr, #expr)
#endif
/* *INDENT-ON* */
/* clang-format on */

488
src/dpl.c Normal file
View File

@ -0,0 +1,488 @@
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
#include "internals.h"
static inline size_t dpl_size2bytes(ptrdiff_t size) {
assert(size > CURSOR_STACK_SIZE && (size_t)size <= PAGELIST_LIMIT);
#if MDBX_DPL_PREALLOC_FOR_RADIXSORT
size += size;
#endif /* MDBX_DPL_PREALLOC_FOR_RADIXSORT */
STATIC_ASSERT(MDBX_ASSUME_MALLOC_OVERHEAD + sizeof(dpl_t) +
(PAGELIST_LIMIT * (MDBX_DPL_PREALLOC_FOR_RADIXSORT + 1)) * sizeof(dp_t) +
MDBX_PNL_GRANULATE * sizeof(void *) * 2 <
SIZE_MAX / 4 * 3);
size_t bytes = ceil_powerof2(MDBX_ASSUME_MALLOC_OVERHEAD + sizeof(dpl_t) + size * sizeof(dp_t),
MDBX_PNL_GRANULATE * sizeof(void *) * 2) -
MDBX_ASSUME_MALLOC_OVERHEAD;
return bytes;
}
static inline size_t dpl_bytes2size(const ptrdiff_t bytes) {
size_t size = (bytes - sizeof(dpl_t)) / sizeof(dp_t);
#if MDBX_DPL_PREALLOC_FOR_RADIXSORT
size >>= 1;
#endif /* MDBX_DPL_PREALLOC_FOR_RADIXSORT */
assert(size > CURSOR_STACK_SIZE && size <= PAGELIST_LIMIT + MDBX_PNL_GRANULATE);
return size;
}
void dpl_free(MDBX_txn *txn) {
if (likely(txn->wr.dirtylist)) {
osal_free(txn->wr.dirtylist);
txn->wr.dirtylist = nullptr;
}
}
dpl_t *dpl_reserve(MDBX_txn *txn, size_t size) {
tASSERT(txn, (txn->flags & MDBX_TXN_RDONLY) == 0);
tASSERT(txn, (txn->flags & MDBX_WRITEMAP) == 0 || MDBX_AVOID_MSYNC);
size_t bytes = dpl_size2bytes((size < PAGELIST_LIMIT) ? size : PAGELIST_LIMIT);
dpl_t *const dl = osal_realloc(txn->wr.dirtylist, bytes);
if (likely(dl)) {
#ifdef osal_malloc_usable_size
bytes = osal_malloc_usable_size(dl);
#endif /* osal_malloc_usable_size */
dl->detent = dpl_bytes2size(bytes);
tASSERT(txn, txn->wr.dirtylist == nullptr || dl->length <= dl->detent);
txn->wr.dirtylist = dl;
}
return dl;
}
int dpl_alloc(MDBX_txn *txn) {
tASSERT(txn, (txn->flags & MDBX_TXN_RDONLY) == 0);
tASSERT(txn, (txn->flags & MDBX_WRITEMAP) == 0 || MDBX_AVOID_MSYNC);
const size_t wanna = (txn->env->options.dp_initial < txn->geo.upper) ? txn->env->options.dp_initial : txn->geo.upper;
#if MDBX_FORCE_ASSERTIONS || MDBX_DEBUG
if (txn->wr.dirtylist)
/* обнуляем чтобы не сработал ассерт внутри dpl_reserve() */
txn->wr.dirtylist->sorted = txn->wr.dirtylist->length = 0;
#endif /* asertions enabled */
if (unlikely(!txn->wr.dirtylist || txn->wr.dirtylist->detent < wanna || txn->wr.dirtylist->detent > wanna + wanna) &&
unlikely(!dpl_reserve(txn, wanna)))
return MDBX_ENOMEM;
/* LY: wr.dirtylist не может быть nullptr, так как либо уже выделен, либо будет выделен в dpl_reserve(). */
/* coverity[var_deref_model] */
dpl_clear(txn->wr.dirtylist);
return MDBX_SUCCESS;
}
#define MDBX_DPL_EXTRACT_KEY(ptr) ((ptr)->pgno)
RADIXSORT_IMPL(dp, dp_t, MDBX_DPL_EXTRACT_KEY, MDBX_DPL_PREALLOC_FOR_RADIXSORT, 1)
#define DP_SORT_CMP(first, last) ((first).pgno < (last).pgno)
SORT_IMPL(dp_sort, false, dp_t, DP_SORT_CMP)
__hot __noinline dpl_t *dpl_sort_slowpath(const MDBX_txn *txn) {
tASSERT(txn, (txn->flags & MDBX_TXN_RDONLY) == 0);
tASSERT(txn, (txn->flags & MDBX_WRITEMAP) == 0 || MDBX_AVOID_MSYNC);
dpl_t *dl = txn->wr.dirtylist;
assert(dl->items[0].pgno == 0 && dl->items[dl->length + 1].pgno == P_INVALID);
const size_t unsorted = dl->length - dl->sorted;
if (likely(unsorted < MDBX_RADIXSORT_THRESHOLD) || unlikely(!dp_radixsort(dl->items + 1, dl->length))) {
if (dl->sorted > unsorted / 4 + 4 &&
(MDBX_DPL_PREALLOC_FOR_RADIXSORT || dl->length + unsorted < dl->detent + dpl_gap_mergesort)) {
dp_t *const sorted_begin = dl->items + 1;
dp_t *const sorted_end = sorted_begin + dl->sorted;
dp_t *const end =
dl->items + (MDBX_DPL_PREALLOC_FOR_RADIXSORT ? dl->length + dl->length + 1 : dl->detent + dpl_reserve_gap);
dp_t *const tmp = end - unsorted;
assert(dl->items + dl->length + 1 < tmp);
/* copy unsorted to the end of allocated space and sort it */
memcpy(tmp, sorted_end, unsorted * sizeof(dp_t));
dp_sort(tmp, tmp + unsorted);
/* merge two parts from end to begin */
dp_t *__restrict w = dl->items + dl->length;
dp_t *__restrict l = dl->items + dl->sorted;
dp_t *__restrict r = end - 1;
do {
const bool cmp = expect_with_probability(l->pgno > r->pgno, 0, .5);
#if defined(__LCC__) || __CLANG_PREREQ(13, 0) || !MDBX_HAVE_CMOV
*w = cmp ? *l-- : *r--;
#else
*w = cmp ? *l : *r;
l -= cmp;
r += (ptrdiff_t)cmp - 1;
#endif
} while (likely(--w > l));
assert(r == tmp - 1);
assert(dl->items[0].pgno == 0 && dl->items[dl->length + 1].pgno == P_INVALID);
if (ASSERT_ENABLED())
for (size_t i = 0; i <= dl->length; ++i)
assert(dl->items[i].pgno < dl->items[i + 1].pgno);
} else {
dp_sort(dl->items + 1, dl->items + dl->length + 1);
assert(dl->items[0].pgno == 0 && dl->items[dl->length + 1].pgno == P_INVALID);
}
} else {
assert(dl->items[0].pgno == 0 && dl->items[dl->length + 1].pgno == P_INVALID);
}
dl->sorted = dl->length;
return dl;
}
/* Returns the index of the first dirty-page whose pgno
* member is greater than or equal to id. */
#define DP_SEARCH_CMP(dp, id) ((dp).pgno < (id))
SEARCH_IMPL(dp_bsearch, dp_t, pgno_t, DP_SEARCH_CMP)
__hot __noinline size_t dpl_search(const MDBX_txn *txn, pgno_t pgno) {
tASSERT(txn, (txn->flags & MDBX_TXN_RDONLY) == 0);
tASSERT(txn, (txn->flags & MDBX_WRITEMAP) == 0 || MDBX_AVOID_MSYNC);
dpl_t *dl = txn->wr.dirtylist;
assert(dl->items[0].pgno == 0 && dl->items[dl->length + 1].pgno == P_INVALID);
if (AUDIT_ENABLED()) {
for (const dp_t *ptr = dl->items + dl->sorted; --ptr > dl->items;) {
assert(ptr[0].pgno < ptr[1].pgno);
assert(ptr[0].pgno >= NUM_METAS);
}
}
switch (dl->length - dl->sorted) {
default:
/* sort a whole */
dpl_sort_slowpath(txn);
break;
case 0:
/* whole sorted cases */
break;
#define LINEAR_SEARCH_CASE(N) \
case N: \
if (dl->items[dl->length - N + 1].pgno == pgno) \
return dl->length - N + 1; \
__fallthrough
/* use linear scan until the threshold */
LINEAR_SEARCH_CASE(7); /* fall through */
LINEAR_SEARCH_CASE(6); /* fall through */
LINEAR_SEARCH_CASE(5); /* fall through */
LINEAR_SEARCH_CASE(4); /* fall through */
LINEAR_SEARCH_CASE(3); /* fall through */
LINEAR_SEARCH_CASE(2); /* fall through */
case 1:
if (dl->items[dl->length].pgno == pgno)
return dl->length;
/* continue bsearch on the sorted part */
break;
}
return dp_bsearch(dl->items + 1, dl->sorted, pgno) - dl->items;
}
const page_t *debug_dpl_find(const MDBX_txn *txn, const pgno_t pgno) {
tASSERT(txn, (txn->flags & MDBX_TXN_RDONLY) == 0);
const dpl_t *dl = txn->wr.dirtylist;
if (dl) {
tASSERT(txn, (txn->flags & MDBX_WRITEMAP) == 0 || MDBX_AVOID_MSYNC);
assert(dl->items[0].pgno == 0 && dl->items[dl->length + 1].pgno == P_INVALID);
for (size_t i = dl->length; i > dl->sorted; --i)
if (dl->items[i].pgno == pgno)
return dl->items[i].ptr;
if (dl->sorted) {
const size_t i = dp_bsearch(dl->items + 1, dl->sorted, pgno) - dl->items;
if (dl->items[i].pgno == pgno)
return dl->items[i].ptr;
}
} else {
tASSERT(txn, (txn->flags & MDBX_WRITEMAP) != 0 && !MDBX_AVOID_MSYNC);
}
return nullptr;
}
void dpl_remove_ex(const MDBX_txn *txn, size_t i, size_t npages) {
tASSERT(txn, (txn->flags & MDBX_TXN_RDONLY) == 0);
tASSERT(txn, (txn->flags & MDBX_WRITEMAP) == 0 || MDBX_AVOID_MSYNC);
dpl_t *dl = txn->wr.dirtylist;
assert((intptr_t)i > 0 && i <= dl->length);
assert(dl->items[0].pgno == 0 && dl->items[dl->length + 1].pgno == P_INVALID);
dl->pages_including_loose -= npages;
dl->sorted -= dl->sorted >= i;
dl->length -= 1;
memmove(dl->items + i, dl->items + i + 1, (dl->length - i + 2) * sizeof(dl->items[0]));
assert(dl->items[0].pgno == 0 && dl->items[dl->length + 1].pgno == P_INVALID);
}
int __must_check_result dpl_append(MDBX_txn *txn, pgno_t pgno, page_t *page, size_t npages) {
tASSERT(txn, (txn->flags & MDBX_TXN_RDONLY) == 0);
tASSERT(txn, (txn->flags & MDBX_WRITEMAP) == 0 || MDBX_AVOID_MSYNC);
const dp_t dp = {page, pgno, (pgno_t)npages};
if ((txn->flags & MDBX_WRITEMAP) == 0) {
size_t *const ptr = ptr_disp(page, -(ptrdiff_t)sizeof(size_t));
*ptr = txn->wr.dirtylru;
}
dpl_t *dl = txn->wr.dirtylist;
tASSERT(txn, dl->length <= PAGELIST_LIMIT + MDBX_PNL_GRANULATE);
tASSERT(txn, dl->items[0].pgno == 0 && dl->items[dl->length + 1].pgno == P_INVALID);
if (AUDIT_ENABLED()) {
for (size_t i = dl->length; i > 0; --i) {
assert(dl->items[i].pgno != dp.pgno);
if (unlikely(dl->items[i].pgno == dp.pgno)) {
ERROR("Page %u already exist in the DPL at %zu", dp.pgno, i);
return MDBX_PROBLEM;
}
}
}
if (unlikely(dl->length == dl->detent)) {
if (unlikely(dl->detent >= PAGELIST_LIMIT)) {
ERROR("DPL is full (PAGELIST_LIMIT %zu)", PAGELIST_LIMIT);
return MDBX_TXN_FULL;
}
const size_t size = (dl->detent < MDBX_PNL_INITIAL * 42) ? dl->detent + dl->detent : dl->detent + dl->detent / 2;
dl = dpl_reserve(txn, size);
if (unlikely(!dl))
return MDBX_ENOMEM;
tASSERT(txn, dl->length < dl->detent);
}
/* Сортировка нужна для быстрого поиска, используем несколько тактик:
* 1) Сохраняем упорядоченность при естественной вставке в нужном порядке.
* 2) Добавляем в не-сортированный хвост, который сортируем и сливаем
* с отсортированной головой по необходимости, а пока хвост короткий
* ищем в нём сканированием, избегая большой пересортировки.
* 3) Если не-сортированный хвост короткий, а добавляемый элемент близок
* к концу отсортированной головы, то выгоднее сразу вставить элемент
* в нужное место.
*
* Алгоритмически:
* - добавлять в не-сортированный хвост следует только если вставка сильно
* дорогая, т.е. если целевая позиция элемента сильно далека от конца;
* - для быстрой проверки достаточно сравнить добавляемый элемент с отстоящим
* от конца на максимально-приемлемое расстояние;
* - если список короче, либо элемент в этой позиции меньше вставляемого,
* то следует перемещать элементы и вставлять в отсортированную голову;
* - если не-сортированный хвост длиннее, либо элемент в этой позиции больше,
* то следует добавлять в не-сортированный хвост. */
dl->pages_including_loose += npages;
dp_t *i = dl->items + dl->length;
const ptrdiff_t pivot = (ptrdiff_t)dl->length - dpl_insertion_threshold;
#if MDBX_HAVE_CMOV
const pgno_t pivot_pgno =
dl->items[(dl->length < dpl_insertion_threshold) ? 0 : dl->length - dpl_insertion_threshold].pgno;
#endif /* MDBX_HAVE_CMOV */
/* copy the stub beyond the end */
i[2] = i[1];
dl->length += 1;
if (likely(pivot <= (ptrdiff_t)dl->sorted) &&
#if MDBX_HAVE_CMOV
pivot_pgno < dp.pgno) {
#else
(pivot <= 0 || dl->items[pivot].pgno < dp.pgno)) {
#endif /* MDBX_HAVE_CMOV */
dl->sorted += 1;
/* сдвигаем несортированный хвост */
while (i >= dl->items + dl->sorted) {
#if !defined(__GNUC__) /* пытаемся избежать вызова memmove() */
i[1] = *i;
#elif MDBX_WORDBITS == 64 && (defined(__SIZEOF_INT128__) || (defined(_INTEGRAL_MAX_BITS) && _INTEGRAL_MAX_BITS >= 128))
STATIC_ASSERT(sizeof(dp) == sizeof(__uint128_t));
((__uint128_t *)i)[1] = *(volatile __uint128_t *)i;
#else
i[1].ptr = i->ptr;
i[1].pgno = i->pgno;
i[1].npages = i->npages;
#endif
--i;
}
/* ищем нужную позицию сдвигая отсортированные элементы */
while (i->pgno > pgno) {
tASSERT(txn, i > dl->items);
i[1] = *i;
--i;
}
tASSERT(txn, i->pgno < dp.pgno);
}
i[1] = dp;
assert(dl->items[0].pgno == 0 && dl->items[dl->length + 1].pgno == P_INVALID);
assert(dl->sorted <= dl->length);
return MDBX_SUCCESS;
}
__cold bool dpl_check(MDBX_txn *txn) {
tASSERT(txn, (txn->flags & MDBX_TXN_RDONLY) == 0);
const dpl_t *const dl = txn->wr.dirtylist;
if (!dl) {
tASSERT(txn, (txn->flags & MDBX_WRITEMAP) != 0 && !MDBX_AVOID_MSYNC);
return true;
}
tASSERT(txn, (txn->flags & MDBX_WRITEMAP) == 0 || MDBX_AVOID_MSYNC);
assert(dl->items[0].pgno == 0 && dl->items[dl->length + 1].pgno == P_INVALID);
tASSERT(txn,
txn->wr.dirtyroom + dl->length == (txn->parent ? txn->parent->wr.dirtyroom : txn->env->options.dp_limit));
if (!AUDIT_ENABLED())
return true;
size_t loose = 0, pages = 0;
for (size_t i = dl->length; i > 0; --i) {
const page_t *const dp = dl->items[i].ptr;
if (!dp)
continue;
tASSERT(txn, dp->pgno == dl->items[i].pgno);
if (unlikely(dp->pgno != dl->items[i].pgno))
return false;
if ((txn->flags & MDBX_WRITEMAP) == 0) {
const uint32_t age = dpl_age(txn, i);
tASSERT(txn, age < UINT32_MAX / 3);
if (unlikely(age > UINT32_MAX / 3))
return false;
}
tASSERT(txn, dp->flags == P_LOOSE || is_modifable(txn, dp));
if (dp->flags == P_LOOSE) {
loose += 1;
} else if (unlikely(!is_modifable(txn, dp)))
return false;
const unsigned num = dpl_npages(dl, i);
pages += num;
tASSERT(txn, txn->geo.first_unallocated >= dp->pgno + num);
if (unlikely(txn->geo.first_unallocated < dp->pgno + num))
return false;
if (i < dl->sorted) {
tASSERT(txn, dl->items[i + 1].pgno >= dp->pgno + num);
if (unlikely(dl->items[i + 1].pgno < dp->pgno + num))
return false;
}
const size_t rpa = pnl_search(txn->wr.repnl, dp->pgno, txn->geo.first_unallocated);
tASSERT(txn, rpa > MDBX_PNL_GETSIZE(txn->wr.repnl) || txn->wr.repnl[rpa] != dp->pgno);
if (rpa <= MDBX_PNL_GETSIZE(txn->wr.repnl) && unlikely(txn->wr.repnl[rpa] == dp->pgno))
return false;
if (num > 1) {
const size_t rpb = pnl_search(txn->wr.repnl, dp->pgno + num - 1, txn->geo.first_unallocated);
tASSERT(txn, rpa == rpb);
if (unlikely(rpa != rpb))
return false;
}
}
tASSERT(txn, loose == txn->wr.loose_count);
if (unlikely(loose != txn->wr.loose_count))
return false;
tASSERT(txn, pages == dl->pages_including_loose);
if (unlikely(pages != dl->pages_including_loose))
return false;
for (size_t i = 1; i <= MDBX_PNL_GETSIZE(txn->wr.retired_pages); ++i) {
const page_t *const dp = debug_dpl_find(txn, txn->wr.retired_pages[i]);
tASSERT(txn, !dp);
if (unlikely(dp))
return false;
}
return true;
}
/*----------------------------------------------------------------------------*/
__noinline void dpl_lru_reduce(MDBX_txn *txn) {
VERBOSE("lru-reduce %u -> %u", txn->wr.dirtylru, txn->wr.dirtylru >> 1);
tASSERT(txn, (txn->flags & (MDBX_TXN_RDONLY | MDBX_WRITEMAP)) == 0);
do {
txn->wr.dirtylru >>= 1;
dpl_t *dl = txn->wr.dirtylist;
for (size_t i = 1; i <= dl->length; ++i) {
size_t *const ptr = ptr_disp(dl->items[i].ptr, -(ptrdiff_t)sizeof(size_t));
*ptr >>= 1;
}
txn = txn->parent;
} while (txn);
}
void dpl_sift(MDBX_txn *const txn, pnl_t pl, const bool spilled) {
tASSERT(txn, (txn->flags & MDBX_TXN_RDONLY) == 0);
tASSERT(txn, (txn->flags & MDBX_WRITEMAP) == 0 || MDBX_AVOID_MSYNC);
if (MDBX_PNL_GETSIZE(pl) && txn->wr.dirtylist->length) {
tASSERT(txn, pnl_check_allocated(pl, (size_t)txn->geo.first_unallocated << spilled));
dpl_t *dl = dpl_sort(txn);
/* Scanning in ascend order */
const intptr_t step = MDBX_PNL_ASCENDING ? 1 : -1;
const intptr_t begin = MDBX_PNL_ASCENDING ? 1 : MDBX_PNL_GETSIZE(pl);
const intptr_t end = MDBX_PNL_ASCENDING ? MDBX_PNL_GETSIZE(pl) + 1 : 0;
tASSERT(txn, pl[begin] <= pl[end - step]);
size_t w, r = dpl_search(txn, pl[begin] >> spilled);
tASSERT(txn, dl->sorted == dl->length);
for (intptr_t i = begin; r <= dl->length;) { /* scan loop */
assert(i != end);
tASSERT(txn, !spilled || (pl[i] & 1) == 0);
pgno_t pl_pgno = pl[i] >> spilled;
pgno_t dp_pgno = dl->items[r].pgno;
if (likely(dp_pgno != pl_pgno)) {
const bool cmp = dp_pgno < pl_pgno;
r += cmp;
i += cmp ? 0 : step;
if (likely(i != end))
continue;
return;
}
/* update loop */
unsigned npages;
w = r;
remove_dl:
npages = dpl_npages(dl, r);
dl->pages_including_loose -= npages;
if (!MDBX_AVOID_MSYNC || !(txn->flags & MDBX_WRITEMAP))
page_shadow_release(txn->env, dl->items[r].ptr, npages);
++r;
next_i:
i += step;
if (unlikely(i == end)) {
while (r <= dl->length)
dl->items[w++] = dl->items[r++];
} else {
while (r <= dl->length) {
assert(i != end);
tASSERT(txn, !spilled || (pl[i] & 1) == 0);
pl_pgno = pl[i] >> spilled;
dp_pgno = dl->items[r].pgno;
if (dp_pgno < pl_pgno)
dl->items[w++] = dl->items[r++];
else if (dp_pgno > pl_pgno)
goto next_i;
else
goto remove_dl;
}
}
dl->sorted = dpl_setlen(dl, w - 1);
txn->wr.dirtyroom += r - w;
tASSERT(txn, txn->wr.dirtyroom + txn->wr.dirtylist->length ==
(txn->parent ? txn->parent->wr.dirtyroom : txn->env->options.dp_limit));
return;
}
}
}
void dpl_release_shadows(MDBX_txn *txn) {
tASSERT(txn, (txn->flags & (MDBX_TXN_RDONLY | MDBX_WRITEMAP)) == 0);
MDBX_env *env = txn->env;
dpl_t *const dl = txn->wr.dirtylist;
for (size_t i = 1; i <= dl->length; i++)
page_shadow_release(env, dl->items[i].ptr, dpl_npages(dl, i));
dpl_clear(dl);
}

134
src/dpl.h Normal file
View File

@ -0,0 +1,134 @@
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
#pragma once
#include "essentials.h"
static inline size_t dpl_setlen(dpl_t *dl, size_t len) {
static const page_t dpl_stub_pageE = {INVALID_TXNID,
0,
P_BAD,
{0},
/* pgno */ ~(pgno_t)0};
assert(dpl_stub_pageE.flags == P_BAD && dpl_stub_pageE.pgno == P_INVALID);
dl->length = len;
dl->items[len + 1].ptr = (page_t *)&dpl_stub_pageE;
dl->items[len + 1].pgno = P_INVALID;
dl->items[len + 1].npages = 1;
return len;
}
static inline void dpl_clear(dpl_t *dl) {
static const page_t dpl_stub_pageB = {INVALID_TXNID,
0,
P_BAD,
{0},
/* pgno */ 0};
assert(dpl_stub_pageB.flags == P_BAD && dpl_stub_pageB.pgno == 0);
dl->sorted = dpl_setlen(dl, 0);
dl->pages_including_loose = 0;
dl->items[0].ptr = (page_t *)&dpl_stub_pageB;
dl->items[0].pgno = 0;
dl->items[0].npages = 1;
assert(dl->items[0].pgno == 0 && dl->items[dl->length + 1].pgno == P_INVALID);
}
MDBX_INTERNAL int __must_check_result dpl_alloc(MDBX_txn *txn);
MDBX_INTERNAL void dpl_free(MDBX_txn *txn);
MDBX_INTERNAL dpl_t *dpl_reserve(MDBX_txn *txn, size_t size);
MDBX_INTERNAL __noinline dpl_t *dpl_sort_slowpath(const MDBX_txn *txn);
static inline dpl_t *dpl_sort(const MDBX_txn *txn) {
tASSERT(txn, (txn->flags & MDBX_TXN_RDONLY) == 0);
tASSERT(txn, (txn->flags & MDBX_WRITEMAP) == 0 || MDBX_AVOID_MSYNC);
dpl_t *dl = txn->wr.dirtylist;
tASSERT(txn, dl->length <= PAGELIST_LIMIT);
tASSERT(txn, dl->sorted <= dl->length);
tASSERT(txn, dl->items[0].pgno == 0 && dl->items[dl->length + 1].pgno == P_INVALID);
return likely(dl->sorted == dl->length) ? dl : dpl_sort_slowpath(txn);
}
MDBX_NOTHROW_PURE_FUNCTION MDBX_INTERNAL __noinline size_t dpl_search(const MDBX_txn *txn, pgno_t pgno);
MDBX_MAYBE_UNUSED MDBX_INTERNAL const page_t *debug_dpl_find(const MDBX_txn *txn, const pgno_t pgno);
MDBX_NOTHROW_PURE_FUNCTION static inline unsigned dpl_npages(const dpl_t *dl, size_t i) {
assert(0 <= (intptr_t)i && i <= dl->length);
unsigned n = dl->items[i].npages;
assert(n == (is_largepage(dl->items[i].ptr) ? dl->items[i].ptr->pages : 1));
return n;
}
MDBX_NOTHROW_PURE_FUNCTION static inline pgno_t dpl_endpgno(const dpl_t *dl, size_t i) {
return dpl_npages(dl, i) + dl->items[i].pgno;
}
MDBX_NOTHROW_PURE_FUNCTION static inline bool dpl_intersect(const MDBX_txn *txn, pgno_t pgno, size_t npages) {
tASSERT(txn, (txn->flags & MDBX_TXN_RDONLY) == 0);
tASSERT(txn, (txn->flags & MDBX_WRITEMAP) == 0 || MDBX_AVOID_MSYNC);
dpl_t *dl = txn->wr.dirtylist;
tASSERT(txn, dl->sorted == dl->length);
tASSERT(txn, dl->items[0].pgno == 0 && dl->items[dl->length + 1].pgno == P_INVALID);
size_t const n = dpl_search(txn, pgno);
tASSERT(txn, n >= 1 && n <= dl->length + 1);
tASSERT(txn, pgno <= dl->items[n].pgno);
tASSERT(txn, pgno > dl->items[n - 1].pgno);
const bool rc =
/* intersection with founded */ pgno + npages > dl->items[n].pgno ||
/* intersection with prev */ dpl_endpgno(dl, n - 1) > pgno;
if (ASSERT_ENABLED()) {
bool check = false;
for (size_t i = 1; i <= dl->length; ++i) {
const page_t *const dp = dl->items[i].ptr;
if (!(dp->pgno /* begin */ >= /* end */ pgno + npages || dpl_endpgno(dl, i) /* end */ <= /* begin */ pgno))
check |= true;
}
tASSERT(txn, check == rc);
}
return rc;
}
MDBX_NOTHROW_PURE_FUNCTION static inline size_t dpl_exist(const MDBX_txn *txn, pgno_t pgno) {
tASSERT(txn, (txn->flags & MDBX_WRITEMAP) == 0 || MDBX_AVOID_MSYNC);
dpl_t *dl = txn->wr.dirtylist;
size_t i = dpl_search(txn, pgno);
tASSERT(txn, (int)i > 0);
return (dl->items[i].pgno == pgno) ? i : 0;
}
MDBX_INTERNAL void dpl_remove_ex(const MDBX_txn *txn, size_t i, size_t npages);
static inline void dpl_remove(const MDBX_txn *txn, size_t i) {
dpl_remove_ex(txn, i, dpl_npages(txn->wr.dirtylist, i));
}
MDBX_INTERNAL int __must_check_result dpl_append(MDBX_txn *txn, pgno_t pgno, page_t *page, size_t npages);
MDBX_MAYBE_UNUSED MDBX_INTERNAL bool dpl_check(MDBX_txn *txn);
MDBX_NOTHROW_PURE_FUNCTION static inline uint32_t dpl_age(const MDBX_txn *txn, size_t i) {
tASSERT(txn, (txn->flags & (MDBX_TXN_RDONLY | MDBX_WRITEMAP)) == 0);
const dpl_t *dl = txn->wr.dirtylist;
assert((intptr_t)i > 0 && i <= dl->length);
size_t *const ptr = ptr_disp(dl->items[i].ptr, -(ptrdiff_t)sizeof(size_t));
return txn->wr.dirtylru - (uint32_t)*ptr;
}
MDBX_INTERNAL void dpl_lru_reduce(MDBX_txn *txn);
static inline uint32_t dpl_lru_turn(MDBX_txn *txn) {
txn->wr.dirtylru += 1;
if (unlikely(txn->wr.dirtylru > UINT32_MAX / 3) && (txn->flags & MDBX_WRITEMAP) == 0)
dpl_lru_reduce(txn);
return txn->wr.dirtylru;
}
MDBX_INTERNAL void dpl_sift(MDBX_txn *const txn, pnl_t pl, const bool spilled);
MDBX_INTERNAL void dpl_release_shadows(MDBX_txn *txn);

1337
src/dxb.c Normal file

File diff suppressed because it is too large Load Diff

602
src/env.c Normal file
View File

@ -0,0 +1,602 @@
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
#include "internals.h"
MDBX_txn *env_owned_wrtxn(const MDBX_env *env) {
if (likely(env->basal_txn)) {
const bool is_owned = (env->flags & MDBX_NOSTICKYTHREADS) ? (env->basal_txn->owner != 0)
: (env->basal_txn->owner == osal_thread_self());
if (is_owned)
return env->txn ? env->txn : env->basal_txn;
}
return nullptr;
}
int env_page_auxbuffer(MDBX_env *env) {
const int err = env->page_auxbuf
? MDBX_SUCCESS
: osal_memalign_alloc(globals.sys_pagesize, env->ps * (size_t)NUM_METAS, &env->page_auxbuf);
if (likely(err == MDBX_SUCCESS)) {
memset(env->page_auxbuf, -1, env->ps * (size_t)2);
memset(ptr_disp(env->page_auxbuf, env->ps * (size_t)2), 0, env->ps);
}
return err;
}
__cold unsigned env_setup_pagesize(MDBX_env *env, const size_t pagesize) {
STATIC_ASSERT(PTRDIFF_MAX > MAX_MAPSIZE);
STATIC_ASSERT(MDBX_MIN_PAGESIZE > sizeof(page_t) + sizeof(meta_t));
ENSURE(env, is_powerof2(pagesize));
ENSURE(env, pagesize >= MDBX_MIN_PAGESIZE);
ENSURE(env, pagesize <= MDBX_MAX_PAGESIZE);
ENSURE(env, !env->page_auxbuf && env->ps != pagesize);
env->ps = (unsigned)pagesize;
STATIC_ASSERT(MAX_GC1OVPAGE(MDBX_MIN_PAGESIZE) > 4);
STATIC_ASSERT(MAX_GC1OVPAGE(MDBX_MAX_PAGESIZE) < PAGELIST_LIMIT);
const intptr_t maxgc_ov1page = (pagesize - PAGEHDRSZ) / sizeof(pgno_t) - 1;
ENSURE(env, maxgc_ov1page > 42 && maxgc_ov1page < (intptr_t)PAGELIST_LIMIT / 4);
env->maxgc_large1page = (unsigned)maxgc_ov1page;
env->maxgc_per_branch = (unsigned)((pagesize - PAGEHDRSZ) / (sizeof(indx_t) + sizeof(node_t) + sizeof(txnid_t)));
STATIC_ASSERT(LEAF_NODE_MAX(MDBX_MIN_PAGESIZE) > sizeof(tree_t) + NODESIZE + 42);
STATIC_ASSERT(LEAF_NODE_MAX(MDBX_MAX_PAGESIZE) < UINT16_MAX);
STATIC_ASSERT(LEAF_NODE_MAX(MDBX_MIN_PAGESIZE) >= BRANCH_NODE_MAX(MDBX_MIN_PAGESIZE));
STATIC_ASSERT(BRANCH_NODE_MAX(MDBX_MAX_PAGESIZE) > NODESIZE + 42);
STATIC_ASSERT(BRANCH_NODE_MAX(MDBX_MAX_PAGESIZE) < UINT16_MAX);
const intptr_t branch_nodemax = BRANCH_NODE_MAX(pagesize);
const intptr_t leaf_nodemax = LEAF_NODE_MAX(pagesize);
ENSURE(env, branch_nodemax > (intptr_t)(NODESIZE + 42) && branch_nodemax % 2 == 0 &&
leaf_nodemax > (intptr_t)(sizeof(tree_t) + NODESIZE + 42) && leaf_nodemax >= branch_nodemax &&
leaf_nodemax < (int)UINT16_MAX && leaf_nodemax % 2 == 0);
env->leaf_nodemax = (uint16_t)leaf_nodemax;
env->branch_nodemax = (uint16_t)branch_nodemax;
env->ps2ln = (uint8_t)log2n_powerof2(pagesize);
eASSERT(env, pgno2bytes(env, 1) == pagesize);
eASSERT(env, bytes2pgno(env, pagesize + pagesize) == 2);
recalculate_merge_thresholds(env);
recalculate_subpage_thresholds(env);
env_options_adjust_dp_limit(env);
return env->ps;
}
__cold int env_sync(MDBX_env *env, bool force, bool nonblock) {
if (unlikely(env->flags & MDBX_RDONLY))
return MDBX_EACCESS;
MDBX_txn *const txn_owned = env_owned_wrtxn(env);
bool should_unlock = false;
int rc = MDBX_RESULT_TRUE /* means "nothing to sync" */;
retry:;
unsigned flags = env->flags & ~(MDBX_NOMETASYNC | txn_shrink_allowed);
if (unlikely((flags & (ENV_FATAL_ERROR | ENV_ACTIVE)) != ENV_ACTIVE)) {
rc = (flags & ENV_FATAL_ERROR) ? MDBX_PANIC : MDBX_EPERM;
goto bailout;
}
const troika_t troika = (txn_owned || should_unlock) ? env->basal_txn->wr.troika : meta_tap(env);
const meta_ptr_t head = meta_recent(env, &troika);
const uint64_t unsynced_pages = atomic_load64(&env->lck->unsynced_pages, mo_Relaxed);
if (unsynced_pages == 0) {
const uint32_t synched_meta_txnid_u32 = atomic_load32(&env->lck->meta_sync_txnid, mo_Relaxed);
if (synched_meta_txnid_u32 == (uint32_t)head.txnid && head.is_steady)
goto bailout;
}
if (should_unlock && (env->flags & MDBX_WRITEMAP) &&
unlikely(head.ptr_c->geometry.first_unallocated > bytes2pgno(env, env->dxb_mmap.current))) {
if (unlikely(env->stuck_meta >= 0) && troika.recent != (uint8_t)env->stuck_meta) {
NOTICE("skip %s since wagering meta-page (%u) is mispatch the recent "
"meta-page (%u)",
"sync datafile", env->stuck_meta, troika.recent);
rc = MDBX_RESULT_TRUE;
} else {
rc = dxb_resize(env, head.ptr_c->geometry.first_unallocated, head.ptr_c->geometry.now, head.ptr_c->geometry.upper,
implicit_grow);
if (unlikely(rc != MDBX_SUCCESS))
goto bailout;
}
}
const size_t autosync_threshold = atomic_load32(&env->lck->autosync_threshold, mo_Relaxed);
const uint64_t autosync_period = atomic_load64(&env->lck->autosync_period, mo_Relaxed);
uint64_t eoos_timestamp;
if (force || (autosync_threshold && unsynced_pages >= autosync_threshold) ||
(autosync_period && (eoos_timestamp = atomic_load64(&env->lck->eoos_timestamp, mo_Relaxed)) &&
osal_monotime() - eoos_timestamp >= autosync_period))
flags &= MDBX_WRITEMAP /* clear flags for full steady sync */;
if (!txn_owned) {
if (!should_unlock) {
#if MDBX_ENABLE_PGOP_STAT
unsigned wops = 0;
#endif /* MDBX_ENABLE_PGOP_STAT */
int err;
/* pre-sync to avoid latency for writer */
if (unsynced_pages > /* FIXME: define threshold */ 42 && (flags & MDBX_SAFE_NOSYNC) == 0) {
eASSERT(env, ((flags ^ env->flags) & MDBX_WRITEMAP) == 0);
if (flags & MDBX_WRITEMAP) {
/* Acquire guard to avoid collision with remap */
#if defined(_WIN32) || defined(_WIN64)
imports.srwl_AcquireShared(&env->remap_guard);
#else
err = osal_fastmutex_acquire(&env->remap_guard);
if (unlikely(err != MDBX_SUCCESS))
return err;
#endif
const size_t usedbytes = pgno_align2os_bytes(env, head.ptr_c->geometry.first_unallocated);
err = osal_msync(&env->dxb_mmap, 0, usedbytes, MDBX_SYNC_DATA);
#if defined(_WIN32) || defined(_WIN64)
imports.srwl_ReleaseShared(&env->remap_guard);
#else
int unlock_err = osal_fastmutex_release(&env->remap_guard);
if (unlikely(unlock_err != MDBX_SUCCESS) && err == MDBX_SUCCESS)
err = unlock_err;
#endif
} else
err = osal_fsync(env->lazy_fd, MDBX_SYNC_DATA);
if (unlikely(err != MDBX_SUCCESS))
return err;
#if MDBX_ENABLE_PGOP_STAT
wops = 1;
#endif /* MDBX_ENABLE_PGOP_STAT */
/* pre-sync done */
rc = MDBX_SUCCESS /* means "some data was synced" */;
}
err = lck_txn_lock(env, nonblock);
if (unlikely(err != MDBX_SUCCESS))
return err;
should_unlock = true;
#if MDBX_ENABLE_PGOP_STAT
env->lck->pgops.wops.weak += wops;
#endif /* MDBX_ENABLE_PGOP_STAT */
env->basal_txn->wr.troika = meta_tap(env);
eASSERT(env, !env->txn && !env->basal_txn->nested);
goto retry;
}
eASSERT(env, head.txnid == recent_committed_txnid(env));
env->basal_txn->txnid = head.txnid;
txn_gc_detent(env->basal_txn);
flags |= txn_shrink_allowed;
}
eASSERT(env, txn_owned || should_unlock);
eASSERT(env, !txn_owned || (flags & txn_shrink_allowed) == 0);
if (!head.is_steady && unlikely(env->stuck_meta >= 0) && troika.recent != (uint8_t)env->stuck_meta) {
NOTICE("skip %s since wagering meta-page (%u) is mispatch the recent "
"meta-page (%u)",
"sync datafile", env->stuck_meta, troika.recent);
rc = MDBX_RESULT_TRUE;
goto bailout;
}
if (!head.is_steady || ((flags & MDBX_SAFE_NOSYNC) == 0 && unsynced_pages)) {
DEBUG("meta-head %" PRIaPGNO ", %s, sync_pending %" PRIu64, data_page(head.ptr_c)->pgno,
durable_caption(head.ptr_c), unsynced_pages);
meta_t meta = *head.ptr_c;
rc = dxb_sync_locked(env, flags, &meta, &env->basal_txn->wr.troika);
if (unlikely(rc != MDBX_SUCCESS))
goto bailout;
}
/* LY: sync meta-pages if MDBX_NOMETASYNC enabled
* and someone was not synced above. */
if (atomic_load32(&env->lck->meta_sync_txnid, mo_Relaxed) != (uint32_t)head.txnid)
rc = meta_sync(env, head);
bailout:
if (should_unlock)
lck_txn_unlock(env);
return rc;
}
__cold int env_open(MDBX_env *env, mdbx_mode_t mode) {
/* Использование O_DSYNC или FILE_FLAG_WRITE_THROUGH:
*
* 0) Если размер страниц БД меньше системной страницы ОЗУ, то ядру ОС
* придется чаще обновлять страницы в unified page cache.
*
* Однако, O_DSYNC не предполагает отключение unified page cache,
* поэтому подобные затруднения будем считать проблемой ОС и/или
* ожидаемым пенальти из-за использования мелких страниц БД.
*
* 1) В режиме MDBX_SYNC_DURABLE - O_DSYNC для записи как данных,
* так и мета-страниц. Однако, на Linux отказ от O_DSYNC с последующим
* fdatasync() может быть выгоднее при использовании HDD, так как
* позволяет io-scheduler переупорядочить запись с учетом актуального
* расположения файла БД на носителе.
*
* 2) В режиме MDBX_NOMETASYNC - O_DSYNC можно использовать для данных,
* но в этом может не быть смысла, так как fdatasync() всё равно
* требуется для гарантии фиксации мета после предыдущей транзакции.
*
* В итоге на нормальных системах (не Windows) есть два варианта:
* - при возможности O_DIRECT и/или io_ring для данных, скорее всего,
* есть смысл вызвать fdatasync() перед записью данных, а затем
* использовать O_DSYNC;
* - не использовать O_DSYNC и вызывать fdatasync() после записи данных.
*
* На Windows же следует минимизировать использование FlushFileBuffers()
* из-за проблем с производительностью. Поэтому на Windows в режиме
* MDBX_NOMETASYNC:
* - мета обновляется через дескриптор без FILE_FLAG_WRITE_THROUGH;
* - перед началом записи данных вызывается FlushFileBuffers(), если
* meta_sync_txnid не совпадает с последней записанной мета;
* - данные записываются через дескриптор с FILE_FLAG_WRITE_THROUGH.
*
* 3) В режиме MDBX_SAFE_NOSYNC - O_DSYNC нет смысла использовать, пока не
* будет реализована возможность полностью асинхронной "догоняющей"
* записи в выделенном процессе-сервере с io-ring очередями внутри.
*
* -----
*
* Использование O_DIRECT или FILE_FLAG_NO_BUFFERING:
*
* Назначение этих флагов в отключении файлового дескриптора от
* unified page cache, т.е. от отображенных в память данных в случае
* libmdbx.
*
* Поэтому, использование direct i/o в libmdbx без MDBX_WRITEMAP лишено
* смысла и контр-продуктивно, ибо так мы провоцируем ядро ОС на
* не-когерентность отображения в память с содержимым файла на носителе,
* либо требуем дополнительных проверок и действий направленных на
* фактическое отключение O_DIRECT для отображенных в память данных.
*
* В режиме MDBX_WRITEMAP когерентность отображенных данных обеспечивается
* физически. Поэтому использование direct i/o может иметь смысл, если у
* ядра ОС есть какие-то проблемы с msync(), в том числе с
* производительностью:
* - использование io_ring или gather-write может быть дешевле, чем
* просмотр PTE ядром и запись измененных/грязных;
* - но проблема в том, что записываемые из user mode страницы либо не
* будут помечены чистыми (и соответственно будут записаны ядром
* еще раз), либо ядру необходимо искать и чистить PTE при получении
* запроса на запись.
*
* Поэтому O_DIRECT или FILE_FLAG_NO_BUFFERING используется:
* - только в режиме MDBX_SYNC_DURABLE с MDBX_WRITEMAP;
* - когда ps >= me_os_psize;
* - опция сборки MDBX_AVOID_MSYNC != 0, которая по-умолчанию включена
* только на Windows (см ниже).
*
* -----
*
* Использование FILE_FLAG_OVERLAPPED на Windows:
*
* У Windows очень плохо с I/O (за исключением прямых постраничных
* scatter/gather, которые работают в обход проблемного unified page
* cache и поэтому почти бесполезны в libmdbx).
*
* При этом всё еще хуже при использовании FlushFileBuffers(), что также
* требуется после FlushViewOfFile() в режиме MDBX_WRITEMAP. Поэтому
* на Windows вместо FlushViewOfFile() и FlushFileBuffers() следует
* использовать запись через дескриптор с FILE_FLAG_WRITE_THROUGH.
*
* В свою очередь, запись с FILE_FLAG_WRITE_THROUGH дешевле/быстрее
* при использовании FILE_FLAG_OVERLAPPED. В результате, на Windows
* в durable-режимах запись данных всегда в overlapped-режиме,
* при этом для записи мета требуется отдельный не-overlapped дескриптор.
*/
env->pid = osal_getpid();
int rc = osal_openfile((env->flags & MDBX_RDONLY) ? MDBX_OPEN_DXB_READ : MDBX_OPEN_DXB_LAZY, env, env->pathname.dxb,
&env->lazy_fd, mode);
if (unlikely(rc != MDBX_SUCCESS))
return rc;
#if MDBX_LOCKING == MDBX_LOCKING_SYSV
env->me_sysv_ipc.key = ftok(env->pathname.dxb, 42);
if (unlikely(env->me_sysv_ipc.key == -1))
return errno;
#endif /* MDBX_LOCKING */
/* Set the position in files outside of the data to avoid corruption
* due to erroneous use of file descriptors in the application code. */
const uint64_t safe_parking_lot_offset = UINT64_C(0x7fffFFFF80000000);
osal_fseek(env->lazy_fd, safe_parking_lot_offset);
env->fd4meta = env->lazy_fd;
#if defined(_WIN32) || defined(_WIN64)
eASSERT(env, env->ioring.overlapped_fd == 0);
bool ior_direct = false;
if (!(env->flags & (MDBX_RDONLY | MDBX_SAFE_NOSYNC | MDBX_NOMETASYNC | MDBX_EXCLUSIVE))) {
if (MDBX_AVOID_MSYNC && (env->flags & MDBX_WRITEMAP)) {
/* Запрошен режим MDBX_SYNC_DURABLE | MDBX_WRITEMAP при активной опции
* MDBX_AVOID_MSYNC.
*
* 1) В этой комбинации наиболее выгодно использовать WriteFileGather(),
* но для этого необходимо открыть файл с флагом FILE_FLAG_NO_BUFFERING и
* после обеспечивать выравнивание адресов и размера данных на границу
* системной страницы, что в свою очередь возможно если размер страницы БД
* не меньше размера системной страницы ОЗУ. Поэтому для открытия файла в
* нужном режиме требуется знать размер страницы БД.
*
* 2) Кроме этого, в Windows запись в заблокированный регион файла
* возможно только через тот-же дескриптор. Поэтому изначальный захват
* блокировок посредством lck_seize(), захват/освобождение блокировок
* во время пишущих транзакций и запись данных должны выполнятся через
* один дескриптор.
*
* Таким образом, требуется прочитать волатильный заголовок БД, чтобы
* узнать размер страницы, чтобы открыть дескриптор файла в режиме нужном
* для записи данных, чтобы использовать именно этот дескриптор для
* изначального захвата блокировок. */
meta_t header;
uint64_t dxb_filesize;
int err = dxb_read_header(env, &header, MDBX_SUCCESS, true);
if ((err == MDBX_SUCCESS && header.pagesize >= globals.sys_pagesize) ||
(err == MDBX_ENODATA && mode && env->ps >= globals.sys_pagesize &&
osal_filesize(env->lazy_fd, &dxb_filesize) == MDBX_SUCCESS && dxb_filesize == 0))
/* Может быть коллизия, если два процесса пытаются одновременно создать
* БД с разным размером страницы, который у одного меньше системной
* страницы, а у другого НЕ меньше. Эта допустимая, но очень странная
* ситуация. Поэтому считаем её ошибочной и не пытаемся разрешить. */
ior_direct = true;
}
rc = osal_openfile(ior_direct ? MDBX_OPEN_DXB_OVERLAPPED_DIRECT : MDBX_OPEN_DXB_OVERLAPPED, env, env->pathname.dxb,
&env->ioring.overlapped_fd, 0);
if (unlikely(rc != MDBX_SUCCESS))
return rc;
env->dxb_lock_event = CreateEventW(nullptr, true, false, nullptr);
if (unlikely(!env->dxb_lock_event))
return (int)GetLastError();
osal_fseek(env->ioring.overlapped_fd, safe_parking_lot_offset);
}
#else
if (mode == 0) {
/* pickup mode for lck-file */
struct stat st;
if (unlikely(fstat(env->lazy_fd, &st)))
return errno;
mode = st.st_mode;
}
mode = (/* inherit read permissions for group and others */ mode & (S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH)) |
/* always add read/write for owner */ S_IRUSR | S_IWUSR |
((mode & S_IRGRP) ? /* +write if readable by group */ S_IWGRP : 0) |
((mode & S_IROTH) ? /* +write if readable by others */ S_IWOTH : 0);
#endif /* !Windows */
const int lck_rc = lck_setup(env, mode);
if (unlikely(MDBX_IS_ERROR(lck_rc)))
return lck_rc;
if (env->lck_mmap.fd != INVALID_HANDLE_VALUE)
osal_fseek(env->lck_mmap.fd, safe_parking_lot_offset);
eASSERT(env, env->dsync_fd == INVALID_HANDLE_VALUE);
if (!(env->flags & (MDBX_RDONLY | MDBX_SAFE_NOSYNC | DEPRECATED_MAPASYNC
#if defined(_WIN32) || defined(_WIN64)
| MDBX_EXCLUSIVE
#endif /* !Windows */
))) {
rc = osal_openfile(MDBX_OPEN_DXB_DSYNC, env, env->pathname.dxb, &env->dsync_fd, 0);
if (unlikely(MDBX_IS_ERROR(rc)))
return rc;
if (env->dsync_fd != INVALID_HANDLE_VALUE) {
if ((env->flags & MDBX_NOMETASYNC) == 0)
env->fd4meta = env->dsync_fd;
osal_fseek(env->dsync_fd, safe_parking_lot_offset);
}
}
const MDBX_env_flags_t lazy_flags = MDBX_SAFE_NOSYNC | MDBX_UTTERLY_NOSYNC | MDBX_NOMETASYNC;
const MDBX_env_flags_t mode_flags = lazy_flags | MDBX_LIFORECLAIM | MDBX_NORDAHEAD | MDBX_RDONLY | MDBX_WRITEMAP;
lck_t *const lck = env->lck_mmap.lck;
if (lck && lck_rc != MDBX_RESULT_TRUE && (env->flags & MDBX_RDONLY) == 0) {
MDBX_env_flags_t snap_flags;
while ((snap_flags = atomic_load32(&lck->envmode, mo_AcquireRelease)) == MDBX_RDONLY) {
if (atomic_cas32(&lck->envmode, MDBX_RDONLY, (snap_flags = (env->flags & mode_flags)))) {
/* The case:
* - let's assume that for some reason the DB file is smaller
* than it should be according to the geometry,
* but not smaller than the last page used;
* - the first process that opens the database (lck_rc == RESULT_TRUE)
* does this in readonly mode and therefore cannot bring
* the file size back to normal;
* - some next process (lck_rc != RESULT_TRUE) opens the DB in
* read-write mode and now is here.
*
* FIXME: Should we re-check and set the size of DB-file right here? */
break;
}
atomic_yield();
}
if (env->flags & MDBX_ACCEDE) {
/* Pickup current mode-flags (MDBX_LIFORECLAIM, MDBX_NORDAHEAD, etc). */
const MDBX_env_flags_t diff =
(snap_flags ^ env->flags) & ((snap_flags & lazy_flags) ? mode_flags : mode_flags & ~MDBX_WRITEMAP);
env->flags ^= diff;
NOTICE("accede mode-flags: 0x%X, 0x%X -> 0x%X", diff, env->flags ^ diff, env->flags);
}
/* Ранее упущенный не очевидный момент: При работе БД в режимах
* не-синхронной/отложенной фиксации на диске, все процессы-писатели должны
* иметь одинаковый режим MDBX_WRITEMAP.
*
* В противном случае, сброс на диск следует выполнять дважды: сначала
* msync(), затем fdatasync(). При этом msync() не обязан отрабатывать
* в процессах без MDBX_WRITEMAP, так как файл в память отображен только
* для чтения. Поэтому, в общем случае, различия по MDBX_WRITEMAP не
* позволяют выполнить фиксацию данных на диск, после их изменения в другом
* процессе.
*
* В режиме MDBX_UTTERLY_NOSYNC позволять совместную работу с MDBX_WRITEMAP
* также не следует, поскольку никакой процесс (в том числе последний) не
* может гарантированно сбросить данные на диск, а следовательно не должен
* помечать какую-либо транзакцию как steady.
*
* В результате, требуется либо запретить совместную работу процессам с
* разным MDBX_WRITEMAP в режиме отложенной записи, либо отслеживать такое
* смешивание и блокировать steady-пометки - что контрпродуктивно. */
const MDBX_env_flags_t rigorous_flags = (snap_flags & lazy_flags)
? MDBX_SAFE_NOSYNC | MDBX_UTTERLY_NOSYNC | MDBX_WRITEMAP
: MDBX_SAFE_NOSYNC | MDBX_UTTERLY_NOSYNC;
const MDBX_env_flags_t rigorous_diff = (snap_flags ^ env->flags) & rigorous_flags;
if (rigorous_diff) {
ERROR("current mode/flags 0x%X incompatible with requested 0x%X, "
"rigorous diff 0x%X",
env->flags, snap_flags, rigorous_diff);
return MDBX_INCOMPATIBLE;
}
}
mincore_clean_cache(env);
const int dxb_rc = dxb_setup(env, lck_rc, mode);
if (MDBX_IS_ERROR(dxb_rc))
return dxb_rc;
rc = osal_check_fs_incore(env->lazy_fd);
env->incore = false;
if (rc == MDBX_RESULT_TRUE) {
env->incore = true;
NOTICE("%s", "in-core database");
rc = MDBX_SUCCESS;
} else if (unlikely(rc != MDBX_SUCCESS)) {
ERROR("check_fs_incore(), err %d", rc);
return rc;
}
if (unlikely(/* recovery mode */ env->stuck_meta >= 0) &&
(lck_rc != /* exclusive */ MDBX_RESULT_TRUE || (env->flags & MDBX_EXCLUSIVE) == 0)) {
ERROR("%s", "recovery requires exclusive mode");
return MDBX_BUSY;
}
DEBUG("opened dbenv %p", (void *)env);
env->flags |= ENV_ACTIVE;
if (!lck || lck_rc == MDBX_RESULT_TRUE) {
env->lck->envmode.weak = env->flags & mode_flags;
env->lck->meta_sync_txnid.weak = (uint32_t)recent_committed_txnid(env);
env->lck->readers_check_timestamp.weak = osal_monotime();
}
if (lck) {
if (lck_rc == MDBX_RESULT_TRUE) {
rc = lck_downgrade(env);
DEBUG("lck-downgrade-%s: rc %i", (env->flags & MDBX_EXCLUSIVE) ? "partial" : "full", rc);
if (rc != MDBX_SUCCESS)
return rc;
} else {
rc = mvcc_cleanup_dead(env, false, nullptr);
if (MDBX_IS_ERROR(rc))
return rc;
}
}
rc = (env->flags & MDBX_RDONLY) ? MDBX_SUCCESS
: osal_ioring_create(&env->ioring
#if defined(_WIN32) || defined(_WIN64)
,
ior_direct, env->ioring.overlapped_fd
#endif /* Windows */
);
return rc;
}
__cold int env_close(MDBX_env *env, bool resurrect_after_fork) {
const unsigned flags = env->flags;
env->flags &= ~ENV_INTERNAL_FLAGS;
if (flags & ENV_TXKEY) {
thread_key_delete(env->me_txkey);
env->me_txkey = 0;
}
if (env->lck)
munlock_all(env);
rthc_lock();
int rc = rthc_remove(env);
rthc_unlock();
#if MDBX_ENABLE_DBI_LOCKFREE
for (defer_free_item_t *next, *ptr = env->defer_free; ptr; ptr = next) {
next = ptr->next;
osal_free(ptr);
}
env->defer_free = nullptr;
#endif /* MDBX_ENABLE_DBI_LOCKFREE */
if ((env->flags & MDBX_RDONLY) == 0)
osal_ioring_destroy(&env->ioring);
env->lck = nullptr;
if (env->lck_mmap.lck)
osal_munmap(&env->lck_mmap);
if (env->dxb_mmap.base) {
osal_munmap(&env->dxb_mmap);
#ifdef ENABLE_MEMCHECK
VALGRIND_DISCARD(env->valgrind_handle);
env->valgrind_handle = -1;
#endif /* ENABLE_MEMCHECK */
}
#if defined(_WIN32) || defined(_WIN64)
eASSERT(env, !env->ioring.overlapped_fd || env->ioring.overlapped_fd == INVALID_HANDLE_VALUE);
if (env->dxb_lock_event != INVALID_HANDLE_VALUE) {
CloseHandle(env->dxb_lock_event);
env->dxb_lock_event = INVALID_HANDLE_VALUE;
}
eASSERT(env, !resurrect_after_fork);
if (env->pathname_char) {
osal_free(env->pathname_char);
env->pathname_char = nullptr;
}
#endif /* Windows */
if (env->dsync_fd != INVALID_HANDLE_VALUE) {
(void)osal_closefile(env->dsync_fd);
env->dsync_fd = INVALID_HANDLE_VALUE;
}
if (env->lazy_fd != INVALID_HANDLE_VALUE) {
(void)osal_closefile(env->lazy_fd);
env->lazy_fd = INVALID_HANDLE_VALUE;
}
if (env->lck_mmap.fd != INVALID_HANDLE_VALUE) {
(void)osal_closefile(env->lck_mmap.fd);
env->lck_mmap.fd = INVALID_HANDLE_VALUE;
}
if (!resurrect_after_fork) {
if (env->kvs) {
for (size_t i = CORE_DBS; i < env->n_dbi; ++i)
if (env->kvs[i].name.iov_len)
osal_free(env->kvs[i].name.iov_base);
osal_free(env->kvs);
env->n_dbi = CORE_DBS;
env->kvs = nullptr;
}
if (env->page_auxbuf) {
osal_memalign_free(env->page_auxbuf);
env->page_auxbuf = nullptr;
}
if (env->dbi_seqs) {
osal_free(env->dbi_seqs);
env->dbi_seqs = nullptr;
}
if (env->dbs_flags) {
osal_free(env->dbs_flags);
env->dbs_flags = nullptr;
}
if (env->pathname.buffer) {
osal_free(env->pathname.buffer);
env->pathname.buffer = nullptr;
}
if (env->basal_txn) {
txn_basal_destroy(env->basal_txn);
env->basal_txn = nullptr;
}
}
env->stuck_meta = -1;
return rc;
}

125
src/essentials.h Normal file
View File

@ -0,0 +1,125 @@
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
#pragma once
#define LIBMDBX_INTERNALS
#define MDBX_DEPRECATED
#ifdef MDBX_CONFIG_H
#include MDBX_CONFIG_H
#endif
#include "preface.h"
#ifdef xMDBX_ALLOY
/* Amalgamated build */
#define MDBX_INTERNAL static
#else
/* Non-amalgamated build */
#define MDBX_INTERNAL
#endif /* xMDBX_ALLOY */
#include "../mdbx.h"
/*----------------------------------------------------------------------------*/
/* Basic constants and types */
typedef struct iov_ctx iov_ctx_t;
#include "osal.h"
#include "options.h"
#include "atomics-types.h"
#include "layout-dxb.h"
#include "layout-lck.h"
#define MIN_MAPSIZE (MDBX_MIN_PAGESIZE * MIN_PAGENO)
#if defined(_WIN32) || defined(_WIN64)
#define MAX_MAPSIZE32 UINT32_C(0x38000000)
#else
#define MAX_MAPSIZE32 UINT32_C(0x7f000000)
#endif
#define MAX_MAPSIZE64 ((MAX_PAGENO + 1) * (uint64_t)MDBX_MAX_PAGESIZE)
#if MDBX_WORDBITS >= 64
#define MAX_MAPSIZE MAX_MAPSIZE64
#define PAGELIST_LIMIT ((size_t)MAX_PAGENO)
#else
#define MAX_MAPSIZE MAX_MAPSIZE32
#define PAGELIST_LIMIT (MAX_MAPSIZE32 / MDBX_MIN_PAGESIZE)
#endif /* MDBX_WORDBITS */
#define MDBX_GOLD_RATIO_DBL 1.6180339887498948482
#define MEGABYTE ((size_t)1 << 20)
/*----------------------------------------------------------------------------*/
union logger_union {
void *ptr;
MDBX_debug_func *fmt;
MDBX_debug_func_nofmt *nofmt;
};
struct libmdbx_globals {
bin128_t bootid;
unsigned sys_pagesize, sys_allocation_granularity;
uint8_t sys_pagesize_ln2;
uint8_t runtime_flags;
uint8_t loglevel;
#if defined(_WIN32) || defined(_WIN64)
bool running_under_Wine;
#elif defined(__linux__) || defined(__gnu_linux__)
bool running_on_WSL1 /* Windows Subsystem 1 for Linux */;
uint32_t linux_kernel_version;
#endif /* Linux */
union logger_union logger;
osal_fastmutex_t debug_lock;
size_t logger_buffer_size;
char *logger_buffer;
};
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
extern struct libmdbx_globals globals;
#if defined(_WIN32) || defined(_WIN64)
extern struct libmdbx_imports imports;
#endif /* Windows */
#include "logging_and_debug.h"
#include "utils.h"
#include "pnl.h"
#ifdef __cplusplus
}
#endif /* __cplusplus */
#define mdbx_sourcery_anchor XCONCAT(mdbx_sourcery_, MDBX_BUILD_SOURCERY)
#if defined(xMDBX_TOOLS)
extern LIBMDBX_API const char *const mdbx_sourcery_anchor;
#endif
#define MDBX_IS_ERROR(rc) ((rc) != MDBX_RESULT_TRUE && (rc) != MDBX_RESULT_FALSE)
/*----------------------------------------------------------------------------*/
MDBX_NOTHROW_CONST_FUNCTION MDBX_MAYBE_UNUSED static inline pgno_t int64pgno(int64_t i64) {
if (likely(i64 >= (int64_t)MIN_PAGENO && i64 <= (int64_t)MAX_PAGENO + 1))
return (pgno_t)i64;
return (i64 < (int64_t)MIN_PAGENO) ? MIN_PAGENO : MAX_PAGENO;
}
MDBX_NOTHROW_CONST_FUNCTION MDBX_MAYBE_UNUSED static inline pgno_t pgno_add(size_t base, size_t augend) {
assert(base <= MAX_PAGENO + 1 && augend < MAX_PAGENO);
return int64pgno((int64_t)base + (int64_t)augend);
}
MDBX_NOTHROW_CONST_FUNCTION MDBX_MAYBE_UNUSED static inline pgno_t pgno_sub(size_t base, size_t subtrahend) {
assert(base >= MIN_PAGENO && base <= MAX_PAGENO + 1 && subtrahend < MAX_PAGENO);
return int64pgno((int64_t)base - (int64_t)subtrahend);
}

1367
src/gc-get.c Normal file

File diff suppressed because it is too large Load Diff

1513
src/gc-put.c Normal file

File diff suppressed because it is too large Load Diff

80
src/gc.h Normal file
View File

@ -0,0 +1,80 @@
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
#pragma once
#include "essentials.h"
/* Гистограмма решения нарезки фрагментов для ситуации нехватки идентификаторов/слотов. */
typedef struct gc_dense_histogram {
/* Размер массива одновременно задаёт максимальный размер последовательностей,
* с которыми решается задача распределения.
*
* Использование длинных последовательностей контрпродуктивно, так как такие последовательности будут
* создавать/воспроизводить/повторять аналогичные затруднения при последующей переработке. Однако,
* в редких ситуациях это может быть единственным выходом. */
unsigned end;
pgno_t array[31];
} gc_dense_histogram_t;
typedef struct gc_update_context {
unsigned loop;
unsigned goodchunk;
bool dense;
pgno_t prev_first_unallocated;
size_t retired_stored;
size_t return_reserved_lo, return_reserved_hi;
txnid_t gc_first;
intptr_t return_left;
#ifndef MDBX_DEBUG_GCU
#define MDBX_DEBUG_GCU 0
#endif
#if MDBX_DEBUG_GCU
struct {
txnid_t prev;
unsigned n;
} dbg;
#endif /* MDBX_DEBUG_GCU */
rkl_t ready4reuse, sequel;
#if MDBX_ENABLE_BIGFOOT
txnid_t bigfoot;
#endif /* MDBX_ENABLE_BIGFOOT */
union {
MDBX_cursor cursor;
cursor_couple_t couple;
};
gc_dense_histogram_t dense_histogram;
} gcu_t;
MDBX_INTERNAL int gc_put_init(MDBX_txn *txn, gcu_t *ctx);
MDBX_INTERNAL void gc_put_destroy(gcu_t *ctx);
#define ALLOC_DEFAULT 0 /* штатное/обычное выделение страниц */
#define ALLOC_UNIMPORTANT 1 /* запрос неважен, невозможность выделения не приведет к ошибке транзакции */
#define ALLOC_RESERVE 2 /* подготовка резерва для обновления GC, без аллокации */
#define ALLOC_COALESCE 4 /* внутреннее состояние/флажок */
#define ALLOC_SHOULD_SCAN 8 /* внутреннее состояние/флажок */
#define ALLOC_LIFO 16 /* внутреннее состояние/флажок */
MDBX_INTERNAL pgr_t gc_alloc_ex(const MDBX_cursor *const mc, const size_t num, uint8_t flags);
MDBX_INTERNAL pgr_t gc_alloc_single(const MDBX_cursor *const mc);
MDBX_INTERNAL int gc_update(MDBX_txn *txn, gcu_t *ctx);
MDBX_NOTHROW_PURE_FUNCTION static inline size_t gc_stockpile(const MDBX_txn *txn) {
return MDBX_PNL_GETSIZE(txn->wr.repnl) + txn->wr.loose_count;
}
MDBX_NOTHROW_PURE_FUNCTION static inline size_t gc_chunk_bytes(const size_t chunk) {
return (chunk + 1) * sizeof(pgno_t);
}
MDBX_INTERNAL bool gc_repnl_has_span(const MDBX_txn *txn, const size_t num);
static inline bool gc_is_reclaimed(const MDBX_txn *txn, const txnid_t id) {
return rkl_contain(&txn->wr.gc.reclaimed, id) || rkl_contain(&txn->wr.gc.comeback, id);
}
static inline txnid_t txnid_min(txnid_t a, txnid_t b) { return (a < b) ? a : b; }
static inline txnid_t txnid_max(txnid_t a, txnid_t b) { return (a > b) ? a : b; }

474
src/global.c Normal file
View File

@ -0,0 +1,474 @@
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
#include "internals.h"
static void mdbx_init(void);
static void mdbx_fini(void);
/*----------------------------------------------------------------------------*/
/* mdbx constructor/destructor */
#if defined(_WIN32) || defined(_WIN64)
#if MDBX_BUILD_SHARED_LIBRARY
#if MDBX_WITHOUT_MSVC_CRT && defined(NDEBUG)
/* DEBUG/CHECKED builds still require MSVC's CRT for runtime checks.
*
* Define dll's entry point only for Release build when NDEBUG is defined and
* MDBX_WITHOUT_MSVC_CRT=ON. if the entry point isn't defined then MSVC's will
* automatically use DllMainCRTStartup() from CRT library, which also
* automatically call DllMain() from our mdbx.dll */
#pragma comment(linker, "/ENTRY:DllMain")
#endif /* MDBX_WITHOUT_MSVC_CRT */
BOOL APIENTRY DllMain(HANDLE module, DWORD reason, LPVOID reserved)
#else
#if !MDBX_MANUAL_MODULE_HANDLER
static
#endif /* !MDBX_MANUAL_MODULE_HANDLER */
void NTAPI
mdbx_module_handler(PVOID module, DWORD reason, PVOID reserved)
#endif /* MDBX_BUILD_SHARED_LIBRARY */
{
(void)reserved;
switch (reason) {
case DLL_PROCESS_ATTACH:
windows_import();
mdbx_init();
break;
case DLL_PROCESS_DETACH:
mdbx_fini();
break;
case DLL_THREAD_ATTACH:
break;
case DLL_THREAD_DETACH:
rthc_thread_dtor(module);
break;
}
#if MDBX_BUILD_SHARED_LIBRARY
return TRUE;
#endif
}
#if !MDBX_BUILD_SHARED_LIBRARY && !MDBX_MANUAL_MODULE_HANDLER
/* *INDENT-OFF* */
/* clang-format off */
#if defined(_MSC_VER)
# pragma const_seg(push)
# pragma data_seg(push)
# ifndef _M_IX86
/* kick a linker to create the TLS directory if not already done */
# pragma comment(linker, "/INCLUDE:_tls_used")
/* Force some symbol references. */
# pragma comment(linker, "/INCLUDE:mdbx_tls_anchor")
/* specific const-segment for WIN64 */
# pragma const_seg(".CRT$XLB")
const
# else
/* kick a linker to create the TLS directory if not already done */
# pragma comment(linker, "/INCLUDE:__tls_used")
/* Force some symbol references. */
# pragma comment(linker, "/INCLUDE:_mdbx_tls_anchor")
/* specific data-segment for WIN32 */
# pragma data_seg(".CRT$XLB")
# endif
__declspec(allocate(".CRT$XLB")) PIMAGE_TLS_CALLBACK mdbx_tls_anchor = mdbx_module_handler;
# pragma data_seg(pop)
# pragma const_seg(pop)
#elif defined(__GNUC__)
# ifndef _M_IX86
const
# endif
PIMAGE_TLS_CALLBACK mdbx_tls_anchor __attribute__((__section__(".CRT$XLB"), used)) = mdbx_module_handler;
#else
# error FIXME
#endif
/* *INDENT-ON* */
/* clang-format on */
#endif /* !MDBX_BUILD_SHARED_LIBRARY && !MDBX_MANUAL_MODULE_HANDLER */
#else
#if defined(__linux__) || defined(__gnu_linux__)
#include <sys/utsname.h>
MDBX_EXCLUDE_FOR_GPROF
__cold static uint8_t probe_for_WSL(const char *tag) {
const char *const WSL = strstr(tag, "WSL");
if (WSL && WSL[3] >= '2' && WSL[3] <= '9')
return WSL[3] - '0';
const char *const wsl = strstr(tag, "wsl");
if (wsl && wsl[3] >= '2' && wsl[3] <= '9')
return wsl[3] - '0';
if (WSL || wsl || strcasestr(tag, "Microsoft"))
/* Expecting no new kernel within WSL1, either it will explicitly
* marked by an appropriate WSL-version hint. */
return (globals.linux_kernel_version < /* 4.19.x */ 0x04130000) ? 1 : 2;
return 0;
}
#endif /* Linux */
#ifdef ENABLE_GPROF
extern void _mcleanup(void);
extern void monstartup(unsigned long, unsigned long);
extern void _init(void);
extern void _fini(void);
extern void __gmon_start__(void) __attribute__((__weak__));
#endif /* ENABLE_GPROF */
MDBX_EXCLUDE_FOR_GPROF
__cold static __attribute__((__constructor__)) void mdbx_global_constructor(void) {
#ifdef ENABLE_GPROF
if (!&__gmon_start__)
monstartup((uintptr_t)&_init, (uintptr_t)&_fini);
#endif /* ENABLE_GPROF */
#if defined(__linux__) || defined(__gnu_linux__)
struct utsname buffer;
if (uname(&buffer) == 0) {
int i = 0;
char *p = buffer.release;
while (*p && i < 4) {
if (*p >= '0' && *p <= '9') {
long number = strtol(p, &p, 10);
if (number > 0) {
if (number > 255)
number = 255;
globals.linux_kernel_version += number << (24 - i * 8);
}
++i;
} else {
++p;
}
}
/* "Official" way of detecting WSL1 but not WSL2
* https://github.com/Microsoft/WSL/issues/423#issuecomment-221627364
*
* WARNING: False negative detection of WSL1 will result in DATA LOSS!
* So, the REQUIREMENTS for this code:
* 1. MUST detect WSL1 without false-negatives.
* 2. DESIRABLE detect WSL2 but without the risk of violating the first. */
globals.running_on_WSL1 =
probe_for_WSL(buffer.version) == 1 || probe_for_WSL(buffer.sysname) == 1 || probe_for_WSL(buffer.release) == 1;
}
#endif /* Linux */
mdbx_init();
}
MDBX_EXCLUDE_FOR_GPROF
__cold static __attribute__((__destructor__)) void mdbx_global_destructor(void) {
mdbx_fini();
#ifdef ENABLE_GPROF
if (!&__gmon_start__)
_mcleanup();
#endif /* ENABLE_GPROF */
}
#endif /* ! Windows */
/******************************************************************************/
struct libmdbx_globals globals;
__cold static void mdbx_init(void) {
globals.runtime_flags = ((MDBX_DEBUG) > 0) * MDBX_DBG_ASSERT + ((MDBX_DEBUG) > 1) * MDBX_DBG_AUDIT;
globals.loglevel = MDBX_LOG_FATAL;
ENSURE(nullptr, osal_fastmutex_init(&globals.debug_lock) == 0);
osal_ctor();
assert(globals.sys_pagesize > 0 && (globals.sys_pagesize & (globals.sys_pagesize - 1)) == 0);
rthc_ctor();
#if MDBX_DEBUG
ENSURE(nullptr, troika_verify_fsm());
ENSURE(nullptr, pv2pages_verify());
#endif /* MDBX_DEBUG*/
}
MDBX_EXCLUDE_FOR_GPROF
__cold static void mdbx_fini(void) {
const uint32_t current_pid = osal_getpid();
TRACE(">> pid %d", current_pid);
rthc_dtor(current_pid);
osal_dtor();
TRACE("<< pid %d\n", current_pid);
ENSURE(nullptr, osal_fastmutex_destroy(&globals.debug_lock) == 0);
}
/******************************************************************************/
/* *INDENT-OFF* */
/* clang-format off */
__dll_export
#ifdef __attribute_used__
__attribute_used__
#elif defined(__GNUC__) || __has_attribute(__used__)
__attribute__((__used__))
#endif
#ifdef __attribute_externally_visible__
__attribute_externally_visible__
#elif (defined(__GNUC__) && !defined(__clang__)) || \
__has_attribute(__externally_visible__)
__attribute__((__externally_visible__))
#endif
const struct MDBX_build_info mdbx_build = {
#ifdef MDBX_BUILD_TIMESTAMP
MDBX_BUILD_TIMESTAMP
#else
"\"" __DATE__ " " __TIME__ "\""
#endif /* MDBX_BUILD_TIMESTAMP */
,
#ifdef MDBX_BUILD_TARGET
MDBX_BUILD_TARGET
#else
#if defined(__ANDROID_API__)
"Android" MDBX_STRINGIFY(__ANDROID_API__)
#elif defined(__linux__) || defined(__gnu_linux__)
"Linux"
#elif defined(EMSCRIPTEN) || defined(__EMSCRIPTEN__)
"webassembly"
#elif defined(__CYGWIN__)
"CYGWIN"
#elif defined(_WIN64) || defined(_WIN32) || defined(__TOS_WIN__) \
|| defined(__WINDOWS__)
"Windows"
#elif defined(__APPLE__)
#if (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) \
|| (defined(TARGET_IPHONE_SIMULATOR) && TARGET_IPHONE_SIMULATOR)
"iOS"
#else
"MacOS"
#endif
#elif defined(__FreeBSD__)
"FreeBSD"
#elif defined(__DragonFly__)
"DragonFlyBSD"
#elif defined(__NetBSD__)
"NetBSD"
#elif defined(__OpenBSD__)
"OpenBSD"
#elif defined(__bsdi__)
"UnixBSDI"
#elif defined(__MACH__)
"MACH"
#elif (defined(_HPUX_SOURCE) || defined(__hpux) || defined(__HP_aCC))
"HPUX"
#elif defined(_AIX)
"AIX"
#elif defined(__sun) && defined(__SVR4)
"Solaris"
#elif defined(__BSD__) || defined(BSD)
"UnixBSD"
#elif defined(__unix__) || defined(UNIX) || defined(__unix) \
|| defined(__UNIX) || defined(__UNIX__)
"UNIX"
#elif defined(_POSIX_VERSION)
"POSIX" MDBX_STRINGIFY(_POSIX_VERSION)
#else
"UnknownOS"
#endif /* Target OS */
"-"
#if defined(__amd64__)
"AMD64"
#elif defined(__ia32__)
"IA32"
#elif defined(__e2k__) || defined(__elbrus__)
"Elbrus"
#elif defined(__alpha__) || defined(__alpha) || defined(_M_ALPHA)
"Alpha"
#elif defined(__aarch64__) || defined(_M_ARM64)
"ARM64"
#elif defined(__arm__) || defined(__thumb__) || defined(__TARGET_ARCH_ARM) \
|| defined(__TARGET_ARCH_THUMB) || defined(_ARM) || defined(_M_ARM) \
|| defined(_M_ARMT) || defined(__arm)
"ARM"
#elif defined(__mips64) || defined(__mips64__) || (defined(__mips) && (__mips >= 64))
"MIPS64"
#elif defined(__mips__) || defined(__mips) || defined(_R4000) || defined(__MIPS__)
"MIPS"
#elif defined(__hppa64__) || defined(__HPPA64__) || defined(__hppa64)
"PARISC64"
#elif defined(__hppa__) || defined(__HPPA__) || defined(__hppa)
"PARISC"
#elif defined(__ia64__) || defined(__ia64) || defined(_IA64) \
|| defined(__IA64__) || defined(_M_IA64) || defined(__itanium__)
"Itanium"
#elif defined(__powerpc64__) || defined(__ppc64__) || defined(__ppc64) \
|| defined(__powerpc64) || defined(_ARCH_PPC64)
"PowerPC64"
#elif defined(__powerpc__) || defined(__ppc__) || defined(__powerpc) \
|| defined(__ppc) || defined(_ARCH_PPC) || defined(__PPC__) || defined(__POWERPC__)
"PowerPC"
#elif defined(__sparc64__) || defined(__sparc64)
"SPARC64"
#elif defined(__sparc__) || defined(__sparc)
"SPARC"
#elif defined(__s390__) || defined(__s390) || defined(__zarch__) || defined(__zarch)
"S390"
#else
"UnknownARCH"
#endif
#endif /* MDBX_BUILD_TARGET */
#ifdef MDBX_BUILD_TYPE
# if defined(_MSC_VER)
# pragma message("Configuration-depended MDBX_BUILD_TYPE: " MDBX_BUILD_TYPE)
# endif
"-" MDBX_BUILD_TYPE
#endif /* MDBX_BUILD_TYPE */
,
"MDBX_DEBUG=" MDBX_STRINGIFY(MDBX_DEBUG)
#ifdef ENABLE_GPROF
" ENABLE_GPROF"
#endif /* ENABLE_GPROF */
" MDBX_WORDBITS=" MDBX_STRINGIFY(MDBX_WORDBITS)
" BYTE_ORDER="
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
"LITTLE_ENDIAN"
#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
"BIG_ENDIAN"
#else
#error "FIXME: Unsupported byte order"
#endif /* __BYTE_ORDER__ */
" MDBX_ENABLE_BIGFOOT=" MDBX_STRINGIFY(MDBX_ENABLE_BIGFOOT)
" MDBX_ENV_CHECKPID=" MDBX_ENV_CHECKPID_CONFIG
" MDBX_TXN_CHECKOWNER=" MDBX_TXN_CHECKOWNER_CONFIG
" MDBX_64BIT_ATOMIC=" MDBX_64BIT_ATOMIC_CONFIG
" MDBX_64BIT_CAS=" MDBX_64BIT_CAS_CONFIG
" MDBX_TRUST_RTC=" MDBX_TRUST_RTC_CONFIG
" MDBX_AVOID_MSYNC=" MDBX_STRINGIFY(MDBX_AVOID_MSYNC)
" MDBX_ENABLE_REFUND=" MDBX_STRINGIFY(MDBX_ENABLE_REFUND)
" MDBX_USE_MINCORE=" MDBX_STRINGIFY(MDBX_USE_MINCORE)
" MDBX_ENABLE_PGOP_STAT=" MDBX_STRINGIFY(MDBX_ENABLE_PGOP_STAT)
" MDBX_ENABLE_PROFGC=" MDBX_STRINGIFY(MDBX_ENABLE_PROFGC)
#if MDBX_DISABLE_VALIDATION
" MDBX_DISABLE_VALIDATION=YES"
#endif /* MDBX_DISABLE_VALIDATION */
#ifdef __SANITIZE_ADDRESS__
" SANITIZE_ADDRESS=YES"
#endif /* __SANITIZE_ADDRESS__ */
#ifdef ENABLE_MEMCHECK
" ENABLE_MEMCHECK=YES"
#endif /* ENABLE_MEMCHECK */
#if MDBX_FORCE_ASSERTIONS
" MDBX_FORCE_ASSERTIONS=YES"
#endif /* MDBX_FORCE_ASSERTIONS */
#ifdef _GNU_SOURCE
" _GNU_SOURCE=YES"
#else
" _GNU_SOURCE=NO"
#endif /* _GNU_SOURCE */
#ifdef __APPLE__
" MDBX_APPLE_SPEED_INSTEADOF_DURABILITY=" MDBX_STRINGIFY(MDBX_APPLE_SPEED_INSTEADOF_DURABILITY)
#endif /* MacOS */
#if defined(_WIN32) || defined(_WIN64)
" MDBX_WITHOUT_MSVC_CRT=" MDBX_STRINGIFY(MDBX_WITHOUT_MSVC_CRT)
" MDBX_BUILD_SHARED_LIBRARY=" MDBX_STRINGIFY(MDBX_BUILD_SHARED_LIBRARY)
#if !MDBX_BUILD_SHARED_LIBRARY
" MDBX_MANUAL_MODULE_HANDLER=" MDBX_STRINGIFY(MDBX_MANUAL_MODULE_HANDLER)
#endif
" WINVER=" MDBX_STRINGIFY(WINVER)
#else /* Windows */
" MDBX_LOCKING=" MDBX_LOCKING_CONFIG
" MDBX_USE_OFDLOCKS=" MDBX_USE_OFDLOCKS_CONFIG
#endif /* !Windows */
" MDBX_CACHELINE_SIZE=" MDBX_STRINGIFY(MDBX_CACHELINE_SIZE)
" MDBX_CPU_WRITEBACK_INCOHERENT=" MDBX_STRINGIFY(MDBX_CPU_WRITEBACK_INCOHERENT)
" MDBX_MMAP_INCOHERENT_CPU_CACHE=" MDBX_STRINGIFY(MDBX_MMAP_INCOHERENT_CPU_CACHE)
" MDBX_MMAP_INCOHERENT_FILE_WRITE=" MDBX_STRINGIFY(MDBX_MMAP_INCOHERENT_FILE_WRITE)
" MDBX_UNALIGNED_OK=" MDBX_STRINGIFY(MDBX_UNALIGNED_OK)
" MDBX_PNL_ASCENDING=" MDBX_STRINGIFY(MDBX_PNL_ASCENDING)
,
#ifdef MDBX_BUILD_COMPILER
MDBX_BUILD_COMPILER
#else
#ifdef __INTEL_COMPILER
"Intel C/C++ " MDBX_STRINGIFY(__INTEL_COMPILER)
#elif defined(__apple_build_version__)
"Apple clang " MDBX_STRINGIFY(__apple_build_version__)
#elif defined(__ibmxl__)
"IBM clang C " MDBX_STRINGIFY(__ibmxl_version__) "." MDBX_STRINGIFY(__ibmxl_release__)
"." MDBX_STRINGIFY(__ibmxl_modification__) "." MDBX_STRINGIFY(__ibmxl_ptf_fix_level__)
#elif defined(__clang__)
"clang " MDBX_STRINGIFY(__clang_version__)
#elif defined(__MINGW64__)
"MINGW-64 " MDBX_STRINGIFY(__MINGW64_MAJOR_VERSION) "." MDBX_STRINGIFY(__MINGW64_MINOR_VERSION)
#elif defined(__MINGW32__)
"MINGW-32 " MDBX_STRINGIFY(__MINGW32_MAJOR_VERSION) "." MDBX_STRINGIFY(__MINGW32_MINOR_VERSION)
#elif defined(__MINGW__)
"MINGW " MDBX_STRINGIFY(__MINGW_MAJOR_VERSION) "." MDBX_STRINGIFY(__MINGW_MINOR_VERSION)
#elif defined(__IBMC__)
"IBM C " MDBX_STRINGIFY(__IBMC__)
#elif defined(__GNUC__)
"GNU C/C++ "
#ifdef __VERSION__
__VERSION__
#else
MDBX_STRINGIFY(__GNUC__) "." MDBX_STRINGIFY(__GNUC_MINOR__) "." MDBX_STRINGIFY(__GNUC_PATCHLEVEL__)
#endif
#elif defined(_MSC_VER)
"MSVC " MDBX_STRINGIFY(_MSC_FULL_VER) "-" MDBX_STRINGIFY(_MSC_BUILD)
#else
"Unknown compiler"
#endif
#endif /* MDBX_BUILD_COMPILER */
,
#ifdef MDBX_BUILD_FLAGS_CONFIG
MDBX_BUILD_FLAGS_CONFIG
#endif /* MDBX_BUILD_FLAGS_CONFIG */
#if defined(MDBX_BUILD_FLAGS_CONFIG) && defined(MDBX_BUILD_FLAGS)
" "
#endif
#ifdef MDBX_BUILD_FLAGS
MDBX_BUILD_FLAGS
#endif /* MDBX_BUILD_FLAGS */
#if !(defined(MDBX_BUILD_FLAGS_CONFIG) || defined(MDBX_BUILD_FLAGS))
"undefined (please use correct build script)"
#ifdef _MSC_VER
#pragma message("warning: Build flags undefined. Please use correct build script")
#else
#warning "Build flags undefined. Please use correct build script"
#endif // _MSC_VER
#endif
, MDBX_BUILD_METADATA
};
#ifdef __SANITIZE_ADDRESS__
#if !defined(_MSC_VER) || __has_attribute(weak)
LIBMDBX_API __attribute__((__weak__))
#endif
const char *__asan_default_options(void) {
return "symbolize=1:allow_addr2line=1:"
#if MDBX_DEBUG
"debug=1:"
"verbosity=2:"
#endif /* MDBX_DEBUG */
"log_threads=1:"
"report_globals=1:"
"replace_str=1:replace_intrin=1:"
"malloc_context_size=9:"
#if !defined(__APPLE__)
"detect_leaks=1:"
#endif
"check_printf=1:"
"detect_deadlocks=1:"
#ifndef LTO_ENABLED
"check_initialization_order=1:"
#endif
"detect_stack_use_after_return=1:"
"intercept_tls_get_addr=1:"
"decorate_proc_maps=1:"
"abort_on_error=1";
}
#endif /* __SANITIZE_ADDRESS__ */
/* *INDENT-ON* */
/* clang-format on */

File diff suppressed because it is too large Load Diff

288
src/layout-dxb.h Normal file
View File

@ -0,0 +1,288 @@
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \note Please refer to the COPYRIGHT file for explanations license change,
/// credits and acknowledgments.
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
#pragma once
#include "essentials.h"
#pragma pack(push, 4)
/* A stamp that identifies a file as an MDBX file.
* There's nothing special about this value other than that it is easily
* recognizable, and it will reflect any byte order mismatches. */
#define MDBX_MAGIC UINT64_C(/* 56-bit prime */ 0x59659DBDEF4C11)
/* FROZEN: The version number for a database's datafile format. */
#define MDBX_DATA_VERSION 3
#define MDBX_DATA_MAGIC ((MDBX_MAGIC << 8) + MDBX_PNL_ASCENDING * 64 + MDBX_DATA_VERSION)
#define MDBX_DATA_MAGIC_LEGACY_COMPAT ((MDBX_MAGIC << 8) + MDBX_PNL_ASCENDING * 64 + 2)
#define MDBX_DATA_MAGIC_LEGACY_DEVEL ((MDBX_MAGIC << 8) + 255)
/* handle for the DB used to track free pages. */
#define FREE_DBI 0
/* handle for the default DB. */
#define MAIN_DBI 1
/* Number of DBs in metapage (free and main) - also hardcoded elsewhere */
#define CORE_DBS 2
/* Number of meta pages - also hardcoded elsewhere */
#define NUM_METAS 3
/* A page number in the database.
*
* MDBX uses 32 bit for page numbers. This limits database
* size up to 2^44 bytes, in case of 4K pages. */
typedef uint32_t pgno_t;
typedef mdbx_atomic_uint32_t atomic_pgno_t;
#define PRIaPGNO PRIu32
#define MAX_PAGENO UINT32_C(0x7FFFffff)
#define MIN_PAGENO NUM_METAS
/* An invalid page number.
* Mainly used to denote an empty tree. */
#define P_INVALID (~(pgno_t)0)
/* A transaction ID. */
typedef uint64_t txnid_t;
typedef mdbx_atomic_uint64_t atomic_txnid_t;
#define PRIaTXN PRIi64
#define MIN_TXNID UINT64_C(1)
#define MAX_TXNID (SAFE64_INVALID_THRESHOLD - 1)
#define INITIAL_TXNID (MIN_TXNID + NUM_METAS - 1)
#define INVALID_TXNID UINT64_MAX
/* Used for offsets within a single page. */
typedef uint16_t indx_t;
typedef struct tree {
uint16_t flags; /* see mdbx_dbi_open */
uint16_t height; /* height of this tree */
uint32_t dupfix_size; /* key-size for MDBX_DUPFIXED (DUPFIX pages) */
pgno_t root; /* the root page of this tree */
pgno_t branch_pages; /* number of branch pages */
pgno_t leaf_pages; /* number of leaf pages */
pgno_t large_pages; /* number of large pages */
uint64_t sequence; /* table sequence counter */
uint64_t items; /* number of data items */
uint64_t mod_txnid; /* txnid of last committed modification */
} tree_t;
/* database size-related parameters */
typedef struct geo {
uint16_t grow_pv; /* datafile growth step as a 16-bit packed (exponential
quantized) value */
uint16_t shrink_pv; /* datafile shrink threshold as a 16-bit packed
(exponential quantized) value */
pgno_t lower; /* minimal size of datafile in pages */
pgno_t upper; /* maximal size of datafile in pages */
union {
pgno_t now; /* current size of datafile in pages */
pgno_t end_pgno;
};
union {
pgno_t first_unallocated; /* first unused page in the datafile,
but actually the file may be shorter. */
pgno_t next_pgno;
};
} geo_t;
/* Meta page content.
* A meta page is the start point for accessing a database snapshot.
* Pages 0-2 are meta pages. */
typedef struct meta {
/* Stamp identifying this as an MDBX file.
* It must be set to MDBX_MAGIC with MDBX_DATA_VERSION. */
uint32_t magic_and_version[2];
/* txnid that committed this meta, the first of a two-phase-update pair */
union {
mdbx_atomic_uint32_t txnid_a[2];
uint64_t unsafe_txnid;
};
uint16_t reserve16; /* extra flags, zero (nothing) for now */
uint8_t validator_id; /* ID of checksum and page validation method,
* zero (nothing) for now */
int8_t extra_pagehdr; /* extra bytes in the page header,
* zero (nothing) for now */
geo_t geometry; /* database size-related parameters */
union {
struct {
tree_t gc, main;
} trees;
__anonymous_struct_extension__ struct {
uint16_t gc_flags;
uint16_t gc_height;
uint32_t pagesize;
};
};
MDBX_canary canary;
#define DATASIGN_NONE 0u
#define DATASIGN_WEAK 1u
#define SIGN_IS_STEADY(sign) ((sign) > DATASIGN_WEAK)
union {
uint32_t sign[2];
uint64_t unsafe_sign;
};
/* txnid that committed this meta, the second of a two-phase-update pair */
mdbx_atomic_uint32_t txnid_b[2];
/* Number of non-meta pages which were put in GC after COW. May be 0 in case
* DB was previously handled by libmdbx without corresponding feature.
* This value in couple with reader.snapshot_pages_retired allows fast
* estimation of "how much reader is restraining GC recycling". */
uint32_t pages_retired[2];
/* The analogue /proc/sys/kernel/random/boot_id or similar to determine
* whether the system was rebooted after the last use of the database files.
* If there was no reboot, but there is no need to rollback to the last
* steady sync point. Zeros mean that no relevant information is available
* from the system. */
bin128_t bootid;
/* GUID базы данных, начиная с v0.13.1 */
bin128_t dxbid;
} meta_t;
#pragma pack(1)
typedef enum page_type {
P_BRANCH = 0x01u /* branch page */,
P_LEAF = 0x02u /* leaf page */,
P_LARGE = 0x04u /* large/overflow page */,
P_META = 0x08u /* meta page */,
P_LEGACY_DIRTY = 0x10u /* legacy P_DIRTY flag prior to v0.10 958fd5b9 */,
P_BAD = P_LEGACY_DIRTY /* explicit flag for invalid/bad page */,
P_DUPFIX = 0x20u /* for MDBX_DUPFIXED records */,
P_SUBP = 0x40u /* for MDBX_DUPSORT sub-pages */,
P_SPILLED = 0x2000u /* spilled in parent txn */,
P_LOOSE = 0x4000u /* page was dirtied then freed, can be reused */,
P_FROZEN = 0x8000u /* used for retire page with known status */,
P_ILL_BITS = (uint16_t)~(P_BRANCH | P_LEAF | P_DUPFIX | P_LARGE | P_SPILLED),
page_broken = 0,
page_large = P_LARGE,
page_branch = P_BRANCH,
page_leaf = P_LEAF,
page_dupfix_leaf = P_DUPFIX,
page_sub_leaf = P_SUBP | P_LEAF,
page_sub_dupfix_leaf = P_SUBP | P_DUPFIX,
page_sub_broken = P_SUBP,
} page_type_t;
/* Common header for all page types. The page type depends on flags.
*
* P_BRANCH and P_LEAF pages have unsorted 'node_t's at the end, with
* sorted entries[] entries referring to them. Exception: P_DUPFIX pages
* omit entries and pack sorted MDBX_DUPFIXED values after the page header.
*
* P_LARGE records occupy one or more contiguous pages where only the
* first has a page header. They hold the real data of N_BIG nodes.
*
* P_SUBP sub-pages are small leaf "pages" with duplicate data.
* A node with flag N_DUP but not N_TREE contains a sub-page.
* (Duplicate data can also go in tables, which use normal pages.)
*
* P_META pages contain meta_t, the start point of an MDBX snapshot.
*
* Each non-metapage up to meta_t.mm_last_pg is reachable exactly once
* in the snapshot: Either used by a database or listed in a GC record. */
typedef struct page {
uint64_t txnid; /* txnid which created page, maybe zero in legacy DB */
uint16_t dupfix_ksize; /* key size if this is a DUPFIX page */
uint16_t flags;
union {
uint32_t pages; /* number of overflow pages */
__anonymous_struct_extension__ struct {
indx_t lower; /* lower bound of free space */
indx_t upper; /* upper bound of free space */
};
};
pgno_t pgno; /* page number */
#if FLEXIBLE_ARRAY_MEMBERS
indx_t entries[] /* dynamic size */;
#endif /* FLEXIBLE_ARRAY_MEMBERS */
} page_t;
/* Size of the page header, excluding dynamic data at the end */
#define PAGEHDRSZ 20u
/* Header for a single key/data pair within a page.
* Used in pages of type P_BRANCH and P_LEAF without P_DUPFIX.
* We guarantee 2-byte alignment for 'node_t's.
*
* Leaf node flags describe node contents. N_BIG says the node's
* data part is the page number of an overflow page with actual data.
* N_DUP and N_TREE can be combined giving duplicate data in
* a sub-page/table, and named databases (just N_TREE). */
typedef struct node {
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
union {
uint32_t dsize;
uint32_t child_pgno;
};
uint8_t flags; /* see node_flags */
uint8_t extra;
uint16_t ksize; /* key size */
#else
uint16_t ksize; /* key size */
uint8_t extra;
uint8_t flags; /* see node_flags */
union {
uint32_t child_pgno;
uint32_t dsize;
};
#endif /* __BYTE_ORDER__ */
#if FLEXIBLE_ARRAY_MEMBERS
uint8_t payload[] /* key and data are appended here */;
#endif /* FLEXIBLE_ARRAY_MEMBERS */
} node_t;
/* Size of the node header, excluding dynamic data at the end */
#define NODESIZE 8u
typedef enum node_flags {
N_BIG = 0x01 /* data put on large page */,
N_TREE = 0x02 /* data is a b-tree */,
N_DUP = 0x04 /* data has duplicates */
} node_flags_t;
#pragma pack(pop)
MDBX_MAYBE_UNUSED MDBX_NOTHROW_PURE_FUNCTION static inline uint8_t page_type(const page_t *mp) { return mp->flags; }
MDBX_MAYBE_UNUSED MDBX_NOTHROW_PURE_FUNCTION static inline uint8_t page_type_compat(const page_t *mp) {
/* Drop legacy P_DIRTY flag for sub-pages for compatilibity,
* for assertions only. */
return unlikely(mp->flags & P_SUBP) ? mp->flags & ~(P_SUBP | P_LEGACY_DIRTY) : mp->flags;
}
MDBX_MAYBE_UNUSED MDBX_NOTHROW_PURE_FUNCTION static inline bool is_leaf(const page_t *mp) {
return (mp->flags & P_LEAF) != 0;
}
MDBX_MAYBE_UNUSED MDBX_NOTHROW_PURE_FUNCTION static inline bool is_dupfix_leaf(const page_t *mp) {
return (mp->flags & P_DUPFIX) != 0;
}
MDBX_MAYBE_UNUSED MDBX_NOTHROW_PURE_FUNCTION static inline bool is_branch(const page_t *mp) {
return (mp->flags & P_BRANCH) != 0;
}
MDBX_MAYBE_UNUSED MDBX_NOTHROW_PURE_FUNCTION static inline bool is_largepage(const page_t *mp) {
return (mp->flags & P_LARGE) != 0;
}
MDBX_MAYBE_UNUSED MDBX_NOTHROW_PURE_FUNCTION static inline bool is_subpage(const page_t *mp) {
return (mp->flags & P_SUBP) != 0;
}

289
src/layout-lck.h Normal file
View File

@ -0,0 +1,289 @@
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \note Please refer to the COPYRIGHT file for explanations license change,
/// credits and acknowledgments.
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
#pragma once
#include "essentials.h"
/* The version number for a database's lockfile format. */
#define MDBX_LOCK_VERSION 6
#if MDBX_LOCKING == MDBX_LOCKING_WIN32FILES
#define MDBX_LCK_SIGN UINT32_C(0xF10C)
typedef void osal_ipclock_t;
#elif MDBX_LOCKING == MDBX_LOCKING_SYSV
#define MDBX_LCK_SIGN UINT32_C(0xF18D)
typedef mdbx_pid_t osal_ipclock_t;
#elif MDBX_LOCKING == MDBX_LOCKING_POSIX2001 || MDBX_LOCKING == MDBX_LOCKING_POSIX2008
#define MDBX_LCK_SIGN UINT32_C(0x8017)
typedef pthread_mutex_t osal_ipclock_t;
#elif MDBX_LOCKING == MDBX_LOCKING_POSIX1988
#define MDBX_LCK_SIGN UINT32_C(0xFC29)
typedef sem_t osal_ipclock_t;
#else
#error "FIXME"
#endif /* MDBX_LOCKING */
/* Статистика профилирования работы GC */
typedef struct gc_prof_stat {
/* Монотонное время по "настенным часам"
* затраченное на чтение и поиск внутри GC */
uint64_t rtime_monotonic;
/* Процессорное время в режим пользователя
* на подготовку страниц извлекаемых из GC, включая подкачку с диска. */
uint64_t xtime_cpu;
/* Количество итераций чтения-поиска внутри GC при выделении страниц */
uint32_t rsteps;
/* Количество запросов на выделение последовательностей страниц,
* т.е. когда запрашивает выделение больше одной страницы */
uint32_t xpages;
/* Счетчик выполнения по медленному пути (slow path execution count) */
uint32_t spe_counter;
/* page faults (hard page faults) */
uint32_t majflt;
/* Для разборок с pnl_merge() */
struct {
uint64_t time;
uint64_t volume;
uint32_t calls;
} pnl_merge;
} gc_prof_stat_t;
/* Statistics of pages operations for all transactions,
* including incomplete and aborted. */
typedef struct pgops {
mdbx_atomic_uint64_t newly; /* Quantity of a new pages added */
mdbx_atomic_uint64_t cow; /* Quantity of pages copied for update */
mdbx_atomic_uint64_t clone; /* Quantity of parent's dirty pages clones
for nested transactions */
mdbx_atomic_uint64_t split; /* Page splits */
mdbx_atomic_uint64_t merge; /* Page merges */
mdbx_atomic_uint64_t spill; /* Quantity of spilled dirty pages */
mdbx_atomic_uint64_t unspill; /* Quantity of unspilled/reloaded pages */
mdbx_atomic_uint64_t wops; /* Number of explicit write operations (not a pages) to a disk */
mdbx_atomic_uint64_t msync; /* Number of explicit msync/flush-to-disk operations */
mdbx_atomic_uint64_t fsync; /* Number of explicit fsync/flush-to-disk operations */
mdbx_atomic_uint64_t prefault; /* Number of prefault write operations */
mdbx_atomic_uint64_t mincore; /* Number of mincore() calls */
mdbx_atomic_uint32_t incoherence; /* number of https://libmdbx.dqdkfa.ru/dead-github/issues/269
caught */
mdbx_atomic_uint32_t reserved;
/* Статистика для профилирования GC.
* Логически эти данные, возможно, стоит вынести в другую структуру,
* но разница будет сугубо косметическая. */
struct {
/* Затраты на поддержку данных пользователя */
gc_prof_stat_t work;
/* Затраты на поддержку и обновления самой GC */
gc_prof_stat_t self;
/* Итераций обновления GC,
* больше 1 если были повторы/перезапуски */
uint32_t wloops;
/* Итерации слияния записей GC */
uint32_t coalescences;
/* Уничтожения steady-точек фиксации в MDBX_UTTERLY_NOSYNC */
uint32_t wipes;
/* Сбросы данные на диск вне MDBX_UTTERLY_NOSYNC */
uint32_t flushes;
/* Попытки пнуть тормозящих читателей */
uint32_t kicks;
} gc_prof;
} pgop_stat_t;
/* Reader Lock Table
*
* Readers don't acquire any locks for their data access. Instead, they
* simply record their transaction ID in the reader table. The reader
* mutex is needed just to find an empty slot in the reader table. The
* slot's address is saved in thread-specific data so that subsequent
* read transactions started by the same thread need no further locking to
* proceed.
*
* If MDBX_NOSTICKYTHREADS is set, the slot address is not saved in
* thread-specific data. No reader table is used if the database is on a
* read-only filesystem.
*
* Since the database uses multi-version concurrency control, readers don't
* actually need any locking. This table is used to keep track of which
* readers are using data from which old transactions, so that we'll know
* when a particular old transaction is no longer in use. Old transactions
* that have discarded any data pages can then have those pages reclaimed
* for use by a later write transaction.
*
* The lock table is constructed such that reader slots are aligned with the
* processor's cache line size. Any slot is only ever used by one thread.
* This alignment guarantees that there will be no contention or cache
* thrashing as threads update their own slot info, and also eliminates
* any need for locking when accessing a slot.
*
* A writer thread will scan every slot in the table to determine the oldest
* outstanding reader transaction. Any freed pages older than this will be
* reclaimed by the writer. The writer doesn't use any locks when scanning
* this table. This means that there's no guarantee that the writer will
* see the most up-to-date reader info, but that's not required for correct
* operation - all we need is to know the upper bound on the oldest reader,
* we don't care at all about the newest reader. So the only consequence of
* reading stale information here is that old pages might hang around a
* while longer before being reclaimed. That's actually good anyway, because
* the longer we delay reclaiming old pages, the more likely it is that a
* string of contiguous pages can be found after coalescing old pages from
* many old transactions together. */
/* The actual reader record, with cacheline padding. */
typedef struct reader_slot {
/* Current Transaction ID when this transaction began, or INVALID_TXNID.
* Multiple readers that start at the same time will probably have the
* same ID here. Again, it's not important to exclude them from
* anything; all we need to know is which version of the DB they
* started from so we can avoid overwriting any data used in that
* particular version. */
atomic_txnid_t txnid;
/* The information we store in a single slot of the reader table.
* In addition to a transaction ID, we also record the process and
* thread ID that owns a slot, so that we can detect stale information,
* e.g. threads or processes that went away without cleaning up.
*
* NOTE: We currently don't check for stale records.
* We simply re-init the table when we know that we're the only process
* opening the lock file. */
/* Псевдо thread_id для пометки вытесненных читающих транзакций. */
#define MDBX_TID_TXN_OUSTED (UINT64_MAX - 1)
/* Псевдо thread_id для пометки припаркованных читающих транзакций. */
#define MDBX_TID_TXN_PARKED UINT64_MAX
/* The thread ID of the thread owning this txn. */
mdbx_atomic_uint64_t tid;
/* The process ID of the process owning this reader txn. */
mdbx_atomic_uint32_t pid;
/* The number of pages used in the reader's MVCC snapshot,
* i.e. the value of meta->geometry.first_unallocated and
* txn->geo.first_unallocated */
atomic_pgno_t snapshot_pages_used;
/* Number of retired pages at the time this reader starts transaction. So,
* at any time the difference meta.pages_retired -
* reader.snapshot_pages_retired will give the number of pages which this
* reader restraining from reuse. */
mdbx_atomic_uint64_t snapshot_pages_retired;
} reader_slot_t;
/* The header for the reader table (a memory-mapped lock file). */
typedef struct shared_lck {
/* Stamp identifying this as an MDBX file.
* It must be set to MDBX_MAGIC with MDBX_LOCK_VERSION. */
uint64_t magic_and_version;
/* Format of this lock file. Must be set to MDBX_LOCK_FORMAT. */
uint32_t os_and_format;
/* Flags which environment was opened. */
mdbx_atomic_uint32_t envmode;
/* Threshold of un-synced-with-disk pages for auto-sync feature,
* zero means no-threshold, i.e. auto-sync is disabled. */
atomic_pgno_t autosync_threshold;
/* Low 32-bit of txnid with which meta-pages was synced,
* i.e. for sync-polling in the MDBX_NOMETASYNC mode. */
#define MDBX_NOMETASYNC_LAZY_UNK (UINT32_MAX / 3)
#define MDBX_NOMETASYNC_LAZY_FD (MDBX_NOMETASYNC_LAZY_UNK + UINT32_MAX / 8)
#define MDBX_NOMETASYNC_LAZY_WRITEMAP (MDBX_NOMETASYNC_LAZY_UNK - UINT32_MAX / 8)
mdbx_atomic_uint32_t meta_sync_txnid;
/* Period for timed auto-sync feature, i.e. at the every steady checkpoint
* the mti_unsynced_timeout sets to the current_time + autosync_period.
* The time value is represented in a suitable system-dependent form, for
* example clock_gettime(CLOCK_BOOTTIME) or clock_gettime(CLOCK_MONOTONIC).
* Zero means timed auto-sync is disabled. */
mdbx_atomic_uint64_t autosync_period;
/* Marker to distinguish uniqueness of DB/CLK. */
mdbx_atomic_uint64_t bait_uniqueness;
/* Paired counter of processes that have mlock()ed part of mmapped DB.
* The (mlcnt[0] - mlcnt[1]) > 0 means at least one process
* lock at least one page, so therefore madvise() could return EINVAL. */
mdbx_atomic_uint32_t mlcnt[2];
MDBX_ALIGNAS(MDBX_CACHELINE_SIZE) /* cacheline ----------------------------*/
/* Statistics of costly ops of all (running, completed and aborted)
* transactions */
pgop_stat_t pgops;
MDBX_ALIGNAS(MDBX_CACHELINE_SIZE) /* cacheline ----------------------------*/
#if MDBX_LOCKING > 0
/* Write transaction lock. */
osal_ipclock_t wrt_lock;
#endif /* MDBX_LOCKING > 0 */
atomic_txnid_t cached_oldest;
/* Timestamp of entering an out-of-sync state. Value is represented in a
* suitable system-dependent form, for example clock_gettime(CLOCK_BOOTTIME)
* or clock_gettime(CLOCK_MONOTONIC). */
mdbx_atomic_uint64_t eoos_timestamp;
/* Number un-synced-with-disk pages for auto-sync feature. */
mdbx_atomic_uint64_t unsynced_pages;
/* Timestamp of the last readers check. */
mdbx_atomic_uint64_t readers_check_timestamp;
/* Number of page which was discarded last time by madvise(DONTNEED). */
atomic_pgno_t discarded_tail;
/* Shared anchor for tracking readahead edge and enabled/disabled status. */
pgno_t readahead_anchor;
/* Shared cache for mincore() results */
struct {
pgno_t begin[4];
uint64_t mask[4];
} mincore_cache;
MDBX_ALIGNAS(MDBX_CACHELINE_SIZE) /* cacheline ----------------------------*/
#if MDBX_LOCKING > 0
/* Readeaders table lock. */
osal_ipclock_t rdt_lock;
#endif /* MDBX_LOCKING > 0 */
/* The number of slots that have been used in the reader table.
* This always records the maximum count, it is not decremented
* when readers release their slots. */
mdbx_atomic_uint32_t rdt_length;
mdbx_atomic_uint32_t rdt_refresh_flag;
#if FLEXIBLE_ARRAY_MEMBERS
MDBX_ALIGNAS(MDBX_CACHELINE_SIZE) /* cacheline ----------------------------*/
reader_slot_t rdt[] /* dynamic size */;
/* Lockfile format signature: version, features and field layout */
#define MDBX_LOCK_FORMAT \
(MDBX_LCK_SIGN * 27733 + (unsigned)sizeof(reader_slot_t) * 13 + \
(unsigned)offsetof(reader_slot_t, snapshot_pages_used) * 251 + (unsigned)offsetof(lck_t, cached_oldest) * 83 + \
(unsigned)offsetof(lck_t, rdt_length) * 37 + (unsigned)offsetof(lck_t, rdt) * 29)
#endif /* FLEXIBLE_ARRAY_MEMBERS */
} lck_t;
#define MDBX_LOCK_MAGIC ((MDBX_MAGIC << 8) + MDBX_LOCK_VERSION)
#define MDBX_READERS_LIMIT 32767

File diff suppressed because it is too large Load Diff

View File

@ -1,18 +1,7 @@
/*
* Copyright 2015-2021 Leonid Yuriev <leo@yuriev.ru>
* and other libmdbx authors: please see AUTHORS file.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted only as authorized by the OpenLDAP
* Public License.
*
* A copy of this license is available in the file LICENSE in the
* top-level directory of the distribution or, alternatively, at
* <http://www.OpenLDAP.org/license.html>.
*/
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
#if defined(_WIN32) || defined(_WIN64) /* Windows LCK-implementation */
#if defined(_WIN32) || defined(_WIN64)
/* PREAMBLE FOR WINDOWS:
*
@ -22,233 +11,188 @@
#include "internals.h"
static void mdbx_winnt_import(void);
#if MDBX_BUILD_SHARED_LIBRARY
#if MDBX_WITHOUT_MSVC_CRT && defined(NDEBUG)
/* DEBUG/CHECKED builds still require MSVC's CRT for runtime checks.
*
* Define dll's entry point only for Release build when NDEBUG is defined and
* MDBX_WITHOUT_MSVC_CRT=ON. if the entry point isn't defined then MSVC's will
* automatically use DllMainCRTStartup() from CRT library, which also
* automatically call DllMain() from our mdbx.dll */
#pragma comment(linker, "/ENTRY:DllMain")
#endif /* MDBX_WITHOUT_MSVC_CRT */
BOOL APIENTRY DllMain(HANDLE module, DWORD reason, LPVOID reserved)
#else
#if !MDBX_MANUAL_MODULE_HANDLER
static
#endif /* !MDBX_MANUAL_MODULE_HANDLER */
void NTAPI
mdbx_module_handler(PVOID module, DWORD reason, PVOID reserved)
#endif /* MDBX_BUILD_SHARED_LIBRARY */
{
(void)reserved;
switch (reason) {
case DLL_PROCESS_ATTACH:
mdbx_winnt_import();
mdbx_rthc_global_init();
break;
case DLL_PROCESS_DETACH:
mdbx_rthc_global_dtor();
break;
case DLL_THREAD_ATTACH:
break;
case DLL_THREAD_DETACH:
mdbx_rthc_thread_dtor(module);
break;
}
#if MDBX_BUILD_SHARED_LIBRARY
return TRUE;
#endif
}
#if !MDBX_BUILD_SHARED_LIBRARY && !MDBX_MANUAL_MODULE_HANDLER
/* *INDENT-OFF* */
/* clang-format off */
#if defined(_MSC_VER)
# pragma const_seg(push)
# pragma data_seg(push)
# ifdef _WIN64
/* kick a linker to create the TLS directory if not already done */
# pragma comment(linker, "/INCLUDE:_tls_used")
/* Force some symbol references. */
# pragma comment(linker, "/INCLUDE:mdbx_tls_anchor")
/* specific const-segment for WIN64 */
# pragma const_seg(".CRT$XLB")
const
# else
/* kick a linker to create the TLS directory if not already done */
# pragma comment(linker, "/INCLUDE:__tls_used")
/* Force some symbol references. */
# pragma comment(linker, "/INCLUDE:_mdbx_tls_anchor")
/* specific data-segment for WIN32 */
# pragma data_seg(".CRT$XLB")
# endif
__declspec(allocate(".CRT$XLB")) PIMAGE_TLS_CALLBACK mdbx_tls_anchor = mdbx_module_handler;
# pragma data_seg(pop)
# pragma const_seg(pop)
#elif defined(__GNUC__)
# ifdef _WIN64
const
# endif
PIMAGE_TLS_CALLBACK mdbx_tls_anchor __attribute__((__section__(".CRT$XLB"), used)) = mdbx_module_handler;
#else
# error FIXME
#endif
/* *INDENT-ON* */
/* clang-format on */
#endif /* !MDBX_BUILD_SHARED_LIBRARY && !MDBX_MANUAL_MODULE_HANDLER */
/*----------------------------------------------------------------------------*/
#define LCK_SHARED 0
#define LCK_EXCLUSIVE LOCKFILE_EXCLUSIVE_LOCK
#define LCK_WAITFOR 0
#define LCK_DONTWAIT LOCKFILE_FAIL_IMMEDIATELY
static __inline BOOL flock(mdbx_filehandle_t fd, DWORD flags, uint64_t offset,
size_t bytes) {
static int flock_with_event(HANDLE fd, HANDLE event, unsigned flags, size_t offset, size_t bytes) {
TRACE("lock>>: fd %p, event %p, flags 0x%x offset %zu, bytes %zu >>", fd, event, flags, offset, bytes);
OVERLAPPED ov;
ov.hEvent = 0;
ov.Internal = 0;
ov.InternalHigh = 0;
ov.hEvent = event;
ov.Offset = (DWORD)offset;
ov.OffsetHigh = HIGH_DWORD(offset);
return LockFileEx(fd, flags, 0, (DWORD)bytes, HIGH_DWORD(bytes), &ov);
if (LockFileEx(fd, flags, 0, (DWORD)bytes, HIGH_DWORD(bytes), &ov)) {
TRACE("lock<<: fd %p, event %p, flags 0x%x offset %zu, bytes %zu << %s", fd, event, flags, offset, bytes, "done");
return MDBX_SUCCESS;
}
DWORD rc = GetLastError();
if (rc == ERROR_IO_PENDING) {
if (event) {
if (GetOverlappedResult(fd, &ov, &rc, true)) {
TRACE("lock<<: fd %p, event %p, flags 0x%x offset %zu, bytes %zu << %s", fd, event, flags, offset, bytes,
"overlapped-done");
return MDBX_SUCCESS;
}
rc = GetLastError();
} else
CancelIo(fd);
}
TRACE("lock<<: fd %p, event %p, flags 0x%x offset %zu, bytes %zu << err %d", fd, event, flags, offset, bytes,
(int)rc);
return (int)rc;
}
static __inline BOOL funlock(mdbx_filehandle_t fd, uint64_t offset,
size_t bytes) {
return UnlockFile(fd, (DWORD)offset, HIGH_DWORD(offset), (DWORD)bytes,
HIGH_DWORD(bytes));
static inline int flock(HANDLE fd, unsigned flags, size_t offset, size_t bytes) {
return flock_with_event(fd, 0, flags, offset, bytes);
}
static inline int flock_data(const MDBX_env *env, unsigned flags, size_t offset, size_t bytes) {
const HANDLE fd4data = env->ioring.overlapped_fd ? env->ioring.overlapped_fd : env->lazy_fd;
return flock_with_event(fd4data, env->dxb_lock_event, flags, offset, bytes);
}
static int funlock(mdbx_filehandle_t fd, size_t offset, size_t bytes) {
TRACE("unlock: fd %p, offset %zu, bytes %zu", fd, offset, bytes);
return UnlockFile(fd, (DWORD)offset, HIGH_DWORD(offset), (DWORD)bytes, HIGH_DWORD(bytes)) ? MDBX_SUCCESS
: (int)GetLastError();
}
/*----------------------------------------------------------------------------*/
/* global `write` lock for write-txt processing,
* exclusive locking both meta-pages) */
#define LCK_MAXLEN (1u + ((~(size_t)0) >> 1))
#define LCK_META_OFFSET 0
#define LCK_META_LEN (MAX_PAGESIZE * NUM_METAS)
#define LCK_BODY_OFFSET LCK_META_LEN
#define LCK_BODY_LEN (LCK_MAXLEN - LCK_BODY_OFFSET)
#define LCK_BODY LCK_BODY_OFFSET, LCK_BODY_LEN
#define LCK_WHOLE 0, LCK_MAXLEN
#ifdef _WIN64
#define DXB_MAXLEN UINT64_C(0x7fffFFFFfff00000)
#else
#define DXB_MAXLEN UINT32_C(0x7ff00000)
#endif
#define DXB_BODY (env->ps * (size_t)NUM_METAS), DXB_MAXLEN
#define DXB_WHOLE 0, DXB_MAXLEN
int mdbx_txn_lock(MDBX_env *env, bool dontwait) {
int lck_txn_lock(MDBX_env *env, bool dontwait) {
if (dontwait) {
if (!TryEnterCriticalSection(&env->me_windowsbug_lock))
if (!TryEnterCriticalSection(&env->windowsbug_lock))
return MDBX_BUSY;
} else {
__try {
EnterCriticalSection(&env->me_windowsbug_lock);
}
__except ((GetExceptionCode() ==
0xC0000194 /* STATUS_POSSIBLE_DEADLOCK / EXCEPTION_POSSIBLE_DEADLOCK */)
EnterCriticalSection(&env->windowsbug_lock);
} __except ((GetExceptionCode() == 0xC0000194 /* STATUS_POSSIBLE_DEADLOCK / EXCEPTION_POSSIBLE_DEADLOCK */)
? EXCEPTION_EXECUTE_HANDLER
: EXCEPTION_CONTINUE_SEARCH) {
return ERROR_POSSIBLE_DEADLOCK;
return MDBX_EDEADLK;
}
}
if ((env->me_flags & MDBX_EXCLUSIVE) ||
flock(env->me_lazy_fd,
dontwait ? (LCK_EXCLUSIVE | LCK_DONTWAIT)
: (LCK_EXCLUSIVE | LCK_WAITFOR),
LCK_BODY))
eASSERT(env, !env->basal_txn->owner);
if (env->flags & MDBX_EXCLUSIVE)
goto done;
const HANDLE fd4data = env->ioring.overlapped_fd ? env->ioring.overlapped_fd : env->lazy_fd;
int rc = flock_with_event(fd4data, env->dxb_lock_event,
dontwait ? (LCK_EXCLUSIVE | LCK_DONTWAIT) : (LCK_EXCLUSIVE | LCK_WAITFOR), DXB_BODY);
if (rc == ERROR_LOCK_VIOLATION && dontwait) {
SleepEx(0, true);
rc = flock_with_event(fd4data, env->dxb_lock_event, LCK_EXCLUSIVE | LCK_DONTWAIT, DXB_BODY);
if (rc == ERROR_LOCK_VIOLATION) {
SleepEx(0, true);
rc = flock_with_event(fd4data, env->dxb_lock_event, LCK_EXCLUSIVE | LCK_DONTWAIT, DXB_BODY);
}
}
if (rc == MDBX_SUCCESS) {
done:
/* Zap: Failing to release lock 'env->windowsbug_lock'
* in function 'mdbx_txn_lock' */
MDBX_SUPPRESS_GOOFY_MSVC_ANALYZER(26115);
env->basal_txn->owner = osal_thread_self();
return MDBX_SUCCESS;
int rc = (int)GetLastError();
LeaveCriticalSection(&env->me_windowsbug_lock);
}
LeaveCriticalSection(&env->windowsbug_lock);
return (!dontwait || rc != ERROR_LOCK_VIOLATION) ? rc : MDBX_BUSY;
}
void mdbx_txn_unlock(MDBX_env *env) {
int rc = (env->me_flags & MDBX_EXCLUSIVE)
? TRUE
: funlock(env->me_lazy_fd, LCK_BODY);
LeaveCriticalSection(&env->me_windowsbug_lock);
if (!rc)
mdbx_panic("%s failed: err %u", __func__, (int)GetLastError());
void lck_txn_unlock(MDBX_env *env) {
eASSERT(env, env->basal_txn->owner == osal_thread_self());
if ((env->flags & MDBX_EXCLUSIVE) == 0) {
const HANDLE fd4data = env->ioring.overlapped_fd ? env->ioring.overlapped_fd : env->lazy_fd;
int err = funlock(fd4data, DXB_BODY);
if (err != MDBX_SUCCESS)
mdbx_panic("%s failed: err %u", __func__, err);
}
env->basal_txn->owner = 0;
LeaveCriticalSection(&env->windowsbug_lock);
}
/*----------------------------------------------------------------------------*/
/* global `read` lock for readers registration,
* exclusive locking `mti_numreaders` (second) cacheline */
* exclusive locking `rdt_length` (second) cacheline */
#define LCK_LO_OFFSET 0
#define LCK_LO_LEN offsetof(MDBX_lockinfo, mti_numreaders)
#define LCK_LO_LEN offsetof(lck_t, rdt_length)
#define LCK_UP_OFFSET LCK_LO_LEN
#define LCK_UP_LEN (sizeof(MDBX_lockinfo) - LCK_UP_OFFSET)
#define LCK_UP_LEN (sizeof(lck_t) - LCK_UP_OFFSET)
#define LCK_LOWER LCK_LO_OFFSET, LCK_LO_LEN
#define LCK_UPPER LCK_UP_OFFSET, LCK_UP_LEN
MDBX_INTERNAL_FUNC int mdbx_rdt_lock(MDBX_env *env) {
mdbx_srwlock_AcquireShared(&env->me_remap_guard);
if (env->me_lfd == INVALID_HANDLE_VALUE)
MDBX_INTERNAL int lck_rdt_lock(MDBX_env *env) {
imports.srwl_AcquireShared(&env->remap_guard);
if (env->lck_mmap.fd == INVALID_HANDLE_VALUE)
return MDBX_SUCCESS; /* readonly database in readonly filesystem */
/* transition from S-? (used) to S-E (locked),
* e.g. exclusive lock upper-part */
if ((env->me_flags & MDBX_EXCLUSIVE) ||
flock(env->me_lfd, LCK_EXCLUSIVE | LCK_WAITFOR, LCK_UPPER))
if (env->flags & MDBX_EXCLUSIVE)
return MDBX_SUCCESS;
int rc = (int)GetLastError();
mdbx_srwlock_ReleaseShared(&env->me_remap_guard);
int rc = flock(env->lck_mmap.fd, LCK_EXCLUSIVE | LCK_WAITFOR, LCK_UPPER);
if (rc == MDBX_SUCCESS)
return MDBX_SUCCESS;
imports.srwl_ReleaseShared(&env->remap_guard);
return rc;
}
MDBX_INTERNAL_FUNC void mdbx_rdt_unlock(MDBX_env *env) {
if (env->me_lfd != INVALID_HANDLE_VALUE) {
MDBX_INTERNAL void lck_rdt_unlock(MDBX_env *env) {
if (env->lck_mmap.fd != INVALID_HANDLE_VALUE && (env->flags & MDBX_EXCLUSIVE) == 0) {
/* transition from S-E (locked) to S-? (used), e.g. unlock upper-part */
if ((env->me_flags & MDBX_EXCLUSIVE) == 0 &&
!funlock(env->me_lfd, LCK_UPPER))
mdbx_panic("%s failed: err %u", __func__, (int)GetLastError());
int err = funlock(env->lck_mmap.fd, LCK_UPPER);
if (err != MDBX_SUCCESS)
mdbx_panic("%s failed: err %u", __func__, err);
}
mdbx_srwlock_ReleaseShared(&env->me_remap_guard);
imports.srwl_ReleaseShared(&env->remap_guard);
}
MDBX_INTERNAL_FUNC int mdbx_lockfile(mdbx_filehandle_t fd, bool wait) {
return flock(fd,
wait ? LCK_EXCLUSIVE | LCK_WAITFOR
: LCK_EXCLUSIVE | LCK_DONTWAIT,
0, LCK_MAXLEN)
? MDBX_SUCCESS
: (int)GetLastError();
MDBX_INTERNAL int osal_lockfile(mdbx_filehandle_t fd, bool wait) {
return flock(fd, wait ? LCK_EXCLUSIVE | LCK_WAITFOR : LCK_EXCLUSIVE | LCK_DONTWAIT, 0, DXB_MAXLEN);
}
static int suspend_and_append(mdbx_handle_array_t **array,
const DWORD ThreadId) {
static int suspend_and_append(mdbx_handle_array_t **array, const DWORD ThreadId) {
const unsigned limit = (*array)->limit;
if ((*array)->count == limit) {
void *ptr = mdbx_realloc(
(limit > ARRAY_LENGTH((*array)->handles))
? *array
: /* don't free initial array on the stack */ NULL,
sizeof(mdbx_handle_array_t) +
sizeof(HANDLE) * (limit * 2 - ARRAY_LENGTH((*array)->handles)));
mdbx_handle_array_t *const ptr = osal_realloc(
(limit > ARRAY_LENGTH((*array)->handles)) ? *array : /* don't free initial array on the stack */ nullptr,
sizeof(mdbx_handle_array_t) + sizeof(HANDLE) * (limit * (size_t)2 - ARRAY_LENGTH((*array)->handles)));
if (!ptr)
return MDBX_ENOMEM;
if (limit == ARRAY_LENGTH((*array)->handles))
memcpy(ptr, *array, sizeof(mdbx_handle_array_t));
*array = (mdbx_handle_array_t *)ptr;
*ptr = **array;
*array = ptr;
(*array)->limit = limit * 2;
}
HANDLE hThread = OpenThread(THREAD_SUSPEND_RESUME | THREAD_QUERY_INFORMATION,
FALSE, ThreadId);
if (hThread == NULL)
HANDLE hThread = OpenThread(THREAD_SUSPEND_RESUME | THREAD_QUERY_INFORMATION, FALSE, ThreadId);
if (hThread == nullptr)
return (int)GetLastError();
if (SuspendThread(hThread) == (DWORD)-1) {
int err = (int)GetLastError();
DWORD ExitCode;
if (err == /* workaround for Win10 UCRT bug */ ERROR_ACCESS_DENIED ||
!GetExitCodeThread(hThread, &ExitCode) || ExitCode != STILL_ACTIVE)
if (err == /* workaround for Win10 UCRT bug */ ERROR_ACCESS_DENIED || !GetExitCodeThread(hThread, &ExitCode) ||
ExitCode != STILL_ACTIVE)
err = MDBX_SUCCESS;
CloseHandle(hThread);
return err;
@ -258,31 +202,27 @@ static int suspend_and_append(mdbx_handle_array_t **array,
return MDBX_SUCCESS;
}
MDBX_INTERNAL_FUNC int
mdbx_suspend_threads_before_remap(MDBX_env *env, mdbx_handle_array_t **array) {
mdbx_assert(env, (env->me_flags & MDBX_NOTLS) == 0);
MDBX_INTERNAL int osal_suspend_threads_before_remap(MDBX_env *env, mdbx_handle_array_t **array) {
eASSERT(env, (env->flags & MDBX_NOSTICKYTHREADS) == 0);
const uintptr_t CurrentTid = GetCurrentThreadId();
int rc;
if (env->me_lck_mmap.lck) {
if (env->lck_mmap.lck) {
/* Scan LCK for threads of the current process */
const MDBX_reader *const begin = env->me_lck_mmap.lck->mti_readers;
const MDBX_reader *const end =
begin +
atomic_load32(&env->me_lck_mmap.lck->mti_numreaders, mo_AcquireRelease);
const uintptr_t WriteTxnOwner = env->me_txn0 ? env->me_txn0->mt_owner : 0;
for (const MDBX_reader *reader = begin; reader < end; ++reader) {
if (reader->mr_pid.weak != env->me_pid || !reader->mr_tid.weak) {
const reader_slot_t *const begin = env->lck_mmap.lck->rdt;
const reader_slot_t *const end = begin + atomic_load32(&env->lck_mmap.lck->rdt_length, mo_AcquireRelease);
const uintptr_t WriteTxnOwner = env->basal_txn ? env->basal_txn->owner : 0;
for (const reader_slot_t *reader = begin; reader < end; ++reader) {
if (reader->pid.weak != env->pid || !reader->tid.weak || reader->tid.weak >= MDBX_TID_TXN_OUSTED) {
skip_lck:
continue;
}
if (reader->mr_tid.weak == CurrentTid ||
reader->mr_tid.weak == WriteTxnOwner)
if (reader->tid.weak == CurrentTid || reader->tid.weak == WriteTxnOwner)
goto skip_lck;
rc = suspend_and_append(array, (mdbx_tid_t)reader->mr_tid.weak);
rc = suspend_and_append(array, (mdbx_tid_t)reader->tid.weak);
if (rc != MDBX_SUCCESS) {
bailout_lck:
(void)mdbx_resume_threads_after_remap(*array);
(void)osal_resume_threads_after_remap(*array);
return rc;
}
}
@ -294,7 +234,7 @@ mdbx_suspend_threads_before_remap(MDBX_env *env, mdbx_handle_array_t **array) {
} else {
/* Without LCK (i.e. read-only mode).
* Walk through a snapshot of all running threads */
mdbx_assert(env, env->me_flags & (MDBX_EXCLUSIVE | MDBX_RDONLY));
eASSERT(env, env->flags & (MDBX_EXCLUSIVE | MDBX_RDONLY));
const HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
if (hSnapshot == INVALID_HANDLE_VALUE)
return (int)GetLastError();
@ -306,13 +246,12 @@ mdbx_suspend_threads_before_remap(MDBX_env *env, mdbx_handle_array_t **array) {
rc = (int)GetLastError();
bailout_toolhelp:
CloseHandle(hSnapshot);
(void)mdbx_resume_threads_after_remap(*array);
(void)osal_resume_threads_after_remap(*array);
return rc;
}
do {
if (entry.th32OwnerProcessID != env->me_pid ||
entry.th32ThreadID == CurrentTid)
if (entry.th32OwnerProcessID != env->pid || entry.th32ThreadID == CurrentTid)
continue;
rc = suspend_and_append(array, entry.th32ThreadID);
@ -330,16 +269,15 @@ mdbx_suspend_threads_before_remap(MDBX_env *env, mdbx_handle_array_t **array) {
return MDBX_SUCCESS;
}
MDBX_INTERNAL_FUNC int
mdbx_resume_threads_after_remap(mdbx_handle_array_t *array) {
MDBX_INTERNAL int osal_resume_threads_after_remap(mdbx_handle_array_t *array) {
int rc = MDBX_SUCCESS;
for (unsigned i = 0; i < array->count; ++i) {
const HANDLE hThread = array->handles[i];
if (ResumeThread(hThread) == (DWORD)-1) {
const int err = (int)GetLastError();
DWORD ExitCode;
if (err != /* workaround for Win10 UCRT bug */ ERROR_ACCESS_DENIED &&
GetExitCodeThread(hThread, &ExitCode) && ExitCode == STILL_ACTIVE)
if (err != /* workaround for Win10 UCRT bug */ ERROR_ACCESS_DENIED && GetExitCodeThread(hThread, &ExitCode) &&
ExitCode == STILL_ACTIVE)
rc = err;
}
CloseHandle(hThread);
@ -360,6 +298,7 @@ mdbx_resume_threads_after_remap(mdbx_handle_array_t *array) {
* Only 6 states of FSM are used, which 2 of ones are transitive.
*
* States:
* LO HI
* ?-? = free, i.e. unlocked
* S-? = used, i.e. shared lock
* E-? = exclusive-read, i.e. operational exclusive
@ -370,56 +309,49 @@ mdbx_resume_threads_after_remap(mdbx_handle_array_t *array) {
* E-S
* E-E = exclusive-write, i.e. exclusive due (re)initialization
*
* The mdbx_lck_seize() moves the locking-FSM from the initial free/unlocked
* The lck_seize() moves the locking-FSM from the initial free/unlocked
* state to the "exclusive write" (and returns MDBX_RESULT_TRUE) if possible,
* or to the "used" (and returns MDBX_RESULT_FALSE).
*
* The mdbx_lck_downgrade() moves the locking-FSM from "exclusive write"
* The lck_downgrade() moves the locking-FSM from "exclusive write"
* state to the "used" (i.e. shared) state.
*
* The mdbx_lck_upgrade() moves the locking-FSM from "used" (i.e. shared)
* The lck_upgrade() moves the locking-FSM from "used" (i.e. shared)
* state to the "exclusive write" state.
*/
static void lck_unlock(MDBX_env *env) {
int err;
if (env->me_lfd != INVALID_HANDLE_VALUE) {
if (env->lck_mmap.fd != INVALID_HANDLE_VALUE) {
/* double `unlock` for robustly remove overlapped shared/exclusive locks */
while (funlock(env->me_lfd, LCK_LOWER))
;
err = (int)GetLastError();
assert(err == ERROR_NOT_LOCKED ||
(mdbx_RunningUnderWine() && err == ERROR_LOCK_VIOLATION));
(void)err;
do
err = funlock(env->lck_mmap.fd, LCK_LOWER);
while (err == MDBX_SUCCESS);
assert(err == ERROR_NOT_LOCKED || (globals.running_under_Wine && err == ERROR_LOCK_VIOLATION));
SetLastError(ERROR_SUCCESS);
while (funlock(env->me_lfd, LCK_UPPER))
;
err = (int)GetLastError();
assert(err == ERROR_NOT_LOCKED ||
(mdbx_RunningUnderWine() && err == ERROR_LOCK_VIOLATION));
(void)err;
do
err = funlock(env->lck_mmap.fd, LCK_UPPER);
while (err == MDBX_SUCCESS);
assert(err == ERROR_NOT_LOCKED || (globals.running_under_Wine && err == ERROR_LOCK_VIOLATION));
SetLastError(ERROR_SUCCESS);
}
if (env->me_lazy_fd != INVALID_HANDLE_VALUE) {
const HANDLE fd4data = env->ioring.overlapped_fd ? env->ioring.overlapped_fd : env->lazy_fd;
if (fd4data != INVALID_HANDLE_VALUE) {
/* explicitly unlock to avoid latency for other processes (windows kernel
* releases such locks via deferred queues) */
while (funlock(env->me_lazy_fd, LCK_BODY))
;
err = (int)GetLastError();
assert(err == ERROR_NOT_LOCKED ||
(mdbx_RunningUnderWine() && err == ERROR_LOCK_VIOLATION));
(void)err;
do
err = funlock(fd4data, DXB_BODY);
while (err == MDBX_SUCCESS);
assert(err == ERROR_NOT_LOCKED || (globals.running_under_Wine && err == ERROR_LOCK_VIOLATION));
SetLastError(ERROR_SUCCESS);
while (funlock(env->me_lazy_fd, LCK_WHOLE))
;
err = (int)GetLastError();
assert(err == ERROR_NOT_LOCKED ||
(mdbx_RunningUnderWine() && err == ERROR_LOCK_VIOLATION));
(void)err;
do
err = funlock(fd4data, DXB_WHOLE);
while (err == MDBX_SUCCESS);
assert(err == ERROR_NOT_LOCKED || (globals.running_under_Wine && err == ERROR_LOCK_VIOLATION));
SetLastError(ERROR_SUCCESS);
}
}
@ -428,180 +360,191 @@ static void lck_unlock(MDBX_env *env) {
* or as 'used' (S-? and returns MDBX_RESULT_FALSE).
* Otherwise returns an error. */
static int internal_seize_lck(HANDLE lfd) {
int rc;
assert(lfd != INVALID_HANDLE_VALUE);
/* 1) now on ?-? (free), get ?-E (middle) */
mdbx_jitter4testing(false);
if (!flock(lfd, LCK_EXCLUSIVE | LCK_WAITFOR, LCK_UPPER)) {
rc = (int)GetLastError() /* 2) something went wrong, give up */;
mdbx_error("%s, err %u", "?-?(free) >> ?-E(middle)", rc);
jitter4testing(false);
int rc = flock(lfd, LCK_EXCLUSIVE | LCK_WAITFOR, LCK_UPPER);
if (rc != MDBX_SUCCESS) {
/* 2) something went wrong, give up */;
ERROR("%s, err %u", "?-?(free) >> ?-E(middle)", rc);
return rc;
}
/* 3) now on ?-E (middle), try E-E (exclusive-write) */
mdbx_jitter4testing(false);
if (flock(lfd, LCK_EXCLUSIVE | LCK_DONTWAIT, LCK_LOWER))
jitter4testing(false);
rc = flock(lfd, LCK_EXCLUSIVE | LCK_DONTWAIT, LCK_LOWER);
if (rc == MDBX_SUCCESS)
return MDBX_RESULT_TRUE /* 4) got E-E (exclusive-write), done */;
/* 5) still on ?-E (middle) */
rc = (int)GetLastError();
mdbx_jitter4testing(false);
jitter4testing(false);
if (rc != ERROR_SHARING_VIOLATION && rc != ERROR_LOCK_VIOLATION) {
/* 6) something went wrong, give up */
if (!funlock(lfd, LCK_UPPER))
mdbx_panic("%s(%s) failed: err %u", __func__, "?-E(middle) >> ?-?(free)",
(int)GetLastError());
rc = funlock(lfd, LCK_UPPER);
if (rc != MDBX_SUCCESS)
mdbx_panic("%s(%s) failed: err %u", __func__, "?-E(middle) >> ?-?(free)", rc);
return rc;
}
/* 7) still on ?-E (middle), try S-E (locked) */
mdbx_jitter4testing(false);
rc = flock(lfd, LCK_SHARED | LCK_DONTWAIT, LCK_LOWER) ? MDBX_RESULT_FALSE
: (int)GetLastError();
jitter4testing(false);
rc = flock(lfd, LCK_SHARED | LCK_DONTWAIT, LCK_LOWER);
mdbx_jitter4testing(false);
if (rc != MDBX_RESULT_FALSE)
mdbx_error("%s, err %u", "?-E(middle) >> S-E(locked)", rc);
jitter4testing(false);
if (rc != MDBX_SUCCESS)
ERROR("%s, err %u", "?-E(middle) >> S-E(locked)", rc);
/* 8) now on S-E (locked) or still on ?-E (middle),
* transition to S-? (used) or ?-? (free) */
if (!funlock(lfd, LCK_UPPER))
mdbx_panic("%s(%s) failed: err %u", __func__,
"X-E(locked/middle) >> X-?(used/free)", (int)GetLastError());
int err = funlock(lfd, LCK_UPPER);
if (err != MDBX_SUCCESS)
mdbx_panic("%s(%s) failed: err %u", __func__, "X-E(locked/middle) >> X-?(used/free)", err);
/* 9) now on S-? (used, DONE) or ?-? (free, FAILURE) */
return rc;
}
MDBX_INTERNAL_FUNC int mdbx_lck_seize(MDBX_env *env) {
int rc;
assert(env->me_lazy_fd != INVALID_HANDLE_VALUE);
if (env->me_flags & MDBX_EXCLUSIVE)
MDBX_INTERNAL int lck_seize(MDBX_env *env) {
const HANDLE fd4data = env->ioring.overlapped_fd ? env->ioring.overlapped_fd : env->lazy_fd;
assert(fd4data != INVALID_HANDLE_VALUE);
if (env->flags & MDBX_EXCLUSIVE)
return MDBX_RESULT_TRUE /* nope since files were must be opened
non-shareable */
;
if (env->me_lfd == INVALID_HANDLE_VALUE) {
if (env->lck_mmap.fd == INVALID_HANDLE_VALUE) {
/* LY: without-lck mode (e.g. on read-only filesystem) */
mdbx_jitter4testing(false);
if (!flock(env->me_lazy_fd, LCK_SHARED | LCK_DONTWAIT, LCK_WHOLE)) {
rc = (int)GetLastError();
mdbx_error("%s, err %u", "without-lck", rc);
return rc;
}
return MDBX_RESULT_FALSE;
jitter4testing(false);
int rc = flock_data(env, LCK_SHARED | LCK_DONTWAIT, DXB_WHOLE);
if (rc != MDBX_SUCCESS)
ERROR("%s, err %u", "without-lck", rc);
return rc;
}
rc = internal_seize_lck(env->me_lfd);
mdbx_jitter4testing(false);
if (rc == MDBX_RESULT_TRUE && (env->me_flags & MDBX_RDONLY) == 0) {
int rc = internal_seize_lck(env->lck_mmap.fd);
jitter4testing(false);
if (rc == MDBX_RESULT_TRUE && (env->flags & MDBX_RDONLY) == 0) {
/* Check that another process don't operates in without-lck mode.
* Doing such check by exclusive locking the body-part of db. Should be
* noted:
* - we need an exclusive lock for do so;
* - we can't lock meta-pages, otherwise other process could get an error
* while opening db in valid (non-conflict) mode. */
if (!flock(env->me_lazy_fd, LCK_EXCLUSIVE | LCK_DONTWAIT, LCK_BODY)) {
rc = (int)GetLastError();
mdbx_error("%s, err %u", "lock-against-without-lck", rc);
mdbx_jitter4testing(false);
int err = flock_data(env, LCK_EXCLUSIVE | LCK_DONTWAIT, DXB_WHOLE);
if (err != MDBX_SUCCESS) {
ERROR("%s, err %u", "lock-against-without-lck", err);
jitter4testing(false);
lck_unlock(env);
} else {
mdbx_jitter4testing(false);
if (!funlock(env->me_lazy_fd, LCK_BODY))
mdbx_panic("%s(%s) failed: err %u", __func__,
"unlock-against-without-lck", (int)GetLastError());
return err;
}
jitter4testing(false);
err = funlock(fd4data, DXB_WHOLE);
if (err != MDBX_SUCCESS)
mdbx_panic("%s(%s) failed: err %u", __func__, "unlock-against-without-lck", err);
}
return rc;
}
MDBX_INTERNAL_FUNC int mdbx_lck_downgrade(MDBX_env *env) {
MDBX_INTERNAL int lck_downgrade(MDBX_env *env) {
const HANDLE fd4data = env->ioring.overlapped_fd ? env->ioring.overlapped_fd : env->lazy_fd;
/* Transite from exclusive-write state (E-E) to used (S-?) */
assert(env->me_lazy_fd != INVALID_HANDLE_VALUE);
assert(env->me_lfd != INVALID_HANDLE_VALUE);
assert(fd4data != INVALID_HANDLE_VALUE);
assert(env->lck_mmap.fd != INVALID_HANDLE_VALUE);
if (env->me_flags & MDBX_EXCLUSIVE)
if (env->flags & MDBX_EXCLUSIVE)
return MDBX_SUCCESS /* nope since files were must be opened non-shareable */
;
/* 1) now at E-E (exclusive-write), transition to ?_E (middle) */
if (!funlock(env->me_lfd, LCK_LOWER))
mdbx_panic("%s(%s) failed: err %u", __func__,
"E-E(exclusive-write) >> ?-E(middle)", (int)GetLastError());
int rc = funlock(env->lck_mmap.fd, LCK_LOWER);
if (rc != MDBX_SUCCESS)
mdbx_panic("%s(%s) failed: err %u", __func__, "E-E(exclusive-write) >> ?-E(middle)", rc);
/* 2) now at ?-E (middle), transition to S-E (locked) */
if (!flock(env->me_lfd, LCK_SHARED | LCK_DONTWAIT, LCK_LOWER)) {
int rc = (int)GetLastError() /* 3) something went wrong, give up */;
mdbx_error("%s, err %u", "?-E(middle) >> S-E(locked)", rc);
rc = flock(env->lck_mmap.fd, LCK_SHARED | LCK_DONTWAIT, LCK_LOWER);
if (rc != MDBX_SUCCESS) {
/* 3) something went wrong, give up */;
ERROR("%s, err %u", "?-E(middle) >> S-E(locked)", rc);
return rc;
}
/* 4) got S-E (locked), continue transition to S-? (used) */
if (!funlock(env->me_lfd, LCK_UPPER))
mdbx_panic("%s(%s) failed: err %u", __func__, "S-E(locked) >> S-?(used)",
(int)GetLastError());
rc = funlock(env->lck_mmap.fd, LCK_UPPER);
if (rc != MDBX_SUCCESS)
mdbx_panic("%s(%s) failed: err %u", __func__, "S-E(locked) >> S-?(used)", rc);
return MDBX_SUCCESS /* 5) now at S-? (used), done */;
}
MDBX_INTERNAL_FUNC int mdbx_lck_upgrade(MDBX_env *env) {
MDBX_INTERNAL int lck_upgrade(MDBX_env *env, bool dont_wait) {
/* Transite from used state (S-?) to exclusive-write (E-E) */
assert(env->me_lfd != INVALID_HANDLE_VALUE);
assert(env->lck_mmap.fd != INVALID_HANDLE_VALUE);
if (env->me_flags & MDBX_EXCLUSIVE)
if (env->flags & MDBX_EXCLUSIVE)
return MDBX_SUCCESS /* nope since files were must be opened non-shareable */
;
int rc;
/* 1) now on S-? (used), try S-E (locked) */
mdbx_jitter4testing(false);
if (!flock(env->me_lfd, LCK_EXCLUSIVE | LCK_DONTWAIT, LCK_UPPER)) {
rc = (int)GetLastError() /* 2) something went wrong, give up */;
mdbx_verbose("%s, err %u", "S-?(used) >> S-E(locked)", rc);
jitter4testing(false);
int rc = flock(env->lck_mmap.fd, dont_wait ? LCK_EXCLUSIVE | LCK_DONTWAIT : LCK_EXCLUSIVE, LCK_UPPER);
if (rc != MDBX_SUCCESS) {
/* 2) something went wrong, give up */;
VERBOSE("%s, err %u", "S-?(used) >> S-E(locked)", rc);
return rc;
}
/* 3) now on S-E (locked), transition to ?-E (middle) */
if (!funlock(env->me_lfd, LCK_LOWER))
mdbx_panic("%s(%s) failed: err %u", __func__, "S-E(locked) >> ?-E(middle)",
(int)GetLastError());
rc = funlock(env->lck_mmap.fd, LCK_LOWER);
if (rc != MDBX_SUCCESS)
mdbx_panic("%s(%s) failed: err %u", __func__, "S-E(locked) >> ?-E(middle)", rc);
/* 4) now on ?-E (middle), try E-E (exclusive-write) */
mdbx_jitter4testing(false);
if (!flock(env->me_lfd, LCK_EXCLUSIVE | LCK_DONTWAIT, LCK_LOWER)) {
rc = (int)GetLastError() /* 5) something went wrong, give up */;
mdbx_verbose("%s, err %u", "?-E(middle) >> E-E(exclusive-write)", rc);
jitter4testing(false);
rc = flock(env->lck_mmap.fd, dont_wait ? LCK_EXCLUSIVE | LCK_DONTWAIT : LCK_EXCLUSIVE, LCK_LOWER);
if (rc != MDBX_SUCCESS) {
/* 5) something went wrong, give up */;
VERBOSE("%s, err %u", "?-E(middle) >> E-E(exclusive-write)", rc);
return rc;
}
return MDBX_SUCCESS /* 6) now at E-E (exclusive-write), done */;
}
MDBX_INTERNAL_FUNC int mdbx_lck_init(MDBX_env *env,
MDBX_env *inprocess_neighbor,
int global_uniqueness_flag) {
MDBX_INTERNAL int lck_init(MDBX_env *env, MDBX_env *inprocess_neighbor, int global_uniqueness_flag) {
(void)env;
(void)inprocess_neighbor;
(void)global_uniqueness_flag;
if (imports.SetFileIoOverlappedRange && !(env->flags & MDBX_RDONLY)) {
HANDLE token = INVALID_HANDLE_VALUE;
TOKEN_PRIVILEGES privileges;
privileges.PrivilegeCount = 1;
privileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &token) ||
!LookupPrivilegeValue(nullptr, SE_LOCK_MEMORY_NAME, &privileges.Privileges[0].Luid) ||
!AdjustTokenPrivileges(token, FALSE, &privileges, sizeof(privileges), nullptr, nullptr) ||
GetLastError() != ERROR_SUCCESS)
imports.SetFileIoOverlappedRange = nullptr;
if (token != INVALID_HANDLE_VALUE)
CloseHandle(token);
}
return MDBX_SUCCESS;
}
MDBX_INTERNAL_FUNC int mdbx_lck_destroy(MDBX_env *env,
MDBX_env *inprocess_neighbor) {
MDBX_INTERNAL int lck_destroy(MDBX_env *env, MDBX_env *inprocess_neighbor, const uint32_t current_pid) {
(void)current_pid;
/* LY: should unmap before releasing the locks to avoid race condition and
* STATUS_USER_MAPPED_FILE/ERROR_USER_MAPPED_FILE */
if (env->me_map)
mdbx_munmap(&env->me_dxb_mmap);
if (env->me_lck_mmap.lck) {
const bool synced = env->me_lck_mmap.lck->mti_unsynced_pages.weak == 0;
mdbx_munmap(&env->me_lck_mmap);
if (synced && !inprocess_neighbor && env->me_lfd != INVALID_HANDLE_VALUE &&
mdbx_lck_upgrade(env) == MDBX_SUCCESS)
if (env->dxb_mmap.base)
osal_munmap(&env->dxb_mmap);
if (env->lck_mmap.lck) {
const bool synced = env->lck_mmap.lck->unsynced_pages.weak == 0;
osal_munmap(&env->lck_mmap);
if (synced && !inprocess_neighbor && env->lck_mmap.fd != INVALID_HANDLE_VALUE &&
lck_upgrade(env, true) == MDBX_SUCCESS)
/* this will fail if LCK is used/mmapped by other process(es) */
mdbx_ftruncate(env->me_lfd, 0);
osal_ftruncate(env->lck_mmap.fd, 0);
}
lck_unlock(env);
return MDBX_SUCCESS;
@ -610,12 +553,12 @@ MDBX_INTERNAL_FUNC int mdbx_lck_destroy(MDBX_env *env,
/*----------------------------------------------------------------------------*/
/* reader checking (by pid) */
MDBX_INTERNAL_FUNC int mdbx_rpid_set(MDBX_env *env) {
MDBX_INTERNAL int lck_rpid_set(MDBX_env *env) {
(void)env;
return MDBX_SUCCESS;
}
MDBX_INTERNAL_FUNC int mdbx_rpid_clear(MDBX_env *env) {
MDBX_INTERNAL int lck_rpid_clear(MDBX_env *env) {
(void)env;
return MDBX_SUCCESS;
}
@ -626,7 +569,7 @@ MDBX_INTERNAL_FUNC int mdbx_rpid_clear(MDBX_env *env) {
* MDBX_RESULT_TRUE, if pid is live (unable to acquire lock)
* MDBX_RESULT_FALSE, if pid is dead (lock acquired)
* or otherwise the errcode. */
MDBX_INTERNAL_FUNC int mdbx_rpid_check(MDBX_env *env, uint32_t pid) {
MDBX_INTERNAL int lck_rpid_check(MDBX_env *env, uint32_t pid) {
(void)env;
HANDLE hProcess = OpenProcess(SYNCHRONIZE, FALSE, pid);
int rc;
@ -659,150 +602,4 @@ MDBX_INTERNAL_FUNC int mdbx_rpid_check(MDBX_env *env, uint32_t pid) {
}
}
//----------------------------------------------------------------------------
// Stub for slim read-write lock
// Copyright (C) 1995-2002 Brad Wilson
static void WINAPI stub_srwlock_Init(MDBX_srwlock *srwl) {
srwl->readerCount = srwl->writerCount = 0;
}
static void WINAPI stub_srwlock_AcquireShared(MDBX_srwlock *srwl) {
while (true) {
assert(srwl->writerCount >= 0 && srwl->readerCount >= 0);
// If there's a writer already, spin without unnecessarily
// interlocking the CPUs
if (srwl->writerCount != 0) {
YieldProcessor();
continue;
}
// Add to the readers list
_InterlockedIncrement(&srwl->readerCount);
// Check for writers again (we may have been preempted). If
// there are no writers writing or waiting, then we're done.
if (srwl->writerCount == 0)
break;
// Remove from the readers list, spin, try again
_InterlockedDecrement(&srwl->readerCount);
YieldProcessor();
}
}
static void WINAPI stub_srwlock_ReleaseShared(MDBX_srwlock *srwl) {
assert(srwl->readerCount > 0);
_InterlockedDecrement(&srwl->readerCount);
}
static void WINAPI stub_srwlock_AcquireExclusive(MDBX_srwlock *srwl) {
while (true) {
assert(srwl->writerCount >= 0 && srwl->readerCount >= 0);
// If there's a writer already, spin without unnecessarily
// interlocking the CPUs
if (srwl->writerCount != 0) {
YieldProcessor();
continue;
}
// See if we can become the writer (expensive, because it inter-
// locks the CPUs, so writing should be an infrequent process)
if (_InterlockedExchange(&srwl->writerCount, 1) == 0)
break;
}
// Now we're the writer, but there may be outstanding readers.
// Spin until there aren't any more; new readers will wait now
// that we're the writer.
while (srwl->readerCount != 0) {
assert(srwl->writerCount >= 0 && srwl->readerCount >= 0);
YieldProcessor();
}
}
static void WINAPI stub_srwlock_ReleaseExclusive(MDBX_srwlock *srwl) {
assert(srwl->writerCount == 1 && srwl->readerCount >= 0);
srwl->writerCount = 0;
}
static uint64_t WINAPI stub_GetTickCount64(void) {
LARGE_INTEGER Counter, Frequency;
return (QueryPerformanceFrequency(&Frequency) &&
QueryPerformanceCounter(&Counter))
? Counter.QuadPart * 1000ul / Frequency.QuadPart
: 0;
}
/*----------------------------------------------------------------------------*/
#ifndef xMDBX_ALLOY
MDBX_srwlock_function mdbx_srwlock_Init, mdbx_srwlock_AcquireShared,
mdbx_srwlock_ReleaseShared, mdbx_srwlock_AcquireExclusive,
mdbx_srwlock_ReleaseExclusive;
MDBX_NtExtendSection mdbx_NtExtendSection;
MDBX_GetFileInformationByHandleEx mdbx_GetFileInformationByHandleEx;
MDBX_GetVolumeInformationByHandleW mdbx_GetVolumeInformationByHandleW;
MDBX_GetFinalPathNameByHandleW mdbx_GetFinalPathNameByHandleW;
MDBX_SetFileInformationByHandle mdbx_SetFileInformationByHandle;
MDBX_NtFsControlFile mdbx_NtFsControlFile;
MDBX_PrefetchVirtualMemory mdbx_PrefetchVirtualMemory;
MDBX_GetTickCount64 mdbx_GetTickCount64;
MDBX_RegGetValueA mdbx_RegGetValueA;
#endif /* xMDBX_ALLOY */
static void mdbx_winnt_import(void) {
const HINSTANCE hNtdll = GetModuleHandleA("ntdll.dll");
#define GET_PROC_ADDR(dll, ENTRY) \
mdbx_##ENTRY = (MDBX_##ENTRY)GetProcAddress(dll, #ENTRY)
if (GetProcAddress(hNtdll, "wine_get_version")) {
assert(mdbx_RunningUnderWine());
} else {
GET_PROC_ADDR(hNtdll, NtFsControlFile);
GET_PROC_ADDR(hNtdll, NtExtendSection);
assert(!mdbx_RunningUnderWine());
}
const HINSTANCE hKernel32dll = GetModuleHandleA("kernel32.dll");
GET_PROC_ADDR(hKernel32dll, GetFileInformationByHandleEx);
GET_PROC_ADDR(hKernel32dll, GetTickCount64);
if (!mdbx_GetTickCount64)
mdbx_GetTickCount64 = stub_GetTickCount64;
if (!mdbx_RunningUnderWine()) {
GET_PROC_ADDR(hKernel32dll, SetFileInformationByHandle);
GET_PROC_ADDR(hKernel32dll, GetVolumeInformationByHandleW);
GET_PROC_ADDR(hKernel32dll, GetFinalPathNameByHandleW);
GET_PROC_ADDR(hKernel32dll, PrefetchVirtualMemory);
}
const HINSTANCE hAdvapi32dll = GetModuleHandleA("advapi32.dll");
GET_PROC_ADDR(hAdvapi32dll, RegGetValueA);
#undef GET_PROC_ADDR
const MDBX_srwlock_function init =
(MDBX_srwlock_function)GetProcAddress(hKernel32dll, "InitializeSRWLock");
if (init != NULL) {
mdbx_srwlock_Init = init;
mdbx_srwlock_AcquireShared = (MDBX_srwlock_function)GetProcAddress(
hKernel32dll, "AcquireSRWLockShared");
mdbx_srwlock_ReleaseShared = (MDBX_srwlock_function)GetProcAddress(
hKernel32dll, "ReleaseSRWLockShared");
mdbx_srwlock_AcquireExclusive = (MDBX_srwlock_function)GetProcAddress(
hKernel32dll, "AcquireSRWLockExclusive");
mdbx_srwlock_ReleaseExclusive = (MDBX_srwlock_function)GetProcAddress(
hKernel32dll, "ReleaseSRWLockExclusive");
} else {
mdbx_srwlock_Init = stub_srwlock_Init;
mdbx_srwlock_AcquireShared = stub_srwlock_AcquireShared;
mdbx_srwlock_ReleaseShared = stub_srwlock_ReleaseShared;
mdbx_srwlock_AcquireExclusive = stub_srwlock_AcquireExclusive;
mdbx_srwlock_ReleaseExclusive = stub_srwlock_ReleaseExclusive;
}
}
#endif /* Windows LCK-implementation */
#endif /* Windows */

174
src/lck.c Normal file
View File

@ -0,0 +1,174 @@
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
#include "internals.h"
__cold static int lck_setup_locked(MDBX_env *env) {
int err = rthc_register(env);
if (unlikely(err != MDBX_SUCCESS))
return err;
int lck_seize_rc = lck_seize(env);
if (unlikely(MDBX_IS_ERROR(lck_seize_rc)))
return lck_seize_rc;
if (env->lck_mmap.fd == INVALID_HANDLE_VALUE) {
env->lck = lckless_stub(env);
env->max_readers = UINT_MAX;
DEBUG("lck-setup:%s%s%s", " lck-less", (env->flags & MDBX_RDONLY) ? " readonly" : "",
(lck_seize_rc == MDBX_RESULT_TRUE) ? " exclusive" : " cooperative");
return lck_seize_rc;
}
DEBUG("lck-setup:%s%s%s", " with-lck", (env->flags & MDBX_RDONLY) ? " readonly" : "",
(lck_seize_rc == MDBX_RESULT_TRUE) ? " exclusive" : " cooperative");
MDBX_env *inprocess_neighbor = nullptr;
err = rthc_uniq_check(&env->lck_mmap, &inprocess_neighbor);
if (unlikely(MDBX_IS_ERROR(err)))
return err;
if (inprocess_neighbor) {
if ((globals.runtime_flags & MDBX_DBG_LEGACY_MULTIOPEN) == 0 || (inprocess_neighbor->flags & MDBX_EXCLUSIVE) != 0)
return MDBX_BUSY;
if (lck_seize_rc == MDBX_RESULT_TRUE) {
err = lck_downgrade(env);
if (unlikely(err != MDBX_SUCCESS))
return err;
lck_seize_rc = MDBX_RESULT_FALSE;
}
}
uint64_t size = 0;
err = osal_filesize(env->lck_mmap.fd, &size);
if (unlikely(err != MDBX_SUCCESS))
return err;
if (lck_seize_rc == MDBX_RESULT_TRUE) {
size = ceil_powerof2(env->max_readers * sizeof(reader_slot_t) + sizeof(lck_t), globals.sys_pagesize);
jitter4testing(false);
} else {
if (env->flags & MDBX_EXCLUSIVE)
return MDBX_BUSY;
if (size > INT_MAX || (size & (globals.sys_pagesize - 1)) != 0 || size < globals.sys_pagesize) {
ERROR("lck-file has invalid size %" PRIu64 " bytes", size);
return MDBX_PROBLEM;
}
}
const size_t maxreaders = ((size_t)size - sizeof(lck_t)) / sizeof(reader_slot_t);
if (maxreaders < 4) {
ERROR("lck-size too small (up to %" PRIuPTR " readers)", maxreaders);
return MDBX_PROBLEM;
}
env->max_readers = (maxreaders <= MDBX_READERS_LIMIT) ? (unsigned)maxreaders : (unsigned)MDBX_READERS_LIMIT;
err =
osal_mmap((env->flags & MDBX_EXCLUSIVE) | MDBX_WRITEMAP, &env->lck_mmap, (size_t)size, (size_t)size,
lck_seize_rc ? MMAP_OPTION_TRUNCATE | MMAP_OPTION_SEMAPHORE : MMAP_OPTION_SEMAPHORE, env->pathname.lck);
if (unlikely(err != MDBX_SUCCESS))
return err;
#ifdef MADV_DODUMP
err = madvise(env->lck_mmap.lck, size, MADV_DODUMP) ? ignore_enosys_and_eagain(errno) : MDBX_SUCCESS;
if (unlikely(MDBX_IS_ERROR(err)))
return err;
#endif /* MADV_DODUMP */
#ifdef MADV_WILLNEED
err = madvise(env->lck_mmap.lck, size, MADV_WILLNEED) ? ignore_enosys_and_eagain(errno) : MDBX_SUCCESS;
if (unlikely(MDBX_IS_ERROR(err)))
return err;
#elif defined(POSIX_MADV_WILLNEED)
err = ignore_enosys(posix_madvise(env->lck_mmap.lck, size, POSIX_MADV_WILLNEED));
if (unlikely(MDBX_IS_ERROR(err)))
return err;
#endif /* MADV_WILLNEED */
lck_t *lck = env->lck_mmap.lck;
if (lck_seize_rc == MDBX_RESULT_TRUE) {
/* If we succeed got exclusive lock, then nobody is using the lock region
* and we should initialize it. */
memset(lck, 0, (size_t)size);
jitter4testing(false);
lck->magic_and_version = MDBX_LOCK_MAGIC;
lck->os_and_format = MDBX_LOCK_FORMAT;
#if MDBX_ENABLE_PGOP_STAT
lck->pgops.wops.weak = 1;
#endif /* MDBX_ENABLE_PGOP_STAT */
err = osal_msync(&env->lck_mmap, 0, (size_t)size, MDBX_SYNC_DATA | MDBX_SYNC_SIZE);
if (unlikely(err != MDBX_SUCCESS)) {
ERROR("initial-%s for lck-file failed, err %d", "msync/fsync", err);
eASSERT(env, MDBX_IS_ERROR(err));
return err;
}
} else {
if (lck->magic_and_version != MDBX_LOCK_MAGIC) {
const bool invalid = (lck->magic_and_version >> 8) != MDBX_MAGIC;
ERROR("lock region has %s", invalid ? "invalid magic"
: "incompatible version (only applications with nearly or the "
"same versions of libmdbx can share the same database)");
return invalid ? MDBX_INVALID : MDBX_VERSION_MISMATCH;
}
if (lck->os_and_format != MDBX_LOCK_FORMAT) {
ERROR("lock region has os/format signature 0x%" PRIx32 ", expected 0x%" PRIx32, lck->os_and_format,
MDBX_LOCK_FORMAT);
return MDBX_VERSION_MISMATCH;
}
}
err = lck_init(env, inprocess_neighbor, lck_seize_rc);
if (unlikely(err != MDBX_SUCCESS)) {
eASSERT(env, MDBX_IS_ERROR(err));
return err;
}
env->lck = lck;
eASSERT(env, !MDBX_IS_ERROR(lck_seize_rc));
return lck_seize_rc;
}
__cold int lck_setup(MDBX_env *env, mdbx_mode_t mode) {
eASSERT(env, env->lazy_fd != INVALID_HANDLE_VALUE);
eASSERT(env, env->lck_mmap.fd == INVALID_HANDLE_VALUE);
int err = osal_openfile(MDBX_OPEN_LCK, env, env->pathname.lck, &env->lck_mmap.fd, mode);
if (err != MDBX_SUCCESS) {
switch (err) {
case MDBX_EACCESS:
case MDBX_EPERM:
if (F_ISSET(env->flags, MDBX_RDONLY | MDBX_EXCLUSIVE))
break;
__fallthrough /* fall through */;
case MDBX_ENOFILE:
case MDBX_EROFS:
if (env->flags & MDBX_RDONLY) {
/* ENSURE the file system is read-only */
int err_rofs = osal_check_fs_rdonly(env->lazy_fd, env->pathname.lck, err);
if (err_rofs == MDBX_SUCCESS ||
/* ignore ERROR_NOT_SUPPORTED for exclusive mode */
(err_rofs == MDBX_ENOSYS && (env->flags & MDBX_EXCLUSIVE)))
break;
if (err_rofs != MDBX_ENOSYS)
err = err_rofs;
}
__fallthrough /* fall through */;
default:
ERROR("unable to open lck-file %" MDBX_PRIsPATH ", env-flags 0x%X, err %d", env->pathname.lck, env->flags, err);
return err;
}
/* LY: without-lck mode (e.g. exclusive or on read-only filesystem) */
env->lck_mmap.fd = INVALID_HANDLE_VALUE;
NOTICE("continue %" MDBX_PRIsPATH " within without-lck mode, env-flags 0x%X, lck-error %d", env->pathname.dxb,
env->flags, err);
}
rthc_lock();
err = lck_setup_locked(env);
rthc_unlock();
return err;
}
void mincore_clean_cache(const MDBX_env *const env) {
memset(env->lck->mincore_cache.begin, -1, sizeof(env->lck->mincore_cache.begin));
}

110
src/lck.h Normal file
View File

@ -0,0 +1,110 @@
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
#pragma once
#include "essentials.h"
MDBX_INTERNAL int lck_setup(MDBX_env *env, mdbx_mode_t mode);
#if MDBX_LOCKING > MDBX_LOCKING_SYSV
MDBX_INTERNAL int lck_ipclock_stubinit(osal_ipclock_t *ipc);
MDBX_INTERNAL int lck_ipclock_destroy(osal_ipclock_t *ipc);
#endif /* MDBX_LOCKING > MDBX_LOCKING_SYSV */
/// \brief Initialization of synchronization primitives linked with MDBX_env
/// instance both in LCK-file and within the current process.
/// \param
/// global_uniqueness_flag = true - denotes that there are no other processes
/// working with DB and LCK-file. Thus the function MUST initialize
/// shared synchronization objects in memory-mapped LCK-file.
/// global_uniqueness_flag = false - denotes that at least one process is
/// already working with DB and LCK-file, including the case when DB
/// has already been opened in the current process. Thus the function
/// MUST NOT initialize shared synchronization objects in memory-mapped
/// LCK-file that are already in use.
/// \return Error code or zero on success.
MDBX_INTERNAL int lck_init(MDBX_env *env, MDBX_env *inprocess_neighbor, int global_uniqueness_flag);
/// \brief Disconnects from shared interprocess objects and destructs
/// synchronization objects linked with MDBX_env instance
/// within the current process.
/// \param
/// inprocess_neighbor = nullptr - if the current process does not have other
/// instances of MDBX_env linked with the DB being closed.
/// Thus the function MUST check for other processes working with DB or
/// LCK-file, and keep or destroy shared synchronization objects in
/// memory-mapped LCK-file depending on the result.
/// inprocess_neighbor = not-nullptr - pointer to another instance of MDBX_env
/// (anyone of there is several) working with DB or LCK-file within the
/// current process. Thus the function MUST NOT try to acquire exclusive
/// lock and/or try to destruct shared synchronization objects linked with
/// DB or LCK-file. Moreover, the implementation MUST ensure correct work
/// of other instances of MDBX_env within the current process, e.g.
/// restore POSIX-fcntl locks after the closing of file descriptors.
/// \return Error code (MDBX_PANIC) or zero on success.
MDBX_INTERNAL int lck_destroy(MDBX_env *env, MDBX_env *inprocess_neighbor, const uint32_t current_pid);
/// \brief Connects to shared interprocess locking objects and tries to acquire
/// the maximum lock level (shared if exclusive is not available)
/// Depending on implementation or/and platform (Windows) this function may
/// acquire the non-OS super-level lock (e.g. for shared synchronization
/// objects initialization), which will be downgraded to OS-exclusive or
/// shared via explicit calling of lck_downgrade().
/// \return
/// MDBX_RESULT_TRUE (-1) - if an exclusive lock was acquired and thus
/// the current process is the first and only after the last use of DB.
/// MDBX_RESULT_FALSE (0) - if a shared lock was acquired and thus
/// DB has already been opened and now is used by other processes.
/// Otherwise (not 0 and not -1) - error code.
MDBX_INTERNAL int lck_seize(MDBX_env *env);
/// \brief Downgrades the level of initially acquired lock to
/// operational level specified by argument. The reason for such downgrade:
/// - unblocking of other processes that are waiting for access, i.e.
/// if (env->flags & MDBX_EXCLUSIVE) != 0, then other processes
/// should be made aware that access is unavailable rather than
/// wait for it.
/// - freeing locks that interfere file operation (especially for Windows)
/// (env->flags & MDBX_EXCLUSIVE) == 0 - downgrade to shared lock.
/// (env->flags & MDBX_EXCLUSIVE) != 0 - downgrade to exclusive
/// operational lock.
/// \return Error code or zero on success
MDBX_INTERNAL int lck_downgrade(MDBX_env *env);
MDBX_MAYBE_UNUSED MDBX_INTERNAL int lck_upgrade(MDBX_env *env, bool dont_wait);
/// \brief Locks LCK-file or/and table of readers for (de)registering.
/// \return Error code or zero on success
MDBX_INTERNAL int lck_rdt_lock(MDBX_env *env);
/// \brief Unlocks LCK-file or/and table of readers after (de)registering.
MDBX_INTERNAL void lck_rdt_unlock(MDBX_env *env);
/// \brief Acquires write-transaction lock.
/// \return Error code or zero on success
MDBX_INTERNAL int lck_txn_lock(MDBX_env *env, bool dont_wait);
/// \brief Releases write-transaction lock..
MDBX_INTERNAL void lck_txn_unlock(MDBX_env *env);
/// \brief Sets alive-flag of reader presence (indicative lock) for PID of
/// the current process. The function does no more than needed for
/// the correct working of lck_rpid_check() in other processes.
/// \return Error code or zero on success
MDBX_INTERNAL int lck_rpid_set(MDBX_env *env);
/// \brief Resets alive-flag of reader presence (indicative lock)
/// for PID of the current process. The function does no more than needed
/// for the correct working of lck_rpid_check() in other processes.
/// \return Error code or zero on success
MDBX_INTERNAL int lck_rpid_clear(MDBX_env *env);
/// \brief Checks for reading process status with the given pid with help of
/// alive-flag of presence (indicative lock) or using another way.
/// \return
/// MDBX_RESULT_TRUE (-1) - if the reader process with the given PID is alive
/// and working with DB (indicative lock is present).
/// MDBX_RESULT_FALSE (0) - if the reader process with the given PID is absent
/// or not working with DB (indicative lock is not present).
/// Otherwise (not 0 and not -1) - error code.
MDBX_INTERNAL int lck_rpid_check(MDBX_env *env, uint32_t pid);

250
src/logging_and_debug.c Normal file
View File

@ -0,0 +1,250 @@
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
#include "internals.h"
__cold void debug_log_va(int level, const char *function, int line, const char *fmt, va_list args) {
ENSURE(nullptr, osal_fastmutex_acquire(&globals.debug_lock) == 0);
if (globals.logger.ptr) {
if (globals.logger_buffer == nullptr)
globals.logger.fmt(level, function, line, fmt, args);
else {
const int len = vsnprintf(globals.logger_buffer, globals.logger_buffer_size, fmt, args);
if (len > 0)
globals.logger.nofmt(level, function, line, globals.logger_buffer, len);
}
} else {
#if defined(_WIN32) || defined(_WIN64)
if (IsDebuggerPresent()) {
int prefix_len = 0;
char *prefix = nullptr;
if (function && line > 0)
prefix_len = osal_asprintf(&prefix, "%s:%d ", function, line);
else if (function)
prefix_len = osal_asprintf(&prefix, "%s: ", function);
else if (line > 0)
prefix_len = osal_asprintf(&prefix, "%d: ", line);
if (prefix_len > 0 && prefix) {
OutputDebugStringA(prefix);
osal_free(prefix);
}
char *msg = nullptr;
int msg_len = osal_vasprintf(&msg, fmt, args);
if (msg_len > 0 && msg) {
OutputDebugStringA(msg);
osal_free(msg);
}
}
#else
if (function && line > 0)
fprintf(stderr, "%s:%d ", function, line);
else if (function)
fprintf(stderr, "%s: ", function);
else if (line > 0)
fprintf(stderr, "%d: ", line);
vfprintf(stderr, fmt, args);
fflush(stderr);
#endif
}
ENSURE(nullptr, osal_fastmutex_release(&globals.debug_lock) == 0);
}
__cold void debug_log(int level, const char *function, int line, const char *fmt, ...) {
va_list args;
va_start(args, fmt);
debug_log_va(level, function, line, fmt, args);
va_end(args);
}
__cold void log_error(const int err, const char *func, unsigned line) {
assert(err != MDBX_SUCCESS);
if (unlikely(globals.loglevel >= MDBX_LOG_DEBUG)) {
const bool is_error = err != MDBX_RESULT_TRUE && err != MDBX_NOTFOUND;
char buf[256];
debug_log(is_error ? MDBX_LOG_ERROR : MDBX_LOG_VERBOSE, func, line, "%s %d (%s)\n",
is_error ? "error" : "condition", err, mdbx_strerror_r(err, buf, sizeof(buf)));
}
}
/* Dump a val in ascii or hexadecimal. */
__cold const char *mdbx_dump_val(const MDBX_val *val, char *const buf, const size_t bufsize) {
if (!val)
return "<null>";
if (!val->iov_len)
return "<empty>";
if (!buf || bufsize < 4)
return nullptr;
if (!val->iov_base) {
int len = snprintf(buf, bufsize, "<nullptr.%zu>", val->iov_len);
assert(len > 0 && (size_t)len < bufsize);
(void)len;
return buf;
}
bool is_ascii = true;
const uint8_t *const data = val->iov_base;
for (size_t i = 0; i < val->iov_len; i++)
if (data[i] < ' ' || data[i] > '~') {
is_ascii = false;
break;
}
if (is_ascii) {
int len = snprintf(buf, bufsize, "%.*s", (val->iov_len > INT_MAX) ? INT_MAX : (int)val->iov_len, data);
assert(len > 0 && (size_t)len < bufsize);
(void)len;
} else {
char *const detent = buf + bufsize - 2;
char *ptr = buf;
*ptr++ = '<';
for (size_t i = 0; i < val->iov_len && ptr < detent; i++) {
const char hex[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
*ptr++ = hex[data[i] >> 4];
*ptr++ = hex[data[i] & 15];
}
if (ptr < detent)
*ptr++ = '>';
*ptr = '\0';
}
return buf;
}
/*------------------------------------------------------------------------------
LY: debug stuff */
__cold const char *pagetype_caption(const uint8_t type, char buf4unknown[16]) {
switch (type) {
case P_BRANCH:
return "branch";
case P_LEAF:
return "leaf";
case P_LEAF | P_SUBP:
return "subleaf";
case P_LEAF | P_DUPFIX:
return "dupfix-leaf";
case P_LEAF | P_DUPFIX | P_SUBP:
return "dupfix-subleaf";
case P_LEAF | P_DUPFIX | P_SUBP | P_LEGACY_DIRTY:
return "dupfix-subleaf.legacy-dirty";
case P_LARGE:
return "large";
default:
snprintf(buf4unknown, 16, "unknown_0x%x", type);
return buf4unknown;
}
}
__cold static const char *leafnode_type(node_t *n) {
static const char *const tp[2][2] = {{"", ": DB"}, {": sub-page", ": sub-DB"}};
return (node_flags(n) & N_BIG) ? ": large page" : tp[!!(node_flags(n) & N_DUP)][!!(node_flags(n) & N_TREE)];
}
/* Display all the keys in the page. */
__cold void page_list(page_t *mp) {
pgno_t pgno = mp->pgno;
const char *type;
node_t *node;
size_t i, nkeys, nsize, total = 0;
MDBX_val key;
DKBUF;
switch (page_type(mp)) {
case P_BRANCH:
type = "Branch page";
break;
case P_LEAF:
type = "Leaf page";
break;
case P_LEAF | P_SUBP:
type = "Leaf sub-page";
break;
case P_LEAF | P_DUPFIX:
type = "Leaf2 page";
break;
case P_LEAF | P_DUPFIX | P_SUBP:
type = "Leaf2 sub-page";
break;
case P_LARGE:
VERBOSE("Overflow page %" PRIaPGNO " pages %u\n", pgno, mp->pages);
return;
case P_META:
VERBOSE("Meta-page %" PRIaPGNO " txnid %" PRIu64 "\n", pgno, unaligned_peek_u64(4, page_meta(mp)->txnid_a));
return;
default:
VERBOSE("Bad page %" PRIaPGNO " flags 0x%X\n", pgno, mp->flags);
return;
}
nkeys = page_numkeys(mp);
VERBOSE("%s %" PRIaPGNO " numkeys %zu\n", type, pgno, nkeys);
for (i = 0; i < nkeys; i++) {
if (is_dupfix_leaf(mp)) { /* DUPFIX pages have no entries[] or node headers */
key = page_dupfix_key(mp, i, nsize = mp->dupfix_ksize);
total += nsize;
VERBOSE("key %zu: nsize %zu, %s\n", i, nsize, DKEY(&key));
continue;
}
node = page_node(mp, i);
key.iov_len = node_ks(node);
key.iov_base = node->payload;
nsize = NODESIZE + key.iov_len;
if (is_branch(mp)) {
VERBOSE("key %zu: page %" PRIaPGNO ", %s\n", i, node_pgno(node), DKEY(&key));
total += nsize;
} else {
if (node_flags(node) & N_BIG)
nsize += sizeof(pgno_t);
else
nsize += node_ds(node);
total += nsize;
nsize += sizeof(indx_t);
VERBOSE("key %zu: nsize %zu, %s%s\n", i, nsize, DKEY(&key), leafnode_type(node));
}
total = EVEN_CEIL(total);
}
VERBOSE("Total: header %u + contents %zu + unused %zu\n", is_dupfix_leaf(mp) ? PAGEHDRSZ : PAGEHDRSZ + mp->lower,
total, page_room(mp));
}
__cold static int setup_debug(MDBX_log_level_t level, MDBX_debug_flags_t flags, union logger_union logger, char *buffer,
size_t buffer_size) {
ENSURE(nullptr, osal_fastmutex_acquire(&globals.debug_lock) == 0);
const int rc = globals.runtime_flags | (globals.loglevel << 16);
if (level != MDBX_LOG_DONTCHANGE)
globals.loglevel = (uint8_t)level;
if (flags != MDBX_DBG_DONTCHANGE) {
flags &=
#if MDBX_DEBUG
MDBX_DBG_ASSERT | MDBX_DBG_AUDIT | MDBX_DBG_JITTER |
#endif
MDBX_DBG_DUMP | MDBX_DBG_LEGACY_MULTIOPEN | MDBX_DBG_LEGACY_OVERLAP | MDBX_DBG_DONT_UPGRADE;
globals.runtime_flags = (uint8_t)flags;
}
assert(MDBX_LOGGER_DONTCHANGE == ((MDBX_debug_func *)(intptr_t)-1));
if (logger.ptr != (void *)((intptr_t)-1)) {
globals.logger.ptr = logger.ptr;
globals.logger_buffer = buffer;
globals.logger_buffer_size = buffer_size;
}
ENSURE(nullptr, osal_fastmutex_release(&globals.debug_lock) == 0);
return rc;
}
__cold int mdbx_setup_debug_nofmt(MDBX_log_level_t level, MDBX_debug_flags_t flags, MDBX_debug_func_nofmt *logger,
char *buffer, size_t buffer_size) {
union logger_union thunk;
thunk.nofmt = (logger && buffer && buffer_size) ? logger : MDBX_LOGGER_NOFMT_DONTCHANGE;
return setup_debug(level, flags, thunk, buffer, buffer_size);
}
__cold int mdbx_setup_debug(MDBX_log_level_t level, MDBX_debug_flags_t flags, MDBX_debug_func *logger) {
union logger_union thunk;
thunk.fmt = logger;
return setup_debug(level, flags, thunk, nullptr, 0);
}

159
src/logging_and_debug.h Normal file
View File

@ -0,0 +1,159 @@
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
#pragma once
#include "essentials.h"
#ifndef __Wpedantic_format_voidptr
MDBX_MAYBE_UNUSED static inline const void *__Wpedantic_format_voidptr(const void *ptr) { return ptr; }
#define __Wpedantic_format_voidptr(ARG) __Wpedantic_format_voidptr(ARG)
#endif /* __Wpedantic_format_voidptr */
MDBX_INTERNAL void MDBX_PRINTF_ARGS(4, 5) debug_log(int level, const char *function, int line, const char *fmt, ...)
MDBX_PRINTF_ARGS(4, 5);
MDBX_INTERNAL void debug_log_va(int level, const char *function, int line, const char *fmt, va_list args);
#if MDBX_DEBUG
#define LOG_ENABLED(LVL) unlikely(LVL <= globals.loglevel)
#define AUDIT_ENABLED() unlikely((globals.runtime_flags & (unsigned)MDBX_DBG_AUDIT))
#else /* MDBX_DEBUG */
#define LOG_ENABLED(LVL) (LVL < MDBX_LOG_VERBOSE && LVL <= globals.loglevel)
#define AUDIT_ENABLED() (0)
#endif /* LOG_ENABLED() & AUDIT_ENABLED() */
#if MDBX_FORCE_ASSERTIONS
#define ASSERT_ENABLED() (1)
#elif MDBX_DEBUG
#define ASSERT_ENABLED() likely((globals.runtime_flags & (unsigned)MDBX_DBG_ASSERT))
#else
#define ASSERT_ENABLED() (0)
#endif /* ASSERT_ENABLED() */
#define DEBUG_EXTRA(fmt, ...) \
do { \
if (LOG_ENABLED(MDBX_LOG_EXTRA)) \
debug_log(MDBX_LOG_EXTRA, __func__, __LINE__, fmt, __VA_ARGS__); \
} while (0)
#define DEBUG_EXTRA_PRINT(fmt, ...) \
do { \
if (LOG_ENABLED(MDBX_LOG_EXTRA)) \
debug_log(MDBX_LOG_EXTRA, nullptr, 0, fmt, __VA_ARGS__); \
} while (0)
#define TRACE(fmt, ...) \
do { \
if (LOG_ENABLED(MDBX_LOG_TRACE)) \
debug_log(MDBX_LOG_TRACE, __func__, __LINE__, fmt "\n", __VA_ARGS__); \
} while (0)
#define DEBUG(fmt, ...) \
do { \
if (LOG_ENABLED(MDBX_LOG_DEBUG)) \
debug_log(MDBX_LOG_DEBUG, __func__, __LINE__, fmt "\n", __VA_ARGS__); \
} while (0)
#define VERBOSE(fmt, ...) \
do { \
if (LOG_ENABLED(MDBX_LOG_VERBOSE)) \
debug_log(MDBX_LOG_VERBOSE, __func__, __LINE__, fmt "\n", __VA_ARGS__); \
} while (0)
#define NOTICE(fmt, ...) \
do { \
if (LOG_ENABLED(MDBX_LOG_NOTICE)) \
debug_log(MDBX_LOG_NOTICE, __func__, __LINE__, fmt "\n", __VA_ARGS__); \
} while (0)
#define WARNING(fmt, ...) \
do { \
if (LOG_ENABLED(MDBX_LOG_WARN)) \
debug_log(MDBX_LOG_WARN, __func__, __LINE__, fmt "\n", __VA_ARGS__); \
} while (0)
#undef ERROR /* wingdi.h \
Yeah, morons from M$ put such definition to the public header. */
#define ERROR(fmt, ...) \
do { \
if (LOG_ENABLED(MDBX_LOG_ERROR)) \
debug_log(MDBX_LOG_ERROR, __func__, __LINE__, fmt "\n", __VA_ARGS__); \
} while (0)
#define FATAL(fmt, ...) debug_log(MDBX_LOG_FATAL, __func__, __LINE__, fmt "\n", __VA_ARGS__);
#if MDBX_DEBUG
#define ASSERT_FAIL(env, msg, func, line) mdbx_assert_fail(env, msg, func, line)
#else /* MDBX_DEBUG */
MDBX_NORETURN __cold void assert_fail(const char *msg, const char *func, unsigned line);
#define ASSERT_FAIL(env, msg, func, line) \
do { \
(void)(env); \
assert_fail(msg, func, line); \
} while (0)
#endif /* MDBX_DEBUG */
#define ENSURE_MSG(env, expr, msg) \
do { \
if (unlikely(!(expr))) \
ASSERT_FAIL(env, msg, __func__, __LINE__); \
} while (0)
#define ENSURE(env, expr) ENSURE_MSG(env, expr, #expr)
/* assert(3) variant in environment context */
#define eASSERT(env, expr) \
do { \
if (ASSERT_ENABLED()) \
ENSURE(env, expr); \
} while (0)
/* assert(3) variant in cursor context */
#define cASSERT(mc, expr) eASSERT((mc)->txn->env, expr)
/* assert(3) variant in transaction context */
#define tASSERT(txn, expr) eASSERT((txn)->env, expr)
#ifndef xMDBX_TOOLS /* Avoid using internal eASSERT() */
#undef assert
#define assert(expr) eASSERT(nullptr, expr)
#endif
MDBX_MAYBE_UNUSED static inline void jitter4testing(bool tiny) {
#if MDBX_DEBUG
if (globals.runtime_flags & (unsigned)MDBX_DBG_JITTER)
osal_jitter(tiny);
#else
(void)tiny;
#endif
}
MDBX_MAYBE_UNUSED MDBX_INTERNAL void page_list(page_t *mp);
MDBX_INTERNAL const char *pagetype_caption(const uint8_t type, char buf4unknown[16]);
/* Key size which fits in a DKBUF (debug key buffer). */
#define DKBUF_MAX 127
#define DKBUF char dbg_kbuf[DKBUF_MAX * 4 + 2]
#define DKEY(x) mdbx_dump_val(x, dbg_kbuf, DKBUF_MAX * 2 + 1)
#define DVAL(x) mdbx_dump_val(x, dbg_kbuf + DKBUF_MAX * 2 + 1, DKBUF_MAX * 2 + 1)
#if MDBX_DEBUG
#define DKBUF_DEBUG DKBUF
#define DKEY_DEBUG(x) DKEY(x)
#define DVAL_DEBUG(x) DVAL(x)
#else
#define DKBUF_DEBUG ((void)(0))
#define DKEY_DEBUG(x) ("-")
#define DVAL_DEBUG(x) ("-")
#endif
MDBX_INTERNAL void log_error(const int err, const char *func, unsigned line);
MDBX_MAYBE_UNUSED static inline int log_if_error(const int err, const char *func, unsigned line) {
if (unlikely(err != MDBX_SUCCESS))
log_error(err, func, line);
return err;
}
#define LOG_IFERR(err) log_if_error((err), __func__, __LINE__)

View File

@ -1,6 +1,6 @@
.\" Copyright 2015-2021 Leonid Yuriev <leo@yuriev.ru>.
.\" Copyright 2015-2025 Leonid Yuriev <leo@yuriev.ru>.
.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
.TH MDBX_CHK 1 "2021-05-09" "MDBX 0.10.0"
.TH MDBX_CHK 1 "2025-01-14" "MDBX 0.14"
.SH NAME
mdbx_chk \- MDBX checking tool
.SH SYNOPSIS
@ -22,12 +22,12 @@ mdbx_chk \- MDBX checking tool
[\c
.BR \-i ]
[\c
.BI \-s \ subdb\fR]
.BI \-s \ table\fR]
.BR \ dbpath
.SH DESCRIPTION
The
.B mdbx_chk
utility intended to check an MDBX database file.
utility is intended to check an MDBX database file.
.SH OPTIONS
.TP
.BR \-V
@ -55,7 +55,7 @@ check, including full check of all meta-pages and actual size of database file.
.BR \-w
Open environment in read-write mode and lock for writing while checking.
This could be impossible if environment already used by another process(s)
in an incompatible read-write mode. This allow rollback to last steady commit
in an incompatible read-write mode. This allows rollback to last steady commit
(in case environment was not closed properly) and then check transaction IDs
of meta-pages. Otherwise, without \fB\-w\fP option environment will be
opened in read-only mode.
@ -69,8 +69,8 @@ pages.
Ignore wrong order errors, which will likely false-positive if custom
comparator(s) was used.
.TP
.BR \-s \ subdb
Verify and show info only for a specific subdatabase.
.BR \-s \ table
Verify and show info only for a specific table.
.TP
.BR \-0 | \-1 | \-2
Using specific meta-page 0, or 2 for checking.
@ -81,9 +81,16 @@ Turn to a specified meta-page on successful check.
.BR \-T
Turn to a specified meta-page EVEN ON UNSUCCESSFUL CHECK!
.TP
.BR \-u
Warms up the DB before checking via notifying OS kernel of subsequent access to the database pages.
.TP
.BR \-U
Warms up the DB before checking, notifying the OS kernel of subsequent access to the database pages,
then forcibly loads ones by sequential access and tries to lock database pages in memory.
.TP
.BR \-n
Open MDBX environment(s) which do not use subdirectories.
This is legacy option. For now MDBX handles this automatically.
This is a legacy option. For now MDBX handles this automatically.
.SH DIAGNOSTICS
Exit status is zero if no errors occur. Errors result in a non-zero exit status
@ -96,4 +103,4 @@ if no quiet mode was requested.
.BR mdbx_load (1)
.BR mdbx_drop (1)
.SH AUTHOR
Leonid Yuriev <https://github.com/erthink>
Leonid Yuriev <https://gitflic.ru/user/erthink>

Some files were not shown because too many files have changed in this diff Show More