mdbx: new mdbx_get_cached() with separated tests (v0.14.1-234-g329ac7b7).

2026-01-04 mdbx: update ChangeLog.
2026-01-04 mdbx-docs: add BTC and SOL to donations.
2026-01-04 mdbx-docs: refine common/title doxygen descriptions.
2026-01-04 mdbx-cmake: add more docs files.
2026-01-04 mdbx-doc: doxygen comments for `mdbx_cache_get()`.
2026-01-04 mdbx-test: add multithread case into `extra/get_cached`.
2026-01-03 mdbx: fix ABA-like issue and add `MDBX_CACHE_UNABLE`.
2026-01-03 mdbx: merge branch 'get-cached' into `devel`.
\
 2025-12-06 mdbx-test: initial basic test for a multithreaded get-cached API.
 2025-12-02 mdbx-test: using randomly/generated path for walking deep-transition matrix inside `extra/get-cached`.
 2025-12-02 mdbx-test: add basic test for `get-cached` feature.
 2025-11-27 mdbx++: initial/minimal support for `get-cached` feature (draft).
 2025-12-01 mdbx: preview of `mdbx_get_cached()`.
This commit is contained in:
Леонид Юрьев (Leonid Yuriev)
2026-01-04 14:57:50 +03:00
parent 13a7157cec
commit 03a75a91c7
15 changed files with 660 additions and 46 deletions
+4 -1
View File
@@ -1,7 +1,9 @@
# Copyright (c) 2020-2026 Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> ###############################################
# SPDX-License-Identifier: Apache-2.0
#
# Donations are welcome to ETH `0xD104d8f8B2dC312aaD74899F83EBf3EEBDC1EA3A`. Всё будет хорошо!
# Donations are welcome to ETH `0xD104d8f8B2dC312aaD74899F83EBf3EEBDC1EA3A`,
# BTC `bc1qzvl9uegf2ea6cwlytnanrscyv8snwsvrc0xfsu`, SOL `FTCTgbHajoLVZGr8aEFWMzx3NDMyS5wXJgfeMTmJznRi`.
# Всё будет хорошо!
# libmdbx = { Revised and extended descendant of Symas LMDB. }
# Please see README.md at https://sourcecraft.dev/dqdkfa/libmdbx
@@ -573,6 +575,7 @@ else()
endif()
message(FATAL_ERROR "This \"${CMAKE_CURRENT_LIST_FILE}\" has been trimmed and is therefore intended only for build from an amalgamated form of the source code.")
endif(MDBX_AMALGAMATED_SOURCE)
if(MDBX_BUILD_CXX)
message(STATUS "Use C${MDBX_C_STANDARD} and C++${MDBX_CXX_STANDARD} for libmdbx")
list(APPEND LIBMDBX_PUBLIC_HEADERS mdbx.h++)
+12 -6
View File
@@ -5,7 +5,9 @@ English version [by liar Google](https://libmdbx-dqdkfa-ru.translate.goog/md__ch
The source code is availale on [SourceCraft](https://sourcecraft.dev/dqdkfa/libmdbx) and mirrors on [abf.io](https://abf.io/erthink/libmdbx) and [GitFlic](https://gitflic.ru/project/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`. Всё будет хорошо!
Donations are welcome to ETH `0xD104d8f8B2dC312aaD74899F83EBf3EEBDC1EA3A`,
BTC `bc1qzvl9uegf2ea6cwlytnanrscyv8snwsvrc0xfsu`, SOL `FTCTgbHajoLVZGr8aEFWMzx3NDMyS5wXJgfeMTmJznRi`.
Всё будет хорошо!
## v0.14.2 в разработке без конкретизации даты выпуска
@@ -29,7 +31,13 @@ Donations are welcome to ETH `0xD104d8f8B2dC312aaD74899F83EBf3EEBDC1EA3A`. Вс
Объём накладных расходов теперь более пропорционален объему совершаемых операций. Поэтому, в большинстве сценариев накладные расходы чуть меньше, но наоборот чуть больше при отмене вложенных транзакций.
- В API копирования БД добавлена опция `MDBX_CP_OVERWRITE` (перезапись целевого файла), а в утилиту `mdbx_copy` аналогичная по смыслу опция командной строки `-f` .
- Расширение API:
- в функции копирования БД добавлена опция `MDBX_CP_OVERWRITE` (перезапись целевого файла), а в утилиту `mdbx_copy` аналогичная по смыслу опция командной строки `-f` .
- добавлена простая реализация функции `mdbx_cursor_bunch_delete()`, которая выполняет массивное удаление соседствующих элементов значительно быстрее, за счёт исключения из дерева целиком страниц и ветвей с удаляемыми элементами.
- добавлены функции получения/поиска данных с "кэшированием" `mdbx_cache_get()` и `mdbx_cache_get_SingleThreaded()`.
- Поддержка Harmony OS (OHOS).
@@ -42,10 +50,8 @@ Donations are welcome to ETH `0xD104d8f8B2dC312aaD74899F83EBf3EEBDC1EA3A`. Вс
- Основной репозиторий libmdbx перенесен с Gitflic на SourceCraft.
Со своей стороны, я удручен необходимостью совершать такие манипуляции, ибо они создают существенные неудобства пользователям, но (к сожалению) для того есть достаточные причины:
- Вместо обещанной интернационализации на Gitflic осталась только русскоязычная локализация с массой технический особенностей, затрудняющих использование систем машинного перевода. Это сделало невозможным использование сервиса многими пользователями и породило ряд справедливых жалоб/упреков, в том числе от разработчиков из Китая, Бразилии, Кореи, Ирана и т.д.
- За три года, прошедшие с миграции на Gitflic, не исправлено несколько возмутительных ошибок в редакторе markdown, что превращало оформление релизов в раздражающую борьбу. Кроме этого из публичного пространства пропал roadmap развития. Суммарно это заставило меня отказаться от Gitflic.
- В API добавлена простая реализация функции `mdbx_cursor_bunch_delete()`, которая выполняет массивное удаление соседствующих элементов знаничельно быстрее, за счёт исключения из дерева целиком страниц и ветвей с удаляемыми элементами.
- Вместо обещанной интернационализации на Gitflic осталась только русскоязычная локализация с массой технический особенностей, затрудняющих использование систем машинного перевода. Это сделало невозможным использование сервиса многими пользователями и породило ряд справедливых жалоб/упреков, в том числе от разработчиков из Китая, Бразилии, Кореи, Ирана и т.д.
- За три года, прошедшие с миграции на Gitflic, не исправлено несколько возмутительных ошибок в редакторе markdown, что превращало оформление релизов в раздражающую борьбу. Кроме этого из публичного пространства пропал roadmap развития. Суммарно это заставило меня отказаться от Gitflic.
Исправления:
+2 -1
View File
@@ -8,7 +8,8 @@ documentation, C++ API description and links to the original git repo
with the source code. Questions, feedback and suggestions are welcome
to the Telegram' group https://t.me/libmdbx.
Donations are welcome to the Ethereum/ERC-20 `0xD104d8f8B2dC312aaD74899F83EBf3EEBDC1EA3A`.
Donations are welcome to ETH `0xD104d8f8B2dC312aaD74899F83EBf3EEBDC1EA3A`,
BTC `bc1qzvl9uegf2ea6cwlytnanrscyv8snwsvrc0xfsu`, SOL `FTCTgbHajoLVZGr8aEFWMzx3NDMyS5wXJgfeMTmJznRi`.
Всё будет хорошо!
Copyright 2015-2026 Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru>
+2 -1
View File
@@ -25,7 +25,8 @@ Historically, _libmdbx_ is a deeply revised and extended descendant of the legen
[![Telergam: Support | Discussions | News](https://img.shields.io/endpoint?color=scarlet&logo=telegram&label=Support%20%7C%20Discussions%20%7C%20News&url=https%3A%2F%2Ftg.sumanjay.workers.dev%2Flibmdbx)](https://t.me/libmdbx)
> Please refer to the online [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://sourcecraft.dev/dqdkfa/libmdbx/blob?file=mdbx.h%2B%2B#line-num-1). Donations are welcome to the Ethereum/ERC-20 `0xD104d8f8B2dC312aaD74899F83EBf3EEBDC1EA3A`.
> 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://sourcecraft.dev/dqdkfa/libmdbx/blob?file=mdbx.h%2B%2B#line-num-1).Donations are welcome to ETH `0xD104d8f8B2dC312aaD74899F83EBf3EEBDC1EA3A`,
BTC `bc1qzvl9uegf2ea6cwlytnanrscyv8snwsvrc0xfsu`, SOL `FTCTgbHajoLVZGr8aEFWMzx3NDMyS5wXJgfeMTmJznRi`.
> Всё будет хорошо!
Telegram Group archive: [1](https://libmdbx.dqdkfa.ru/tg-archive/messages1.html), [2](https://libmdbx.dqdkfa.ru/tg-archive/messages2.html), [3](https://libmdbx.dqdkfa.ru/tg-archive/messages3.html), [4](https://libmdbx.dqdkfa.ru/tg-archive/messages4.html), [5](https://libmdbx.dqdkfa.ru/tg-archive/messages5.html), [6](https://libmdbx.dqdkfa.ru/tg-archive/messages6.html), [7](https://libmdbx.dqdkfa.ru/tg-archive/messages7.html).
+1 -1
View File
@@ -1 +1 @@
{ "git_describe": "v0.14.1-220-g0f8b288c", "git_timestamp": "2026-01-02T23:27:02+03:00", "git_tree": "d490bb4b78537c494ffecf7b31b961c48a715d94", "git_commit": "0f8b288ce9d1e1028f8bb760f5f87eafa8e94bfe", "semver": "0.14.1.220" }
{ "git_describe": "v0.14.1-234-g329ac7b7", "git_timestamp": "2026-01-04T14:41:25+03:00", "git_tree": "26e213307787c195455ed09e75f197acc4c22d3d", "git_commit": "329ac7b72663cd5524733f61ce5a7cff9615f5bc", "semver": "0.14.1.234" }
+386 -5
View File
@@ -4,7 +4,7 @@
#define xMDBX_ALLOY 1 /* alloyed build */
#define MDBX_BUILD_SOURCERY e30d838fb2509384f10339f050c97b34b790926488f78b959203d21608b421aa_v0_14_1_220_g0f8b288c
#define MDBX_BUILD_SOURCERY 225dda0a47ebc6c40e06f1d7820ff41263a6b47263cc0ea1832d12f5e38319c8_v0_14_1_234_g329ac7b7
#define LIBMDBX_INTERNALS
#define MDBX_DEPRECATED
@@ -11358,6 +11358,387 @@ __cold const char *mdbx_ratio2percents(uint64_t value, uint64_t whole, char *buf
else
return ratio2percent(value, whole, (ratio2digits_buffer_t *)buffer);
}
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2025-2026
static MDBX_cache_result_t cache_get(const MDBX_txn *txn, MDBX_dbi dbi, const MDBX_val *key, MDBX_val *data,
MDBX_cache_entry_t *entry);
static inline bool is_outside_dxb(const MDBX_txn *txn, const void *ptr) {
const MDBX_env *env = txn->env;
const ptrdiff_t offset = ptr_dist(ptr, env->dxb_mmap.base);
return offset < 0 || (size_t)offset >= pgno2bytes(env, txn->geo.first_unallocated);
}
static inline bool is_not_commited(const MDBX_txn *txn, const page_t *mp) {
tASSERT(txn, mp >= (const page_t *)txn->env->dxb_mmap.base &&
mp < (const page_t *)(ptr_disp(txn->env->dxb_mmap.base,
pgno2bytes(txn->env, txn->geo.first_unallocated))));
return mp->txnid > txn_basis_snapshot(txn);
}
MDBX_MAYBE_UNUSED static inline bool is_inside_dxb_and_commited(const MDBX_txn *txn, const void *ptr) {
return !is_outside_dxb(txn, ptr) && !is_not_commited(txn, ptr2page(txn->env, ptr));
}
static inline MDBX_cache_result_t cache_result(int err, MDBX_cache_status_t status) {
MDBX_cache_result_t result = {.errcode = err, .status = status};
return result;
}
static inline MDBX_cache_result_t cache_error(int err) {
assert(err != MDBX_SUCCESS && err != MDBX_RESULT_TRUE);
return cache_result(err, MDBX_CACHE_ERROR);
}
static inline MDBX_cache_result_t cache_fallback(const MDBX_txn *txn, MDBX_dbi dbi, const MDBX_val *key, MDBX_val *data,
MDBX_cache_status_t status) {
MDBX_cache_entry_t stub = {.last_confirmed_txnid = 0, .trunk_txnid = 0};
MDBX_cache_result_t result = cache_get(txn, dbi, key, data, &stub);
if (result.status > MDBX_CACHE_DIRTY)
result.status = status;
return result;
}
static inline bool is_afterlast(MDBX_cursor *mc, const page_t *mp, const MDBX_val *key) {
MDBX_val last = get_key(page_node(mp, page_numkeys(mp) - 1));
return mc->clc->k.cmp(key, &last) > 0;
}
__hot static MDBX_cache_result_t cache_get(const MDBX_txn *txn, MDBX_dbi dbi, const MDBX_val *key, MDBX_val *data,
MDBX_cache_entry_t *entry) {
DKBUF_DEBUG;
DEBUG("===> cached-get dbi %u, key [%s], entry %p (trunk %" PRIaTXN ", last_confirmed %" PRIaTXN
", data offset %zu, len %u)",
dbi, DKEY_DEBUG(key), __Wpedantic_format_voidptr(entry), entry->trunk_txnid, entry->last_confirmed_txnid,
entry->offset, entry->length);
if (unlikely(entry->trunk_txnid > entry->last_confirmed_txnid))
return cache_error(LOG_IFERR(MDBX_INVALID));
int err = check_txn(txn, MDBX_TXN_BLOCKED);
if (unlikely(err != MDBX_SUCCESS))
return cache_error(LOG_IFERR(err));
tASSERT(txn, entry->offset || entry->length == 0);
tASSERT(txn, entry->last_confirmed_txnid <= MAX_TXNID);
if (unlikely(txn->txnid < entry->trunk_txnid))
/* the used/read MVCC-snapshot is behind the accessible MVCC-range */
return cache_fallback(txn, dbi, key, data, MDBX_CACHE_BEHIND);
if (likely(txn->txnid <= entry->last_confirmed_txnid)) {
if (entry->offset) {
/* cache hit fast-path */
data->iov_base = ptr_disp(txn->env->dxb_mmap.base, entry->offset);
data->iov_len = entry->length;
tASSERT(txn, (!entry->offset && !entry->length) || is_inside_dxb_and_commited(txn, data->iov_base));
return cache_result(MDBX_SUCCESS, MDBX_CACHE_HIT);
}
if (txn->txnid == entry->last_confirmed_txnid || txn->txnid == entry->trunk_txnid) {
data->iov_base = nullptr;
data->iov_len = 0;
tASSERT(txn, (!entry->offset && !entry->length) || is_inside_dxb_and_commited(txn, data->iov_base));
return cache_result(MDBX_NOTFOUND, MDBX_CACHE_HIT);
}
/* Здесь ABA-подобная проблема:
*
* В случае когда txn->txnid > entry->trunk_txnid и txn->txnid < entry->last_confirmed_txnid
* возможна ситуация, когда в истории транзакции целевой ключ (и значение) отсутствовали
* в версии entry->trunk_txnid и entry->last_confirmed_txnid, но присутствовали в MVCC-снимке txn->txnid.
*
* Теоретически можно подумать о том, чтобы обработать такие случаи без полного поиска. Но сначала требуется
* понять как часто будут возникать такие ситуации на практике и стоит-ли с этим заморачиваться. */
return cache_fallback(txn, dbi, key, data, MDBX_CACHE_UNABLE);
}
err = dbi_check(txn, dbi);
if (unlikely(err != MDBX_SUCCESS))
return cache_error(LOG_IFERR(err));
const uint64_t committed_snapshot_txnid = txn_basis_snapshot(txn);
txnid_t trunk_txnid = txn->front_txnid;
if (unlikely(txn->dbi_state[dbi] & DBI_STALE)) {
err = tbl_refresh((MDBX_txn *)txn, dbi);
if (unlikely(err != MDBX_SUCCESS)) {
if (err == MDBX_NOTFOUND) {
/* the corresponding table has been deleted */
tASSERT(txn, !entry->offset || trunk_txnid > entry->trunk_txnid);
not_found:
data->iov_base = nullptr;
data->iov_len = 0;
MDBX_cache_status_t status = MDBX_CACHE_DIRTY;
if (trunk_txnid <= committed_snapshot_txnid) {
status = MDBX_CACHE_CONFIRMED;
if (entry->offset || !entry->trunk_txnid) {
status = MDBX_CACHE_REFRESHED;
tASSERT(txn, (!entry->offset && !entry->trunk_txnid) || trunk_txnid > entry->trunk_txnid);
entry->offset = 0;
entry->length = 0;
entry->trunk_txnid = trunk_txnid;
}
entry->last_confirmed_txnid = committed_snapshot_txnid;
}
return cache_result(MDBX_NOTFOUND, status);
}
return cache_error(LOG_IFERR(err));
}
}
/* lookup the trunk mvcc/txn-id thru nested transactions stack. */
if (txn->dbs[dbi].mod_txnid /* tree->mod_txnid maybe zero in a legacy DB */)
trunk_txnid = txn->dbs[dbi].mod_txnid;
if ((txn->flags & MDBX_TXN_RDONLY) == 0) {
const MDBX_txn *scan = txn;
do
if ((scan->flags & MDBX_TXN_DIRTY) && (dbi == MAIN_DBI || (scan->dbi_state[dbi] & DBI_DIRTY))) {
/* После коммита вложенных тразакций может быть mod_txnid > front */
trunk_txnid = scan->front_txnid;
break;
}
while (unlikely((scan = scan->parent) != nullptr));
}
if (trunk_txnid <= entry->last_confirmed_txnid) {
/* the corresponding table has not been changed since the last check. */
tASSERT(txn, (txn->dbi_state[dbi] & DBI_DIRTY) == 0);
tASSERT(txn, trunk_txnid == entry->trunk_txnid);
confirmed:
tASSERT(txn, trunk_txnid <= committed_snapshot_txnid && trunk_txnid <= entry->last_confirmed_txnid);
data->iov_base = entry->offset ? ptr_disp(txn->env->dxb_mmap.base, entry->offset) : 0;
data->iov_len = entry->length;
tASSERT(txn, (!entry->offset && !entry->length) || is_inside_dxb_and_commited(txn, data->iov_base));
if (entry->last_confirmed_txnid == committed_snapshot_txnid)
return cache_result(data->iov_base ? MDBX_SUCCESS : MDBX_NOTFOUND, MDBX_CACHE_HIT);
entry->last_confirmed_txnid = committed_snapshot_txnid;
return cache_result(data->iov_base ? MDBX_SUCCESS : MDBX_NOTFOUND, MDBX_CACHE_CONFIRMED);
}
if (unlikely(txn->dbs[dbi].root == P_INVALID)) {
/* the corresponding table is empty now */
tASSERT(txn, !entry->offset || trunk_txnid > entry->trunk_txnid);
goto not_found;
}
cursor_couple_t cx;
err = cursor_init(&cx.outer, txn, dbi);
if (unlikely(err != MDBX_SUCCESS))
return cache_error(LOG_IFERR(err));
alignkey_t aligned;
err = check_key(&cx.outer, key, &aligned);
if (unlikely(err != MDBX_SUCCESS))
return cache_error(LOG_IFERR(err));
page_t *mp = nullptr;
err = page_get(&cx.outer, txn->dbs[dbi].root, &mp, trunk_txnid);
if (unlikely(err != MDBX_SUCCESS))
return cache_error(LOG_IFERR(err));
cx.outer.pg[cx.outer.top = 0] = mp;
/* walking along the branches of the tree to the leaves */
while (is_branch(mp)) {
if (mp->txnid <= entry->last_confirmed_txnid) {
/* Страница НЕ была обновлена после последней проверки,
* проверяем остальные условия для раннего завершения поиска. */
if (!entry->offset) {
/* Ключа не было и он не мог быть добавлен без изменения страницы. */
notfound_elevate_trunk:
if (trunk_txnid > entry->trunk_txnid)
goto not_found;
for (intptr_t i = cx.outer.top - 1; i >= 0; --i) {
trunk_txnid = cx.outer.pg[i]->txnid;
if (trunk_txnid > entry->trunk_txnid)
goto not_found;
}
trunk_txnid = txn->dbs[dbi].mod_txnid ? txn->dbs[dbi].mod_txnid : txn->front_txnid;
tASSERT(txn, trunk_txnid > entry->trunk_txnid);
goto not_found;
}
if (mp->txnid < entry->trunk_txnid) {
/* Попали на более старую страницу, такое может быть только при "схлопывании" b-tree,
* когда целевой ключ был удален вместе с ведущими к нему branch-страницами. */
goto notfound_elevate_trunk;
}
/* Искомый ключ больше или равен первому на этой странице и меньше следующего с родительской,
* но может быть больше последнего ключа на этой странице. */
if (!is_afterlast(&cx.outer, mp, &aligned.key)) {
/* Следовательно, если ключ не-больше последнего на этой страницы,
* то он не мог быть изменен или удалён. */
trunk_txnid = mp->txnid;
goto confirmed;
}
/* Но если искомый ключ больше последнего на этой страницы, то он мог быть удалён вместе со всеми
* страницами расположенными дальше/после текущей и до следующего узла родительской страницы.
* Следует продолжить поиск. */
}
trunk_txnid = mp->txnid;
const size_t ki = tree_search_branch(&cx.outer, key);
cx.outer.ki[cx.outer.top] = (indx_t)ki;
err = page_get(&cx.outer, node_pgno(page_node(mp, ki)), &mp, trunk_txnid);
if (unlikely(err != MDBX_SUCCESS))
return cache_error(LOG_IFERR(err));
err = cursor_push(&cx.outer, mp, 0);
if (unlikely(err != MDBX_SUCCESS))
return cache_error(LOG_IFERR(err));
}
if (!MDBX_DISABLE_VALIDATION && unlikely(!check_leaf_type(&cx.outer, mp))) {
ERROR("unexpected leaf-page #%" PRIaPGNO " type 0x%x seen by cursor", mp->pgno, mp->flags);
err = MDBX_CORRUPTED;
return cache_error(LOG_IFERR(err));
}
if (mp->txnid <= entry->last_confirmed_txnid) {
if (!entry->offset) {
/* Ключа не было и он не мог быть добавлен без изменения страницы. */
goto notfound_elevate_trunk;
}
if (mp->txnid < entry->trunk_txnid) {
/* Попали на более старую страницу, такое может быть только при "схлопывании" b-tree,
* когда целевой ключ был удален вместе с ведущими к нему branch-страницами. */
tASSERT(txn, !node_search(&cx.outer, &aligned.key).exact);
goto notfound_elevate_trunk;
}
/* Искомый ключ больше или равен первому на этой странице и меньше следующего с родительской,
* но может быть больше последнего ключа на этой странице. Страница не была обновлена после
* последней проверки наличия ключа. */
if (!is_afterlast(&cx.outer, mp, &aligned.key)) {
/* Следовательно, если ключ не-больше последнего на этой страницы,
* то он не мог быть изменен или удалён. */
trunk_txnid = mp->txnid;
goto confirmed;
}
/* Но если искомый ключ больше последнего на этой страницы, то он мог быть удалён вместе со всеми
* страницами расположенными дальше/после текущей и до следующего узла родительской страницы.
*
* В зоне branch-страниц дерева следовало-бы продолжить поиск, но в случае листовой страницы можно
* быть уверенным что ключ отсутствует, так он больше последнего ключа на этой странице и меньше
* первого на следующей, а таких ключей нет физически. */
tASSERT(txn, !node_search(&cx.outer, &aligned.key).exact);
goto notfound_elevate_trunk;
}
trunk_txnid = mp->txnid;
struct node_search_result nsr = node_search(&cx.outer, &aligned.key);
if (!nsr.exact) {
tASSERT(txn, !entry->offset || trunk_txnid > entry->trunk_txnid);
goto not_found;
}
if (unlikely(node_flags(nsr.node) & N_DUP)) {
/* TODO: It is possible to implement support for multivalues, but need to think through the usage scenarios. */
err = MDBX_EMULTIVAL;
return cache_error(LOG_IFERR(err));
}
err = node_read(&cx.outer, nsr.node, data, mp);
if (unlikely(err != MDBX_SUCCESS))
return cache_error(LOG_IFERR(err));
if (trunk_txnid > committed_snapshot_txnid) {
tASSERT(txn, trunk_txnid > entry->last_confirmed_txnid && trunk_txnid > entry->trunk_txnid);
return cache_result(MDBX_SUCCESS, MDBX_CACHE_DIRTY);
}
tASSERT(txn, is_inside_dxb_and_commited(txn, data->iov_base));
tASSERT(txn, trunk_txnid <= committed_snapshot_txnid && trunk_txnid > entry->last_confirmed_txnid &&
trunk_txnid > entry->trunk_txnid);
entry->offset = ptr_dist(data->iov_base, txn->env->dxb_mmap.base);
entry->length = (uint32_t)data->iov_len;
entry->trunk_txnid = trunk_txnid;
entry->last_confirmed_txnid = committed_snapshot_txnid;
return cache_result(MDBX_SUCCESS, MDBX_CACHE_REFRESHED);
}
/*----------------------------------------------------------------------------*/
__hot MDBX_cache_result_t mdbx_cache_get(const MDBX_txn *txn, MDBX_dbi dbi, const MDBX_val *key, MDBX_val *data,
volatile MDBX_cache_entry_t *entry) {
if (unlikely(!key || !data || !entry))
return cache_error(LOG_IFERR(MDBX_EINVAL));
MDBX_cache_entry_t local = *entry;
while (true) {
MDBX_cache_entry_t again;
again.last_confirmed_txnid = safe64_read((mdbx_atomic_uint64_t *)&entry->last_confirmed_txnid);
if (unlikely(again.last_confirmed_txnid > MAX_TXNID)) {
#if MDBX_DEBUG
return cache_fallback(txn, dbi, key, data, MDBX_CACHE_RACE);
#else
atomic_yield();
again.last_confirmed_txnid = safe64_read((mdbx_atomic_uint64_t *)&entry->last_confirmed_txnid);
if (unlikely(again.last_confirmed_txnid > MAX_TXNID)) {
atomic_yield();
atomic_yield();
again.last_confirmed_txnid = safe64_read((mdbx_atomic_uint64_t *)&entry->last_confirmed_txnid);
if (unlikely(again.last_confirmed_txnid > MAX_TXNID)) {
atomic_yield();
atomic_yield();
atomic_yield();
again.last_confirmed_txnid = safe64_read((mdbx_atomic_uint64_t *)&entry->last_confirmed_txnid);
if (unlikely(again.last_confirmed_txnid > MAX_TXNID))
return cache_fallback(txn, dbi, key, data, MDBX_CACHE_RACE);
}
}
#endif /* MDBX_DEBUG */
}
again.trunk_txnid = entry->trunk_txnid;
again.offset = entry->offset;
again.length = entry->length;
if (likely(local.last_confirmed_txnid == again.last_confirmed_txnid && local.trunk_txnid == again.trunk_txnid &&
local.offset == again.offset && local.length == again.length &&
local.last_confirmed_txnid == safe64_read((mdbx_atomic_uint64_t *)&entry->last_confirmed_txnid)))
break;
local = again;
atomic_yield();
}
MDBX_cache_result_t result = cache_get(txn, dbi, key, data, &local);
if (result.status > MDBX_CACHE_HIT) {
tASSERT(txn, local.last_confirmed_txnid < MAX_TXNID && local.trunk_txnid <= local.last_confirmed_txnid &&
local.trunk_txnid > 0);
while (true) {
const txnid_t snap = safe64_read((mdbx_atomic_uint64_t *)&entry->last_confirmed_txnid);
if (snap >= local.last_confirmed_txnid) {
result.status = MDBX_CACHE_RACE;
break;
}
if (likely(safe64_reset_compare((mdbx_atomic_uint64_t *)&entry->last_confirmed_txnid, snap))) {
entry->trunk_txnid = 0;
osal_compiler_barrier();
entry->offset = local.offset;
entry->length = local.length;
entry->trunk_txnid = local.trunk_txnid;
safe64_write((mdbx_atomic_uint64_t *)&entry->last_confirmed_txnid, local.last_confirmed_txnid);
break;
}
atomic_yield();
}
}
return result;
}
__hot MDBX_cache_result_t mdbx_cache_get_SingleThreaded(const MDBX_txn *txn, MDBX_dbi dbi, const MDBX_val *key,
MDBX_val *data, MDBX_cache_entry_t *entry) {
if (unlikely(!key || !data || !entry))
return cache_error(LOG_IFERR(MDBX_EINVAL));
return cache_get(txn, dbi, key, data, entry);
}
LIBMDBX_API void mdbx_cache_init(MDBX_cache_entry_t *entry) { __inline_mdbx_cache_init(entry); }
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2026
@@ -37921,7 +38302,7 @@ int txn_basal_commit(MDBX_txn *txn, struct commit_timestamp *ts) {
if (commit_txnid > txn->txnid)
VERBOSE("use @%" PRIaTXN " (+%zu) for committing bigfoot-txn", commit_txnid, (size_t)(commit_txnid - txn->txnid));
else
VERBOSE("committing @%" PRIaTXN "txn", commit_txnid);
VERBOSE("committing @%" PRIaTXN " txn", commit_txnid);
gc_put_destroy(&gcu_ctx);
if (ts)
@@ -39904,10 +40285,10 @@ __dll_export
0,
14,
1,
220,
234,
"", /* pre-release suffix of SemVer
0.14.1.220 */
{"2026-01-02T23:27:02+03:00", "d490bb4b78537c494ffecf7b31b961c48a715d94", "0f8b288ce9d1e1028f8bb760f5f87eafa8e94bfe", "v0.14.1-220-g0f8b288c"},
0.14.1.234 */
{"2026-01-04T14:41:25+03:00", "26e213307787c195455ed09e75f197acc4c22d3d", "329ac7b72663cd5524733f61ce5a7cff9615f5bc", "v0.14.1-234-g329ac7b7"},
sourcery};
__dll_export
+1 -1
View File
@@ -2,7 +2,7 @@
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2026
/* clang-format off */
#define MDBX_BUILD_SOURCERY e30d838fb2509384f10339f050c97b34b790926488f78b959203d21608b421aa_v0_14_1_220_g0f8b288c
#define MDBX_BUILD_SOURCERY 225dda0a47ebc6c40e06f1d7820ff41263a6b47263cc0ea1832d12f5e38319c8_v0_14_1_234_g329ac7b7
#define LIBMDBX_INTERNALS
#define MDBX_DEPRECATED
+200 -6
View File
@@ -1,6 +1,9 @@
/**
_libmdbx_ (aka MDBX) is an extremely fast, compact, powerful, embeddable,
\file mdbx.h
\brief The libmdbx C API header file.
\details _libmdbx_ (aka MDBX) is an extremely fast, compact, powerful, embeddable,
transactional [key-value
store](https://en.wikipedia.org/wiki/Key-value_database), with [Apache 2.0
license](./LICENSE). _MDBX_ has a specific set of properties and capabilities,
@@ -18,21 +21,36 @@ C++ API description and links to the origin 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 ETH `0xD104d8f8B2dC312aaD74899F83EBf3EEBDC1EA3A`,
BTC `bc1qzvl9uegf2ea6cwlytnanrscyv8snwsvrc0xfsu`, SOL `FTCTgbHajoLVZGr8aEFWMzx3NDMyS5wXJgfeMTmJznRi`.
Всё будет хорошо!
\note The libmdbx project has been completely relocated to the jurisdiction of the Russian Federation.
It is still open and provided with first-class free support. Please refer to https://libmdbx.dqdkfa.ru for documentation
and https://sourcecraft.dev/dqdkfa/libmdbx for the source code.
The _libmdbx_ project has been completely relocated to the jurisdiction of the Russian Federation.
\note _libmdbx_ is still open and provided with first-class free support.
\section copyright LICENSE & COPYRIGHT
\copyright SPDX-License-Identifier: Apache-2.0
\note Please refer to the COPYRIGHT file for explanations license change, credits and acknowledgments.
Please refer to the COPYRIGHT file for explanations license change, credits and acknowledgments.
\author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2026
*******************************************************************************/
#pragma once
/*
* Tested with, since 2026:
* - Elbrus LCC >= 1.28 (http://www.mcst.ru/lcc);
* - GNU C >= 11.3;
* - CLANG >= 14.0;
* - MSVC >= 19.44 (Visual Studio 2022 toolchain v143),
* before 2026:
* - Elbrus LCC >= 1.23 (http://www.mcst.ru/lcc);
* - GNU C >= 4.8;
* - CLANG >= 3.9;
* - MSVC >= 14.0 (Visual Studio 2015),
* but 19.2x could hang due optimizer bug;
* - AppleClang.
*/
#ifndef LIBMDBX_H
#define LIBMDBX_H
@@ -4858,6 +4876,8 @@ LIBMDBX_API int mdbx_drop(MDBX_txn *txn, MDBX_dbi dbi, bool del);
* \note Values returned from the table are valid only until a
* subsequent update operation, or the end of the transaction.
*
* \see mdbx_cache_get()
*
* \param [in] txn A transaction handle returned by \ref mdbx_txn_begin().
* \param [in] dbi A table handle returned by \ref mdbx_dbi_open().
* \param [in] key The key to search for in the table.
@@ -4933,6 +4953,180 @@ LIBMDBX_API int mdbx_get_ex(const MDBX_txn *txn, MDBX_dbi dbi, MDBX_val *key, MD
* \retval MDBX_EINVAL An invalid parameter was specified. */
LIBMDBX_API int mdbx_get_equal_or_great(const MDBX_txn *txn, MDBX_dbi dbi, MDBX_val *key, MDBX_val *data);
/** \brief Lightweight transparent cache entry structure used by \ref mdbx_cache_get().
* \ingroup c_crud
*
* The approach of these caching is to preserve address of a value retrieved from the database with an extremely fast
* check of relevance it based on a transaction ID within an internal b-tree structures. Event a b-tree was modified
* then the search for the corresponding key from the root of the b-tree to leaf pages stops as soon as reaches a page
* that has not been modified after the last check of given cache entry. This way, the minimum actions is performed,
* which is no slower than a usual key search in the worst case, and at best, only a few lightweight checks will be do.
*
* \note The cache structure allows it to be placed in shared memory and used by multiple processes.
* However, such interaction and management are not provided by libmdbx in any way yet now.
*
* \note An each cache entry must be initialized by \ref mdbx_cache_init() before first use. */
typedef struct MDBX_cache_entry {
uint64_t trunk_txnid; /**< The transaction/MVCC-snapshot ID of a page or other internal DB structure
* that hold the cached data or reflect it state. */
uint64_t last_confirmed_txnid; /**< The recent transaction/MVCC-snapshot ID wherein the cache entry
* was checked and confirmed. */
size_t offset; /**< The offset of cached data value for a corresponding key.
* The zero value means \ref MDBX_NOTFOUND. */
uint32_t length; /**< The length of cached data value for a corresponding key. */
} MDBX_cache_entry_t;
/** \brief Initializes the cache entry before the first use.
* \ingroup c_crud
* \see MDBX_cache_entry
* \see mdbx_cache_get() */
LIBMDBX_INLINE_API(void, mdbx_cache_init, (MDBX_cache_entry_t * entry)) {
entry->offset = 0;
entry->length = 0;
entry->trunk_txnid = 0;
entry->last_confirmed_txnid = 0;
}
/** \brief Cache entry status returned by \ref mdbx_cache_get().
* \ingroup c_crud
* \see MDBX_cache_entry
* \see mdbx_cache_init() */
typedef enum MDBX_cache_status {
/** \brief The error other than \ref MDBX_NOTFOUND has occurred.
* \details There is no correct result since an error has occurred that is not related
* to the absence of the desired key-value pair.
* The given cache entry has not been changed. */
MDBX_CACHE_ERROR = -3,
/** \brief The result was obtained by bypassing the cache, because
* the transaction is too old to using the cache entry.
* \details The cache entry reflects a newer version of the data that is unavailable within
* an MVCC-snapshot used by current transaction.
* The given cache entry has not been changed.
* The result of getting a value is correct until the transaction end. */
MDBX_CACHE_BEHIND = -2,
/** \brief The result of getting a value is correct, but it cannot be cached since there
* the ABA-like issue is in the data history, either other similar reason.
* \details When a cache entry is used by different threads reading different MVCC snapshots,
* there may be a situation in which the key and associated value are missing from the old
* and new MVCC snapshots, but are present in one of the MVCC snapshots between ones.
* In such circumstances the result from the cache may be false negative, therefore,
* in order to avoid an incorrect result, a search is performed bypassing cache.
* The given cache entry has not been changed.
* The result of getting a value is correct until the transaction end. */
MDBX_CACHE_UNABLE = -1,
/** \brief The result was obtained by bypassing the cache, because
* the given cache entry being updated by another thread.
* \details When accessing the cache entry, a race condition was detected with its update by another thread.
* Therefore, the result was obtained without using the cache entry and without affecting an operation of other
* threads using it, including the ones performing an update. For a read transaction, the result is correct until
* the transaction end. For a write transactions, the result is correct until the value is explicitly changed or
* the transaction is completed. */
MDBX_CACHE_RACE = 0,
/** \brief The result of getting a value is correct, but it cannot be cached since
* the changes have not been committed.
* \details The requested value of a pair is in a dirty state itself or on a dirty page with other updated items.
* This cache entry has not been changed because the corresponding data changes have not yet been committed
* and could be aborted.
* The result of the get operation and data value are valid within the current write transaction
* until any next modification. */
MDBX_CACHE_DIRTY = 1,
/** \brief The result of getting a value is correct and was retrieved from the cache entry which is untouched.
* \details There were no changes in the cached data after the last check.
* The given cache entry was not altered as it is complete up-to-date.
* For a read transaction, the result is correct until the transaction end.
* For a write transactions, the result is correct until the value is explicitly changed
* or the transaction is completed. */
MDBX_CACHE_HIT = 2,
/** \brief The result of getting a value is correct and has been retrieved from the cache, which has been
* altered to reflect recently committed transactions.
* \details There were no changes in the cached data after the last check.
* The given cache entry has been slightly updated to reflect the relevance of the data for recent committed
* transaction(s). For a read transaction, the result is correct until the transaction end. For a write transactions,
* the result is correct until the value is explicitly changed or the transaction is completed. */
MDBX_CACHE_CONFIRMED = 3,
/** \brief The result of getting a value is correct and corresponds to the fresh data readed from the database,
* which also putted into the cache entry.
* \details After the last check, either the value of the requested pair itself changed,
* or it was moved to a new page due to the updating of neighboring items.
* The given cache entry has been completely updated to reflect the actual data.
* For a read transaction, the result is correct until the transaction end.
* For a write transactions, the result is correct until the value is explicitly changed
* or the transaction is completed. */
MDBX_CACHE_REFRESHED = 4
} MDBX_cache_status_t;
/** \brief Pair of error code and cache status as a result of \ref mdbx_cache_get().
* \ingroup c_crud
* \see mdbx_cache_get()
* \see mdbx_cache_get_SingleThreaded() */
typedef struct MDBX_cache_result {
/** The error code of getting data same as from \ref mdbx_get(). */
MDBX_error_t errcode;
/** The result of cache operation as the value of \ref MDBX_cache_status_t. */
MDBX_cache_status_t status;
} MDBX_cache_result_t;
/** \brief Gets items from a table using cache including multithreaded cases.
* \ingroup c_crud
* \details The essence of this "caching" is using a cached information to check as quickly as possible whether the data
* has changed or not, with early exit when searching though a DB. For this a petty version information is stored in
* a \ref MDBX_cache_entry_t structure, along with the offset to the "cached" data inside the memory-mapped database
* file. Instead of a full B-tree search it stops when reaches a DB page that has not been modified after the last
* check. Thus a minimum number of steps are performed which provides dramatic acceleration in many cases.
*
* \note This function is supports multi-threaded cases and automatically resolves collisions using lockfree approach,
* nonetheless \ref MDBX_NOSTICKYTHREADS mode is required to use it within a different threads.
*
* \see mdbx_cache_get_SingleThreaded()
* \see MDBX_cache_entry_t
* \see mdbx_cache_init()
* \see mdbx_get()
*
* \param [in] txn A transaction handle returned by \ref mdbx_txn_begin().
* \param [in] dbi A table handle returned by \ref mdbx_dbi_open().
* \param [in] key The key to search for in the table.
* \param [in,out] data The data corresponding to the key.
* \param [in,out] entry The cache entry corresponding to the key.
*
* \returns The \ref MDBX_cache_result_t with a pair of the error codes for getting a data
* and the cache entry processing both. */
LIBMDBX_API MDBX_cache_result_t mdbx_cache_get(const MDBX_txn *txn, MDBX_dbi dbi, const MDBX_val *key, MDBX_val *data,
volatile MDBX_cache_entry_t *entry);
/** \brief Gets items from a table using cache within single-thread cases only.
* \ingroup c_crud
* \details The essence of this "caching" is using a cached information to check as quickly as possible whether the data
* has changed or not, with early exit when searching though a DB. For this a petty version information is stored in
* a \ref MDBX_cache_entry_t structure, along with the offset to the "cached" data inside the memory-mapped database
* file. Instead of a full B-tree search it stops when reaches a DB page that has not been modified after the last
* check. Thus a minimum number of steps are performed which provides dramatic acceleration in many cases.
*
* \note This function is intended to be used with a given cache entry only in single-threaded cases, otherwise
* behaviour is undefined.
*
* \see mdbx_cache_get()
* \see MDBX_cache_entry_t
* \see mdbx_cache_init()
* \see mdbx_get()
*
* \param [in] txn A transaction handle returned by \ref mdbx_txn_begin().
* \param [in] dbi A table handle returned by \ref mdbx_dbi_open().
* \param [in] key The key to search for in the table.
* \param [in,out] data The data corresponding to the key.
* \param [in,out] entry The cache entry corresponding to the key.
*
* \returns The \ref MDBX_cache_result_t with a pair of the error codes for getting a data
* and the cache entry processing both. */
LIBMDBX_API MDBX_cache_result_t mdbx_cache_get_SingleThreaded(const MDBX_txn *txn, MDBX_dbi dbi, const MDBX_val *key,
MDBX_val *data, MDBX_cache_entry_t *entry);
/** \brief Store items into a table.
* \ingroup c_crud
*
+46 -18
View File
@@ -1,30 +1,45 @@
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2020-2026
///
/// Donations are welcome to ETH `0xD104d8f8B2dC312aaD74899F83EBf3EEBDC1EA3A`.
/// Всё будет хорошо!
///
/// \file mdbx.h++
/// \file mdbx.h++
/// \brief The libmdbx C++ API header file.
///
/// Tested with:
/// - Elbrus LCC >= 1.23 (http://www.mcst.ru/lcc);
/// - GNU C++ >= 4.8;
/// - clang >= 3.9;
/// - MSVC >= 14.0 (Visual Studio 2015),
/// but 19.2x could hang due optimizer bug;
/// - AppleClang, but without C++20 concepts.
/// \details _libmdbx_ (aka MDBX) is an extremely fast, compact, powerful, embeddable,
/// transactional [key-value
/// store](https://en.wikipedia.org/wiki/Key-value_database), with [Apache 2.0
/// license](./LICENSE). _MDBX_ has a specific set of properties and capabilities,
/// focused on creating unique lightweight solutions with extraordinary performance.
///
/// Please visit https://libmdbx.dqdkfa.ru for more information, documentation,
/// C++ API description and links to the origin 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`,
/// BTC `bc1qzvl9uegf2ea6cwlytnanrscyv8snwsvrc0xfsu`, SOL `FTCTgbHajoLVZGr8aEFWMzx3NDMyS5wXJgfeMTmJznRi`.
/// Всё будет хорошо!
///
/// The libmdbx project has been completely relocated to the jurisdiction of the Russian Federation.
/// It is still open and provided with first-class free support.
/// Please refer to https://libmdbx.dqdkfa.ru for documentation
/// and https://sourcecraft.dev/dqdkfa/libmdbx for the source code.
/// \note _libmdbx_ is still open and provided with first-class free support.
///
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2020-2026
///
#pragma once
//
// Tested with, since 2026:
// - Elbrus LCC >= 1.28 (http://www.mcst.ru/lcc);
// - GNU C++ >= 11.3;
// - CLANG >= 14.0;
// - MSVC >= 19.44 (Visual Studio 2022 toolchain v143),
// before 2026:
// - Elbrus LCC >= 1.23 (http://www.mcst.ru/lcc);
// - GNU C++ >= 4.8;
// - CLANG >= 3.9;
// - MSVC >= 14.0 (Visual Studio 2015),
// but 19.2x could hang due optimizer bug;
// - AppleClang, but without C++20 concepts.
//
/* Workaround for modern libstdc++ with CLANG < 4.x */
#if defined(__SIZEOF_INT128__) && !defined(__GLIBCXX_TYPE_INT_N_0) && defined(__clang__) && __clang_major__ < 4
#define __GLIBCXX_BITSIZE_INT_N_0 128
@@ -1058,6 +1073,19 @@ protected:
MDBX_CXX11_CONSTEXPR slice(size_t invalid_length) noexcept : ::MDBX_val({nullptr, invalid_length}) {}
};
/// \brief Cache entry for get-cached API (initial draft).
class cache_entry : public MDBX_cache_entry_t {
public:
cache_entry() noexcept { reset(); }
cache_entry(const cache_entry &) noexcept = default;
cache_entry &operator=(const cache_entry &) noexcept = default;
cache_entry(cache_entry &&other) noexcept {
*this = other;
other.reset();
}
void reset() noexcept { mdbx_cache_init(this); }
};
//------------------------------------------------------------------------------
namespace allocation_aware_details {
+1 -1
View File
@@ -18,7 +18,7 @@
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2026
#define MDBX_BUILD_SOURCERY e30d838fb2509384f10339f050c97b34b790926488f78b959203d21608b421aa_v0_14_1_220_g0f8b288c
#define MDBX_BUILD_SOURCERY 225dda0a47ebc6c40e06f1d7820ff41263a6b47263cc0ea1832d12f5e38319c8_v0_14_1_234_g329ac7b7
#define LIBMDBX_INTERNALS
#define MDBX_DEPRECATED
+1 -1
View File
@@ -18,7 +18,7 @@
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2026
#define MDBX_BUILD_SOURCERY e30d838fb2509384f10339f050c97b34b790926488f78b959203d21608b421aa_v0_14_1_220_g0f8b288c
#define MDBX_BUILD_SOURCERY 225dda0a47ebc6c40e06f1d7820ff41263a6b47263cc0ea1832d12f5e38319c8_v0_14_1_234_g329ac7b7
#define LIBMDBX_INTERNALS
#define MDBX_DEPRECATED
+1 -1
View File
@@ -18,7 +18,7 @@
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2026
#define MDBX_BUILD_SOURCERY e30d838fb2509384f10339f050c97b34b790926488f78b959203d21608b421aa_v0_14_1_220_g0f8b288c
#define MDBX_BUILD_SOURCERY 225dda0a47ebc6c40e06f1d7820ff41263a6b47263cc0ea1832d12f5e38319c8_v0_14_1_234_g329ac7b7
#define LIBMDBX_INTERNALS
#define MDBX_DEPRECATED
+1 -1
View File
@@ -18,7 +18,7 @@
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2026
#define MDBX_BUILD_SOURCERY e30d838fb2509384f10339f050c97b34b790926488f78b959203d21608b421aa_v0_14_1_220_g0f8b288c
#define MDBX_BUILD_SOURCERY 225dda0a47ebc6c40e06f1d7820ff41263a6b47263cc0ea1832d12f5e38319c8_v0_14_1_234_g329ac7b7
#define LIBMDBX_INTERNALS
#define MDBX_DEPRECATED
+1 -1
View File
@@ -18,7 +18,7 @@
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2026
#define MDBX_BUILD_SOURCERY e30d838fb2509384f10339f050c97b34b790926488f78b959203d21608b421aa_v0_14_1_220_g0f8b288c
#define MDBX_BUILD_SOURCERY 225dda0a47ebc6c40e06f1d7820ff41263a6b47263cc0ea1832d12f5e38319c8_v0_14_1_234_g329ac7b7
#define LIBMDBX_INTERNALS
#define MDBX_DEPRECATED
+1 -1
View File
@@ -18,7 +18,7 @@
/// \copyright SPDX-License-Identifier: Apache-2.0
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2026
#define MDBX_BUILD_SOURCERY e30d838fb2509384f10339f050c97b34b790926488f78b959203d21608b421aa_v0_14_1_220_g0f8b288c
#define MDBX_BUILD_SOURCERY 225dda0a47ebc6c40e06f1d7820ff41263a6b47263cc0ea1832d12f5e38319c8_v0_14_1_234_g329ac7b7
#define LIBMDBX_INTERNALS
#define MDBX_DEPRECATED