mirror of
https://gitflic.ru/project/erthink/libmdbx.git
synced 2026-07-14 18:46:08 +00:00
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:
+4
-1
@@ -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
@@ -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.
|
||||
|
||||
Исправления:
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -25,7 +25,8 @@ Historically, _libmdbx_ is a deeply revised and extended descendant of the legen
|
||||
|
||||
[](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
@@ -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" }
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
*
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user