Compare commits

...

58 Commits

Author SHA1 Message Date
Леонид Юрьев (Leonid Yuriev)
4d58857f8f mdbx: release v0.13.8 "Всеобуч" (v`seabooch).
The supporting release of a stable branch with bug fixes,
on the day of the 100th anniversary of the Resolution of Russian Central Executive Committee on common free primary education.

 - enabled old Linux kernels starting from 3.16;
 - fixed unexpected `SIGBUS` is not enough space in a filesystem;
 - fixed inappropriate/irrelevant `MDBX_WANNA_RECOVERY` when the DB size is not rounded to sys-allocation-granularity.

For more information please see [ChangeLog](https://libmdbx.dqdkfa.ru/md__change_log.html).

git diff' stat: 9 files changed, 101 insertions(+), 36 deletions(-)
Signed-off-by: Леонид Юрьев (Leonid Yuriev) <leo@yuriev.ru>
2025-08-31 14:56:32 +03:00
Леонид Юрьев (Leonid Yuriev)
db389cde2a mdbx: update ChangeLog. 2025-08-29 11:08:35 +03:00
Леонид Юрьев (Leonid Yuriev)
43cb9133ee mdbx: fix inappropriate/irrelevant MDBX_WANNA_RECOVERY when the DB size is not rounded to sys-allocation-granularity.
The check corrected by this commit was never fundamentally important,
but it was added to verify the logic of rounding the database size in
various cases.
However, after the commit `2930b304dc674bbccd188b7ce7c3f83755ef706e`
(using `fallocate()` to prevent `SIGBUS`), the behavior change led to
the returning error `MDBX_WANNA_RECOVERY` in fairly harmless conditions.

This fix is not perfect, but it is just a patch that eliminates
regression by making the least risky/invasive changes.

A complete fix involves refining the rounding of a database size, which
is not difficult, but it is likely to lead to a few minor unexpected
regressions. So for the `stable` branch, I preferred minimal
conservative patch.
2025-08-28 10:50:00 +03:00
Леонид Юрьев (Leonid Yuriev)
71df2ec129 mdbx-tests: fix silently/unclear failures of stochastic test due an errors from mdbx_chk. 2025-08-28 10:30:55 +03:00
Леонид Юрьев (Leonid Yuriev)
e2fb593504 mdbx: update ChangeLog. 2025-08-26 23:32:29 +03:00
Леонид Юрьев (Leonid Yuriev)
2930b304dc mdbx: fix unexpected SIGBUS is not enough space in a filesystem (backport, squashed).
On a modern Linux the allocation of space for a file can be deferred
and/or lazy, rather than when setting its length using `ftruncate()`.
The actual allocation of space occurs when writing to the corresponding
areas of the file, or when reading ones (in this case, the file system
fills these areas with zeros).

The specific behavior depends on the type of file system and the kernel
version, but the main thing is that possibilities currently are, when
setting the file size, just the instantaneous ability to allocate space
is checked, without any booking.

If the file system is running out of space, an `ENOSPC` error may occur
when processing (inside a OS kernel) a page fault when accessing one of
the added pages after the database has been enlarged. In this case, the
OS kernel has no other alternative but to send a `SIGBUS` signal to the
process.

This commit fixes the problem by adding the use of system calls to
explicitly allocate space for a given file size.
Related-to https://github.com/erigontech/erigon/issues/16709

This is a simple improvement, however which is complicated by the need
to take into account the availability of the appropriate system API and
handle non-fatal errors from file systems that do not support the
appropriate operations. Therefore, there is a risk of regressions in
unusual/rare situations, including when hosting databases on network
media.
2025-08-24 10:30:17 +03:00
Леонид Юрьев (Leonid Yuriev)
a39dfd99a7 mdbx: update ChangeLog. 2025-08-05 13:12:22 +03:00
Леонид Юрьев (Leonid Yuriev)
66d116c301 mdbx: enabling older Linux kernels starting from 3.16 (backport). 2025-08-05 13:03:30 +03:00
Леонид Юрьев (Leonid Yuriev)
f71b729759 mdbx: patch update for older versions of buildroot (backport). 2025-08-04 00:20:02 +03:00
Леонид Юрьев (Leonid Yuriev)
566b0f93c7 mdbx: release v0.13.7 "Дружба" (Friendship).
The supporting release of a stable branch with bug fixes and bug fixes,
on [International Friendship Day](https://www.un.org/ru/observances/friendship-day).

For the list of improvements and changes, please see [ChangeLog](https://libmdbx.dqdkfa.ru/md__change_log.html).

git diff' stat: 22 files changed, 682 insertions(+), 291 deletions(-)
Signed-off-by: Леонид Юрьев (Leonid Yuriev) <leo@yuriev.ru>
2025-07-30 11:44:04 +03:00
Леонид Юрьев (Leonid Yuriev)
df3a18d0cf mdbx: update ChangeLog (v0.3.17 release candidate). 2025-07-26 15:40:22 +03:00
Леонид Юрьев (Leonid Yuriev)
675b462601 mdbx-tests: random order of a parameterized tests inside the stochastic script (backport). 2025-07-26 15:40:04 +03:00
Леонид Юрьев (Leonid Yuriev)
7c990542e3 mdbx: *** using english for commit titles at the request of community ***.
At the request of several non-Russian-speaking developers, it was
decided to return to using English in the commit' brief-headers at least.
2025-07-26 14:57:43 +03:00
Леонид Юрьев (Leonid Yuriev)
9093435ce6 mdbx: ссылки на зеркала в документации (backport). 2025-07-26 14:54:26 +03:00
Леонид Юрьев (Leonid Yuriev)
7ef7a1f983 mdbx-tests: актуализация секции cross-qemu в GNUmakefile (backport). 2025-07-25 13:55:41 +03:00
Леонид Юрьев (Leonid Yuriev)
ac275d246b mdbx-tests: использование SysV-семафоров при cross-тестах посредством qemu (backport). 2025-07-25 13:55:41 +03:00
Леонид Юрьев (Leonid Yuriev)
d2f4697df3 mdbx-tests: явное NUMA-распределение в battery/tmux-тесте (backport). 2025-07-25 13:55:41 +03:00
Леонид Юрьев (Leonid Yuriev)
b107842c3d mdbx-tests: поддержка опции --numa # для привязки стохастического теста к NUMA-узлу (backport). 2025-07-25 13:55:41 +03:00
Леонид Юрьев (Leonid Yuriev)
cb428e613f mdbx: дополнение ChangeLog. 2025-07-20 20:46:33 +03:00
Леонид Юрьев (Leonid Yuriev)
5c69cb322a mdbx: переработка ошибок файловых блокировок в API копирования с устранением проблемы на OSX (backport).
На POSIX-платформах внутри API копирования используются файловый
блокировки `fcntl(F_SETLK)` и `flock()`, так как только совместное
использование обеспечивает блокировку на всех платформах и файловых
системах, включая NFS и SMB.

Однако, в зависимости от платформы, версии ядра ОС, типа файловой
системы, а в случае NFS/SMB также от удаленной стороны, используемые
системные файловые блокировки могут не работать или конфликтовать между
собой (в частности на OSX).

Поэтому в этом коммите реализуется более гибкий подход. Если кратко,
то допускается отказ одной из блокировок при успехе другой:

 - При успехе fcntl(F_SETLK) допускается EAGAIN/EWOULDBLOCK и EREMOTEIO от flock(),
   если целевой файл на не-локальной файловой системе, а также на не-Linux платформах,
   где одновременная блокировка может быть не разрешена fcntl(F_SETLK) и flock().

 - При успехе flock() допускается ENOTSUP и REMOTEIO от fcntl(F_SETLK),
   если целевой файл на не-локальной файловой системе.
2025-07-20 17:05:31 +03:00
Леонид Юрьев (Leonid Yuriev)
6318ca701a mdbx: добавление ignore_enosys_and_einval() и её использование для отказа от OFD-блокировок (backport). 2025-07-20 17:05:31 +03:00
Леонид Юрьев (Leonid Yuriev)
9c1f3aa4b5 mdbx: разделение ignore_enosys() и ignore_enosys_and_eagain() (backport-required). 2025-07-20 17:05:31 +03:00
Леонид Юрьев (Leonid Yuriev)
759d0442d4 mdbx-tests: дополнение extra/dbi (backport). 2025-07-19 21:56:11 +03:00
Леонид Юрьев (Leonid Yuriev)
6cb9305b31 mdbx: устранение возможности неверного возврата MDBX_DBS_FULL при открытии DBI-дескрипторов (backport).
В lockfree-пути открытия DBI-дескрипторов, при просмотре уже открытых
таблиц, пропускались элементы отличающиеся не только по имени, но также
и при несовпадении запрашиваемых флагов и актуальных флагов уже открытой
таблицы.

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

Спасибо [Артёму Воротникову](https://github.com/vorot93) за сообщение о проблеме!
2025-07-19 21:56:05 +03:00
Леонид Юрьев (Leonid Yuriev)
68f9dc18be mdbx-tests: дополнение extra/txn сценарием провоцирующим гонку за атрибуты MainDB-DBI (backport). 2025-07-19 21:56:05 +03:00
Леонид Юрьев (Leonid Yuriev)
f5aa804a2d mdbx: устранение неожиданной ошибки MDBX_BAD_DBI при гонках внутри процесса (backport).
Запуск читающих и пишущих транзакций взаимно не блокируется. Однако,
внутри одного процесса, DBI-хендлы и атрибуты таблиц используются
совместно всеми транзакциями (в рамках экземпляра среды работы с БД).
Поэтому после изменения атрибутов таблиц, в том числе при первоначальном
чтении актуальных атрибутов MainDB, может возникать состояние гонок при
одновременном старте нескольких транзакций.

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

Формально ошибка присутствовала начиная с коммита `e6af7d7c53428ca2892bcbf7eec1c2acee06fd44` от 2023-11-05.
Однако, до этого (исторически, как было унаследовано от LMDB)
отсутствовал какой-либо контроль смены атрибутов MainDB во время старта
и/или работы транзакций. Поэтому вместо возврата каких-либо ошибок
подобные состояние гонок и/или связанные с изменением атрибутов MainDB
оставались необработанными/незамеченными, либо проявлялись как редкие
неуловимые сбои пользовательских приложений.

Спасибо [Артёму Воротникову](https://github.com/vorot93) за сообщение о проблеме!
2025-07-19 21:55:52 +03:00
Леонид Юрьев (Leonid Yuriev)
7468ce6ada mdbx: исправление resurrect-after-fork при использовании SysV-семафоров (backport).
Ошибка/недоработка была с первой реализации resurrect-after-fork в
ноябре 2023, но оставалась не замеченной из-за отсутствия
CI-тестирования на платформе OSX/Mac (где нет поддержки разделяемых
мьютексов).
2025-07-16 23:18:28 +03:00
Леонид Юрьев (Leonid Yuriev)
decf34fd2b mdbx-make: изменение кол-ва внутренних итераций прогона тестов для уменьшения затрат на CI (backport). 2025-07-16 23:18:28 +03:00
Леонид Юрьев (Leonid Yuriev)
f1b933c541 mdbx: отключение MSVC предупреждений C5286 и C5287 (backport). 2025-07-16 23:18:28 +03:00
Леонид Юрьев (Leonid Yuriev)
230fbd64f5 mdbx: удаление/исправление лишних assert-проверок (backport, squashed). 2025-07-16 23:17:38 +03:00
Леонид Юрьев (Leonid Yuriev)
e8bfffc9f6 mdbx: исправление опечатки MDBX_ENOMEM (backport).
Вместо `MDBX_ENOMEM` был использован идентификатор `ENOMEM`,
что могло ломать сборку на не-POSIX/Windows платформах,
в зависимости от конфигурации и/или версии SDK.
2025-06-01 11:32:37 +03:00
Леонид Юрьев (Leonid Yuriev)
6abaa67eb8 mdbx: дополнение ChangeLog. 2025-05-21 14:22:49 +03:00
Леонид Юрьев (Leonid Yuriev)
266937e72d mdbx: перемещение и доработка проверки _FILE_OFFSET_BITS для Android (backport). 2025-05-21 14:20:09 +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)
ab2f661c97 mdbx: дополнение ChangeLog. 2025-04-20 00:01:59 +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)
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)
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)
8d0eceee9f mdbx: отключение использования copy_file_range() для ядер linux 5.3-5.18 включительно. 2025-04-07 05:28:16 +03:00
Леонид Юрьев (Leonid Yuriev)
56a6377622 mdbx: понижение уровня логирования для "skip update meta" (backport).
Спасибо [Илье Михееву](https://github.com/JkLondon) за сообщение о недочете.
2025-03-28 15:14:54 +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)
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
32 changed files with 951 additions and 337 deletions

View File

@@ -4,6 +4,164 @@ ChangeLog
English version [by liar Google](https://libmdbx-dqdkfa-ru.translate.goog/md__change_log.html?_x_tr_sl=ru&_x_tr_tl=en)
and [by Yandex](https://translated.turbopages.org/proxy_u/ru-en.en/https/libmdbx.dqdkfa.ru/md__change_log.html).
The source code is availale on [Gitflic](https://gitflic.ru/project/erthink/libmdbx) and mirrors on [abf.io](https://abf.io/erthink/libmdbx), [hub.mos.ru](https://hub.mos.ru/leo/libmdbx) and [Github](https://github.com/erthink/libmdbx).
Please use the `stable` branch or the latest release for production environment through stagging, but the `master` branch for development a derivative projects.
Donations are welcome to ETH `0xD104d8f8B2dC312aaD74899F83EBf3EEBDC1EA3A`.
Всё будет хорошо!
## v0.13.8 "Всеобуч" (v`seabooch) от 2025-08-31
Поддерживающий выпуск стабильной ветки с исправлением обнаруженных ошибок и устранением недочётов,
в день 100 летнего юбилея Постановления Всероссийского центрального исполнительного комитета о всеобщем бесплатном начальном образовании.
Благодарности:
- [Erigon](https://erigon.tech/) за спонсорство.
Исправления:
- Устранена возможность получения неожиданного `SIGBUS` из-за отложенного/ленивого выделение места в заполненной файловой системе после приращения файла БД.
Более подробное пояснение в комментарии коммита [`2930b304dc674bbccd188b7ce7c3f83755ef706e`](https://gitflic.ru/project/erthink/libmdbx/commit/2930b304dc674bbccd188b7ce7c3f83755ef706e).
Изменение поведения:
- Вновь включена/разрешена на старых ядрах Linux, начиная с версии 3.16, так как
сейчас уже нет причин отказываться от работы на 3.16 поддерживая при этом ядра 4.x,
и еще есть проекты (Isar, Isar-Community, Hive) которым требуется такая поддержка.
- Ошибка `MDBX_WANNA_RECOVERY` при открытии БД в режиме только-чтение теперь возвращается если размер БД не кратен размеру системной страницы,
но игнорируется не кратность размеру блока выделения виртуальной памяти.
Этим устраняется регресс, проявившейся вследствие изменения поведения после задействования
системного вызова `fallocate()` для предотвращения `SIGBUS` после приращения файла БД в заполненной файловой системе.
--------------------------------------------------------------------------------
## v0.13.7 "Дружба" (Friendship) от 2025-07-30.
Поддерживающий выпуск стабильной ветки с исправлением обнаруженных ошибок и устранением недочётов,
в [международный день дружбы](https://www.un.org/ru/observances/friendship-day).
Благодарности:
- [Erigon](https://erigon.tech/) за спонсорство.
- [Артёму Воротникову](https://github.com/vorot93) за сообщение об ошибках и тестировании [призязок для Rust](https://github.com/vorot93/libmdbx-rs).
Исправления:
- Устранена критическая ошибка в функционале `mdbx_env_resurrect_after_fork()` при использовании SysV-семафоров.
Проявлялась ошибка только после порождения дочернего процесса посредством `fork()` на фоне выполняющейся пишущей транзакции, что
приводило к неверной работе семафоров и далее к самым различным ошибкам, вплоть до повреждения БД. Проблема существовала начиная с появления
`mdbx_env_resurrect_after_fork()` и затрагивала OSX, а также POSIX-платформы при сборке с опцией `MDBX_LOCKING=5`.
- Устранена проблема в API копирования БД на отличных от Linux системах POSIX,
а также в некоторых случаях при расположении целевого файла на не-локальной файловой системе.
Проблема проявлялась в основном на OSX, возвратом ошибки `EWOULDBLOCK`/`EAGAIN` (35),
что обусловлено недочетом/конфликтом блокировок `fcntl(F_SETLK)` и `flock()` в ядре ОС.
Переработана обработка ошибок захвата файловых блокировок в API копирования на системах POSIX.
- Устранена ошибка приводившая к неожиданному возврату `MDBX_BAD_DBI` при одновременном старте нескольких транзакций внутри одного процесса после открытия БД.
- Устранена ошибка приводившая к неожиданному возврату `MDBX_DBS_FULL` при повторном открытии уже открытых таблиц и уже достигнутом лимите открытых DBI-дескрипторов.
- Исправлена ошибка сборки для платформы Android при явном определении `_FILE_OFFSET_BITS`.
- Исправлена ошибка использования `ENOMEM` вместо `MDBX_ENOMEM`.
Что могло ломать сборку на не-POSIX/Windows платформах, в зависимости от конфигурации и/или версии SDK.
- Поправлено либо удалено несколько неверных assert-проверок, из-за которых происходили падения отладочных сборок в специфических ситуациях.
Главным образом, в коде функций `txn_end()`, `txn_lock()` и `txn_unlock()` как на Windows, так и на POSIX.
- Устранены несущественные предупреждения MSVC. Отключены предупреждения `C5286` и `C5287`.
Прочие доработки:
- Доработана логика отказа от использования OFD-блокировок на POSIX-платформах.
Теперь кроме `EINVAL` учитываются дополнительные коды ошибок (`ENOSYS`, `ENOIMPL`, `ENOTSUP`, `ENOSUPP`, `EOPNOTSUPP`),
что позволит работать собранной библиотеке в некоторых случаях,
когда актуальное ядро/контейнер/эмулятор не поддерживает требуемых системных вызовов.
- В тестовый фреймворк добавлена поддержка опции --numa # для привязки стохастического теста к NUMA-узлу,
а в battery/tmux-скрипте добавлено явное распределение по NUMA-узлам, что существенно увеличило КПД
при тестировании на NUMA-машинах.
- В стохастическом скрипте реализован случайный порядок запуска отдельных тестов.
--------------------------------------------------------------------------------
## v0.13.6 "Бузина" от 2025-04-22.
Поддерживающий выпуск стабильной ветки с исправлением обнаруженных ошибок и устранением недочётов,
в память о погибшем украинском историке и писателе [Алесе Бузине](https://ru.ruwiki.ru/wiki/Бузина,_Олесь_Алексеевич).
Благодарности:
- [Erigon](https://erigon.tech/) за спонсорство.
- [Илье Михееву](https://t.me/IlyaMkhv) и команде [Erigon](https://github.com/erigontech) за сообщения о проблеме и тестирование.
- [Алексею Костюку (aka Keller)](https://t.me/keller18306) за сообщения о проблеме копирования на NFS.
Исправления:
- Устранён регресс при использовании курсоров для DBI=0 (aka GC/FreeDB) в читающих транзакциях.
После рефакторинга и ряда оптимизаций для завершения/гашения
курсоров в читающих и пишущих транзакций, стал использоваться общий код.
Причем за основу, был взят соответствующий фрагмент относящийся к
пишущим транзакциям, в которых пользователю не позволяется
использоваться курсоры для DBI=0 и поэтому эта итераций пропускалась.
В результате, при завершении читающих транзакциях, курсоры связанные с
DBI=0 не завершались должным образом, а при их повторном использовании
или явном закрытии после завершения читающей транзакции происходило
обращение к уже освобожденной памяти. Если же такие курсоры
отсоединялись или закрывались до завершения читающей транзакции, то
ошибка не имела шансов на проявление.
- Устранён регресс в виде ошибки `EAGAIN` при копировании БД на NFS и CIFS/SMB.
При доработках/развитии API в функции копирования был добавлен захват
файловой блокировки посредством как `fcntl()`, так и `flock()`. Однако,
в зависимости от версии локального ядра, версии удалённого сервера NFS и
опций монтирования, это могло приводить к возврату POSIX-ошибки `EAGAIN`
(`11` на большинстве платформ, включая Linux).
- Устранена ошибка merge/rebase внутри `mdbx_txn_release_all_cursors_ex()`,
что могло приводить к последующим неожиданным ошибкам `MDBX_EBADSIGN` и утечкам памяти.
Для проверки сценария дополнен соответствующий тест.
- Исправлена assert-проверка в пути завершения вложенных транзакций.
Для проверки сценария дополнен соответствующий тест.
- Устранена возможность возврата неожиданной ошибки `MDBX_BUSY` из `mdbx_txn_lock(dont_wait=false)`.
- Для совместимости с GCC 15.x в режиме C23 изменен порядок указания атрибутов функций.
Изменение поведения:
- При невозможности отвязки курсора от его текущей транзакции функция `mdbx_cursor_bind()`
теперь возвращает `MDBX_EINVAL` вместо `MDBX_BAD_TXN`.
Прочие доработки:
- Во избежание потенциальных проблем отключено использование `copy_file_range()` на ядрах Linux 5.3 - 5.18.
- Вброс `std::invalid_argument` теперь производится явным сообщением `MDBX_EINVAL`.
- Уточнен тип адреса для пожертвований.
Ethereum/ERC-20 позволяет перечислять не только ETH, но и другие валюты/токены, в том числе USDC.
- Дополнен тест курсоров extra/cursor-closing.
- В `NOTICE` обновлена информация о Github.
--------------------------------------------------------------------------------
## v0.13.5 "Труба" от 2025-03-21
Поддерживающий выпуск стабильной ветки с исправлением обнаруженных ошибок и устранением недочётов.
@@ -107,7 +265,7 @@ and [by Yandex](https://translated.turbopages.org/proxy_u/ru-en.en/https/libmdbx
поломать родительскую, сделав её продолжение невозможным. Если восстанавливать, то также следует «воскрешать» закрытые
курсоры, что неизбежно приведет к путанице, утечкам памяти и использованию после освобождения.
- В C++ API отменён вброс исключения при запросе транзакции у отсоединённого курсора посредством вывоза `mdbx::cursor::txn()`.
- В C++ API отменён вброс исключения при запросе транзакции у отсоединённого курсора посредством вызова `mdbx::cursor::txn()`.
Прочие доработки:

View File

@@ -384,8 +384,9 @@ endef
define uname2titer
case "$(UNAME)" in
CYGWIN*|MINGW*|MSYS*|Windows*) echo 2;;
Darwin*|Mach*) echo 2;;
*) echo 12;;
*) if [ -z "${CI}" ]; then echo 7; else echo 3; fi;;
esac
endef
@@ -809,17 +810,21 @@ endif
# Cross-compilation simple test
CROSS_LIST = \
mips64-linux-gnuabi64-gcc mips-linux-gnu-gcc \
hppa-linux-gnu-gcc s390x-linux-gnu-gcc \
powerpc64-linux-gnu-gcc powerpc-linux-gnu-gcc \
arm-linux-gnueabihf-gcc aarch64-linux-gnu-gcc
aarch64-linux-gnu-gcc \
arm-linux-gnueabihf-gcc \
hppa-linux-gnu-gcc \
mips64-linux-gnuabi64-gcc \
mips-linux-gnu-gcc \
powerpc64-linux-gnu-gcc\
riscv64-linux-gnu-gcc \
s390x-linux-gnu-gcc \
sh4-linux-gnu-gcc
## On Ubuntu Focal (22.04) with QEMU 6.2 (1:6.2+dfsg-2ubuntu6.6) & GCC 11.3 (11.3.0-1ubuntu1~22.04)
# sh4-linux-gnu-gcc - coredump (qemu mmap-troubles)
# sparc64-linux-gnu-gcc - coredump (qemu mmap-troubles, previously: qemu fails fcntl for F_SETLK/F_GETLK)
# alpha-linux-gnu-gcc - coredump (qemu mmap-troubles)
# risc64-linux-gnu-gcc - coredump (qemu qemu fails fcntl for F_SETLK/F_GETLK)
CROSS_LIST_NOQEMU = sh4-linux-gnu-gcc sparc64-linux-gnu-gcc alpha-linux-gnu-gcc riscv64-linux-gnu-gcc
## On Ubuntu Noble (24.04.2) with QEMU 8.2 (8.2.2+ds-0ubuntu1.7) & GCC 13.3.0 (Ubuntu 13.3.0-6ubuntu2~24.04)
# sparc64-linux-gnu-gcc - fails mmap/BAD_ADDRESS (previously: qemu-coredump sice mmap-troubles, qemu fails fcntl for F_SETLK/F_GETLK)
# alpha-linux-gnu-gcc - qemu-coredump (qemu mmap-troubles)
# powerpc-linux-gnu-gcc - fails mmap/BAD_ADDRESS (previously: qemu-coredump sice mmap-troubles, qemu fails fcntl for F_SETLK/F_GETLK)
CROSS_LIST_NOQEMU = sparc64-linux-gnu-gcc alpha-linux-gnu-gcc powerpc-linux-gnu-gcc
cross-gcc:
@echo ' Re-building by cross-compiler for: $(CROSS_LIST_NOQEMU) $(CROSS_LIST)'
@@ -841,7 +846,7 @@ cross-qemu:
$(QUIET)for CC in $(CROSS_LIST); do \
echo "===================== $$CC + qemu"; \
$(MAKE) IOARENA=false CXXSTD= clean && \
CC=$$CC CXX=$$(echo $$CC | $(SED) 's/-gcc/-g++/') EXE_LDFLAGS=-static MDBX_BUILD_OPTIONS="-DMDBX_SAFE4QEMU $(MDBX_BUILD_OPTIONS)" \
CC=$$CC CXX=$$(echo $$CC | $(SED) 's/-gcc/-g++/') EXE_LDFLAGS=-static MDBX_BUILD_OPTIONS="-DMDBX_LOCKING=5 -DMDBX_SAFE4QEMU $(MDBX_BUILD_OPTIONS)" \
$(MAKE) IOARENA=false smoke-singleprocess test-singleprocess || exit $$?; \
done

30
NOTICE
View File

@@ -8,16 +8,32 @@ 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 ETH `0xD104d8f8B2dC312aaD74899F83EBf3EEBDC1EA3A`.
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 original libmdbx source code
repository https://gitflic.ru/project/erthink/libmdbx
please refer to the COPYRIGHT file within libmdbx source.
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.
---
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.

View File

@@ -1,6 +1,6 @@
<!-- Required extensions: pymdownx.betterem, pymdownx.tilde, pymdownx.emoji, pymdownx.tasklist, pymdownx.superfences -->
> Please refer to the online [documentation](https://libmdbx.dqdkfa.ru)
> Please refer to the online [official libmdbx documentation site](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).
@@ -9,7 +9,7 @@
> [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.
> Donations are welcome to ETH `0xD104d8f8B2dC312aaD74899F83EBf3EEBDC1EA3A`.
> Donations are welcome to the Ethereum/ERC-20 `0xD104d8f8B2dC312aaD74899F83EBf3EEBDC1EA3A`.
> Всё будет хорошо!

View File

@@ -1,4 +1,4 @@
The source code is availale on [Gitflic](https://gitflic.ru/project/erthink/libmdbx).
The source code is availale on [Gitflic](https://gitflic.ru/project/erthink/libmdbx) and mirrors on [abf.io](https://abf.io/erthink/libmdbx), [hub.mos.ru](https://hub.mos.ru/leo/libmdbx) and [Github](https://github.com/erthink/libmdbx).
Donations are welcome to ETH `0xD104d8f8B2dC312aaD74899F83EBf3EEBDC1EA3A`.
Всё будет хорошо!

View File

@@ -1,7 +1,7 @@
From 49256dcd050fd0ee67860b7bc544dabe088d08e9 Mon Sep 17 00:00:00 2001
From f2f1f6e76c1538d044b552d9e7ecedc3433e6cd9 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: Fri, 14 Feb 2025 21:34:25 +0300
Date: Sun, 3 Aug 2025 23:59:11 +0300
Subject: [PATCH] package/libmdbx: new package (library/database).
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
@@ -15,7 +15,7 @@ This patch adds libmdbx:
in terms of reliability, features and performance.
- more information at https://libmdbx.dqdkfa.ru
The 0.13.4 "Sigma Boy" is stable release of _libmdbx_ branch with new superior features.
The 0.13.7 "Дружба" (Friendship) is stable release of _libmdbx_ branch with new superior features.
The complete ChangeLog: https://gitflic.ru/project/erthink/libmdbx/blob?file=ChangeLog.md
@@ -25,8 +25,8 @@ Signed-off-by: Леонид Юрьев (Leonid Yuriev) <leo@yuriev.ru>
package/Config.in | 1 +
package/libmdbx/Config.in | 45 ++++++++++++++++++++++++++++++++++++
package/libmdbx/libmdbx.hash | 6 +++++
package/libmdbx/libmdbx.mk | 42 +++++++++++++++++++++++++++++++++
5 files changed, 97 insertions(+)
package/libmdbx/libmdbx.mk | 41 ++++++++++++++++++++++++++++++++
5 files changed, 96 insertions(+)
create mode 100644 package/libmdbx/Config.in
create mode 100644 package/libmdbx/libmdbx.hash
create mode 100644 package/libmdbx/libmdbx.mk
@@ -110,35 +110,34 @@ index 0000000000..a9a4ac45c5
+ !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..202937e7be
index 0000000000..8c7efb184b
--- /dev/null
+++ b/package/libmdbx/libmdbx.hash
@@ -0,0 +1,6 @@
+# Hashes from: https://libmdbx.dqdkfa.ru/release/SHA256SUMS
+sha256 86df30ca2231c9b3ad71424bb829dca9041947f5539d4295030c653d4982c1be libmdbx-amalgamated-0.13.4.tar.xz
+sha256 d00c1287ec6bbc366363ccdd3eea97bd470ccb5cc102d56b341f84a9fba7e8e9 libmdbx-amalgamated-0.13.7.tar.xz
+
+# Locally calculated
+sha256 0d542e0c8804e39aa7f37eb00da5a762149dc682d7829451287e11b938e94594 LICENSE
+sha256 699a62986b6c8d31124646dffd4b15872c7d3bc5eecea5994edb1f5195df49d1 NOTICE
+sha256 651f71b46c6bb0046d2122df7f9def9cb24f4dc28c5b11cef059f66565cda30f NOTICE
diff --git a/package/libmdbx/libmdbx.mk b/package/libmdbx/libmdbx.mk
new file mode 100644
index 0000000000..a8a6f3dbdf
index 0000000000..bbb37f21a6
--- /dev/null
+++ b/package/libmdbx/libmdbx.mk
@@ -0,0 +1,42 @@
@@ -0,0 +1,41 @@
+################################################################################
+#
+# libmdbx
+#
+################################################################################
+
+LIBMDBX_VERSION = 0.13.4
+LIBMDBX_VERSION = 0.13.7
+LIBMDBX_SOURCE = libmdbx-amalgamated-$(LIBMDBX_VERSION).tar.xz
+LIBMDBX_SITE = https://libmdbx.dqdkfa.ru/release
+LIBMDBX_SUPPORTS_IN_SOURCE_BUILD = NO
+LIBMDBX_LICENSE = Apache-2.0
+LIBMDBX_LICENSE_FILES = LICENSE NOTICE
+LIBMDBX_REDISTRIBUTE = YES
+LIBMDBX_STRIP_COMPONENTS = 0
+LIBMDBX_INSTALL_STAGING = YES
+
@@ -169,5 +168,5 @@ index 0000000000..a8a6f3dbdf
+
+$(eval $(cmake-package))
--
2.48.1
2.50.1

View File

@@ -479,7 +479,7 @@ __cold static int copy_with_compacting(MDBX_env *env, MDBX_txn *txn, mdbx_fileha
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);
return osal_fallocate(fd, whole_size);
const size_t used_size = pgno2bytes(env, meta->geometry.first_unallocated);
memset(data_buffer, 0, (size_t)MDBX_ENVCOPY_WRITEBUF);
@@ -571,11 +571,17 @@ retry_snap_meta:
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;
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;
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;) {
@@ -597,7 +603,7 @@ retry_snap_meta:
continue;
}
rc = MDBX_ENODATA;
if (written == 0 || ignore_enosys(rc = errno) != MDBX_RESULT_TRUE)
if (written == 0 || ignore_enosys_and_eagain(rc = errno) != MDBX_RESULT_TRUE)
break;
sendfile_unavailable = true;
}
@@ -621,7 +627,7 @@ retry_snap_meta:
maybe useful for others FS */
EINVAL)
not_the_same_filesystem = true;
else if (ignore_enosys(rc) == MDBX_RESULT_TRUE)
else if (ignore_enosys_and_eagain(rc) == MDBX_RESULT_TRUE)
copyfilerange_unavailable = true;
else
break;
@@ -642,7 +648,7 @@ retry_snap_meta:
/* Extend file if required */
if (likely(rc == MDBX_SUCCESS) && whole_size != used_size) {
if (!dest_is_pipe)
rc = osal_ftruncate(fd, whole_size);
rc = osal_fallocate(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;) {
@@ -749,24 +755,66 @@ __cold static int copy2pathname(MDBX_txn *txn, const pathchar_t *dest_path, MDBX
S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP
#endif
);
if (unlikely(rc != MDBX_SUCCESS))
return rc;
#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)
#if (defined(__linux__) || defined(__gnu_linux__)) && defined(LOCK_EX) && \
(!defined(__ANDROID_API__) || __ANDROID_API__ >= 24)
|| flock(newfd, LOCK_EX | LOCK_NB)
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;
const int err_fcntl = MDBX_FCNTL(newfd, MDBX_F_SETLK, &lock_op) ? errno : MDBX_SUCCESS;
const int err_flock =
#ifdef LOCK_EX
flock(newfd, LOCK_EX | LOCK_NB) ? errno : MDBX_SUCCESS;
#else
MDBX_ENOSYS;
#endif /* LOCK_EX */
const int err_check_fs_local =
/* avoid call osal_check_fs_local() on success */
(!err_fcntl && !err_flock && !MDBX_DEBUG) ? MDBX_SUCCESS :
#if !defined(__ANDROID_API__) || __ANDROID_API__ >= 24
osal_check_fs_local(newfd, 0);
#else
MDBX_ENOSYS;
#endif
const bool flock_may_fail =
#if defined(__linux__) || defined(__gnu_linux__)
err_check_fs_local != 0;
#else
true;
#endif /* Linux */
)
rc = errno;
if (!err_fcntl &&
(err_flock == EWOULDBLOCK || err_flock == EAGAIN || ignore_enosys_and_eremote(err_flock) == MDBX_RESULT_TRUE)) {
rc = err_flock;
if (flock_may_fail) {
WARNING("ignore %s(%" MDBX_PRIsPATH ") error %d: since %s done, local/remote-fs check %d", "flock", dest_path,
err_flock, "fcntl-lock", err_check_fs_local);
rc = MDBX_SUCCESS;
}
} else if (!err_flock && err_check_fs_local == MDBX_RESULT_TRUE &&
ignore_enosys_and_eremote(err_fcntl) == MDBX_RESULT_TRUE) {
WARNING("ignore %s(%" MDBX_PRIsPATH ") error %d: since %s done, local/remote-fs check %d", "fcntl-lock", dest_path,
err_fcntl, "flock", err_check_fs_local);
} else if (err_fcntl || err_flock) {
ERROR("file-lock(%" MDBX_PRIsPATH ") failed: fcntl-lock %d, flock %d, local/remote-fs check %d", dest_path,
err_fcntl, err_flock, err_check_fs_local);
if (err_fcntl == ENOLCK || err_flock == ENOLCK)
rc = ENOLCK;
else if (err_fcntl == EWOULDBLOCK || err_flock == EWOULDBLOCK)
rc = EWOULDBLOCK;
else if (EWOULDBLOCK != EAGAIN && (err_fcntl == EAGAIN || err_flock == EAGAIN))
rc = EAGAIN;
else
rc = (err_fcntl && ignore_enosys_and_eremote(err_fcntl) != MDBX_RESULT_TRUE) ? err_fcntl : err_flock;
}
#endif /* Windows / POSIX */

View File

@@ -63,7 +63,7 @@ int mdbx_cursor_bind(MDBX_txn *txn, MDBX_cursor *mc, MDBX_dbi dbi) {
return MDBX_SUCCESS;
rc = mdbx_cursor_unbind(mc);
if (unlikely(rc != MDBX_SUCCESS))
return rc;
return (rc == MDBX_BAD_TXN) ? MDBX_EINVAL : rc;
}
cASSERT(mc, mc->next == mc);
@@ -88,8 +88,16 @@ int mdbx_cursor_unbind(MDBX_cursor *mc) {
return LOG_IFERR(MDBX_EINVAL);
int rc = check_txn(mc->txn, MDBX_TXN_FINISHED | MDBX_TXN_HAS_CHILD);
if (unlikely(rc != MDBX_SUCCESS))
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-дескриптор ещё
* не использовался во вложенной транзакции, т.е. курсор ещё не импортирован в дочернюю транзакцию и не имеет
* связанного сохранённого состояния (поэтому mc→backup равен 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);
@@ -244,9 +252,8 @@ int mdbx_txn_release_all_cursors_ex(const MDBX_txn *txn, bool unbind, size_t *co
MDBX_cursor *bk = mc->backup;
mc->next = bk->next;
mc->backup = bk->backup;
mc->backup = nullptr;
bk->backup = nullptr;
bk->signature = 0;
bk = bk->next;
osal_free(bk);
} else {
mc->signature = cur_signature_ready4dispose;

View File

@@ -208,10 +208,19 @@ __cold int mdbx_env_create(MDBX_env **penv) {
}
#if defined(__linux__) || defined(__gnu_linux__)
if (unlikely(globals.linux_kernel_version < 0x04000000)) {
/* 2022-09-01: Прошло уже более двух лет после окончания какой-либо
* поддержки самого "долгоиграющего" ядра 3.16.85 ветки 3.x */
ERROR("too old linux kernel %u.%u.%u.%u, the >= 4.0.0 is required", globals.linux_kernel_version >> 24,
if (unlikely(globals.linux_kernel_version < 0x03100000)) {
/* 2025-08-05: Ядро 3.16 выпущено 11 лет назад и было самым долго поддерживаемым из 3.x до июля 2020.
* Три года назад (в 2022) здесь была заблокирована работа на ядрах меньше 4.x, как устаревших и для которых
* крайне затруднительно обеспечить какое-либо тестирование. Теперь же я решил изменить решение и разрешить
* работу на старых ядрах начиная с 3.16, логика тут такая:
* - поведение старых ядер уже точно не будет меняться,
* а в текущем коде libmdbx есть всё необходимое для работы начиная с 3.16;
* - есть широко-используемые проекты (Isar), которым требуется поддержка старых ядер;
* - сейчас тестирование для 4.x также затруднено, как и для 3.16, уже не приносит какого-либо облегчения
* с тестированием и мне приходится полагаться на гарантии совместимости API ядра и glibc/musl;
* - использование возможностей из новых ядер всё равно требует проверок/ветвлений;
* = поэтому сейчас нет причин отказываться от работы на 3.16 поддерживая ядра 4.0 */
ERROR("too old linux kernel %u.%u.%u.%u, the >= 3.16 is required", globals.linux_kernel_version >> 24,
(globals.linux_kernel_version >> 16) & 255, (globals.linux_kernel_version >> 8) & 255,
globals.linux_kernel_version & 255);
return LOG_IFERR(MDBX_INCOMPATIBLE);

View File

@@ -140,7 +140,7 @@ int mdbx_txn_lock(MDBX_env *env, bool dont_wait) {
if (unlikely(env->flags & MDBX_RDONLY))
return LOG_IFERR(MDBX_EACCESS);
if (unlikely(env->basal_txn->owner || (env->basal_txn->flags & MDBX_TXN_FINISHED) == 0))
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));

View File

@@ -545,15 +545,16 @@ int mdbx_txn_commit_ex(MDBX_txn *txn, MDBX_commit_latency *latency) {
/* Update parent's DBs array */
eASSERT(env, parent->n_dbi == txn->n_dbi);
TXN_FOREACH_DBI_ALL(txn, dbi) {
if (txn->dbi_state[dbi] & (DBI_CREAT | DBI_FRESH | DBI_DIRTY)) {
if (txn->dbi_state[dbi] != (parent->dbi_state[dbi] & ~(DBI_FRESH | DBI_CREAT | DBI_DIRTY))) {
eASSERT(env, (txn->dbi_state[dbi] & (DBI_CREAT | DBI_FRESH | DBI_DIRTY)) != 0 ||
(txn->dbi_state[dbi] | DBI_STALE) ==
(parent->dbi_state[dbi] & ~(DBI_FRESH | DBI_CREAT | DBI_DIRTY)));
parent->dbs[dbi] = txn->dbs[dbi];
/* preserve parent's status */
const uint8_t state = txn->dbi_state[dbi] | (parent->dbi_state[dbi] & (DBI_CREAT | DBI_FRESH | DBI_DIRTY));
DEBUG("dbi %zu dbi-state %s 0x%02x -> 0x%02x", dbi, (parent->dbi_state[dbi] != state) ? "update" : "still",
parent->dbi_state[dbi], state);
parent->dbi_state[dbi] = state;
} else {
eASSERT(env, txn->dbi_state[dbi] == (parent->dbi_state[dbi] & ~(DBI_FRESH | DBI_CREAT | DBI_DIRTY)));
}
}

View File

@@ -213,7 +213,7 @@ __cold static void MDBX_PRINTF_ARGS(5, 6)
issue->next = chk->usr->scope->issues;
chk->usr->scope->issues = issue;
} else
chk_error_rc(scope, ENOMEM, "adding issue");
chk_error_rc(scope, MDBX_ENOMEM, "adding issue");
}
va_list args;

View File

@@ -352,7 +352,7 @@ MDBX_CONST_FUNCTION static inline lck_t *lckless_stub(const MDBX_env *env) {
}
#if !(defined(_WIN32) || defined(_WIN64))
MDBX_MAYBE_UNUSED static inline int ignore_enosys(int err) {
MDBX_CONST_FUNCTION static inline int ignore_enosys(int err) {
#ifdef ENOSYS
if (err == ENOSYS)
return MDBX_RESULT_TRUE;
@@ -373,10 +373,21 @@ MDBX_MAYBE_UNUSED static inline int ignore_enosys(int err) {
if (err == EOPNOTSUPP)
return MDBX_RESULT_TRUE;
#endif /* EOPNOTSUPP */
if (err == EAGAIN)
return MDBX_RESULT_TRUE;
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);
}
MDBX_MAYBE_UNUSED MDBX_CONST_FUNCTION static inline int ignore_enosys_and_eremote(int err) {
return (err == MDBX_EREMOTE) ? MDBX_RESULT_TRUE : ignore_enosys(err);
}
#endif /* defined(_WIN32) || defined(_WIN64) */
static inline int check_env(const MDBX_env *env, const bool wanna_active) {

View File

@@ -84,7 +84,7 @@ __noinline int dbi_import(MDBX_txn *txn, const size_t dbi) {
/* dbi-слот еще не инициализирован в транзакции, а хендл не использовался */
txn->cursors[dbi] = nullptr;
MDBX_txn *const parent = txn->parent;
if (parent) {
if (unlikely(parent)) {
/* вложенная пишущая транзакция */
int rc = dbi_check(parent, dbi);
/* копируем состояние table очищая new-флаги. */
@@ -107,26 +107,31 @@ __noinline int dbi_import(MDBX_txn *txn, const size_t dbi) {
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);
if (unlikely(txn->cursors[dbi])) {
/* хендл уже использовался в транзакции и остались висячие курсоры */
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;
return MDBX_DANGLING_DBI;
}
if (unlikely(txn->dbi_state[dbi] & (DBI_OLDEN | DBI_VALID))) {
/* хендл уже использовался в транзакции, но был закрыт или переоткрыт,
* висячих курсоров нет */
txn->dbi_seqs[dbi] = env->dbi_seqs[dbi].weak;
txn->dbi_state[dbi] = DBI_OLDEN | DBI_LINDO;
return MDBX_BAD_DBI;
}
}
/* хендл не использовался в транзакции, либо явно пере-отрывается при
* отсутствии висячих курсоров */
eASSERT(env, (txn->dbi_state[dbi] & DBI_LINDO) && !txn->cursors[dbi]);
eASSERT(env, (txn->dbi_state[dbi] & (DBI_LINDO | DBI_VALID)) == 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;
txn->dbi_state[dbi] = (dbi >= CORE_DBS) ? DBI_LINDO | DBI_VALID | DBI_STALE : DBI_LINDO | DBI_VALID;
return MDBX_SUCCESS;
}
return MDBX_BAD_DBI;
@@ -380,7 +385,7 @@ static int dbi_open_locked(MDBX_txn *txn, unsigned user_flags, MDBX_dbi *dbi, MD
slot = (slot < scan) ? slot : scan;
continue;
}
if (!env->kvs[MAIN_DBI].clc.k.cmp(&name, &env->kvs[scan].name)) {
if (env->kvs[MAIN_DBI].clc.k.cmp(&name, &env->kvs[scan].name) == 0) {
slot = scan;
int err = dbi_check(txn, slot);
if (err == MDBX_BAD_DBI && txn->dbi_state[slot] == (DBI_OLDEN | DBI_LINDO)) {
@@ -536,54 +541,68 @@ int dbi_open(MDBX_txn *txn, const MDBX_val *const name, unsigned user_flags, MDB
#if MDBX_ENABLE_DBI_LOCKFREE
/* Is the DB already open? */
const MDBX_env *const env = txn->env;
size_t free_slot = env->n_dbi;
bool have_free_slot = env->n_dbi < env->max_dbi;
for (size_t i = CORE_DBS; i < env->n_dbi; ++i) {
retry:
if ((env->dbs_flags[i] & DB_VALID) == 0) {
free_slot = i;
have_free_slot = true;
continue;
}
const uint32_t snap_seq = atomic_load32(&env->dbi_seqs[i], mo_AcquireRelease);
const uint16_t snap_flags = env->dbs_flags[i];
struct dbi_snap_result snap = dbi_snap(env, 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;
if (unlikely(!(snap.flags & DB_VALID) || !snap_name.iov_base || !snap_name.iov_len || !snap_cmp))
/* похоже на столкновение с параллельно работающим обновлением */
goto slowpath_locking;
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) ||
if (unlikely(snap.sequence != 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.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) {
/* похоже на столкновение с параллельно работающим обновлением */
goto slowpath_locking;
if (!name_match)
continue;
osal_flush_incoherent_cpu_writeback();
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)))
/* есть подозрение что пользователь открывает таблицу с другими флагами/атрибутами
* или другими компараторами, поэтому уходим в безопасный режим */
goto slowpath_locking;
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 (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;
}
if (likely(rc == MDBX_SUCCESS)) {
if (unlikely(snap.sequence != 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 slowpath_locking;
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))
if (unlikely(!have_free_slot))
return MDBX_DBS_FULL;
slowpath_locking:
#endif /* MDBX_ENABLE_DBI_LOCKFREE */
rc = osal_fastmutex_acquire(&txn->env->dbi_lock);

View File

@@ -53,6 +53,7 @@ static inline size_t dbi_bitmap_ctz(const MDBX_txn *txn, intptr_t bmi) {
I = (I - 1) | (bitmap_chunk - 1); \
bitmap_item = TXN->dbi_sparse[(1 + I) / bitmap_chunk]; \
if (!bitmap_item) \
/* coverity[const_overflow] */ \
I += bitmap_chunk; \
continue; \
} else if ((bitmap_item & 1) == 0) { \

View File

@@ -234,13 +234,14 @@ __cold int dxb_resize(MDBX_env *const env, const pgno_t used_pgno, const pgno_t
rc = MDBX_RESULT_TRUE;
#if defined(MADV_REMOVE)
if (env->flags & MDBX_WRITEMAP)
rc = madvise(ptr_disp(env->dxb_mmap.base, size_bytes), prev_size - size_bytes, MADV_REMOVE) ? ignore_enosys(errno)
: MDBX_SUCCESS;
rc = madvise(ptr_disp(env->dxb_mmap.base, size_bytes), prev_size - size_bytes, MADV_REMOVE)
? ignore_enosys_and_eagain(errno)
: MDBX_SUCCESS;
#endif /* MADV_REMOVE */
#if defined(MADV_DONTNEED)
if (rc == MDBX_RESULT_TRUE)
rc = madvise(ptr_disp(env->dxb_mmap.base, size_bytes), prev_size - size_bytes, MADV_DONTNEED)
? ignore_enosys(errno)
? ignore_enosys_and_eagain(errno)
: MDBX_SUCCESS;
#elif defined(POSIX_MADV_DONTNEED)
if (rc == MDBX_RESULT_TRUE)
@@ -426,7 +427,7 @@ __cold int dxb_set_readahead(const MDBX_env *env, const pgno_t edge, const bool
void *const ptr = ptr_disp(env->dxb_mmap.base, offset);
if (enable) {
#if defined(MADV_NORMAL)
err = madvise(ptr, length, MADV_NORMAL) ? ignore_enosys(errno) : MDBX_SUCCESS;
err = madvise(ptr, length, MADV_NORMAL) ? ignore_enosys_and_eagain(errno) : MDBX_SUCCESS;
if (unlikely(MDBX_IS_ERROR(err)))
return err;
#elif defined(POSIX_MADV_NORMAL)
@@ -454,7 +455,7 @@ __cold int dxb_set_readahead(const MDBX_env *env, const pgno_t edge, const bool
hint.ra_count = unlikely(length > INT_MAX && sizeof(length) > sizeof(hint.ra_count)) ? INT_MAX : (int)length;
(void)/* Ignore ENOTTY for DB on the ram-disk and so on */ fcntl(env->lazy_fd, F_RDADVISE, &hint);
#elif defined(MADV_WILLNEED)
err = madvise(ptr, length, MADV_WILLNEED) ? ignore_enosys(errno) : MDBX_SUCCESS;
err = madvise(ptr, length, MADV_WILLNEED) ? ignore_enosys_and_eagain(errno) : MDBX_SUCCESS;
if (unlikely(MDBX_IS_ERROR(err)))
return err;
#elif defined(POSIX_MADV_WILLNEED)
@@ -479,7 +480,7 @@ __cold int dxb_set_readahead(const MDBX_env *env, const pgno_t edge, const bool
} else {
mincore_clean_cache(env);
#if defined(MADV_RANDOM)
err = madvise(ptr, length, MADV_RANDOM) ? ignore_enosys(errno) : MDBX_SUCCESS;
err = madvise(ptr, length, MADV_RANDOM) ? ignore_enosys_and_eagain(errno) : MDBX_SUCCESS;
if (unlikely(MDBX_IS_ERROR(err)))
return err;
#elif defined(POSIX_MADV_RANDOM)
@@ -531,7 +532,7 @@ __cold int dxb_setup(MDBX_env *env, const int lck_rc, const mdbx_mode_t mode_bit
if (unlikely(err != MDBX_SUCCESS))
return err;
err = osal_ftruncate(env->lazy_fd, env->dxb_mmap.filesize = env->dxb_mmap.current = env->geo_in_bytes.now);
err = osal_fallocate(env->lazy_fd, env->dxb_mmap.filesize = env->dxb_mmap.current = env->geo_in_bytes.now);
if (unlikely(err != MDBX_SUCCESS))
return err;
@@ -660,9 +661,8 @@ __cold int dxb_setup(MDBX_env *env, const int lck_rc, const mdbx_mode_t mode_bit
}
if (env->flags & MDBX_RDONLY) {
if (filesize_before & (globals.sys_allocation_granularity - 1)) {
ERROR("filesize should be rounded-up to system allocation granularity %u",
globals.sys_allocation_granularity);
if (filesize_before & (globals.sys_pagesize - 1)) {
ERROR("filesize should be rounded-up to system page size %u", globals.sys_pagesize);
return MDBX_WANNA_RECOVERY;
}
WARNING("%s", "ignore filesize mismatch in readonly-mode");
@@ -681,19 +681,21 @@ __cold int dxb_setup(MDBX_env *env, const int lck_rc, const mdbx_mode_t mode_bit
!(env->flags & MDBX_NORDAHEAD) && mdbx_is_readahead_reasonable(used_bytes, 0) == MDBX_RESULT_TRUE;
err = osal_mmap(env->flags, &env->dxb_mmap, env->geo_in_bytes.now, env->geo_in_bytes.upper,
(lck_rc && env->stuck_meta < 0) ? MMAP_OPTION_TRUNCATE : 0, env->pathname.dxb);
(lck_rc && env->stuck_meta < 0) ? MMAP_OPTION_SETLENGTH : 0, env->pathname.dxb);
if (unlikely(err != MDBX_SUCCESS))
return err;
#if defined(MADV_DONTDUMP)
err = madvise(env->dxb_mmap.base, env->dxb_mmap.limit, MADV_DONTDUMP) ? ignore_enosys(errno) : MDBX_SUCCESS;
err =
madvise(env->dxb_mmap.base, env->dxb_mmap.limit, MADV_DONTDUMP) ? ignore_enosys_and_eagain(errno) : MDBX_SUCCESS;
if (unlikely(MDBX_IS_ERROR(err)))
return err;
#endif /* MADV_DONTDUMP */
#if defined(MADV_DODUMP)
if (globals.runtime_flags & MDBX_DBG_DUMP) {
const size_t meta_length_aligned2os = pgno_align2os_bytes(env, NUM_METAS);
err = madvise(env->dxb_mmap.base, meta_length_aligned2os, MADV_DODUMP) ? ignore_enosys(errno) : MDBX_SUCCESS;
err = madvise(env->dxb_mmap.base, meta_length_aligned2os, MADV_DODUMP) ? ignore_enosys_and_eagain(errno)
: MDBX_SUCCESS;
if (unlikely(MDBX_IS_ERROR(err)))
return err;
}
@@ -932,7 +934,7 @@ __cold int dxb_setup(MDBX_env *env, const int lck_rc, const mdbx_mode_t mode_bit
bytes2pgno(env, env->dxb_mmap.current));
err = madvise(ptr_disp(env->dxb_mmap.base, used_aligned2os_bytes), env->dxb_mmap.current - used_aligned2os_bytes,
MADV_REMOVE)
? ignore_enosys(errno)
? ignore_enosys_and_eagain(errno)
: MDBX_SUCCESS;
if (unlikely(MDBX_IS_ERROR(err)))
return err;
@@ -942,7 +944,7 @@ __cold int dxb_setup(MDBX_env *env, const int lck_rc, const mdbx_mode_t mode_bit
NOTICE("open-MADV_%s %u..%u", "DONTNEED", env->lck->discarded_tail.weak, bytes2pgno(env, env->dxb_mmap.current));
err = madvise(ptr_disp(env->dxb_mmap.base, used_aligned2os_bytes), env->dxb_mmap.current - used_aligned2os_bytes,
MADV_DONTNEED)
? ignore_enosys(errno)
? ignore_enosys_and_eagain(errno)
: MDBX_SUCCESS;
if (unlikely(MDBX_IS_ERROR(err)))
return err;
@@ -1034,7 +1036,7 @@ int dxb_sync_locked(MDBX_env *env, unsigned flags, meta_t *const pending, troika
#endif /* MADV_FREE */
int err = madvise(ptr_disp(env->dxb_mmap.base, discard_edge_bytes), prev_discarded_bytes - discard_edge_bytes,
advise)
? ignore_enosys(errno)
? ignore_enosys_and_eagain(errno)
: MDBX_SUCCESS;
#else
int err = ignore_enosys(posix_madvise(ptr_disp(env->dxb_mmap.base, discard_edge_bytes),
@@ -1157,7 +1159,8 @@ int dxb_sync_locked(MDBX_env *env, unsigned flags, meta_t *const pending, troika
if (!head.is_steady && meta_is_steady(pending))
target = (meta_t *)head.ptr_c;
else {
WARNING("%s", "skip update meta");
NOTICE("skip update meta%" PRIaPGNO " for txn#%" PRIaTXN ", since it is already steady",
data_page(head.ptr_c)->pgno, head.txnid);
return MDBX_SUCCESS;
}
} else {

View File

@@ -28,12 +28,6 @@
typedef struct iov_ctx iov_ctx_t;
#include "osal.h"
#if UINTPTR_MAX > 0xffffFFFFul || ULONG_MAX > 0xffffFFFFul || defined(_WIN64)
#define MDBX_WORDBITS 64
#else
#define MDBX_WORDBITS 32
#endif /* MDBX_WORDBITS */
#include "options.h"
#include "atomics-types.h"

View File

@@ -872,10 +872,8 @@ pgr_t gc_alloc_ex(const MDBX_cursor *const mc, const size_t num, uint8_t flags)
//---------------------------------------------------------------------------
if (unlikely(!is_gc_usable(txn, mc, flags))) {
eASSERT(env, (txn->flags & txn_gc_drained) || num > 1);
if (unlikely(!is_gc_usable(txn, mc, flags)))
goto no_gc;
}
eASSERT(env, (flags & (ALLOC_COALESCE | ALLOC_LIFO | ALLOC_SHOULD_SCAN)) == 0);
flags += (env->flags & MDBX_LIFORECLAIM) ? ALLOC_LIFO : 0;

View File

@@ -93,10 +93,11 @@ __cold static void choice_fcntl(void) {
static int lck_op(const mdbx_filehandle_t fd, int cmd, const int lck, const off_t offset, off_t len) {
STATIC_ASSERT(sizeof(off_t) >= sizeof(void *) && sizeof(off_t) >= sizeof(size_t));
#ifdef __ANDROID_API__
STATIC_ASSERT_MSG((sizeof(off_t) * 8 == MDBX_WORDBITS), "The bitness of system `off_t` type is mismatch. Please "
"fix build and/or NDK configuration.");
#endif /* Android */
#if defined(__ANDROID_API__) && __ANDROID_API__ < 24
STATIC_ASSERT_MSG((sizeof(off_t) * CHAR_BIT == MDBX_WORDBITS),
"The bitness of system `off_t` type is mismatch. Please "
"fix build and/or NDK configuration.");
#endif /* Android && API < 24 */
assert(offset >= 0 && len > 0);
assert((uint64_t)offset < (uint64_t)INT64_MAX && (uint64_t)len < (uint64_t)INT64_MAX &&
(uint64_t)(offset + len) > (uint64_t)offset);
@@ -132,7 +133,8 @@ static int lck_op(const mdbx_filehandle_t fd, int cmd, const int lck, const off_
}
rc = errno;
#if MDBX_USE_OFDLOCKS
if (rc == EINVAL && (cmd == MDBX_F_OFD_SETLK || cmd == MDBX_F_OFD_SETLKW || cmd == MDBX_F_OFD_GETLK)) {
if (ignore_enosys_and_einval(rc) == MDBX_RESULT_TRUE &&
(cmd == MDBX_F_OFD_SETLK || cmd == MDBX_F_OFD_SETLKW || cmd == MDBX_F_OFD_GETLK)) {
/* fallback to non-OFD locks */
if (cmd == MDBX_F_OFD_SETLK)
cmd = MDBX_F_SETLK;
@@ -460,6 +462,10 @@ __cold MDBX_INTERNAL int lck_destroy(MDBX_env *env, MDBX_env *inprocess_neighbor
jitter4testing(false);
}
#if MDBX_LOCKING == MDBX_LOCKING_SYSV
env->me_sysv_ipc.semid = -1;
#endif /* MDBX_LOCKING */
if (current_pid != env->pid) {
eASSERT(env, !inprocess_neighbor);
NOTICE("drown env %p after-fork pid %d -> %d", __Wpedantic_format_voidptr(env), env->pid, current_pid);
@@ -776,14 +782,14 @@ static int osal_ipclock_lock(MDBX_env *env, osal_ipclock_t *ipc, const bool dont
return rc;
}
int osal_ipclock_unlock(MDBX_env *env, osal_ipclock_t *ipc) {
static int osal_ipclock_unlock(MDBX_env *env, osal_ipclock_t *ipc) {
int err = MDBX_ENOSYS;
#if MDBX_LOCKING == MDBX_LOCKING_POSIX2001 || MDBX_LOCKING == MDBX_LOCKING_POSIX2008
err = pthread_mutex_unlock(ipc);
#elif MDBX_LOCKING == MDBX_LOCKING_POSIX1988
err = sem_post(ipc) ? errno : MDBX_SUCCESS;
#elif MDBX_LOCKING == MDBX_LOCKING_SYSV
if (unlikely(*ipc != (pid_t)env->pid))
if (unlikely(*ipc != (pid_t)env->pid || env->me_sysv_ipc.key == -1))
err = EPERM;
else {
*ipc = 0;
@@ -823,7 +829,6 @@ MDBX_INTERNAL void lck_rdt_unlock(MDBX_env *env) {
int lck_txn_lock(MDBX_env *env, bool dont_wait) {
TRACE("%swait %s", dont_wait ? "dont-" : "", ">>");
eASSERT(env, env->basal_txn || (env->lck == lckless_stub(env) && (env->flags & MDBX_RDONLY)));
jitter4testing(true);
const int err = osal_ipclock_lock(env, &env->lck->wrt_lock, dont_wait);
int rc = err;
@@ -841,10 +846,8 @@ int lck_txn_lock(MDBX_env *env, bool dont_wait) {
void lck_txn_unlock(MDBX_env *env) {
TRACE("%s", ">>");
if (env->basal_txn) {
eASSERT(env, !env->basal_txn || env->basal_txn->owner == osal_thread_self());
eASSERT(env, env->basal_txn->owner == osal_thread_self());
env->basal_txn->owner = 0;
} else {
eASSERT(env, env->lck == lckless_stub(env) && (env->flags & MDBX_RDONLY));
}
int err = osal_ipclock_unlock(env, &env->lck->wrt_lock);
TRACE("<< err %d", err);

View File

@@ -87,7 +87,7 @@ int lck_txn_lock(MDBX_env *env, bool dontwait) {
}
}
eASSERT(env, !env->basal_txn->owner);
eASSERT(env, !env->basal_txn || !env->basal_txn->owner);
if (env->flags & MDBX_EXCLUSIVE)
goto done;
@@ -104,10 +104,11 @@ int lck_txn_lock(MDBX_env *env, bool dontwait) {
}
if (rc == MDBX_SUCCESS) {
done:
if (env->basal_txn)
env->basal_txn->owner = osal_thread_self();
/* 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;
}
@@ -116,14 +117,15 @@ int lck_txn_lock(MDBX_env *env, bool dontwait) {
}
void lck_txn_unlock(MDBX_env *env) {
eASSERT(env, env->basal_txn->owner == osal_thread_self());
eASSERT(env, !env->basal_txn || 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;
if (env->basal_txn)
env->basal_txn->owner = 0;
LeaveCriticalSection(&env->windowsbug_lock);
}

View File

@@ -62,20 +62,20 @@ __cold static int lck_setup_locked(MDBX_env *env) {
}
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);
err = osal_mmap((env->flags & MDBX_EXCLUSIVE) | MDBX_WRITEMAP, &env->lck_mmap, (size_t)size, (size_t)size,
lck_seize_rc ? MMAP_OPTION_SETLENGTH | 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(errno) : MDBX_SUCCESS;
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(errno) : MDBX_SUCCESS;
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)

View File

@@ -373,7 +373,7 @@ __cold std::string error::message() const {
__cold void error::throw_exception() const {
switch (code()) {
case MDBX_EINVAL:
throw std::invalid_argument("mdbx");
throw std::invalid_argument("MDBX_EINVAL");
case MDBX_ENOMEM:
throw std::bad_alloc();
case MDBX_SUCCESS:

View File

@@ -1592,6 +1592,7 @@ MDBX_INTERNAL int osal_is_pipe(mdbx_filehandle_t fd) {
#endif
}
/* truncate file: just set the length of a file */
MDBX_INTERNAL int osal_ftruncate(mdbx_filehandle_t fd, uint64_t length) {
#if defined(_WIN32) || defined(_WIN64)
if (imports.SetFileInformationByHandle) {
@@ -1611,6 +1612,29 @@ MDBX_INTERNAL int osal_ftruncate(mdbx_filehandle_t fd, uint64_t length) {
#endif
}
/* extend file: set the length of a file AND ensure the space has been allocated */
MDBX_INTERNAL int osal_fallocate(mdbx_filehandle_t fd, uint64_t length) {
assert(length > 0);
int err = MDBX_RESULT_TRUE;
#if (defined(__linux__) || defined(__gnu_linux__)) && \
((defined(_GNU_SOURCE) && __GLIBC_PREREQ(2, 10)) || (defined(__ANDROID_API__) && __ANDROID_API__ >= 21))
err = fallocate(fd, 0, 0, length) ? ignore_enosys_and_eremote(errno) : MDBX_SUCCESS;
#elif defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 200112L && !defined(__APPLE__)
err = posix_fallocate(fd, 0, length) ? ignore_enosys_and_eremote(errno) : MDBX_SUCCESS;
#elif defined(__APPLE__)
fstore_t store = {F_ALLOCATEALL, F_PEOFPOSMODE, 0, length, 0};
if (fcntl(fd, F_PREALLOCATE, &store))
err = ignore_enosys_and_eremote(errno);
#endif /* Apple */
#if !defined(_WIN32) && !defined(_WIN64)
/* Workaround for testing: ignore ENOSPC for TMPFS/RAMFS.
* This is insignificant for production, but it helps in some tests using /dev/shm inside docker/containers. */
if (err == ENOSPC && osal_check_fs_incore(fd) == MDBX_RESULT_TRUE)
err = MDBX_RESULT_TRUE;
#endif /* !Windows */
return (err == MDBX_RESULT_TRUE) ? osal_ftruncate(fd, length) : err;
}
MDBX_INTERNAL int osal_fseek(mdbx_filehandle_t fd, uint64_t pos) {
#if defined(_WIN32) || defined(_WIN64)
LARGE_INTEGER li;
@@ -1745,7 +1769,7 @@ MDBX_INTERNAL int osal_check_fs_incore(mdbx_filehandle_t handle) {
return MDBX_RESULT_FALSE;
}
static int osal_check_fs_local(mdbx_filehandle_t handle, int flags) {
MDBX_INTERNAL int osal_check_fs_local(mdbx_filehandle_t handle, int flags) {
#if defined(_WIN32) || defined(_WIN64)
if (globals.running_under_Wine && !(flags & MDBX_EXCLUSIVE))
return ERROR_NOT_CAPABLE /* workaround for Wine */;
@@ -2061,8 +2085,8 @@ MDBX_INTERNAL int osal_mmap(const int flags, osal_mmap_t *map, size_t size, cons
if (unlikely(err != MDBX_SUCCESS))
return err;
if ((flags & MDBX_RDONLY) == 0 && (options & MMAP_OPTION_TRUNCATE) != 0) {
err = osal_ftruncate(map->fd, size);
if ((flags & MDBX_RDONLY) == 0 && (options & MMAP_OPTION_SETLENGTH) != 0) {
err = osal_fallocate(map->fd, size);
VERBOSE("ftruncate %zu, err %d", size, err);
if (err != MDBX_SUCCESS)
return err;
@@ -2308,7 +2332,7 @@ retry_file_and_section:
}
if ((flags & MDBX_RDONLY) == 0 && map->filesize != size) {
err = osal_ftruncate(map->fd, size);
err = osal_fallocate(map->fd, size);
if (err == MDBX_SUCCESS)
map->filesize = size;
/* ignore error, because Windows unable shrink file
@@ -2386,10 +2410,15 @@ retry_mapview:;
rc = MDBX_EPERM;
map->current = (map->filesize > limit) ? limit : (size_t)map->filesize;
} else {
if (size > map->filesize || (size < map->filesize && (flags & txn_shrink_allowed))) {
rc = osal_ftruncate(map->fd, size);
VERBOSE("ftruncate %zu, err %d", size, rc);
if (rc != MDBX_SUCCESS)
if (map->filesize != size) {
if (size > map->filesize) {
rc = osal_fallocate(map->fd, size);
VERBOSE("f%s-%s %zu, err %d", "allocate", "extend", size, rc);
} else if (flags & txn_shrink_allowed) {
rc = osal_ftruncate(map->fd, size);
VERBOSE("f%s-%s %zu, err %d", "truncate", "shrink", size, rc);
}
if (unlikely(rc != MDBX_SUCCESS))
return rc;
map->filesize = size;
}
@@ -2856,7 +2885,7 @@ __cold static LSTATUS mdbx_RegGetValue(HKEY hKey, LPCSTR lpSubKey, LPCSTR lpValu
}
#endif
__cold MDBX_MAYBE_UNUSED static bool bootid_parse_uuid(bin128_t *s, const void *p, const size_t n) {
MDBX_MAYBE_UNUSED __cold static bool bootid_parse_uuid(bin128_t *s, const void *p, const size_t n) {
if (n > 31) {
unsigned bits = 0;
for (unsigned i = 0; i < n; ++i) /* try parse an UUID in text form */ {

View File

@@ -193,7 +193,14 @@ typedef struct osal_mmap {
#elif defined(__ANDROID_API__)
#if __ANDROID_API__ < 24
/* https://android-developers.googleblog.com/2017/09/introducing-android-native-development.html
* https://android.googlesource.com/platform/bionic/+/master/docs/32-bit-abi.md */
#define MDBX_HAVE_PWRITEV 0
#if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS != MDBX_WORDBITS
#error "_FILE_OFFSET_BITS != MDBX_WORDBITS and __ANDROID_API__ < 24" (_FILE_OFFSET_BITS != MDBX_WORDBITS)
#elif defined(__FILE_OFFSET_BITS) && __FILE_OFFSET_BITS != MDBX_WORDBITS
#error "__FILE_OFFSET_BITS != MDBX_WORDBITS and __ANDROID_API__ < 24" (__FILE_OFFSET_BITS != MDBX_WORDBITS)
#endif
#else
#define MDBX_HAVE_PWRITEV 1
#endif
@@ -427,6 +434,7 @@ enum osal_syncmode_bits {
MDBX_INTERNAL int osal_fsync(mdbx_filehandle_t fd, const enum osal_syncmode_bits mode_bits);
MDBX_INTERNAL int osal_ftruncate(mdbx_filehandle_t fd, uint64_t length);
MDBX_INTERNAL int osal_fallocate(mdbx_filehandle_t fd, uint64_t length);
MDBX_INTERNAL int osal_fseek(mdbx_filehandle_t fd, uint64_t pos);
MDBX_INTERNAL int osal_filesize(mdbx_filehandle_t fd, uint64_t *length);
@@ -462,7 +470,7 @@ MDBX_INTERNAL int osal_removedirectory(const pathchar_t *pathname);
MDBX_INTERNAL int osal_is_pipe(mdbx_filehandle_t fd);
MDBX_INTERNAL int osal_lockfile(mdbx_filehandle_t fd, bool wait);
#define MMAP_OPTION_TRUNCATE 1
#define MMAP_OPTION_SETLENGTH 1
#define MMAP_OPTION_SEMAPHORE 2
MDBX_INTERNAL int osal_mmap(const int flags, osal_mmap_t *map, size_t size, const size_t limit, const unsigned options,
const pathchar_t *pathname4logging);
@@ -481,6 +489,7 @@ MDBX_INTERNAL int osal_resume_threads_after_remap(mdbx_handle_array_t *array);
MDBX_INTERNAL int osal_msync(const osal_mmap_t *map, size_t offset, size_t length, enum osal_syncmode_bits mode_bits);
MDBX_INTERNAL int osal_check_fs_rdonly(mdbx_filehandle_t handle, const pathchar_t *pathname, int err);
MDBX_INTERNAL int osal_check_fs_incore(mdbx_filehandle_t handle);
MDBX_INTERNAL int osal_check_fs_local(mdbx_filehandle_t handle, int flags);
MDBX_MAYBE_UNUSED static inline uint32_t osal_getpid(void) {
STATIC_ASSERT(sizeof(mdbx_pid_t) <= sizeof(uint32_t));

View File

@@ -122,6 +122,8 @@
#pragma warning(disable : 6235) /* <expression> is always a constant */
#pragma warning(disable : 6237) /* <expression> is never evaluated and might \
have side effects */
#pragma warning(disable : 5286) /* implicit conversion from enum type 'type 1' to enum type 'type 2' */
#pragma warning(disable : 5287) /* operands are different enum types 'type 1' and 'type 2' */
#endif
#pragma warning(disable : 4710) /* 'xyz': function not inlined */
#pragma warning(disable : 4711) /* function 'xyz' selected for automatic \
@@ -433,11 +435,6 @@ __extern_C key_t ftok(const char *, int);
#if __ANDROID_API__ >= 21
#include <sys/sendfile.h>
#endif
#if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS != MDBX_WORDBITS
#error "_FILE_OFFSET_BITS != MDBX_WORDBITS" (_FILE_OFFSET_BITS != MDBX_WORDBITS)
#elif defined(__FILE_OFFSET_BITS) && __FILE_OFFSET_BITS != MDBX_WORDBITS
#error "__FILE_OFFSET_BITS != MDBX_WORDBITS" (__FILE_OFFSET_BITS != MDBX_WORDBITS)
#endif
#endif /* Android */
#if defined(HAVE_SYS_STAT_H) || __has_include(<sys/stat.h>)
@@ -522,6 +519,12 @@ __extern_C key_t ftok(const char *, int);
#endif
#endif /* __BYTE_ORDER__ || __ORDER_LITTLE_ENDIAN__ || __ORDER_BIG_ENDIAN__ */
#if UINTPTR_MAX > 0xffffFFFFul || ULONG_MAX > 0xffffFFFFul || defined(_WIN64)
#define MDBX_WORDBITS 64
#else
#define MDBX_WORDBITS 32
#endif /* MDBX_WORDBITS */
/*----------------------------------------------------------------------------*/
/* Availability of CMOV or equivalent */

View File

@@ -8,8 +8,7 @@ __hot txnid_t txn_snapshot_oldest(const MDBX_txn *const txn) {
}
void txn_done_cursors(MDBX_txn *txn, const bool merge) {
tASSERT(txn, txn->cursors[FREE_DBI] == nullptr);
TXN_FOREACH_DBI_FROM(txn, i, /* skip FREE_DBI */ 1) {
TXN_FOREACH_DBI_ALL(txn, i) {
MDBX_cursor *mc = txn->cursors[i];
if (mc) {
txn->cursors[i] = nullptr;
@@ -677,7 +676,7 @@ int txn_renew(MDBX_txn *txn, unsigned flags) {
txn->dbi_seqs[FREE_DBI] = 0;
txn->dbi_seqs[MAIN_DBI] = atomic_load32(&env->dbi_seqs[MAIN_DBI], mo_AcquireRelease);
if (unlikely(env->dbs_flags[MAIN_DBI] != (DB_VALID | txn->dbs[MAIN_DBI].flags))) {
if (unlikely(env->dbs_flags[MAIN_DBI] != (DB_VALID | txn->dbs[MAIN_DBI].flags) || !txn->dbi_seqs[MAIN_DBI])) {
const bool need_txn_lock = env->basal_txn && env->basal_txn->owner != osal_thread_self();
bool should_unlock = false;
if (need_txn_lock) {
@@ -689,24 +688,24 @@ int txn_renew(MDBX_txn *txn, unsigned flags) {
}
rc = osal_fastmutex_acquire(&env->dbi_lock);
if (likely(rc == MDBX_SUCCESS)) {
uint32_t seq = dbi_seq_next(env, MAIN_DBI);
/* проверяем повторно после захвата блокировки */
uint32_t seq = atomic_load32(&env->dbi_seqs[MAIN_DBI], mo_AcquireRelease);
if (env->dbs_flags[MAIN_DBI] != (DB_VALID | txn->dbs[MAIN_DBI].flags)) {
if (!need_txn_lock || should_unlock ||
/* если нет активной пишущей транзакции,
* то следующая будет ждать на dbi_lock */
!env->txn) {
if (env->dbs_flags[MAIN_DBI] != 0 || MDBX_DEBUG)
if (!(env->dbs_flags[MAIN_DBI] & DB_VALID) || !need_txn_lock || should_unlock ||
/* если нет активной пишущей транзакции, * то следующая будет ждать на dbi_lock */ !env->txn) {
if (env->dbs_flags[MAIN_DBI] & DB_VALID) {
NOTICE("renew MainDB for %s-txn %" PRIaTXN " since db-flags changes 0x%x -> 0x%x",
(txn->flags & MDBX_TXN_RDONLY) ? "ro" : "rw", txn->txnid, env->dbs_flags[MAIN_DBI] & ~DB_VALID,
txn->dbs[MAIN_DBI].flags);
env->dbs_flags[MAIN_DBI] = DB_POISON;
atomic_store32(&env->dbi_seqs[MAIN_DBI], seq, mo_AcquireRelease);
seq = dbi_seq_next(env, MAIN_DBI);
env->dbs_flags[MAIN_DBI] = DB_POISON;
atomic_store32(&env->dbi_seqs[MAIN_DBI], seq, mo_AcquireRelease);
}
rc = tbl_setup(env, &env->kvs[MAIN_DBI], &txn->dbs[MAIN_DBI]);
if (likely(rc == MDBX_SUCCESS)) {
seq = dbi_seq_next(env, MAIN_DBI);
env->dbs_flags[MAIN_DBI] = DB_VALID | txn->dbs[MAIN_DBI].flags;
txn->dbi_seqs[MAIN_DBI] = atomic_store32(&env->dbi_seqs[MAIN_DBI], seq, mo_AcquireRelease);
atomic_store32(&env->dbi_seqs[MAIN_DBI], seq, mo_AcquireRelease);
}
} else {
ERROR("MainDB db-flags changes 0x%x -> 0x%x ahead of read-txn "
@@ -715,6 +714,7 @@ int txn_renew(MDBX_txn *txn, unsigned flags) {
rc = MDBX_INCOMPATIBLE;
}
}
txn->dbi_seqs[MAIN_DBI] = seq;
ENSURE(env, osal_fastmutex_release(&env->dbi_lock) == MDBX_SUCCESS);
} else {
DEBUG("dbi_lock failed, err %d", rc);

View File

@@ -3,36 +3,62 @@
# Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru>
# SPDX-License-Identifier: Apache-2.0
TEST="./test/stochastic.sh --skip-make --db-upto-gb 32"
TMUX=tmux
DIR="$(dirname ${BASH_SOURCE[0]})"
TEST="${DIR}/stochastic.sh --skip-make --db-upto-gb 32"
PREFIX="/dev/shm/mdbxtest-"
tmux kill-session -t mdbx
NUMACTL="$(which numactl 2>-)"
NUMALIST=()
NUMAIDX=0
if [ -n "${NUMACTL}" -a $(${NUMACTL} --hardware | grep 'node [0-9]\+ cpus' | wc -l) -gt 1 ]; then
NUMALIST=($(${NUMACTL} --hardware | grep 'node [0-9]\+ cpus' | cut -d ' ' -f 2))
fi
function test_numacycle {
NUMAIDX=$((NUMAIDX + 1))
if [ ${NUMAIDX} -ge ${#NUMALIST[@]} ]; then
NUMAIDX=0
fi
}
function test_numanode {
if [[ ${#NUMALIST[@]} > 1 ]]; then
echo "${TEST} --numa ${NUMALIST[$NUMAIDX]}"
else
echo "${TEST}"
fi
}
${TMUX} kill-session -t mdbx
rm -rf ${PREFIX}*
# git clean -x -f -d && make test-assertions
tmux -f ./test/tmux.conf new-session -d -s mdbx htop
${TMUX} -f "${DIR}/tmux.conf" new-session -d -s mdbx htop
W=0
for ps in min 4k max; do
for from in 1 30000; do
for n in 0 1 2 3; do
CMD="${TEST} --delay $((n * 7)) --page-size ${ps} --from ${from} --dir ${PREFIX}page-${ps}.from-${from}.${n}"
CMD="$(test_numanode) --delay $((n * 7)) --page-size ${ps} --from ${from} --dir ${PREFIX}page-${ps}.from-${from}.${n}"
if [ $n -eq 0 ]; then
tmux new-window -t mdbx:$((++W)) -n "page-${ps}.from-${from}" -k -d "$CMD"
tmux select-layout -E tiled
${TMUX} new-window -t mdbx:$((++W)) -n "page-${ps}.from-${from}" -k -d "$CMD"
${TMUX} select-layout -E tiled
else
tmux split-window -t mdbx:$W -l 20% -d $CMD
${TMUX} split-window -t mdbx:$W -l 20% -d $CMD
fi
test_numacycle
done
for n in 0 1 2 3; do
CMD="${TEST} --delay $((3 + n * 7)) --extra --page-size ${ps} --from ${from} --dir ${PREFIX}page-${ps}.from-${from}.${n}-extra"
CMD="$(test_numanode) --delay $((3 + n * 7)) --extra --page-size ${ps} --from ${from} --dir ${PREFIX}page-${ps}.from-${from}.${n}-extra"
if [ $n -eq 0 ]; then
tmux new-window -t mdbx:$((++W)) -n "page-${ps}.from-${from}-extra" -k -d "$CMD"
tmux select-layout -E tiled
${TMUX} new-window -t mdbx:$((++W)) -n "page-${ps}.from-${from}-extra" -k -d "$CMD"
${TMUX} select-layout -E tiled
else
tmux split-window -t mdbx:$W -l 20% -d $CMD
${TMUX} split-window -t mdbx:$W -l 20% -d $CMD
fi
test_numacycle
done
done
done
tmux attach -t mdbx
${TMUX} attach -t mdbx

View File

@@ -172,9 +172,21 @@ mdbx::map_handle case1_cycle_dbi(std::deque<mdbx::map_handle> &dbi) {
void case1_read_cycle(mdbx::txn txn, std::deque<mdbx::map_handle> &dbi, std::vector<MDBX_cursor *> &pool,
mdbx::cursor pre, bool nested = false) {
for (auto c : pool)
mdbx::cursor(c).bind(txn, case1_cycle_dbi(dbi));
pre.bind(txn, case1_cycle_dbi(dbi));
if (nested) {
for (auto c : pool)
try {
mdbx::cursor(c).bind(txn, case1_cycle_dbi(dbi));
} catch (const std::invalid_argument &) {
}
try {
pre.bind(txn, case1_cycle_dbi(dbi));
} catch (const std::invalid_argument &) {
}
} else {
for (auto c : pool)
mdbx::cursor(c).bind(txn, case1_cycle_dbi(dbi));
pre.bind(txn, case1_cycle_dbi(dbi));
}
for (auto n = prng(3 + dbi.size()); n > 0; --n) {
auto c = txn.open_cursor(dbi[prng(dbi.size())]);
@@ -215,6 +227,16 @@ void case1_read_cycle(mdbx::txn txn, std::deque<mdbx::map_handle> &dbi, std::vec
switch (prng(nested ? 7 : 3)) {
case 0:
if (pre.txn()) {
if (nested)
try {
pre.unbind();
} catch (const std::invalid_argument &) {
return;
}
else
pre.unbind();
}
for (auto i = pool.begin(); i != pool.end();)
if (mdbx_cursor_txn(*i))
i = pool.erase(i);
@@ -253,6 +275,8 @@ void case1_write_cycle(mdbx::txn_managed txn, std::deque<mdbx::map_handle> &dbi,
if (prng(16) > 8)
case1_write_cycle(txn.start_nested(), dbi, pool, pre, true);
case1_read_cycle(txn, dbi, pool, pre, nested);
if (flipcoin())
txn.commit();
else
@@ -332,6 +356,27 @@ bool case1(mdbx::env env) {
//--------------------------------------------------------------------------------------------
bool case2(mdbx::env env) {
bool ok = true;
auto txn = env.start_write();
auto dbi = txn.create_map("case2", mdbx::key_mode::usual, mdbx::value_mode::single);
txn.commit_embark_read();
auto cursor1 = txn.open_cursor(dbi);
auto cursor2 = txn.open_cursor(0);
cursor1.move(mdbx::cursor::next, false);
cursor2.move(mdbx::cursor::next, false);
txn.commit_embark_read();
cursor2.bind(txn, dbi);
cursor1.bind(txn, 0);
cursor1.move(mdbx::cursor::last, false);
cursor2.move(mdbx::cursor::last, false);
return ok;
}
//--------------------------------------------------------------------------------------------
int doit() {
mdbx::path db_filename = "test-cursor-closing";
mdbx::env::remove(db_filename);
@@ -341,6 +386,7 @@ int doit() {
bool ok = case0(env);
ok = case1(env) && ok;
ok = case2(env) && ok;
if (ok) {
std::cout << "OK\n";

View File

@@ -2,17 +2,9 @@
#include <iostream>
static char log_buffer[1024];
mdbx::path db_filename = "test-dbi";
static void logger_nofmt(MDBX_log_level_t loglevel, const char *function, int line, const char *msg,
unsigned length) noexcept {
(void)length;
(void)loglevel;
fprintf(stdout, "%s:%u %s", function, line, msg);
}
int doit() {
mdbx::path db_filename = "test-dbi";
bool case1() {
mdbx::env::remove(db_filename);
mdbx::env::operate_parameters operateParameters(100, 10, mdbx::env::nested_transactions);
@@ -45,15 +37,15 @@ int doit() {
MDBX_stat stat;
int err = mdbx_dbi_stat(txn, dbi, &stat, sizeof(stat));
if (err != MDBX_BAD_DBI) {
std::cerr << "unexpected result err-code " << err;
return EXIT_FAILURE;
std::cerr << "Unexpected err " << err << " (wanna MDBX_BAD_DBI/-30780)\n";
return false;
}
txn.commit();
}
{
// снова проверяем что таблица открывается и хендл доступень в родительской транзакции после коммита открывшей его
// дочерней
// снова проверяем что таблица открывается и хендл доступень в родительской транзакции,
// после коммита открывшей его дочерней
mdbx::txn_managed txn = env.start_write();
mdbx::txn_managed nested = txn.start_nested();
mdbx::map_handle dbi = nested.open_map_accede("fap1");
@@ -63,8 +55,165 @@ int doit() {
env.close_map(dbi);
}
std::cout << "OK\n";
return EXIT_SUCCESS;
return true;
}
bool case2() {
bool ok = true;
mdbx::env_managed::create_parameters createParameters;
mdbx::env::remove(db_filename);
{
mdbx::env::operate_parameters operateParameters(0, 10, mdbx::env::nested_transactions);
mdbx::env_managed env(db_filename, createParameters, operateParameters);
{
mdbx::txn_managed txn = env.start_write();
MDBX_dbi dbi = 0;
int err = mdbx_dbi_open(txn, "test", MDBX_CREATE, &dbi);
if (err != MDBX_DBS_FULL) {
std::cerr << "Unexpected err " << err << " (wanna MDBX_DBS_FULL/-30791)\n";
ok = false;
}
}
{
mdbx::txn_managed txn = env.start_write();
MDBX_dbi dbi = 0;
int err = mdbx_dbi_open(txn, "test", MDBX_CREATE | MDBX_DUPSORT | MDBX_DUPFIXED, &dbi);
if (err != MDBX_DBS_FULL) {
std::cerr << "Unexpected err " << err << " (wanna MDBX_DBS_FULL/-30791)\n";
ok = false;
}
}
}
{
mdbx::env::operate_parameters operateParameters(1, 10, mdbx::env::nested_transactions);
mdbx::env_managed env(db_filename, createParameters, operateParameters);
{
mdbx::txn_managed txn = env.start_write();
mdbx::map_handle dbi = txn.create_map("dup", mdbx::key_mode::ordinal, mdbx::value_mode::multi_ordinal);
txn.commit();
env.close_map(dbi);
}
{
mdbx::txn_managed txn = env.start_write();
mdbx::map_handle dbi = txn.create_map("uni", mdbx::key_mode::reverse, mdbx::value_mode::single);
txn.commit();
env.close_map(dbi);
}
}
{
mdbx::env::operate_parameters operateParameters(0, 10, mdbx::env::nested_transactions);
mdbx::env_managed env(db_filename, createParameters, operateParameters);
{
mdbx::txn_managed txn = env.start_read();
MDBX_dbi dbi = 0;
int err = mdbx_dbi_open(txn, "uni", MDBX_DB_ACCEDE, &dbi);
if (err != MDBX_DBS_FULL) {
std::cerr << "Unexpected err " << err << " (wanna MDBX_DBS_FULL/-30791)\n";
ok = false;
}
if (dbi)
env.close_map(dbi);
}
{
mdbx::txn_managed txn = env.start_read();
MDBX_dbi dbi = 0;
int err = mdbx_dbi_open(txn, "dup", MDBX_DB_ACCEDE, &dbi);
if (err != MDBX_DBS_FULL) {
std::cerr << "Unexpected err " << err << " (wanna MDBX_DBS_FULL/-30791)\n";
ok = false;
}
if (dbi)
env.close_map(dbi);
}
}
{
{
mdbx::env::operate_parameters operateParameters(1, 10, mdbx::env::nested_transactions);
mdbx::env_managed env(db_filename, createParameters, operateParameters);
{
mdbx::txn_managed txn = env.start_read();
MDBX_dbi dbi = 0;
int err = mdbx_dbi_open(txn, "uni", MDBX_DB_ACCEDE, &dbi);
if (err != MDBX_SUCCESS) {
std::cerr << "Unexpected err " << err << "\n";
ok = false;
}
if (dbi)
env.close_map(dbi);
}
{
mdbx::txn_managed txn = env.start_read();
MDBX_dbi dbi = 0;
int err = mdbx_dbi_open(txn, "dup", MDBX_DB_ACCEDE, &dbi);
if (err != MDBX_SUCCESS) {
std::cerr << "Unexpected err " << err << "\n";
ok = false;
}
if (dbi)
env.close_map(dbi);
}
}
}
return ok;
}
bool case3() {
bool ok = true;
mdbx::env_managed::create_parameters createParameters;
mdbx::env::remove(db_filename);
{
mdbx::env::operate_parameters operateParameters(1, 10, mdbx::env::nested_transactions);
mdbx::env_managed env(db_filename, createParameters, operateParameters);
{
mdbx::txn_managed txn = env.start_write();
MDBX_dbi notexists_dbi = 0;
int err = mdbx_dbi_open(txn, "test", MDBX_DB_DEFAULTS, &notexists_dbi);
if (err != MDBX_NOTFOUND) {
std::cerr << "Unexpected err " << err << " (wanna MDBX_NOTFOUND/-30798)\n";
ok = false;
}
mdbx::map_handle dbi = txn.create_map("test", mdbx::key_mode::ordinal, mdbx::value_mode::single);
dbi = txn.open_map("test", mdbx::key_mode::ordinal, mdbx::value_mode::single);
err = mdbx_dbi_close(env, dbi);
if (err != MDBX_DANGLING_DBI) {
std::cerr << "Unexpected err " << err << " (wanna MDBX_DANGLING_DBI/-30412)\n";
ok = false;
}
txn.commit();
env.close_map(dbi);
}
}
return ok;
}
int doit() {
bool ok = true;
ok = case1() && ok;
ok = case2() && ok;
ok = case3() && ok;
if (ok) {
std::cout << "OK\n";
return EXIT_SUCCESS;
} else {
std::cerr << "FAIL\n";
return EXIT_FAILURE;
}
}
static char log_buffer[1024];
static void logger_nofmt(MDBX_log_level_t loglevel, const char *function, int line, const char *msg,
unsigned length) noexcept {
(void)length;
(void)loglevel;
fprintf(stdout, "%s:%u %s", function, line, msg);
}
int main(int argc, char *argv[]) {

View File

@@ -31,15 +31,6 @@ int main(int argc, const char *argv[]) {
#include <latch>
#include <thread>
static char log_buffer[1024];
static void logger_nofmt(MDBX_log_level_t loglevel, const char *function, int line, const char *msg,
unsigned length) noexcept {
(void)length;
(void)loglevel;
fprintf(stdout, "%s:%u %s", function, line, msg);
}
bool case0(const mdbx::path &path) {
mdbx::env_managed::create_parameters createParameters;
createParameters.geometry.make_dynamic(21 * mdbx::env::geometry::MiB, 84 * mdbx::env::geometry::MiB);
@@ -322,19 +313,85 @@ bool case2(const mdbx::path &path, bool no_sticky_threads) {
return true;
}
bool case3(const mdbx::path &path, bool no_sticky_threads) {
mdbx::env::remove(path);
mdbx::env_managed::create_parameters createParameters;
createParameters.geometry.make_dynamic(21 * mdbx::env::geometry::MiB, 84 * mdbx::env::geometry::MiB);
mdbx::env::operate_parameters operateParameters(100, 10);
operateParameters.options.no_sticky_threads = no_sticky_threads;
mdbx::env_managed env(path, createParameters, operateParameters);
mdbx::pair pair = {"key", "val"};
const auto N = std::thread::hardware_concurrency() * 2;
std::latch s0(N + 1), s1(N + 1), s2(N + 1);
std::vector<std::thread> l;
volatile bool ok = true;
for (size_t n = 0; n < N; ++n)
l.push_back(std::thread([&]() {
try {
s0.arrive_and_wait();
{
auto txn = env.start_read();
mdbx::slice value;
int err = mdbx_get(txn, 1, pair.key, &value);
if (err != MDBX_NOTFOUND) {
ok = false;
std::cerr << "Unexpected error " << err << "\n";
}
}
s1.arrive_and_wait();
s2.arrive_and_wait();
{
auto txn = env.start_read();
if (txn.get(1, pair.key) != pair.value)
ok = false;
}
} catch (const std::exception &ex) {
std::cerr << "Exception: " << ex.what() << "\n";
ok = false;
}
}));
s0.arrive_and_wait();
auto txn = env.start_write();
s1.arrive_and_wait();
txn.insert(1, pair);
txn.commit();
s2.arrive_and_wait();
for (auto &t : l)
t.join();
return ok;
}
int doit() {
mdbx::path path = "test-txn";
mdbx::env::remove(path);
bool ok = case0(path);
bool ok = true;
ok = case0(path) && ok;
ok = case1(path) && ok;
ok = case2(path, false) && ok;
ok = case2(path, true) && ok;
ok = case3(path, false) && ok;
ok = case3(path, true) && ok;
std::cout << (ok ? "OK\n" : "FAIL\n");
return ok ? EXIT_SUCCESS : EXIT_FAILURE;
}
static char log_buffer[1024];
static void logger_nofmt(MDBX_log_level_t loglevel, const char *function, int line, const char *msg,
unsigned length) noexcept {
(void)length;
(void)loglevel;
fprintf(stdout, "%s:%u %s", function, line, msg);
}
int main(int argc, char *argv[]) {
(void)argc;
(void)argv;

View File

@@ -510,6 +510,7 @@ int osal_actor_poll(mdbx_pid_t &pid, unsigned timeout) {
options |= WCONTINUED;
#endif
pid = 0;
while (sigalarm_tail == sigalarm_head) {
int status;
pid = waitpid(0, &status, options);

View File

@@ -28,6 +28,7 @@ REPORT_DEPTH=no
REPEAT=11
ROUNDS=1
SMALL=no
NUMABIND=
while [ -n "$1" ]
do
@@ -51,6 +52,7 @@ do
echo "--db-upto-gb NN --''--''--''--''--''--''--''--''-- NN gigabytes"
echo "--no-geometry-jitter Disable jitter for geometry upper-size"
echo "--pagesize NN Use specified page size (256 is minimal and used by default)"
echo "--numa NODE Bind to the specified NUMA node"
echo "--dont-check-ram-size Don't check available RAM"
echo "--extra Iterate extra modes/flags"
echo "--taillog Dump tail of test log on failure"
@@ -209,6 +211,15 @@ do
--small)
SMALL=yes
;;
--numa)
NUMANODE=$2
if [[ ! $NUMANODE =~ ^[0-9]+$ ]]; then
echo "Invalid value '$NUMANODE' for --numa option, expect an integer of NUMA-node"
exit -2
fi
NUMABIND="numactl --membind ${NUMANODE} --cpunodebind ${NUMANODE}"
shift
;;
*)
echo "Unknown option '$1'"
exit -2
@@ -393,7 +404,7 @@ if [ "$SKIP_MAKE" != "yes" ]; then
fi
###############################################################################
# 5. run stochastic iterations
# 5. internal preparations
if which setsid >/dev/null 2>/dev/null; then
SETSID=$(which setsid)
@@ -504,9 +515,9 @@ function probe {
else
exec {LFD}> >(logger)
fi
${MONITOR} ./mdbx_test ${speculum} --random-writemap=no --ignore-dbfull --repeat=${REPEAT} --pathname=${TESTDB_DIR}/long.db --cleanup-after=no --geometry-jitter=${GEOMETRY_JITTER} "$@" $case >&${LFD} \
&& ${MONITOR} ./mdbx_chk -q ${TESTDB_DIR}/long.db | tee ${TESTDB_DIR}/long-chk.log \
&& ([ ! -e ${TESTDB_DIR}/long.db-copy ] || ${MONITOR} ./mdbx_chk -q ${TESTDB_DIR}/long.db-copy | tee ${TESTDB_DIR}/long-chk-copy.log) \
${NUMABIND} ${MONITOR} ./mdbx_test ${speculum} --random-writemap=no --ignore-dbfull --repeat=${REPEAT} --pathname=${TESTDB_DIR}/long.db --cleanup-after=no --geometry-jitter=${GEOMETRY_JITTER} "$@" $case >&${LFD} \
&& ${NUMABIND} ${MONITOR} ./mdbx_chk ${TESTDB_DIR}/long.db | tee ${TESTDB_DIR}/long-chk.log \
&& ([ ! -e ${TESTDB_DIR}/long.db-copy ] || ${NUMABIND} ${MONITOR} ./mdbx_chk ${TESTDB_DIR}/long.db-copy | tee ${TESTDB_DIR}/long-chk-copy.log) \
|| failed
if [ ${LFD} -ne 0 ]; then
echo "@@@ END-OF-LOG/ITERATION" >&${LFD}
@@ -516,6 +527,115 @@ function probe {
done
}
# generate caseset
declare -A caseset_id2caption
declare -A caseset_id2args
cases=0
for ((bits=2**${#options[@]}; --bits >= 0; )); do
split=30
cases=$((++cases))
caseset_id2caption[${cases}]="int-key,with-dups, split=${split}"
caseset_id2args[${cases}]="--table=+key.integer,+data.multi --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=max --mode=$(bits2options $bits)${syncmodes[cases%4]}"
cases=$((++cases))
caseset_id2caption[${cases}]="int-key,int-data, split=${split}"
caseset_id2args[${cases}]="--table=+key.integer,+data.integer --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=max --mode=$(bits2options $bits)${syncmodes[cases%4]}"
cases=$((++cases))
caseset_id2caption[${cases}]="with-dups, split=${split}"
caseset_id2args[${cases}]="--table=+data.multi --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=max --mode=$(bits2options $bits)${syncmodes[cases%4]}"
cases=$((++cases))
caseset_id2caption[${cases}]="int-key,fixdups, split=${split}"
caseset_id2args[${cases}]="--table=+key.integer,+data.fixed --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen=rnd --mode=$(bits2options $bits)${syncmodes[cases%4]}"
cases=$((++cases))
caseset_id2caption[${cases}]="fixdups, split=${split}"
caseset_id2args[${cases}]="--table=+data.fixed --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen=rnd --mode=$(bits2options $bits)${syncmodes[cases%4]}"
split=24
cases=$((++cases))
caseset_id2caption[${cases}]="int-key,with-dups, split=${split}"
caseset_id2args[${cases}]="--table=+key.integer,+data.multi --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=max --mode=$(bits2options $bits)${syncmodes[cases%4]}"
cases=$((++cases))
caseset_id2caption[${cases}]="int-key,int-data, split=${split}"
caseset_id2args[${cases}]="--table=+key.integer,+data.integer --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=max --mode=$(bits2options $bits)${syncmodes[cases%4]}"
cases=$((++cases))
caseset_id2caption[${cases}]="with-dups, split=${split}"
caseset_id2args[${cases}]="--table=+data.multi --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=max --mode=$(bits2options $bits)${syncmodes[cases%4]}"
cases=$((++cases))
caseset_id2caption[${cases}]="int-key,fixdups, split=${split}"
caseset_id2args[${cases}]="--table=+key.integer,+data.fixed --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen=rnd --mode=$(bits2options $bits)${syncmodes[cases%4]}"
cases=$((++cases))
caseset_id2caption[${cases}]="fixdups, split=${split}"
caseset_id2args[${cases}]="--table=+data.fixed --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen=rnd --mode=$(bits2options $bits)${syncmodes[cases%4]}"
split=16
cases=$((++cases))
caseset_id2caption[${cases}]="int-key,w/o-dups, split=${split}"
caseset_id2args[${cases}]="--table=+key.integer,-data.multi --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=1111 --mode=$(bits2options $bits)${syncmodes[cases%4]}"
cases=$((++cases))
caseset_id2caption[${cases}]="int-key,with-dups, split=${split}"
caseset_id2args[${cases}]="--table=+key.integer,+data.multi --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=max --mode=$(bits2options $bits)${syncmodes[cases%4]}"
cases=$((++cases))
caseset_id2caption[${cases}]="int-key,int-data, split=${split}"
caseset_id2args[${cases}]="--table=+key.integer,+data.integer --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=max --mode=$(bits2options $bits)${syncmodes[cases%4]}"
cases=$((++cases))
caseset_id2caption[${cases}]="w/o-dups, split=${split}"
caseset_id2args[${cases}]="--table=-data.multi --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=1111 --mode=$(bits2options $bits)${syncmodes[cases%4]}"
cases=$((++cases))
caseset_id2caption[${cases}]="with-dups, split=${split}"
caseset_id2args[${cases}]="--table=+data.multi --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=max --mode=$(bits2options $bits)${syncmodes[cases%4]}"
cases=$((++cases))
caseset_id2caption[${cases}]="int-key,fixdups, split=${split}"
caseset_id2args[${cases}]="--table=+key.integer,+data.fixed --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen=rnd --mode=$(bits2options $bits)${syncmodes[cases%4]}"
cases=$((++cases))
caseset_id2caption[${cases}]="fixdups, split=${split}"
caseset_id2args[${cases}]="--table=+data.fixed --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen=rnd --mode=$(bits2options $bits)${syncmodes[cases%4]}"
if [ "$EXTRA" != "no" ]; then
split=10
cases=$((++cases))
caseset_id2caption[${cases}]="int-key,w/o-dups, split=${split}"
caseset_id2args[${cases}]="--table=+key.integer,-data.multi --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=1111 --mode=$(bits2options $bits)${syncmodes[cases%4]}"
cases=$((++cases))
caseset_id2caption[${cases}]="int-key,with-dups, split=${split}"
caseset_id2args[${cases}]="--table=+key.integer,+data.multi --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=max --mode=$(bits2options $bits)${syncmodes[cases%4]}"
cases=$((++cases))
caseset_id2caption[${cases}]="int-key,int-data, split=${split}"
caseset_id2args[${cases}]="--table=+key.integer,+data.integer --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=max --mode=$(bits2options $bits)${syncmodes[cases%4]}"
cases=$((++cases))
caseset_id2caption[${cases}]="w/o-dups, split=${split}"
caseset_id2args[${cases}]="--table=-data.multi --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=1111 --mode=$(bits2options $bits)${syncmodes[cases%4]}"
cases=$((++cases))
caseset_id2caption[${cases}]="with-dups, split=${split}"
caseset_id2args[${cases}]="--table=+data.multi --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=max --mode=$(bits2options $bits)${syncmodes[cases%4]}"
cases=$((++cases))
caseset_id2caption[${cases}]="int-key,fixdups, split=${split}"
caseset_id2args[${cases}]="--table=+key.integer,+data.fixed --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen=rnd --mode=$(bits2options $bits)${syncmodes[cases%4]}"
cases=$((++cases))
caseset_id2caption[${cases}]="fixdups, split=${split}"
caseset_id2args[${cases}]="--table=+data.fixed --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen=rnd --mode=$(bits2options $bits)${syncmodes[cases%4]}"
fi
split=4
cases=$((++cases))
caseset_id2caption[${cases}]="int-key,w/o-dups, split=${split}"
caseset_id2args[${cases}]="--table=+key.integer,-data.multi --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=1111 --mode=$(bits2options $bits)${syncmodes[cases%4]}"
cases=$((++cases))
caseset_id2caption[${cases}]="int-key,int-data, split=${split}"
caseset_id2args[${cases}]="--table=+key.integer,+data.integer --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=max --mode=$(bits2options $bits)${syncmodes[cases%4]}"
cases=$((++cases))
caseset_id2caption[${cases}]="w/o-dups, split=${split}"
caseset_id2args[${cases}]="--table=-data.multi --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=1111 --mode=$(bits2options $bits)${syncmodes[cases%4]}"
cases=$((++cases))
caseset_id2caption[${cases}]="int-key,fixdups, split=${split}"
caseset_id2args[${cases}]="--table=+key.integer,+data.fixed --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen=rnd --mode=$(bits2options $bits)${syncmodes[cases%4]}"
cases=$((++cases))
caseset_id2caption[${cases}]="fixdups, split=${split}"
caseset_id2args[${cases}]="--table=+data.fixed --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen=rnd --mode=$(bits2options $bits)${syncmodes[cases%4]}"
done
###############################################################################
# 6. run stochastic iterations
function pass {
for ((round=1; round <= ROUNDS; ++round)); do
echo "======================================================================="
@@ -524,122 +644,22 @@ function pass {
else
${BANNER} "$nops / $wbatch"
fi
seed=$(($(date +%s) + RANDOM))
subcase=0
for ((bits=2**${#options[@]}; --bits >= 0; )); do
seed=$(($(date +%s) + RANDOM))
split=30
caption="$((++count)) int-key,with-dups, split=${split}, case $((++subcase)) of ${cases}" probe \
--prng-seed=${seed} --pagesize=$PAGESIZE --size-upper-upto=${db_size_mb}M --table=+key.integer,+data.multi --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=max \
--nops=$nops --batch.write=$wbatch --mode=$(bits2options $bits)${syncmodes[count%4]}
caption="$((++count)) int-key,int-data, split=${split}, case $((++subcase)) of ${cases}" probe \
--prng-seed=${seed} --pagesize=$PAGESIZE --size-upper-upto=${db_size_mb}M --table=+key.integer,+data.integer --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=max \
--nops=$nops --batch.write=$wbatch --mode=$(bits2options $bits)${syncmodes[count%4]}
caption="$((++count)) with-dups, split=${split}, case $((++subcase)) of ${cases}" probe \
--prng-seed=${seed} --pagesize=$PAGESIZE --size-upper-upto=${db_size_mb}M --table=+data.multi --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=max \
--nops=$nops --batch.write=$wbatch --mode=$(bits2options $bits)${syncmodes[count%4]}
caption="$((++count)) int-key,fixdups, split=${split}, case $((++subcase)) of ${cases}" probe \
--prng-seed=${seed} --pagesize=$PAGESIZE --size-upper-upto=${db_size_mb}M --table=+key.integer,+data.fixed --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen=rnd \
--nops=$nops --batch.write=$wbatch --mode=$(bits2options $bits)${syncmodes[count%4]}
caption="$((++count)) fixdups, split=${split}, case $((++subcase)) of ${cases}" probe \
--prng-seed=${seed} --pagesize=$PAGESIZE --size-upper-upto=${db_size_mb}M --table=+data.fixed --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen=rnd \
--nops=$nops --batch.write=$wbatch --mode=$(bits2options $bits)${syncmodes[count%4]}
split=24
caption="$((++count)) int-key,with-dups, split=${split}, case $((++subcase)) of ${cases}" probe \
--prng-seed=${seed} --pagesize=$PAGESIZE --size-upper-upto=${db_size_mb}M --table=+key.integer,+data.multi --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=max \
--nops=$nops --batch.write=$wbatch --mode=$(bits2options $bits)${syncmodes[count%4]}
caption="$((++count)) int-key,int-data, split=${split}, case $((++subcase)) of ${cases}" probe \
--prng-seed=${seed} --pagesize=$PAGESIZE --size-upper-upto=${db_size_mb}M --table=+key.integer,+data.integer --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=max \
--nops=$nops --batch.write=$wbatch --mode=$(bits2options $bits)${syncmodes[count%4]}
caption="$((++count)) with-dups, split=${split}, case $((++subcase)) of ${cases}" probe \
--prng-seed=${seed} --pagesize=$PAGESIZE --size-upper-upto=${db_size_mb}M --table=+data.multi --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=max \
--nops=$nops --batch.write=$wbatch --mode=$(bits2options $bits)${syncmodes[count%4]}
caption="$((++count)) int-key,fixdups, split=${split}, case $((++subcase)) of ${cases}" probe \
--prng-seed=${seed} --pagesize=$PAGESIZE --size-upper-upto=${db_size_mb}M --table=+key.integer,+data.fixed --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen=rnd \
--nops=$nops --batch.write=$wbatch --mode=$(bits2options $bits)${syncmodes[count%4]}
caption="$((++count)) fixdups, split=${split}, case $((++subcase)) of ${cases}" probe \
--prng-seed=${seed} --pagesize=$PAGESIZE --size-upper-upto=${db_size_mb}M --table=+data.fixed --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen=rnd \
--nops=$nops --batch.write=$wbatch --mode=$(bits2options $bits)${syncmodes[count%4]}
split=16
caption="$((++count)) int-key,w/o-dups, split=${split}, case $((++subcase)) of ${cases}" probe \
--prng-seed=${seed} --pagesize=$PAGESIZE --size-upper-upto=${db_size_mb}M --table=+key.integer,-data.multi --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=1111 \
--nops=$nops --batch.write=$wbatch --mode=$(bits2options $bits)${syncmodes[count%4]}
caption="$((++count)) int-key,with-dups, split=${split}, case $((++subcase)) of ${cases}" probe \
--prng-seed=${seed} --pagesize=$PAGESIZE --size-upper-upto=${db_size_mb}M --table=+key.integer,+data.multi --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=max \
--nops=$nops --batch.write=$wbatch --mode=$(bits2options $bits)${syncmodes[count%4]}
caption="$((++count)) int-key,int-data, split=${split}, case $((++subcase)) of ${cases}" probe \
--prng-seed=${seed} --pagesize=$PAGESIZE --size-upper-upto=${db_size_mb}M --table=+key.integer,+data.integer --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=max \
--nops=$nops --batch.write=$wbatch --mode=$(bits2options $bits)${syncmodes[count%4]}
caption="$((++count)) w/o-dups, split=${split}, case $((++subcase)) of ${cases}" probe \
--prng-seed=${seed} --pagesize=$PAGESIZE --size-upper-upto=${db_size_mb}M --table=-data.multi --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=1111 \
--nops=$nops --batch.write=$wbatch --mode=$(bits2options $bits)${syncmodes[count%4]}
caption="$((++count)) with-dups, split=${split}, case $((++subcase)) of ${cases}" probe \
--prng-seed=${seed} --pagesize=$PAGESIZE --size-upper-upto=${db_size_mb}M --table=+data.multi --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=max \
--nops=$nops --batch.write=$wbatch --mode=$(bits2options $bits)${syncmodes[count%4]}
caption="$((++count)) int-key,fixdups, split=${split}, case $((++subcase)) of ${cases}" probe \
--prng-seed=${seed} --pagesize=$PAGESIZE --size-upper-upto=${db_size_mb}M --table=+key.integer,+data.fixed --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen=rnd \
--nops=$nops --batch.write=$wbatch --mode=$(bits2options $bits)${syncmodes[count%4]}
caption="$((++count)) fixdups, split=${split}, case $((++subcase)) of ${cases}" probe \
--prng-seed=${seed} --pagesize=$PAGESIZE --size-upper-upto=${db_size_mb}M --table=+data.fixed --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen=rnd \
--nops=$nops --batch.write=$wbatch --mode=$(bits2options $bits)${syncmodes[count%4]}
if [ "$EXTRA" != "no" ]; then
split=10
caption="$((++count)) int-key,w/o-dups, split=${split}, case $((++subcase)) of ${cases}" probe \
--prng-seed=${seed} --pagesize=$PAGESIZE --size-upper-upto=${db_size_mb}M --table=+key.integer,-data.multi --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=1111 \
--nops=$nops --batch.write=$wbatch --mode=$(bits2options $bits)${syncmodes[count%4]}
caption="$((++count)) int-key,with-dups, split=${split}, case $((++subcase)) of ${cases}" probe \
--prng-seed=${seed} --pagesize=$PAGESIZE --size-upper-upto=${db_size_mb}M --table=+key.integer,+data.multi --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=max \
--nops=$nops --batch.write=$wbatch --mode=$(bits2options $bits)${syncmodes[count%4]}
caption="$((++count)) int-key,int-data, split=${split}, case $((++subcase)) of ${cases}" probe \
--prng-seed=${seed} --pagesize=$PAGESIZE --size-upper-upto=${db_size_mb}M --table=+key.integer,+data.integer --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=max \
--nops=$nops --batch.write=$wbatch --mode=$(bits2options $bits)${syncmodes[count%4]}
caption="$((++count)) w/o-dups, split=${split}, case $((++subcase)) of ${cases}" probe \
--prng-seed=${seed} --pagesize=$PAGESIZE --size-upper-upto=${db_size_mb}M --table=-data.multi --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=1111 \
--nops=$nops --batch.write=$wbatch --mode=$(bits2options $bits)${syncmodes[count%4]}
caption="$((++count)) with-dups, split=${split}, case $((++subcase)) of ${cases}" probe \
--prng-seed=${seed} --pagesize=$PAGESIZE --size-upper-upto=${db_size_mb}M --table=+data.multi --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=max \
--nops=$nops --batch.write=$wbatch --mode=$(bits2options $bits)${syncmodes[count%4]}
caption="$((++count)) int-key,fixdups, split=${split}, case $((++subcase)) of ${cases}" probe \
--prng-seed=${seed} --pagesize=$PAGESIZE --size-upper-upto=${db_size_mb}M --table=+key.integer,+data.fixed --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen=rnd \
--nops=$nops --batch.write=$wbatch --mode=$(bits2options $bits)${syncmodes[count%4]}
caption="$((++count)) fixdups, split=${split}, case $((++subcase)) of ${cases}" probe \
--prng-seed=${seed} --pagesize=$PAGESIZE --size-upper-upto=${db_size_mb}M --table=+data.fixed --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen=rnd \
--nops=$nops --batch.write=$wbatch --mode=$(bits2options $bits)${syncmodes[count%4]}
fi
split=4
caption="$((++count)) int-key,w/o-dups, split=${split}, case $((++subcase)) of ${cases}" probe \
--prng-seed=${seed} --pagesize=$PAGESIZE --size-upper-upto=${db_size_mb}M --table=+key.integer,-data.multi --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=1111 \
--nops=$nops --batch.write=$wbatch --mode=$(bits2options $bits)${syncmodes[count%4]}
caption="$((++count)) int-key,int-data, split=${split}, case $((++subcase)) of ${cases}" probe \
--prng-seed=${seed} --pagesize=$PAGESIZE --size-upper-upto=${db_size_mb}M --table=+key.integer,+data.integer --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=max \
--nops=$nops --batch.write=$wbatch --mode=$(bits2options $bits)${syncmodes[count%4]}
caption="$((++count)) w/o-dups, split=${split}, case $((++subcase)) of ${cases}" probe \
--prng-seed=${seed} --pagesize=$PAGESIZE --size-upper-upto=${db_size_mb}M --table=-data.multi --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=1111 \
--nops=$nops --batch.write=$wbatch --mode=$(bits2options $bits)${syncmodes[count%4]}
caption="$((++count)) int-key,fixdups, split=${split}, case $((++subcase)) of ${cases}" probe \
--prng-seed=${seed} --pagesize=$PAGESIZE --size-upper-upto=${db_size_mb}M --table=+key.integer,+data.fixed --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen=rnd \
--nops=$nops --batch.write=$wbatch --mode=$(bits2options $bits)${syncmodes[count%4]}
caption="$((++count)) fixdups, split=${split}, case $((++subcase)) of ${cases}" probe \
--prng-seed=${seed} --pagesize=$PAGESIZE --size-upper-upto=${db_size_mb}M --table=+data.fixed --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen=rnd \
--nops=$nops --batch.write=$wbatch --mode=$(bits2options $bits)${syncmodes[count%4]}
done # options
cases="${subcase}"
for id in $(seq 1 ${cases} | shuf); do
caption="$((++count)) ${caseset_id2caption[${id}]}, case $((++subcase))/${id} of ${cases}" probe \
--prng-seed=${seed} --pagesize=$PAGESIZE --size-upper-upto=${db_size_mb}M ${caseset_id2args[${id}]} --nops=$nops --batch.write=$wbatch
done
done
}
#------------------------------------------------------------------------------
if [ "$DELAY" != "0" ]; then
sleep $DELAY
fi
count=0
loop=0
cases='?'
if [[ $SMALL != "yes" ]]; then
for nops in 10 33 100 333 1000 3333 10000 33333 100000 333333 1000000 3333333 10000000 33333333 100000000 333333333 1000000000; do
if [ $nops -lt $FROM ]; then continue; fi