Compare commits

...

24 Commits

Author SHA1 Message Date
Леонид Юрьев (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
21 changed files with 666 additions and 283 deletions

View File

@ -4,9 +4,62 @@ ChangeLog
English version [by liar Google](https://libmdbx-dqdkfa-ru.translate.goog/md__change_log.html?_x_tr_sl=ru&_x_tr_tl=en) 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). and [by Yandex](https://translated.turbopages.org/proxy_u/ru-en.en/https/libmdbx.dqdkfa.ru/md__change_log.html).
## v0.13.7 в процессе накопления изменений, выпуск запланирован на конец мая. 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.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-машинах.
- В стохастическом скрипте реализован случайный порядок запуска отдельных тестов.
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------

View File

@ -384,8 +384,9 @@ endef
define uname2titer define uname2titer
case "$(UNAME)" in case "$(UNAME)" in
CYGWIN*|MINGW*|MSYS*|Windows*) echo 2;;
Darwin*|Mach*) echo 2;; Darwin*|Mach*) echo 2;;
*) echo 12;; *) if [ -z "${CI}" ]; then echo 7; else echo 3; fi;;
esac esac
endef endef
@ -809,17 +810,21 @@ endif
# Cross-compilation simple test # Cross-compilation simple test
CROSS_LIST = \ CROSS_LIST = \
mips64-linux-gnuabi64-gcc mips-linux-gnu-gcc \ aarch64-linux-gnu-gcc \
hppa-linux-gnu-gcc s390x-linux-gnu-gcc \ arm-linux-gnueabihf-gcc \
powerpc64-linux-gnu-gcc powerpc-linux-gnu-gcc \ hppa-linux-gnu-gcc \
arm-linux-gnueabihf-gcc aarch64-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) ## 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)
# sh4-linux-gnu-gcc - coredump (qemu mmap-troubles) # sparc64-linux-gnu-gcc - fails mmap/BAD_ADDRESS (previously: qemu-coredump sice mmap-troubles, qemu fails fcntl for F_SETLK/F_GETLK)
# sparc64-linux-gnu-gcc - coredump (qemu mmap-troubles, previously: qemu fails fcntl for F_SETLK/F_GETLK) # alpha-linux-gnu-gcc - qemu-coredump (qemu mmap-troubles)
# alpha-linux-gnu-gcc - 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)
# risc64-linux-gnu-gcc - coredump (qemu qemu fails fcntl for F_SETLK/F_GETLK) CROSS_LIST_NOQEMU = sparc64-linux-gnu-gcc alpha-linux-gnu-gcc powerpc-linux-gnu-gcc
CROSS_LIST_NOQEMU = sh4-linux-gnu-gcc sparc64-linux-gnu-gcc alpha-linux-gnu-gcc riscv64-linux-gnu-gcc
cross-gcc: cross-gcc:
@echo ' Re-building by cross-compiler for: $(CROSS_LIST_NOQEMU) $(CROSS_LIST)' @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 \ $(QUIET)for CC in $(CROSS_LIST); do \
echo "===================== $$CC + qemu"; \ echo "===================== $$CC + qemu"; \
$(MAKE) IOARENA=false CXXSTD= clean && \ $(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 $$?; \ $(MAKE) IOARENA=false smoke-singleprocess test-singleprocess || exit $$?; \
done done

View File

@ -1,6 +1,6 @@
<!-- Required extensions: pymdownx.betterem, pymdownx.tilde, pymdownx.emoji, pymdownx.tasklist, pymdownx.superfences --> <!-- 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) > 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). > and pay attention to the [`C++` API](https://gitflic.ru/project/erthink/libmdbx/blob?file=mdbx.h%2B%2B#line-num-1).

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`. Donations are welcome to ETH `0xD104d8f8B2dC312aaD74899F83EBf3EEBDC1EA3A`.
Всё будет хорошо! Всё будет хорошо!

View File

@ -603,7 +603,7 @@ retry_snap_meta:
continue; continue;
} }
rc = MDBX_ENODATA; 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; break;
sendfile_unavailable = true; sendfile_unavailable = true;
} }
@ -627,7 +627,7 @@ retry_snap_meta:
maybe useful for others FS */ maybe useful for others FS */
EINVAL) EINVAL)
not_the_same_filesystem = true; 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; copyfilerange_unavailable = true;
else else
break; break;
@ -755,35 +755,67 @@ __cold static int copy2pathname(MDBX_txn *txn, const pathchar_t *dest_path, MDBX
S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP
#endif #endif
); );
if (unlikely(rc != MDBX_SUCCESS))
return rc;
#if defined(_WIN32) || defined(_WIN64) #if defined(_WIN32) || defined(_WIN64)
/* no locking required since the file opened with ShareMode == 0 */ /* no locking required since the file opened with ShareMode == 0 */
#else #else
if (rc == MDBX_SUCCESS) { MDBX_STRUCT_FLOCK lock_op;
MDBX_STRUCT_FLOCK lock_op; memset(&lock_op, 0, sizeof(lock_op));
memset(&lock_op, 0, sizeof(lock_op)); lock_op.l_type = F_WRLCK;
lock_op.l_type = F_WRLCK; lock_op.l_whence = SEEK_SET;
lock_op.l_whence = SEEK_SET; lock_op.l_start = 0;
lock_op.l_start = 0; lock_op.l_len = OFF_T_MAX;
lock_op.l_len = OFF_T_MAX; const int err_fcntl = MDBX_FCNTL(newfd, MDBX_F_SETLK, &lock_op) ? errno : MDBX_SUCCESS;
if (MDBX_FCNTL(newfd, MDBX_F_SETLK, &lock_op))
rc = errno;
}
#if defined(LOCK_EX) && (!defined(__ANDROID_API__) || __ANDROID_API__ >= 24) const int err_flock =
if (rc == MDBX_SUCCESS && flock(newfd, LOCK_EX | LOCK_NB)) { #ifdef LOCK_EX
const int err_flock = errno, err_fs = osal_check_fs_local(newfd, 0); flock(newfd, LOCK_EX | LOCK_NB) ? errno : MDBX_SUCCESS;
if (err_flock != EAGAIN || err_fs != MDBX_EREMOTE) { #else
ERROR("%s flock(%" MDBX_PRIsPATH ") error %d, remote-fs check status %d", "unexpected", dest_path, err_flock, MDBX_ENOSYS;
err_fs); #endif /* LOCK_EX */
rc = err_flock;
} else { const int err_check_fs_local =
WARNING("%s flock(%" MDBX_PRIsPATH ") error %d, remote-fs check status %d", "ignore", dest_path, err_flock, /* avoid call osal_check_fs_local() on success */
err_fs); (!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 */
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 /* LOCK_EX && ANDROID_API >= 24 */
#endif /* Windows / POSIX */ #endif /* Windows / POSIX */
if (rc == MDBX_SUCCESS) if (rc == MDBX_SUCCESS)

View File

@ -213,7 +213,7 @@ __cold static void MDBX_PRINTF_ARGS(5, 6)
issue->next = chk->usr->scope->issues; issue->next = chk->usr->scope->issues;
chk->usr->scope->issues = issue; chk->usr->scope->issues = issue;
} else } else
chk_error_rc(scope, ENOMEM, "adding issue"); chk_error_rc(scope, MDBX_ENOMEM, "adding issue");
} }
va_list args; 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)) #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 #ifdef ENOSYS
if (err == ENOSYS) if (err == ENOSYS)
return MDBX_RESULT_TRUE; return MDBX_RESULT_TRUE;
@ -373,10 +373,21 @@ MDBX_MAYBE_UNUSED static inline int ignore_enosys(int err) {
if (err == EOPNOTSUPP) if (err == EOPNOTSUPP)
return MDBX_RESULT_TRUE; return MDBX_RESULT_TRUE;
#endif /* EOPNOTSUPP */ #endif /* EOPNOTSUPP */
if (err == EAGAIN)
return MDBX_RESULT_TRUE;
return err; 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) */ #endif /* defined(_WIN32) || defined(_WIN64) */
static inline int check_env(const MDBX_env *env, const bool wanna_active) { 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-слот еще не инициализирован в транзакции, а хендл не использовался */ /* dbi-слот еще не инициализирован в транзакции, а хендл не использовался */
txn->cursors[dbi] = nullptr; txn->cursors[dbi] = nullptr;
MDBX_txn *const parent = txn->parent; MDBX_txn *const parent = txn->parent;
if (parent) { if (unlikely(parent)) {
/* вложенная пишущая транзакция */ /* вложенная пишущая транзакция */
int rc = dbi_check(parent, dbi); int rc = dbi_check(parent, dbi);
/* копируем состояние table очищая new-флаги. */ /* копируем состояние table очищая new-флаги. */
@ -107,26 +107,31 @@ __noinline int dbi_import(MDBX_txn *txn, const size_t dbi) {
txn->dbi_state[dbi] = DBI_LINDO; txn->dbi_state[dbi] = DBI_LINDO;
} else { } else {
eASSERT(env, txn->dbi_seqs[dbi] != env->dbi_seqs[dbi].weak); eASSERT(env, txn->dbi_seqs[dbi] != env->dbi_seqs[dbi].weak);
if (unlikely((txn->dbi_state[dbi] & (DBI_VALID | DBI_OLDEN)) || txn->cursors[dbi])) { if (unlikely(txn->cursors[dbi])) {
/* хендл уже использовался в транзакции, но был закрыт или переоткрыт, /* хендл уже использовался в транзакции и остались висячие курсоры */
* либо при явном пере-открытии хендла есть висячие курсоры */
eASSERT(env, (txn->dbi_state[dbi] & DBI_STALE) == 0);
txn->dbi_seqs[dbi] = env->dbi_seqs[dbi].weak; txn->dbi_seqs[dbi] = env->dbi_seqs[dbi].weak;
txn->dbi_state[dbi] = DBI_OLDEN | DBI_LINDO; 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 */ /* читаем актуальные флаги и sequence */
struct dbi_snap_result snap = dbi_snap(env, dbi); struct dbi_snap_result snap = dbi_snap(env, dbi);
txn->dbi_seqs[dbi] = snap.sequence; txn->dbi_seqs[dbi] = snap.sequence;
if (snap.flags & DB_VALID) { if (snap.flags & DB_VALID) {
txn->dbs[dbi].flags = snap.flags & DB_PERSISTENT_FLAGS; 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_SUCCESS;
} }
return MDBX_BAD_DBI; 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; slot = (slot < scan) ? slot : scan;
continue; 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; slot = scan;
int err = dbi_check(txn, slot); int err = dbi_check(txn, slot);
if (err == MDBX_BAD_DBI && txn->dbi_state[slot] == (DBI_OLDEN | DBI_LINDO)) { 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 #if MDBX_ENABLE_DBI_LOCKFREE
/* Is the DB already open? */ /* Is the DB already open? */
const MDBX_env *const env = txn->env; 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) { for (size_t i = CORE_DBS; i < env->n_dbi; ++i) {
retry:
if ((env->dbs_flags[i] & DB_VALID) == 0) { if ((env->dbs_flags[i] & DB_VALID) == 0) {
free_slot = i; have_free_slot = true;
continue; continue;
} }
const uint32_t snap_seq = atomic_load32(&env->dbi_seqs[i], mo_AcquireRelease); struct dbi_snap_result snap = dbi_snap(env, i);
const uint16_t snap_flags = env->dbs_flags[i];
const MDBX_val snap_name = env->kvs[i].name; 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); 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; 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)) if (unlikely(!(snap.flags & DB_VALID) || !snap_name.iov_base || !snap_name.iov_len || !snap_cmp))
continue; /* похоже на столкновение с параллельно работающим обновлением */
goto slowpath_locking;
const bool name_match = snap_cmp(&snap_name, name) == 0; const bool name_match = snap_cmp(&snap_name, name) == 0;
osal_flush_incoherent_cpu_writeback(); if (unlikely(snap.sequence != atomic_load32(&env->dbi_seqs[i], mo_AcquireRelease) ||
if (unlikely(snap_seq != atomic_load32(&env->dbi_seqs[i], mo_AcquireRelease) ||
main_seq != atomic_load32(&env->dbi_seqs[MAIN_DBI], 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)) 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); 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 */ /* Fail, if no free slot and max hit */
if (unlikely(free_slot >= env->max_dbi)) if (unlikely(!have_free_slot))
return MDBX_DBS_FULL; return MDBX_DBS_FULL;
slowpath_locking:
#endif /* MDBX_ENABLE_DBI_LOCKFREE */ #endif /* MDBX_ENABLE_DBI_LOCKFREE */
rc = osal_fastmutex_acquire(&txn->env->dbi_lock); rc = osal_fastmutex_acquire(&txn->env->dbi_lock);

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; rc = MDBX_RESULT_TRUE;
#if defined(MADV_REMOVE) #if defined(MADV_REMOVE)
if (env->flags & MDBX_WRITEMAP) if (env->flags & MDBX_WRITEMAP)
rc = madvise(ptr_disp(env->dxb_mmap.base, size_bytes), prev_size - size_bytes, MADV_REMOVE) ? ignore_enosys(errno) rc = madvise(ptr_disp(env->dxb_mmap.base, size_bytes), prev_size - size_bytes, MADV_REMOVE)
: MDBX_SUCCESS; ? ignore_enosys_and_eagain(errno)
: MDBX_SUCCESS;
#endif /* MADV_REMOVE */ #endif /* MADV_REMOVE */
#if defined(MADV_DONTNEED) #if defined(MADV_DONTNEED)
if (rc == MDBX_RESULT_TRUE) if (rc == MDBX_RESULT_TRUE)
rc = madvise(ptr_disp(env->dxb_mmap.base, size_bytes), prev_size - size_bytes, MADV_DONTNEED) 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; : MDBX_SUCCESS;
#elif defined(POSIX_MADV_DONTNEED) #elif defined(POSIX_MADV_DONTNEED)
if (rc == MDBX_RESULT_TRUE) 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); void *const ptr = ptr_disp(env->dxb_mmap.base, offset);
if (enable) { if (enable) {
#if defined(MADV_NORMAL) #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))) if (unlikely(MDBX_IS_ERROR(err)))
return err; return err;
#elif defined(POSIX_MADV_NORMAL) #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; 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); (void)/* Ignore ENOTTY for DB on the ram-disk and so on */ fcntl(env->lazy_fd, F_RDADVISE, &hint);
#elif defined(MADV_WILLNEED) #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))) if (unlikely(MDBX_IS_ERROR(err)))
return err; return err;
#elif defined(POSIX_MADV_WILLNEED) #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 { } else {
mincore_clean_cache(env); mincore_clean_cache(env);
#if defined(MADV_RANDOM) #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))) if (unlikely(MDBX_IS_ERROR(err)))
return err; return err;
#elif defined(POSIX_MADV_RANDOM) #elif defined(POSIX_MADV_RANDOM)
@ -686,14 +687,16 @@ __cold int dxb_setup(MDBX_env *env, const int lck_rc, const mdbx_mode_t mode_bit
return err; return err;
#if defined(MADV_DONTDUMP) #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))) if (unlikely(MDBX_IS_ERROR(err)))
return err; return err;
#endif /* MADV_DONTDUMP */ #endif /* MADV_DONTDUMP */
#if defined(MADV_DODUMP) #if defined(MADV_DODUMP)
if (globals.runtime_flags & MDBX_DBG_DUMP) { if (globals.runtime_flags & MDBX_DBG_DUMP) {
const size_t meta_length_aligned2os = pgno_align2os_bytes(env, NUM_METAS); 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))) if (unlikely(MDBX_IS_ERROR(err)))
return err; return err;
} }
@ -932,7 +935,7 @@ __cold int dxb_setup(MDBX_env *env, const int lck_rc, const mdbx_mode_t mode_bit
bytes2pgno(env, env->dxb_mmap.current)); bytes2pgno(env, env->dxb_mmap.current));
err = madvise(ptr_disp(env->dxb_mmap.base, used_aligned2os_bytes), env->dxb_mmap.current - used_aligned2os_bytes, err = madvise(ptr_disp(env->dxb_mmap.base, used_aligned2os_bytes), env->dxb_mmap.current - used_aligned2os_bytes,
MADV_REMOVE) MADV_REMOVE)
? ignore_enosys(errno) ? ignore_enosys_and_eagain(errno)
: MDBX_SUCCESS; : MDBX_SUCCESS;
if (unlikely(MDBX_IS_ERROR(err))) if (unlikely(MDBX_IS_ERROR(err)))
return err; return err;
@ -942,7 +945,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)); 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, err = madvise(ptr_disp(env->dxb_mmap.base, used_aligned2os_bytes), env->dxb_mmap.current - used_aligned2os_bytes,
MADV_DONTNEED) MADV_DONTNEED)
? ignore_enosys(errno) ? ignore_enosys_and_eagain(errno)
: MDBX_SUCCESS; : MDBX_SUCCESS;
if (unlikely(MDBX_IS_ERROR(err))) if (unlikely(MDBX_IS_ERROR(err)))
return err; return err;
@ -1034,7 +1037,7 @@ int dxb_sync_locked(MDBX_env *env, unsigned flags, meta_t *const pending, troika
#endif /* MADV_FREE */ #endif /* MADV_FREE */
int err = madvise(ptr_disp(env->dxb_mmap.base, discard_edge_bytes), prev_discarded_bytes - discard_edge_bytes, int err = madvise(ptr_disp(env->dxb_mmap.base, discard_edge_bytes), prev_discarded_bytes - discard_edge_bytes,
advise) advise)
? ignore_enosys(errno) ? ignore_enosys_and_eagain(errno)
: MDBX_SUCCESS; : MDBX_SUCCESS;
#else #else
int err = ignore_enosys(posix_madvise(ptr_disp(env->dxb_mmap.base, discard_edge_bytes), int err = ignore_enosys(posix_madvise(ptr_disp(env->dxb_mmap.base, discard_edge_bytes),

View File

@ -28,12 +28,6 @@
typedef struct iov_ctx iov_ctx_t; typedef struct iov_ctx iov_ctx_t;
#include "osal.h" #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 "options.h"
#include "atomics-types.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))) { if (unlikely(!is_gc_usable(txn, mc, flags)))
eASSERT(env, (txn->flags & txn_gc_drained) || num > 1);
goto no_gc; goto no_gc;
}
eASSERT(env, (flags & (ALLOC_COALESCE | ALLOC_LIFO | ALLOC_SHOULD_SCAN)) == 0); eASSERT(env, (flags & (ALLOC_COALESCE | ALLOC_LIFO | ALLOC_SHOULD_SCAN)) == 0);
flags += (env->flags & MDBX_LIFORECLAIM) ? ALLOC_LIFO : 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 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)); STATIC_ASSERT(sizeof(off_t) >= sizeof(void *) && sizeof(off_t) >= sizeof(size_t));
#ifdef __ANDROID_API__ #if defined(__ANDROID_API__) && __ANDROID_API__ < 24
STATIC_ASSERT_MSG((sizeof(off_t) * 8 == MDBX_WORDBITS), "The bitness of system `off_t` type is mismatch. Please " STATIC_ASSERT_MSG((sizeof(off_t) * CHAR_BIT == MDBX_WORDBITS),
"fix build and/or NDK configuration."); "The bitness of system `off_t` type is mismatch. Please "
#endif /* Android */ "fix build and/or NDK configuration.");
#endif /* Android && API < 24 */
assert(offset >= 0 && len > 0); assert(offset >= 0 && len > 0);
assert((uint64_t)offset < (uint64_t)INT64_MAX && (uint64_t)len < (uint64_t)INT64_MAX && assert((uint64_t)offset < (uint64_t)INT64_MAX && (uint64_t)len < (uint64_t)INT64_MAX &&
(uint64_t)(offset + len) > (uint64_t)offset); (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; rc = errno;
#if MDBX_USE_OFDLOCKS #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 */ /* fallback to non-OFD locks */
if (cmd == MDBX_F_OFD_SETLK) if (cmd == MDBX_F_OFD_SETLK)
cmd = MDBX_F_SETLK; cmd = MDBX_F_SETLK;
@ -460,6 +462,10 @@ __cold MDBX_INTERNAL int lck_destroy(MDBX_env *env, MDBX_env *inprocess_neighbor
jitter4testing(false); jitter4testing(false);
} }
#if MDBX_LOCKING == MDBX_LOCKING_SYSV
env->me_sysv_ipc.semid = -1;
#endif /* MDBX_LOCKING */
if (current_pid != env->pid) { if (current_pid != env->pid) {
eASSERT(env, !inprocess_neighbor); eASSERT(env, !inprocess_neighbor);
NOTICE("drown env %p after-fork pid %d -> %d", __Wpedantic_format_voidptr(env), env->pid, current_pid); 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; 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; int err = MDBX_ENOSYS;
#if MDBX_LOCKING == MDBX_LOCKING_POSIX2001 || MDBX_LOCKING == MDBX_LOCKING_POSIX2008 #if MDBX_LOCKING == MDBX_LOCKING_POSIX2001 || MDBX_LOCKING == MDBX_LOCKING_POSIX2008
err = pthread_mutex_unlock(ipc); err = pthread_mutex_unlock(ipc);
#elif MDBX_LOCKING == MDBX_LOCKING_POSIX1988 #elif MDBX_LOCKING == MDBX_LOCKING_POSIX1988
err = sem_post(ipc) ? errno : MDBX_SUCCESS; err = sem_post(ipc) ? errno : MDBX_SUCCESS;
#elif MDBX_LOCKING == MDBX_LOCKING_SYSV #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; err = EPERM;
else { else {
*ipc = 0; *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) { int lck_txn_lock(MDBX_env *env, bool dont_wait) {
TRACE("%swait %s", dont_wait ? "dont-" : "", ">>"); TRACE("%swait %s", dont_wait ? "dont-" : "", ">>");
eASSERT(env, env->basal_txn || (env->lck == lckless_stub(env) && (env->flags & MDBX_RDONLY)));
jitter4testing(true); jitter4testing(true);
const int err = osal_ipclock_lock(env, &env->lck->wrt_lock, dont_wait); const int err = osal_ipclock_lock(env, &env->lck->wrt_lock, dont_wait);
int rc = err; int rc = err;
@ -841,10 +846,8 @@ int lck_txn_lock(MDBX_env *env, bool dont_wait) {
void lck_txn_unlock(MDBX_env *env) { void lck_txn_unlock(MDBX_env *env) {
TRACE("%s", ">>"); TRACE("%s", ">>");
if (env->basal_txn) { 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; 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); int err = osal_ipclock_unlock(env, &env->lck->wrt_lock);
TRACE("<< err %d", err); 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) if (env->flags & MDBX_EXCLUSIVE)
goto done; goto done;
@ -104,10 +104,11 @@ int lck_txn_lock(MDBX_env *env, bool dontwait) {
} }
if (rc == MDBX_SUCCESS) { if (rc == MDBX_SUCCESS) {
done: done:
if (env->basal_txn)
env->basal_txn->owner = osal_thread_self();
/* Zap: Failing to release lock 'env->windowsbug_lock' /* Zap: Failing to release lock 'env->windowsbug_lock'
* in function 'mdbx_txn_lock' */ * in function 'mdbx_txn_lock' */
MDBX_SUPPRESS_GOOFY_MSVC_ANALYZER(26115); MDBX_SUPPRESS_GOOFY_MSVC_ANALYZER(26115);
env->basal_txn->owner = osal_thread_self();
return MDBX_SUCCESS; return MDBX_SUCCESS;
} }
@ -116,14 +117,15 @@ int lck_txn_lock(MDBX_env *env, bool dontwait) {
} }
void lck_txn_unlock(MDBX_env *env) { 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) { if ((env->flags & MDBX_EXCLUSIVE) == 0) {
const HANDLE fd4data = env->ioring.overlapped_fd ? env->ioring.overlapped_fd : env->lazy_fd; const HANDLE fd4data = env->ioring.overlapped_fd ? env->ioring.overlapped_fd : env->lazy_fd;
int err = funlock(fd4data, DXB_BODY); int err = funlock(fd4data, DXB_BODY);
if (err != MDBX_SUCCESS) if (err != MDBX_SUCCESS)
mdbx_panic("%s failed: err %u", __func__, err); 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); LeaveCriticalSection(&env->windowsbug_lock);
} }

View File

@ -69,13 +69,13 @@ __cold static int lck_setup_locked(MDBX_env *env) {
return err; return err;
#ifdef MADV_DODUMP #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))) if (unlikely(MDBX_IS_ERROR(err)))
return err; return err;
#endif /* MADV_DODUMP */ #endif /* MADV_DODUMP */
#ifdef MADV_WILLNEED #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))) if (unlikely(MDBX_IS_ERROR(err)))
return err; return err;
#elif defined(POSIX_MADV_WILLNEED) #elif defined(POSIX_MADV_WILLNEED)

View File

@ -193,7 +193,14 @@ typedef struct osal_mmap {
#elif defined(__ANDROID_API__) #elif defined(__ANDROID_API__)
#if __ANDROID_API__ < 24 #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 #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 #else
#define MDBX_HAVE_PWRITEV 1 #define MDBX_HAVE_PWRITEV 1
#endif #endif

View File

@ -122,6 +122,8 @@
#pragma warning(disable : 6235) /* <expression> is always a constant */ #pragma warning(disable : 6235) /* <expression> is always a constant */
#pragma warning(disable : 6237) /* <expression> is never evaluated and might \ #pragma warning(disable : 6237) /* <expression> is never evaluated and might \
have side effects */ 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 #endif
#pragma warning(disable : 4710) /* 'xyz': function not inlined */ #pragma warning(disable : 4710) /* 'xyz': function not inlined */
#pragma warning(disable : 4711) /* function 'xyz' selected for automatic \ #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 #if __ANDROID_API__ >= 21
#include <sys/sendfile.h> #include <sys/sendfile.h>
#endif #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 */ #endif /* Android */
#if defined(HAVE_SYS_STAT_H) || __has_include(<sys/stat.h>) #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
#endif /* __BYTE_ORDER__ || __ORDER_LITTLE_ENDIAN__ || __ORDER_BIG_ENDIAN__ */ #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 */ /* Availability of CMOV or equivalent */

View File

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

View File

@ -3,36 +3,62 @@
# Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> # Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru>
# SPDX-License-Identifier: Apache-2.0 # 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-" 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}* rm -rf ${PREFIX}*
# git clean -x -f -d && make test-assertions # 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 W=0
for ps in min 4k max; do for ps in min 4k max; do
for from in 1 30000; do for from in 1 30000; do
for n in 0 1 2 3; 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 if [ $n -eq 0 ]; then
tmux new-window -t mdbx:$((++W)) -n "page-${ps}.from-${from}" -k -d "$CMD" ${TMUX} new-window -t mdbx:$((++W)) -n "page-${ps}.from-${from}" -k -d "$CMD"
tmux select-layout -E tiled ${TMUX} select-layout -E tiled
else else
tmux split-window -t mdbx:$W -l 20% -d $CMD ${TMUX} split-window -t mdbx:$W -l 20% -d $CMD
fi fi
test_numacycle
done done
for n in 0 1 2 3; do 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 if [ $n -eq 0 ]; then
tmux new-window -t mdbx:$((++W)) -n "page-${ps}.from-${from}-extra" -k -d "$CMD" ${TMUX} new-window -t mdbx:$((++W)) -n "page-${ps}.from-${from}-extra" -k -d "$CMD"
tmux select-layout -E tiled ${TMUX} select-layout -E tiled
else else
tmux split-window -t mdbx:$W -l 20% -d $CMD ${TMUX} split-window -t mdbx:$W -l 20% -d $CMD
fi fi
test_numacycle
done done
done done
done done
tmux attach -t mdbx ${TMUX} attach -t mdbx

View File

@ -2,17 +2,9 @@
#include <iostream> #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, bool case1() {
unsigned length) noexcept {
(void)length;
(void)loglevel;
fprintf(stdout, "%s:%u %s", function, line, msg);
}
int doit() {
mdbx::path db_filename = "test-dbi";
mdbx::env::remove(db_filename); mdbx::env::remove(db_filename);
mdbx::env::operate_parameters operateParameters(100, 10, mdbx::env::nested_transactions); mdbx::env::operate_parameters operateParameters(100, 10, mdbx::env::nested_transactions);
@ -45,15 +37,15 @@ int doit() {
MDBX_stat stat; MDBX_stat stat;
int err = mdbx_dbi_stat(txn, dbi, &stat, sizeof(stat)); int err = mdbx_dbi_stat(txn, dbi, &stat, sizeof(stat));
if (err != MDBX_BAD_DBI) { if (err != MDBX_BAD_DBI) {
std::cerr << "unexpected result err-code " << err; std::cerr << "Unexpected err " << err << " (wanna MDBX_BAD_DBI/-30780)\n";
return EXIT_FAILURE; return false;
} }
txn.commit(); txn.commit();
} }
{ {
// снова проверяем что таблица открывается и хендл доступень в родительской транзакции после коммита открывшей его // снова проверяем что таблица открывается и хендл доступень в родительской транзакции,
// дочерней // после коммита открывшей его дочерней
mdbx::txn_managed txn = env.start_write(); mdbx::txn_managed txn = env.start_write();
mdbx::txn_managed nested = txn.start_nested(); mdbx::txn_managed nested = txn.start_nested();
mdbx::map_handle dbi = nested.open_map_accede("fap1"); mdbx::map_handle dbi = nested.open_map_accede("fap1");
@ -63,8 +55,165 @@ int doit() {
env.close_map(dbi); env.close_map(dbi);
} }
std::cout << "OK\n"; return true;
return EXIT_SUCCESS; }
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[]) { int main(int argc, char *argv[]) {

View File

@ -31,15 +31,6 @@ int main(int argc, const char *argv[]) {
#include <latch> #include <latch>
#include <thread> #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) { bool case0(const mdbx::path &path) {
mdbx::env_managed::create_parameters createParameters; mdbx::env_managed::create_parameters createParameters;
createParameters.geometry.make_dynamic(21 * mdbx::env::geometry::MiB, 84 * mdbx::env::geometry::MiB); 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; 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() { int doit() {
mdbx::path path = "test-txn"; mdbx::path path = "test-txn";
mdbx::env::remove(path); mdbx::env::remove(path);
bool ok = case0(path); bool ok = true;
ok = case0(path) && ok;
ok = case1(path) && ok; ok = case1(path) && ok;
ok = case2(path, false) && ok; ok = case2(path, false) && ok;
ok = case2(path, true) && ok; ok = case2(path, true) && ok;
ok = case3(path, false) && ok;
ok = case3(path, true) && ok;
std::cout << (ok ? "OK\n" : "FAIL\n"); std::cout << (ok ? "OK\n" : "FAIL\n");
return ok ? EXIT_SUCCESS : EXIT_FAILURE; 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[]) { int main(int argc, char *argv[]) {
(void)argc; (void)argc;
(void)argv; (void)argv;

View File

@ -28,6 +28,7 @@ REPORT_DEPTH=no
REPEAT=11 REPEAT=11
ROUNDS=1 ROUNDS=1
SMALL=no SMALL=no
NUMABIND=
while [ -n "$1" ] while [ -n "$1" ]
do do
@ -51,6 +52,7 @@ do
echo "--db-upto-gb NN --''--''--''--''--''--''--''--''-- NN gigabytes" echo "--db-upto-gb NN --''--''--''--''--''--''--''--''-- NN gigabytes"
echo "--no-geometry-jitter Disable jitter for geometry upper-size" 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 "--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 "--dont-check-ram-size Don't check available RAM"
echo "--extra Iterate extra modes/flags" echo "--extra Iterate extra modes/flags"
echo "--taillog Dump tail of test log on failure" echo "--taillog Dump tail of test log on failure"
@ -209,6 +211,15 @@ do
--small) --small)
SMALL=yes 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'" echo "Unknown option '$1'"
exit -2 exit -2
@ -393,7 +404,7 @@ if [ "$SKIP_MAKE" != "yes" ]; then
fi fi
############################################################################### ###############################################################################
# 5. run stochastic iterations # 5. internal preparations
if which setsid >/dev/null 2>/dev/null; then if which setsid >/dev/null 2>/dev/null; then
SETSID=$(which setsid) SETSID=$(which setsid)
@ -504,9 +515,9 @@ function probe {
else else
exec {LFD}> >(logger) exec {LFD}> >(logger)
fi 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} \ ${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} \
&& ${MONITOR} ./mdbx_chk -q ${TESTDB_DIR}/long.db | tee ${TESTDB_DIR}/long-chk.log \ && ${NUMABIND} ${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) \ && ([ ! -e ${TESTDB_DIR}/long.db-copy ] || ${NUMABIND} ${MONITOR} ./mdbx_chk -q ${TESTDB_DIR}/long.db-copy | tee ${TESTDB_DIR}/long-chk-copy.log) \
|| failed || failed
if [ ${LFD} -ne 0 ]; then if [ ${LFD} -ne 0 ]; then
echo "@@@ END-OF-LOG/ITERATION" >&${LFD} echo "@@@ END-OF-LOG/ITERATION" >&${LFD}
@ -516,6 +527,115 @@ function probe {
done 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 { function pass {
for ((round=1; round <= ROUNDS; ++round)); do for ((round=1; round <= ROUNDS; ++round)); do
echo "=======================================================================" echo "======================================================================="
@ -524,122 +644,22 @@ function pass {
else else
${BANNER} "$nops / $wbatch" ${BANNER} "$nops / $wbatch"
fi fi
seed=$(($(date +%s) + RANDOM))
subcase=0 subcase=0
for ((bits=2**${#options[@]}; --bits >= 0; )); do for id in $(seq 1 ${cases} | shuf); do
seed=$(($(date +%s) + RANDOM)) 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
split=30 done
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}"
done done
} }
#------------------------------------------------------------------------------
if [ "$DELAY" != "0" ]; then if [ "$DELAY" != "0" ]; then
sleep $DELAY sleep $DELAY
fi fi
count=0 count=0
loop=0 loop=0
cases='?'
if [[ $SMALL != "yes" ]]; then 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 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 if [ $nops -lt $FROM ]; then continue; fi