mdbx: introduce the mdbx_txn_rollback() (v0.14.1-453-ga529b6d0).

2026-03-08 mdbx: update ChangeLog.
2026-03-08 mdbx: add `mdbx_txn_rollback()` with tests.
2026-03-08 mdbx-make: fix order of test-targets to avoid removing `src/version.c`.
2026-03-07 mdbx: add cost-cache for defrag internals.
This commit is contained in:
Леонид Юрьев (Leonid Yuriev)
2026-03-08 20:34:29 +03:00
parent 6b2501db1c
commit 195d7b2640
16 changed files with 133 additions and 24 deletions

View File

@@ -42,6 +42,7 @@ BTC `bc1qzvl9uegf2ea6cwlytnanrscyv8snwsvrc0xfsu`, SOL `FTCTgbHajoLVZGr8aEFWMzx3N
- добавлена функция `mdbx_txn_checkpoint()` для фиксации пишущих транзакций без освобождения блокировок.
- добавлена функция `mdbx_txn_commit_embark_read()` для фиксации пишущей транзакции и запуска читающей без вклинивания других изменений.
- добавлена функция `mdbx_txn_amend()` для изменения данных начиная со снимка данных используемого в заданной транзакции чтения.
- добавлена функция `mdbx_txn_rollback()` для прерывания и перезапуска транзакции с отменой всех изменений, но без освобождения блокировок.
- добавлена поддержка клонирования читающих транзакций посредством `mdbx_txn_clone()`.
- добавлена поддержка вложенных транзакций только для чтения.
- добавлена функция `mdbx_gc_info()` для получения информации о GC, использовании страниц, с возможностью итерирования содержимого GC.

View File

@@ -287,12 +287,14 @@ ctest: cmake-build
run-ut: mdbx_example
$(QUIET)for UT in $^; do echo " Running $$UT" && ./$${UT} || exit -1; done
TEST_TARGETS := mdbx_legacy_example $(call select_by,MDBX_BUILD_CXX,mdbx_modern_example,)
TEST_BUILD_TARGETS := build-test
TEST_TARGETS :=
TEST_BUILD_TARGETS :=
ifneq ($(CMAKE),"")
TEST_TARGETS += ctest
TEST_BUILD_TARGETS += cmake-build
endif
TEST_TARGETS += mdbx_legacy_example $(call select_by,MDBX_BUILD_CXX,mdbx_modern_example,)
TEST_BUILD_TARGETS += build-test
.PHONY: ninja-assertions ninja-debug ninja $(TEST_TARGETS) $(TEST_BUILD_TARGETS) test-ubsan test-asan test-memcheck test-leak test-assertion test build-test smoke check
test: $(TEST_TARGETS)

View File

@@ -1 +1 @@
{ "git_describe": "v0.14.1-449-ga2319c94", "git_timestamp": "2026-03-06T22:09:32+03:00", "git_tree": "722d2f848a0f79d9bec0116ca398f146b041f3ed", "git_commit": "a2319c949e5a2fc30ea7a36432dd7faeb248b94f", "semver": "0.14.1.449" }
{ "git_describe": "v0.14.1-453-ga529b6d0", "git_timestamp": "2026-03-08T20:32:09+03:00", "git_tree": "1d75989481133843e97103dabd0f5dfa8d7481ac", "git_commit": "a529b6d02d5e628738207dc39331c9186d164f88", "semver": "0.14.1.453" }

View File

@@ -1,4 +1,4 @@
/* This file is part of the libmdbx amalgamated source code (v0.14.1-449-ga2319c94 at 2026-03-06T22:09:32+03:00),
/* This file is part of the libmdbx amalgamated source code (v0.14.1-453-ga529b6d0 at 2026-03-08T20:32:09+03:00),
* it is the template for libmdbx's config.h
******************************************************************************/

View File

@@ -1,4 +1,4 @@
/* This file is part of the libmdbx amalgamated source code (v0.14.1-449-ga2319c94 at 2026-03-06T22:09:32+03:00).
/* This file is part of the libmdbx amalgamated source code (v0.14.1-453-ga529b6d0 at 2026-03-08T20:32:09+03:00).
*
* libmdbx (aka MDBX) is an extremely fast, compact, powerful, embeddedable, transactional key-value storage engine with
* open-source code. MDBX has a specific set of properties and capabilities, focused on creating unique lightweight
@@ -24,7 +24,7 @@
#define xMDBX_ALLOY 1 /* alloyed build */
#define MDBX_BUILD_SOURCERY c61e4bb68caf0e66edd5735e42142a872fab4e6ba53310c1dadad3a333300d72_v0_14_1_449_ga2319c94
#define MDBX_BUILD_SOURCERY a21e2e56d1f7e0e3b639be630fc6ddc90c1352abb5e8900d3e2cb154bdba53d5_v0_14_1_453_ga529b6d0
#define LIBMDBX_INTERNALS
#define MDBX_DEPRECATED

89
mdbx.c
View File

@@ -1,4 +1,4 @@
/* This file is part of the libmdbx amalgamated source code (v0.14.1-449-ga2319c94 at 2026-03-06T22:09:32+03:00).
/* This file is part of the libmdbx amalgamated source code (v0.14.1-453-ga529b6d0 at 2026-03-08T20:32:09+03:00).
*
* libmdbx (aka MDBX) is an extremely fast, compact, powerful, embeddedable, transactional key-value storage engine with
* open-source code. MDBX has a specific set of properties and capabilities, focused on creating unique lightweight
@@ -1463,6 +1463,7 @@ MDBX_INTERNAL int txn_nested_create(MDBX_txn *parent, bool readonly);
MDBX_INTERNAL int txn_nested_abort(MDBX_txn *txn);
MDBX_INTERNAL int txn_nested_commit(MDBX_txn *txn, struct commit_timestamp *ts);
MDBX_INTERNAL int txn_nested_checkpoint(MDBX_txn *txn, struct commit_timestamp *ts);
MDBX_INTERNAL int txn_nested_rollback(MDBX_txn *txn);
MDBX_INTERNAL MDBX_txn *txn_nested_fakero_begin(MDBX_txn *parent);
MDBX_INTERNAL int txn_nested_fakero_end(MDBX_txn *txn);
@@ -1473,6 +1474,7 @@ MDBX_INTERNAL int txn_basal_commit(MDBX_txn *txn, struct commit_timestamp *ts);
MDBX_INTERNAL int txn_basal_end(MDBX_txn *txn, bool unlock);
MDBX_INTERNAL int txn_basal_checkpoint(MDBX_txn *txn, MDBX_txn_flags_t weakening_durability,
struct commit_timestamp *ts);
MDBX_INTERNAL int txn_basal_rollback(MDBX_txn *txn);
MDBX_INTERNAL int txn_basal_update_tbl_roots(MDBX_txn *txn);
MDBX_INTERNAL int txn_ro_park(MDBX_txn *txn, bool autounpark);
@@ -3033,6 +3035,10 @@ typedef struct defract_context {
txnid_t stopor;
struct gc_reclaiming_obstacle gc_obstacle;
struct cache_item {
pgno_t pgno;
unsigned cost;
} cache_cost[64];
} dfc_t;
MDBX_INTERNAL int defrag_init(dfc_t *dfc, MDBX_txn *txn, size_t defrag_atleast_pages,
@@ -11288,6 +11294,28 @@ bailout:
return LOG_IFERR(rc);
}
int mdbx_txn_rollback(MDBX_txn *txn) {
int rc = check_txn(txn, MDBX_TXN_BLOCKED - MDBX_TXN_ERROR - MDBX_TXN_PARKED);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
txn->flags |= /* avoid merge cursors' state */ MDBX_TXN_ERROR;
if (txn->flags & txn_may_have_cursors)
txn_done_cursors(txn);
if (likely(txn->flags & txn_ro_flat) == 0) {
if (!txn->parent)
return LOG_IFERR(txn_basal_rollback(txn));
else
return LOG_IFERR(txn_nested_rollback(txn));
}
rc = txn_ro_reset(txn);
if (unlikely(rc != MDBX_SUCCESS))
return LOG_IFERR(rc);
return LOG_IFERR(txn_ro_start(txn, false));
}
int mdbx_txn_amend(MDBX_txn *rtxn, MDBX_txn **ptxn, MDBX_txn_flags_t flags, void *context) {
if (unlikely(!ptxn))
return LOG_IFERR(MDBX_EINVAL);
@@ -16894,8 +16922,7 @@ int dbi_close_release(MDBX_env *env, MDBX_dbi dbi) { return dbi_defer_release(en
static uint64_t defrag_now(uint64_t now_cache) { return now_cache ? now_cache : osal_monotime(); }
uint64_t defrag_result(dfc_t *dfc, MDBX_defrag_result_t *out,
uint64_t now_cache) {
uint64_t defrag_result(dfc_t *dfc, MDBX_defrag_result_t *out, uint64_t now_cache) {
memset(out, 0, sizeof(*out));
if (dfc->txn)
dfc->last_allocated = dfc->txn->geo.first_unallocated;
@@ -17524,7 +17551,7 @@ bailout:
return MDBX_RESULT_TRUE;
}
__hot __noinline static unsigned defrag_move_cost(dfc_t *dfc, pgno_t pgno, pgno_t span) {
__hot __noinline static unsigned defrag_move_cost_uncached(dfc_t *dfc, pgno_t pgno, pgno_t span) {
if (pgno < NUM_METAS || pgno >= dfc->retreat_edge)
return INT_MAX;
@@ -17551,6 +17578,14 @@ __hot __noinline static unsigned defrag_move_cost(dfc_t *dfc, pgno_t pgno, pgno_
return cost;
}
static inline unsigned defrag_move_cost(dfc_t *dfc, pgno_t pgno, pgno_t span) {
struct cache_item *cache = &dfc->cache_cost[pgno % ARRAY_LENGTH(dfc->cache_cost)];
if (cache->pgno == pgno)
return cache->cost;
cache->pgno = pgno;
return cache->cost = defrag_move_cost_uncached(dfc, pgno, span);
}
__hot static int defrag_provide_span(dfc_t *const dfc, const size_t npages) {
assert(npages > 1);
const pnl_t pnl = dfc->txn->wr.repnl;
@@ -17559,6 +17594,7 @@ __hot static int defrag_provide_span(dfc_t *const dfc, const size_t npages) {
if (len < npages)
return MDBX_RESULT_TRUE;
memset(dfc->cache_cost, 0, sizeof(dfc->cache_cost));
size_t best_begin = MAX_PAGENO, best_cost = len;
#if MDBX_PNL_ASCENDING
#error "FIXME"
@@ -37500,6 +37536,16 @@ int txn_basal_checkpoint(MDBX_txn *txn, MDBX_txn_flags_t weakening_durability, s
return (err == MDBX_SUCCESS) ? rc : err;
}
int txn_basal_rollback(MDBX_txn *txn) {
const unsigned preserved_flags = txn->flags & txn_rw_begin_flags;
/* void *const preserved_context = txn->userctx; */
int rc = txn_basal_end(txn, false);
if (likely(rc == MDBX_SUCCESS))
/* txn->userctx = preserved_context; */
rc = txn_basal_start(txn, preserved_flags | txn_rw_already_locked);
return rc;
}
/* Merge pageset of the nested transaction into parent */
static void nested_merge(MDBX_txn *const parent, MDBX_txn *const nested, const size_t parent_retired_len) {
tASSERT(nested, (nested->flags & MDBX_WRITEMAP) == 0);
@@ -37958,6 +38004,7 @@ int txn_nested_create(MDBX_txn *parent, bool readonly) {
static int nested_undo(MDBX_txn *nested) {
tASSERT(nested, !nested->nested && !(nested->flags & MDBX_TXN_HAS_CHILD));
tASSERT(nested, rkl_empty(&nested->wr.gc.comeback));
if (nested->flags & txn_may_have_cursors)
txn_done_cursors(nested);
if (nested->flags & MDBX_TXN_DIRTY)
@@ -38039,6 +38086,7 @@ static int nested_join(MDBX_txn *nested, struct commit_timestamp *ts) {
eASSERT(env, txn_dpl_check(nested));
tASSERT(nested, pnl_check_allocated(nested->wr.repnl, nested->geo.first_unallocated - MDBX_ENABLE_REFUND));
tASSERT(nested, memcmp(&nested->wr.troika, &parent->wr.troika, sizeof(troika_t)) == 0);
tASSERT(nested, rkl_empty(&nested->wr.gc.comeback));
//-------------------------------------------------------------------------
// Preserve space for page lists in the parent transaction.
@@ -38158,7 +38206,7 @@ int txn_nested_commit(MDBX_txn *nested, struct commit_timestamp *ts) {
int txn_nested_checkpoint(MDBX_txn *nested, struct commit_timestamp *ts) {
MDBX_txn *parent = nested->parent;
if (unlikely(!parent || parent->nested != nested || parent->env != nested->env)) {
ERROR("attempt to commit %s txn %p", "strange nested", __Wpedantic_format_voidptr(nested));
ERROR("attempt to %s %s txn %p", "commit", "strange nested", __Wpedantic_format_voidptr(nested));
return MDBX_PROBLEM;
}
@@ -38194,6 +38242,31 @@ int txn_nested_fakero_end(MDBX_txn *nested) {
return MDBX_SUCCESS;
}
int txn_nested_rollback(MDBX_txn *nested) {
tASSERT(nested, nested->flags & txn_ro_nested);
tASSERT(nested, rkl_empty(&nested->wr.gc.comeback));
MDBX_txn *parent = nested->parent;
if (unlikely(!parent || parent->nested != nested || parent->env != nested->env)) {
ERROR("attempt to %s %s txn %p", "rollback", "strange nested", __Wpedantic_format_voidptr(nested));
return MDBX_PROBLEM;
}
if (unlikely(F_ISSET(nested->flags, txn_ro_nested | MDBX_TXN_DIRTY))) {
ERROR("unable to %s %s txn %p", "rollback", "dirty nested fake-readonly", __Wpedantic_format_voidptr(nested));
return MDBX_BAD_TXN;
}
const unsigned preserved_flags = nested->flags & txn_ro_nested;
int rc = nested_undo(nested);
if (likely(rc == MDBX_SUCCESS))
rc = nested_start(nested, parent);
if (likely(rc == MDBX_SUCCESS))
nested->flags |= preserved_flags;
else
txn_nested_abort(nested);
return rc;
}
static inline mdbx_pid_t ro_slot_pid(const reader_slot_t *slot) { return slot->pid.weak; }
static inline uint64_t ro_slot_tid(const reader_slot_t *slot) { return slot->tid.weak; }
@@ -39668,10 +39741,10 @@ __dll_export
0,
14,
1,
449,
453,
"", /* pre-release suffix of SemVer
0.14.1.449 */
{"2026-03-06T22:09:32+03:00", "722d2f848a0f79d9bec0116ca398f146b041f3ed", "a2319c949e5a2fc30ea7a36432dd7faeb248b94f", "v0.14.1-449-ga2319c94"},
0.14.1.453 */
{"2026-03-08T20:32:09+03:00", "1d75989481133843e97103dabd0f5dfa8d7481ac", "a529b6d02d5e628738207dc39331c9186d164f88", "v0.14.1-453-ga529b6d0"},
sourcery};
__dll_export

View File

@@ -1,4 +1,4 @@
/* This file is part of the libmdbx amalgamated source code (v0.14.1-449-ga2319c94 at 2026-03-06T22:09:32+03:00).
/* This file is part of the libmdbx amalgamated source code (v0.14.1-453-ga529b6d0 at 2026-03-08T20:32:09+03:00).
*
* libmdbx (aka MDBX) is an extremely fast, compact, powerful, embeddedable, transactional key-value storage engine with
* open-source code. MDBX has a specific set of properties and capabilities, focused on creating unique lightweight

35
mdbx.h
View File

@@ -1,4 +1,4 @@
/** This file is part of the libmdbx amalgamated source code (v0.14.1-449-ga2319c94 at 2026-03-06T22:09:32+03:00).
/** This file is part of the libmdbx amalgamated source code (v0.14.1-453-ga529b6d0 at 2026-03-08T20:32:09+03:00).
\file mdbx.h
\brief The libmdbx C API header file.
@@ -4197,6 +4197,7 @@ typedef struct MDBX_commit_latency MDBX_commit_latency;
* \see mdbx_txn_begin_ex()
* \see mdbx_txn_abort()
* \see mdbx_txn_abort_ex()
* \see mdbx_txn_rollback()
*
* If the current thread is not eligible to manage the transaction then
* the \ref MDBX_THREAD_MISMATCH error will returned. Otherwise the transaction
@@ -4252,6 +4253,7 @@ LIBMDBX_API int mdbx_txn_commit_ex(MDBX_txn *txn, MDBX_commit_latency *latency);
* \see mdbx_txn_commit_ex()
* \see mdbx_txn_refresh()
* \see mdbx_txn_begin()
* \see mdbx_txn_rollback()
*
* \param [in, out] txn A transaction handle returned by \ref mdbx_txn_begin().
* \param [in] weakening_durability Additional flags to weaken durability for committing changes.
@@ -4331,6 +4333,7 @@ LIBMDBX_API int mdbx_txn_commit_embark_read(MDBX_txn **ptxn, MDBX_commit_latency
*
* \see mdbx_txn_commit_embark_read()
* \see mdbx_txn_checkpoint()
* \see mdbx_txn_rollback()
* \see mdbx_txn_commit_ex()
* \see mdbx_txn_refresh()
* \see mdbx_txn_begin_ex()
@@ -4376,11 +4379,39 @@ LIBMDBX_API int mdbx_txn_commit_embark_read(MDBX_txn **ptxn, MDBX_commit_latency
* \warning This function may be changed in future releases. */
LIBMDBX_API int mdbx_txn_amend(MDBX_txn *read_txn, MDBX_txn **ptr_write_txn, MDBX_txn_flags_t flags, void *context);
/** \brief Rolls back all uncommitted changes within the write transaction and keeps it running.
* \ingroup c_transactions
*
* Aborts and then restarts the transaction, rolling back all uncommitted changes.
* If the current thread is not eligible to manage the transaction then
* the \ref MDBX_THREAD_MISMATCH error will returned.
*
* \param [in] txn A transaction handle returned by \ref mdbx_txn_begin().
*
* \returns A non-zero error value on failure and 0 on success, some possibilities are:
* \retval MDBX_RESULT_TRUE A more recent MVCC-snapshot has been committed after reading transaction
* was started and data cannot be amended based on the desired data snapshot,
* no actions have been performed.
* \retval MDBX_TXN_OVERLAPPING The current thread is already executing a write transaction.
* \retval MDBX_PANIC A fatal error occurred earlier and
* the environment must be shut down.
* \retval MDBX_BAD_TXN Unexpected or wrong transaction state.
* \retval MDBX_EBADSIGN Transaction object has invalid signature,
* e.g. transaction was already terminated
* or memory was corrupted.
* \retval MDBX_THREAD_MISMATCH Given transaction is not owned
* by current thread.
* \retval MDBX_EINVAL Transaction handle is NULL.
*
* \warning This function may be changed in future releases. */
LIBMDBX_API int mdbx_txn_rollback(MDBX_txn *txn);
/** \brief Commits all the operations of the transaction into the database.
* \ingroup c_transactions
*
* \see mdbx_txn_commit_embark_read()
* \see mdbx_txn_checkpoint()
* \see mdbx_txn_rollback()
* \see mdbx_txn_commit_ex()
* \see mdbx_txn_refresh()
* \see mdbx_txn_begin_ex()
@@ -4435,6 +4466,7 @@ LIBMDBX_INLINE_API(int, mdbx_txn_commit, (MDBX_txn * txn)) { return mdbx_txn_com
* \see mdbx_txn_commit_ex()
* \see mdbx_txn_checkpoint()
* \see mdbx_txn_commit_embark_read()
* \see mdbx_txn_rollback()
* \see mdbx_txn_amend()
* \warning This function may be changed in future releases. */
LIBMDBX_API int mdbx_txn_abort_ex(MDBX_txn *txn, MDBX_commit_latency *latency);
@@ -4466,6 +4498,7 @@ LIBMDBX_API int mdbx_txn_abort_ex(MDBX_txn *txn, MDBX_commit_latency *latency);
* \see mdbx_txn_commit()
* \see mdbx_txn_refresh()
* \see mdbx_txn_reset()
* \see mdbx_txn_rollback()
*
* \param [in] txn A transaction handle returned by \ref mdbx_txn_begin().
*

View File

@@ -1,4 +1,4 @@
/// This file is part of the libmdbx amalgamated source code (v0.14.1-449-ga2319c94 at 2026-03-06T22:09:32+03:00).
/// This file is part of the libmdbx amalgamated source code (v0.14.1-453-ga529b6d0 at 2026-03-08T20:32:09+03:00).
/// \file mdbx.h++
/// \brief The libmdbx C++ API header file.
///

View File

@@ -1,4 +1,4 @@
/* This file is part of the libmdbx amalgamated source code (v0.14.1-449-ga2319c94 at 2026-03-06T22:09:32+03:00).
/* This file is part of the libmdbx amalgamated source code (v0.14.1-453-ga529b6d0 at 2026-03-08T20:32:09+03:00).
*
* libmdbx (aka MDBX) is an extremely fast, compact, powerful, embeddedable, transactional key-value storage engine with
* open-source code. MDBX has a specific set of properties and capabilities, focused on creating unique lightweight

View File

@@ -1,4 +1,4 @@
/* This file is part of the libmdbx amalgamated source code (v0.14.1-449-ga2319c94 at 2026-03-06T22:09:32+03:00).
/* This file is part of the libmdbx amalgamated source code (v0.14.1-453-ga529b6d0 at 2026-03-08T20:32:09+03:00).
*
* libmdbx (aka MDBX) is an extremely fast, compact, powerful, embeddedable, transactional key-value storage engine with
* open-source code. MDBX has a specific set of properties and capabilities, focused on creating unique lightweight

View File

@@ -1,4 +1,4 @@
/* This file is part of the libmdbx amalgamated source code (v0.14.1-449-ga2319c94 at 2026-03-06T22:09:32+03:00).
/* This file is part of the libmdbx amalgamated source code (v0.14.1-453-ga529b6d0 at 2026-03-08T20:32:09+03:00).
*
* libmdbx (aka MDBX) is an extremely fast, compact, powerful, embeddedable, transactional key-value storage engine with
* open-source code. MDBX has a specific set of properties and capabilities, focused on creating unique lightweight

View File

@@ -1,4 +1,4 @@
/* This file is part of the libmdbx amalgamated source code (v0.14.1-449-ga2319c94 at 2026-03-06T22:09:32+03:00).
/* This file is part of the libmdbx amalgamated source code (v0.14.1-453-ga529b6d0 at 2026-03-08T20:32:09+03:00).
*
* libmdbx (aka MDBX) is an extremely fast, compact, powerful, embeddedable, transactional key-value storage engine with
* open-source code. MDBX has a specific set of properties and capabilities, focused on creating unique lightweight

View File

@@ -1,4 +1,4 @@
/* This file is part of the libmdbx amalgamated source code (v0.14.1-449-ga2319c94 at 2026-03-06T22:09:32+03:00).
/* This file is part of the libmdbx amalgamated source code (v0.14.1-453-ga529b6d0 at 2026-03-08T20:32:09+03:00).
*
* libmdbx (aka MDBX) is an extremely fast, compact, powerful, embeddedable, transactional key-value storage engine with
* open-source code. MDBX has a specific set of properties and capabilities, focused on creating unique lightweight

View File

@@ -1,4 +1,4 @@
/* This file is part of the libmdbx amalgamated source code (v0.14.1-449-ga2319c94 at 2026-03-06T22:09:32+03:00).
/* This file is part of the libmdbx amalgamated source code (v0.14.1-453-ga529b6d0 at 2026-03-08T20:32:09+03:00).
*
* libmdbx (aka MDBX) is an extremely fast, compact, powerful, embeddedable, transactional key-value storage engine with
* open-source code. MDBX has a specific set of properties and capabilities, focused on creating unique lightweight

View File

@@ -1,4 +1,4 @@
/* This file is part of the libmdbx amalgamated source code (v0.14.1-449-ga2319c94 at 2026-03-06T22:09:32+03:00).
/* This file is part of the libmdbx amalgamated source code (v0.14.1-453-ga529b6d0 at 2026-03-08T20:32:09+03:00).
*
* libmdbx (aka MDBX) is an extremely fast, compact, powerful, embeddedable, transactional key-value storage engine with
* open-source code. MDBX has a specific set of properties and capabilities, focused on creating unique lightweight