diff --git a/mdbx.h b/mdbx.h index e249ed1e..cb14bf33 100644 --- a/mdbx.h +++ b/mdbx.h @@ -1926,6 +1926,12 @@ enum MDBX_error_t { /** Overlapping read and write transactions for the current thread */ MDBX_TXN_OVERLAPPING = -30415, + /** Внутренняя ошибка возвращаемая в случае нехватки запаса свободных страниц + * при обновлении GC. Используется как вспомогательное средство для отладки. + * \note С точки зрения пользователя семантически + * равнозначна \ref MDBX_PROBLEM. */ + MDBX_BACKLOG_DEPLETED = -30414, + /* The last of MDBX-added error codes */ MDBX_LAST_ADDED_ERRCODE = MDBX_TXN_OVERLAPPING, diff --git a/src/bits.md b/src/bits.md index 82c9eed4..99f9f117 100644 --- a/src/bits.md +++ b/src/bits.md @@ -5,7 +5,7 @@ N | MASK | ENV | TXN | DB | PUT | DBI | NOD 2 |0000 0004|ALLOC_NEW |TXN_DIRTY |DUPSORT | |DBI_FRESH |F_DUPDATA|P_OVERFLOW| | 3 |0000 0008|ALLOC_SLOT |TXN_SPILLS |INTEGERKEY| |DBI_CREAT | |P_META | | 4 |0000 0010|ALLOC_FAKE |TXN_HAS_CHILD |DUPFIXED |NOOVERWRITE|DBI_VALID | |P_BAD | | -5 |0000 0020| | |INTEGERDUP|NODUPDATA |DBI_USRVALID| |P_LEAF2 | | +5 |0000 0020| |TXN_DRAINED_GC|INTEGERDUP|NODUPDATA |DBI_USRVALID| |P_LEAF2 | | 6 |0000 0040| | |REVERSEDUP|CURRENT |DBI_DUPDATA | |P_SUBP | | 7 |0000 0080| | | |ALLDUPS |DBI_AUDITED | | | | 8 |0000 0100| _MAY_MOVE | | | | | | | <= | diff --git a/src/core.c b/src/core.c index 0bd29a4d..0962c74c 100644 --- a/src/core.c +++ b/src/core.c @@ -6750,10 +6750,12 @@ static __inline bool is_gc_usable(MDBX_txn *txn, const MDBX_cursor *mc, !(mc->mc_flags & C_GCU)) return false; - /* avoid (recursive) search inside empty tree and while tree is - updating, https://libmdbx.dqdkfa.ru/dead-github/issues/31 */ - if (txn->mt_dbs[FREE_DBI].md_entries == 0) + /* avoid search inside empty tree and while tree is updating, + https://libmdbx.dqdkfa.ru/dead-github/issues/31 */ + if (unlikely(txn->mt_dbs[FREE_DBI].md_entries == 0)) { + txn->mt_flags |= MDBX_TXN_DRAINED_GC; return false; + } return true; } @@ -7090,8 +7092,10 @@ static pgr_t page_alloc_slowpath(const MDBX_cursor *const mc, const size_t num, //--------------------------------------------------------------------------- - if (unlikely(!is_gc_usable(txn, mc, flags))) + if (unlikely(!is_gc_usable(txn, mc, flags))) { + eASSERT(env, txn->mt_flags & MDBX_TXN_DRAINED_GC); goto no_gc; + } eASSERT(env, (flags & (MDBX_ALLOC_COALESCE | MDBX_ALLOC_LIFO | MDBX_ALLOC_SHOULD_SCAN)) == 0); @@ -7178,6 +7182,7 @@ next_gc:; if (unlikely(id >= detent)) goto depleted_gc; } + txn->mt_flags &= ~MDBX_TXN_DRAINED_GC; /* Reading next GC record */ MDBX_val data; @@ -7326,9 +7331,12 @@ scan: } depleted_gc: + TRACE("%s: last id #%" PRIaTXN ", re-len %zu", "gc-depleted", id, + MDBX_PNL_GETSIZE(txn->tw.relist)); ret.err = MDBX_NOTFOUND; if (flags & MDBX_ALLOC_SHOULD_SCAN) goto scan; + txn->mt_flags |= MDBX_TXN_DRAINED_GC; //------------------------------------------------------------------------- @@ -7431,6 +7439,14 @@ depleted_gc: no_gc: eASSERT(env, pgno == 0); +#ifndef MDBX_ENABLE_BACKLOG_DEPLETED +#define MDBX_ENABLE_BACKLOG_DEPLETED 0 +#endif /* MDBX_ENABLE_BACKLOG_DEPLETED*/ + if (MDBX_ENABLE_BACKLOG_DEPLETED && + unlikely(!(txn->mt_flags & MDBX_TXN_DRAINED_GC))) { + ret.err = MDBX_BACKLOG_DEPLETED; + goto fail; + } if (flags & MDBX_ALLOC_RESERVE) { ret.err = MDBX_NOTFOUND; goto fail; @@ -9915,6 +9931,8 @@ static int gcu_prepare_backlog(MDBX_txn *txn, gcu_context_t *ctx) { (size_t)txn->mt_dbs[FREE_DBI].md_leaf_pages, (size_t)txn->mt_dbs[FREE_DBI].md_overflow_pages, (size_t)txn->mt_dbs[FREE_DBI].md_entries); + tASSERT(txn, + err != MDBX_NOTFOUND || (txn->mt_flags & MDBX_TXN_DRAINED_GC) != 0); return (err != MDBX_NOTFOUND) ? err : MDBX_SUCCESS; } diff --git a/src/internals.h b/src/internals.h index 0274cfc3..790ee2ea 100644 --- a/src/internals.h +++ b/src/internals.h @@ -1013,9 +1013,11 @@ struct MDBX_txn { /* Additional flag for sync_locked() */ #define MDBX_SHRINK_ALLOWED UINT32_C(0x40000000) +#define MDBX_TXN_DRAINED_GC 0x20 /* GC was depleted up to oldest reader */ + #define TXN_FLAGS \ (MDBX_TXN_FINISHED | MDBX_TXN_ERROR | MDBX_TXN_DIRTY | MDBX_TXN_SPILLS | \ - MDBX_TXN_HAS_CHILD | MDBX_TXN_INVALID) + MDBX_TXN_HAS_CHILD | MDBX_TXN_INVALID | MDBX_TXN_DRAINED_GC) #if (TXN_FLAGS & (MDBX_TXN_RW_BEGIN_FLAGS | MDBX_TXN_RO_BEGIN_FLAGS)) || \ ((MDBX_TXN_RW_BEGIN_FLAGS | MDBX_TXN_RO_BEGIN_FLAGS | TXN_FLAGS) & \