mdbx: доработка `gc_update()` с отключением нового/нестабильного кода.

This commit is contained in:
Леонид Юрьев (Leonid Yuriev) 2024-05-23 12:42:59 +03:00
parent 8e29fb1f26
commit a2753c9ae1
2 changed files with 109 additions and 52 deletions

View File

@ -3,8 +3,12 @@
#include "internals.h"
MDBX_MAYBE_UNUSED static inline const char *dbg_prefix(gcu_t *ctx) {
return ctx->lifo ? " lifo" : " fifo";
MDBX_NOTHROW_PURE_FUNCTION static bool is_lifo(const MDBX_txn *txn) {
return (txn->env->flags & MDBX_LIFORECLAIM) != 0;
}
MDBX_MAYBE_UNUSED static inline const char *dbg_prefix(const gcu_t *ctx) {
return is_lifo(ctx->cursor.txn) ? " lifo" : " fifo";
}
static inline size_t backlog_size(MDBX_txn *txn) {
@ -314,7 +318,7 @@ static int gcu_retired(MDBX_txn *txn, gcu_t *ctx) {
#endif /* MDBX_DEBUG && (ENABLE_MEMCHECK || __SANITIZE_ADDRESS__) */
if (retired_pages_before == MDBX_PNL_GETSIZE(txn->tw.retired_pages)) {
const size_t at = (ctx->lifo == MDBX_PNL_ASCENDING)
const size_t at = (is_lifo(txn) == MDBX_PNL_ASCENDING)
? left - chunk
: ctx->retired_stored;
pgno_t *const begin = txn->tw.retired_pages + at;
@ -387,7 +391,7 @@ typedef struct gcu_rid_result {
static rid_t get_rid_for_reclaimed(MDBX_txn *txn, gcu_t *ctx,
const size_t left) {
rid_t r;
if (ctx->lifo) {
if (is_lifo(txn)) {
if (txn->tw.gc.reclaimed == nullptr) {
txn->tw.gc.reclaimed = txl_alloc();
if (unlikely(!txn->tw.gc.reclaimed)) {
@ -584,35 +588,43 @@ int gc_update(MDBX_txn *txn, gcu_t *ctx) {
MDBX_env *const env = txn->env;
ctx->cursor.next = txn->cursors[FREE_DBI];
txn->cursors[FREE_DBI] = &ctx->cursor;
int rc;
// tASSERT(txn, MDBX_PNL_GETSIZE(txn->tw.retired_pages) ||
// ctx->cleaned_slot <
// (txn->tw.gc.reclaimed ?
// MDBX_PNL_GETSIZE(txn->tw.gc.reclaimed) : 0)
// || ctx->cleaned_id < txn->tw.gc.last_reclaimed);
pgno_t prev_first_unallocated = 0;
/* txn->tw.relist[] can grow and shrink during this call.
* txn->tw.gc.last_reclaimed and txn->tw.retired_pages[] can only grow.
* But page numbers cannot disappear from txn->tw.retired_pages[]. */
#if MDBX_ENABLE_GC_EXPERIMENTAL
retry_clean_adj:
ctx->reserve_adj = 0;
#endif /* MDBX_ENABLE_GC_EXPERIMENTAL */
retry:
ctx->loop += prev_first_unallocated == txn->geo.first_unallocated;
prev_first_unallocated = txn->geo.first_unallocated;
ctx->loop += ctx->prev_first_unallocated == txn->geo.first_unallocated;
TRACE(">> restart, loop %u", ctx->loop);
if (ctx->loop)
TRACE("%s", " >> restart");
int rc = MDBX_SUCCESS;
tASSERT(txn, pnl_check_allocated(txn->tw.relist, txn->geo.first_unallocated -
MDBX_ENABLE_REFUND));
tASSERT(txn, dpl_check(txn));
if (unlikely(/* paranoia */ ctx->loop > ((MDBX_DEBUG > 0) ? 12 : 42))) {
ERROR("too more loops %zu, bailout", ctx->loop);
ERROR("too more loops %u, bailout", ctx->loop);
rc = MDBX_PROBLEM;
goto bailout;
}
if (unlikely(ctx->dense)) {
if (unlikely(ctx->dense ||
ctx->prev_first_unallocated > txn->geo.first_unallocated)) {
rc = clean_stored_retired(txn, ctx);
if (unlikely(rc != MDBX_SUCCESS))
goto bailout;
}
ctx->prev_first_unallocated = txn->geo.first_unallocated;
rc = MDBX_SUCCESS;
ctx->reserved = 0;
ctx->cleaned_slot = 0;
ctx->reused_slot = 0;
@ -623,19 +635,11 @@ retry:
/* Come back here after each Put() in case retired-list changed */
TRACE("%s", " >> continue");
if (ctx->retired_stored != MDBX_PNL_GETSIZE(txn->tw.retired_pages) &&
(ctx->loop == 1 || ctx->retired_stored > env->maxgc_large1page ||
MDBX_PNL_GETSIZE(txn->tw.retired_pages) > env->maxgc_large1page)) {
rc = prepare_backlog(txn, ctx);
if (unlikely(rc != MDBX_SUCCESS))
goto bailout;
}
tASSERT(txn,
pnl_check_allocated(txn->tw.relist, txn->geo.first_unallocated -
MDBX_ENABLE_REFUND));
MDBX_val key, data;
if (ctx->lifo) {
if (is_lifo(txn)) {
if (ctx->cleaned_slot <
(txn->tw.gc.reclaimed ? MDBX_PNL_GETSIZE(txn->tw.gc.reclaimed) : 0)) {
ctx->reserved = 0;
@ -654,11 +658,9 @@ retry:
continue;
if (unlikely(rc != MDBX_SUCCESS))
goto bailout;
if (likely(!ctx->dense)) {
rc = prepare_backlog(txn, ctx);
if (unlikely(rc != MDBX_SUCCESS))
goto bailout;
}
rc = prepare_backlog(txn, ctx);
if (unlikely(rc != MDBX_SUCCESS))
goto bailout;
tASSERT(txn, ctx->cleaned_id <= env->lck->cached_oldest.weak);
TRACE("%s: cleanup-reclaimed-id [%zu]%" PRIaTXN, dbg_prefix(ctx),
ctx->cleaned_slot, ctx->cleaned_id);
@ -671,7 +673,8 @@ retry:
}
} else {
/* Удаляем оставшиеся вынутые из GC записи. */
while (ctx->cleaned_id <= txn->tw.gc.last_reclaimed) {
while (txn->tw.gc.last_reclaimed &&
ctx->cleaned_id <= txn->tw.gc.last_reclaimed) {
rc = outer_first(&ctx->cursor, &key, nullptr);
if (rc == MDBX_NOTFOUND)
break;
@ -692,11 +695,9 @@ retry:
ctx->cleaned_id = unaligned_peek_u64(4, key.iov_base);
if (ctx->cleaned_id > txn->tw.gc.last_reclaimed)
break;
if (likely(!ctx->dense)) {
rc = prepare_backlog(txn, ctx);
if (unlikely(rc != MDBX_SUCCESS))
goto bailout;
}
rc = prepare_backlog(txn, ctx);
if (unlikely(rc != MDBX_SUCCESS))
goto bailout;
tASSERT(txn, ctx->cleaned_id <= txn->tw.gc.last_reclaimed);
tASSERT(txn, ctx->cleaned_id <= env->lck->cached_oldest.weak);
TRACE("%s: cleanup-reclaimed-id %" PRIaTXN, dbg_prefix(ctx),
@ -744,7 +745,9 @@ retry:
env->maxgc_large1page / 2)) {
TRACE("%s: reclaimed-list changed %zu -> %zu, retry", dbg_prefix(ctx),
ctx->amount, MDBX_PNL_GETSIZE(txn->tw.relist));
#if MDBX_ENABLE_GC_EXPERIMENTAL
ctx->reserve_adj += ctx->reserved - MDBX_PNL_GETSIZE(txn->tw.relist);
#endif /* MDBX_ENABLE_GC_EXPERIMENTAL */
goto retry;
}
ctx->amount = MDBX_PNL_GETSIZE(txn->tw.relist);
@ -768,6 +771,7 @@ retry:
if (unlikely(rc != MDBX_SUCCESS))
goto bailout;
}
#if MDBX_ENABLE_GC_EXPERIMENTAL
const size_t left = ctx->amount - ctx->reserved - ctx->reserve_adj;
TRACE("%s: amount %zu, reserved %zd, reserve_adj %zu, left %zd, "
"lifo-reclaimed-slots %zu, "
@ -775,6 +779,15 @@ retry:
dbg_prefix(ctx), ctx->amount, ctx->reserved, ctx->reserve_adj, left,
txn->tw.gc.reclaimed ? MDBX_PNL_GETSIZE(txn->tw.gc.reclaimed) : 0,
ctx->reused_slot);
#else
const size_t left = ctx->amount - ctx->reserved;
TRACE("%s: amount %zu, reserved %zd, left %zd, "
"lifo-reclaimed-slots %zu, "
"reused-gc-slots %zu",
dbg_prefix(ctx), ctx->amount, ctx->reserved, left,
txn->tw.gc.reclaimed ? MDBX_PNL_GETSIZE(txn->tw.gc.reclaimed) : 0,
ctx->reused_slot);
#endif /* MDBX_ENABLE_GC_EXPERIMENTAL */
if (0 >= (intptr_t)left)
break;
@ -897,7 +910,9 @@ retry:
TRACE("%s", " >> filling");
/* Fill in the reserved records */
#if MDBX_ENABLE_GC_EXPERIMENTAL
size_t excess_slots = 0;
#endif /* MDBX_ENABLE_GC_EXPERIMENTAL */
ctx->fill_idx =
txn->tw.gc.reclaimed
? MDBX_PNL_GETSIZE(txn->tw.gc.reclaimed) - ctx->reused_slot
@ -913,15 +928,12 @@ retry:
size_t left = ctx->amount, excess = 0;
if (txn->tw.gc.reclaimed == nullptr) {
tASSERT(txn, ctx->lifo == 0);
tASSERT(txn, is_lifo(txn) == 0);
rc = outer_first(&ctx->cursor, &key, &data);
if (unlikely(rc != MDBX_SUCCESS)) {
if (rc == MDBX_NOTFOUND && ctx->reserve_adj)
goto retry_clean_adj;
if (unlikely(rc != MDBX_SUCCESS))
goto bailout;
}
} else {
tASSERT(txn, ctx->lifo != 0);
tASSERT(txn, is_lifo(txn) != 0);
}
while (true) {
@ -929,29 +941,37 @@ retry:
TRACE("%s: left %zu of %zu", dbg_prefix(ctx), left,
MDBX_PNL_GETSIZE(txn->tw.relist));
if (txn->tw.gc.reclaimed == nullptr) {
tASSERT(txn, ctx->lifo == 0);
tASSERT(txn, is_lifo(txn) == 0);
fill_gc_id = unaligned_peek_u64(4, key.iov_base);
if (ctx->fill_idx == 0 || fill_gc_id > txn->tw.gc.last_reclaimed) {
#if MDBX_ENABLE_GC_EXPERIMENTAL
if (!left)
break;
#endif /* MDBX_ENABLE_GC_EXPERIMENTAL */
NOTICE("** restart: reserve depleted (fill_idx %zu, fill_id %" PRIaTXN
" > last_reclaimed %" PRIaTXN ", left %zu",
ctx->fill_idx, fill_gc_id, txn->tw.gc.last_reclaimed, left);
#if MDBX_ENABLE_GC_EXPERIMENTAL
ctx->reserve_adj =
(ctx->reserve_adj > left) ? ctx->reserve_adj - left : 0;
#endif /* MDBX_ENABLE_GC_EXPERIMENTAL */
goto retry;
}
ctx->fill_idx -= 1;
} else {
tASSERT(txn, ctx->lifo != 0);
tASSERT(txn, is_lifo(txn) != 0);
if (ctx->fill_idx >= MDBX_PNL_GETSIZE(txn->tw.gc.reclaimed)) {
#if MDBX_ENABLE_GC_EXPERIMENTAL
if (!left)
break;
#endif /* MDBX_ENABLE_GC_EXPERIMENTAL */
NOTICE("** restart: reserve depleted (fill_idx %zu >= "
"gc.reclaimed %zu, left %zu",
ctx->fill_idx, MDBX_PNL_GETSIZE(txn->tw.gc.reclaimed), left);
#if MDBX_ENABLE_GC_EXPERIMENTAL
ctx->reserve_adj =
(ctx->reserve_adj > left) ? ctx->reserve_adj - left : 0;
#endif /* MDBX_ENABLE_GC_EXPERIMENTAL */
goto retry;
}
ctx->fill_idx += 1;
@ -978,12 +998,14 @@ retry:
if (unlikely(chunk > left)) {
const size_t delta = chunk - left;
excess += delta;
TRACE("%s: chunk %zu > left %zu, @%" PRIaTXN, dbg_prefix(ctx), chunk,
left, fill_gc_id);
#if MDBX_ENABLE_GC_EXPERIMENTAL
if (!left) {
excess_slots += 1;
goto next;
}
TRACE("%s: chunk %zu > left %zu, @%" PRIaTXN, dbg_prefix(ctx), chunk,
left, fill_gc_id);
#endif /* MDBX_ENABLE_GC_EXPERIMENTAL */
if ((ctx->loop < 5 && delta > (ctx->loop / 2)) ||
delta > env->maxgc_large1page)
data.iov_len = (left + 1) * sizeof(pgno_t);
@ -999,8 +1021,10 @@ retry:
NOTICE("** restart: reclaimed-list changed (%zu -> %zu, loose +%zu)",
ctx->amount, MDBX_PNL_GETSIZE(txn->tw.relist),
txn->tw.loose_count);
#if MDBX_ENABLE_GC_EXPERIMENTAL
if (ctx->loop < 5 || (ctx->loop > 10 && (ctx->loop & 1)))
goto retry_clean_adj;
#endif /* MDBX_ENABLE_GC_EXPERIMENTAL */
goto retry;
}
@ -1036,28 +1060,39 @@ retry:
goto bailout;
}
#if MDBX_ENABLE_GC_EXPERIMENTAL
next:
#else
if (left == 0)
break;
#endif /* MDBX_ENABLE_GC_EXPERIMENTAL */
if (txn->tw.gc.reclaimed == nullptr) {
tASSERT(txn, ctx->lifo == 0);
tASSERT(txn, is_lifo(txn) == 0);
rc = outer_next(&ctx->cursor, &key, &data, MDBX_NEXT);
if (unlikely(rc != MDBX_SUCCESS)) {
if (rc != MDBX_NOTFOUND)
goto bailout;
rc = MDBX_SUCCESS;
break;
#if MDBX_ENABLE_GC_EXPERIMENTAL
if (rc == MDBX_NOTFOUND && !left) {
rc = MDBX_SUCCESS;
break;
}
#endif /* MDBX_ENABLE_GC_EXPERIMENTAL */
goto bailout;
}
} else {
tASSERT(txn, ctx->lifo != 0);
tASSERT(txn, is_lifo(txn) != 0);
}
}
if (excess) {
#if MDBX_ENABLE_GC_EXPERIMENTAL
size_t n = excess, adj = excess;
while (n >= env->maxgc_large1page)
adj -= n /= env->maxgc_large1page;
ctx->reserve_adj += adj;
TRACE("%s: extra %zu reserved space, adj +%zu (%zu)", dbg_prefix(ctx),
excess, adj, ctx->reserve_adj);
#endif /* MDBX_ENABLE_GC_EXPERIMENTAL */
}
}
@ -1069,6 +1104,7 @@ retry:
goto retry;
}
#if MDBX_ENABLE_GC_EXPERIMENTAL
if (unlikely(excess_slots)) {
const bool will_retry = ctx->loop < 5 || excess_slots > 1;
NOTICE("** %s: reserve excess (excess-slots %zu, filled-slot %zu, adj %zu, "
@ -1078,6 +1114,17 @@ retry:
if (will_retry)
goto retry;
}
#else
if (unlikely(ctx->fill_idx != (txn->tw.gc.reclaimed
? MDBX_PNL_GETSIZE(txn->tw.gc.reclaimed)
: 0))) {
const bool will_retry = ctx->loop < 9;
NOTICE("** %s: reserve excess (filled-idx %zu, loop %u)",
will_retry ? "restart" : "ignore", ctx->fill_idx, ctx->loop);
if (will_retry)
goto retry;
}
#endif /* MDBX_ENABLE_GC_EXPERIMENTAL */
tASSERT(txn, txn->tw.gc.reclaimed == nullptr ||
ctx->cleaned_slot == MDBX_PNL_GETSIZE(txn->tw.gc.reclaimed));
@ -1089,6 +1136,6 @@ bailout:
#if MDBX_ENABLE_PROFGC
env->lck->pgops.gc_prof.wloops += (uint32_t)ctx->loop;
#endif /* MDBX_ENABLE_PROFGC */
TRACE("<<< %zu loops, rc = %d", ctx->loop, rc);
TRACE("<<< %u loops, rc = %d", ctx->loop, rc);
return rc;
}

View File

@ -5,12 +5,22 @@
#include "essentials.h"
#ifndef MDBX_ENABLE_GC_EXPERIMENTAL
#define MDBX_ENABLE_GC_EXPERIMENTAL 0
#elif !(MDBX_ENABLE_GC_EXPERIMENTAL == 0 || MDBX_ENABLE_GC_EXPERIMENTAL == 1)
#error MDBX_ENABLE_GC_EXPERIMENTAL must be defined as 0 or 1
#endif /* MDBX_ENABLE_GC_EXPERIMENTAL */
typedef struct gc_update_context {
size_t loop, reserve_adj;
unsigned loop;
pgno_t prev_first_unallocated;
bool dense;
#if MDBX_ENABLE_GC_EXPERIMENTAL
intptr_t reserve_adj;
#endif /* MDBX_ENABLE_GC_EXPERIMENTAL */
size_t retired_stored;
size_t amount, reserved, cleaned_slot, reused_slot, fill_idx;
txnid_t cleaned_id, rid;
bool lifo, dense;
#if MDBX_ENABLE_BIGFOOT
txnid_t bigfoot;
#endif /* MDBX_ENABLE_BIGFOOT */
@ -22,7 +32,7 @@ typedef struct gc_update_context {
static inline int gc_update_init(MDBX_txn *txn, gcu_t *ctx) {
memset(ctx, 0, offsetof(gcu_t, cursor));
ctx->lifo = (txn->env->flags & MDBX_LIFORECLAIM) != 0;
ctx->dense = txn->txnid < MIN_TXNID;
#if MDBX_ENABLE_BIGFOOT
ctx->bigfoot = txn->txnid;
#endif /* MDBX_ENABLE_BIGFOOT */