mirror of
https://github.com/isar/libmdbx.git
synced 2025-01-06 23:04:13 +08:00
mdbx: rework internal self-audit.
Change-Id: I42a585a61a9040a24d9d8dbd12fe9055d189195c
This commit is contained in:
parent
20022658be
commit
ceac458b4e
148
src/mdbx.c
148
src/mdbx.c
@ -1372,61 +1372,6 @@ static void mdbx_cursor_chk(MDBX_cursor *mc) {
|
|||||||
}
|
}
|
||||||
#endif /* 0 */
|
#endif /* 0 */
|
||||||
|
|
||||||
/* Count all the pages in each DB and in the freelist and make sure
|
|
||||||
* it matches the actual number of pages being used.
|
|
||||||
* All named DBs must be open for a correct count. */
|
|
||||||
static int mdbx_audit(MDBX_txn *txn) {
|
|
||||||
MDBX_cursor mc;
|
|
||||||
MDBX_val key, data;
|
|
||||||
int rc;
|
|
||||||
|
|
||||||
pgno_t freecount = 0;
|
|
||||||
rc = mdbx_cursor_init(&mc, txn, FREE_DBI, NULL);
|
|
||||||
if (unlikely(rc != MDBX_SUCCESS))
|
|
||||||
return rc;
|
|
||||||
while ((rc = mdbx_cursor_get(&mc, &key, &data, MDBX_NEXT)) == 0)
|
|
||||||
freecount += *(pgno_t *)data.iov_base;
|
|
||||||
mdbx_tassert(txn, rc == MDBX_NOTFOUND);
|
|
||||||
|
|
||||||
pgno_t count = 0;
|
|
||||||
for (MDBX_dbi i = 0; i < txn->mt_numdbs; i++) {
|
|
||||||
MDBX_xcursor mx;
|
|
||||||
if (!(txn->mt_dbflags[i] & DB_VALID))
|
|
||||||
continue;
|
|
||||||
rc = mdbx_cursor_init(&mc, txn, i, &mx);
|
|
||||||
if (unlikely(rc != MDBX_SUCCESS))
|
|
||||||
return rc;
|
|
||||||
if (txn->mt_dbs[i].md_root == P_INVALID)
|
|
||||||
continue;
|
|
||||||
count += txn->mt_dbs[i].md_branch_pages + txn->mt_dbs[i].md_leaf_pages +
|
|
||||||
txn->mt_dbs[i].md_overflow_pages;
|
|
||||||
if (txn->mt_dbs[i].md_flags & MDBX_DUPSORT) {
|
|
||||||
rc = mdbx_page_search(&mc, NULL, MDBX_PS_FIRST);
|
|
||||||
for (; rc == MDBX_SUCCESS; rc = mdbx_cursor_sibling(&mc, 1)) {
|
|
||||||
MDBX_page *mp = mc.mc_pg[mc.mc_top];
|
|
||||||
for (unsigned j = 0; j < NUMKEYS(mp); j++) {
|
|
||||||
MDBX_node *leaf = NODEPTR(mp, j);
|
|
||||||
if (leaf->mn_flags & F_SUBDATA) {
|
|
||||||
MDBX_db db;
|
|
||||||
memcpy(&db, NODEDATA(leaf), sizeof(db));
|
|
||||||
count +=
|
|
||||||
db.md_branch_pages + db.md_leaf_pages + db.md_overflow_pages;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mdbx_tassert(txn, rc == MDBX_NOTFOUND);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (freecount + count + NUM_METAS != txn->mt_next_pgno) {
|
|
||||||
mdbx_print("audit: %" PRIaTXN " freecount: %" PRIaPGNO " count: %" PRIaPGNO
|
|
||||||
" total: %" PRIaPGNO " next_pgno: %" PRIaPGNO "\n",
|
|
||||||
txn->mt_txnid, freecount, count + NUM_METAS,
|
|
||||||
freecount + count + NUM_METAS, txn->mt_next_pgno);
|
|
||||||
return MDBX_CORRUPTED;
|
|
||||||
}
|
|
||||||
return MDBX_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
int mdbx_cmp(MDBX_txn *txn, MDBX_dbi dbi, const MDBX_val *a,
|
int mdbx_cmp(MDBX_txn *txn, MDBX_dbi dbi, const MDBX_val *a,
|
||||||
const MDBX_val *b) {
|
const MDBX_val *b) {
|
||||||
mdbx_assert(NULL, txn->mt_signature == MDBX_MT_SIGNATURE);
|
mdbx_assert(NULL, txn->mt_signature == MDBX_MT_SIGNATURE);
|
||||||
@ -3540,6 +3485,92 @@ static int mdbx_prep_backlog(MDBX_txn *txn, MDBX_cursor *mc) {
|
|||||||
return MDBX_SUCCESS;
|
return MDBX_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Count all the pages in each DB and in the freelist and make sure
|
||||||
|
* it matches the actual number of pages being used.
|
||||||
|
* All named DBs must be open for a correct count. */
|
||||||
|
static int mdbx_audit(MDBX_txn *txn, unsigned befree_stored) {
|
||||||
|
MDBX_cursor mc;
|
||||||
|
MDBX_val key, data;
|
||||||
|
|
||||||
|
const pgno_t pending =
|
||||||
|
(txn->mt_flags & MDBX_RDONLY)
|
||||||
|
? 0
|
||||||
|
: txn->mt_loose_count +
|
||||||
|
(txn->mt_env->me_reclaimed_pglist
|
||||||
|
? txn->mt_env->me_reclaimed_pglist[0]
|
||||||
|
: 0) +
|
||||||
|
(txn->mt_befree_pages ? txn->mt_befree_pages[0] - befree_stored
|
||||||
|
: 0);
|
||||||
|
int rc = mdbx_cursor_init(&mc, txn, FREE_DBI, NULL);
|
||||||
|
if (unlikely(rc != MDBX_SUCCESS))
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
pgno_t freecount = 0;
|
||||||
|
while ((rc = mdbx_cursor_get(&mc, &key, &data, MDBX_NEXT)) == 0)
|
||||||
|
freecount += *(pgno_t *)data.iov_base;
|
||||||
|
mdbx_tassert(txn, rc == MDBX_NOTFOUND);
|
||||||
|
|
||||||
|
pgno_t count = 0;
|
||||||
|
for (MDBX_dbi i = FREE_DBI; i <= MAIN_DBI; i++) {
|
||||||
|
MDBX_xcursor mx;
|
||||||
|
if (!(txn->mt_dbflags[i] & DB_VALID))
|
||||||
|
continue;
|
||||||
|
rc = mdbx_cursor_init(&mc, txn, i, &mx);
|
||||||
|
if (unlikely(rc != MDBX_SUCCESS))
|
||||||
|
return rc;
|
||||||
|
if (txn->mt_dbs[i].md_root == P_INVALID)
|
||||||
|
continue;
|
||||||
|
count += txn->mt_dbs[i].md_branch_pages + txn->mt_dbs[i].md_leaf_pages +
|
||||||
|
txn->mt_dbs[i].md_overflow_pages;
|
||||||
|
|
||||||
|
rc = mdbx_page_search(&mc, NULL, MDBX_PS_FIRST);
|
||||||
|
while (rc == MDBX_SUCCESS) {
|
||||||
|
MDBX_page *mp = mc.mc_pg[mc.mc_top];
|
||||||
|
for (unsigned j = 0; j < NUMKEYS(mp); j++) {
|
||||||
|
MDBX_node *leaf = NODEPTR(mp, j);
|
||||||
|
if ((leaf->mn_flags & (F_DUPDATA | F_SUBDATA)) == F_SUBDATA) {
|
||||||
|
MDBX_db db_copy, *db;
|
||||||
|
memcpy(db = &db_copy, NODEDATA(leaf), sizeof(db_copy));
|
||||||
|
if ((txn->mt_flags & MDBX_RDONLY) == 0) {
|
||||||
|
for (MDBX_dbi k = txn->mt_numdbs; --k > MAIN_DBI;) {
|
||||||
|
if ((txn->mt_dbflags[k] & MDBX_TBL_DIRTY) &&
|
||||||
|
/* txn->mt_dbxs[k].md_name.iov_len > 0 && */
|
||||||
|
NODEKSZ(leaf) == txn->mt_dbxs[k].md_name.iov_len &&
|
||||||
|
memcmp(NODEKEY(leaf), txn->mt_dbxs[k].md_name.iov_base,
|
||||||
|
NODEKSZ(leaf)) == 0) {
|
||||||
|
db = txn->mt_dbs + k;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
count +=
|
||||||
|
db->md_branch_pages + db->md_leaf_pages + db->md_overflow_pages;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rc = mdbx_cursor_sibling(&mc, 1);
|
||||||
|
}
|
||||||
|
mdbx_tassert(txn, rc == MDBX_NOTFOUND);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pending + freecount + count + NUM_METAS == txn->mt_next_pgno)
|
||||||
|
return MDBX_SUCCESS;
|
||||||
|
|
||||||
|
if ((txn->mt_flags & MDBX_RDONLY) == 0)
|
||||||
|
mdbx_error(
|
||||||
|
"audit @%" PRIaTXN ": %u(pending) = %u(loose-count) + "
|
||||||
|
"%u(reclaimed-list) + %u(befree-pending) - %u(befree-stored)",
|
||||||
|
txn->mt_txnid, pending, txn->mt_loose_count,
|
||||||
|
txn->mt_env->me_reclaimed_pglist ? txn->mt_env->me_reclaimed_pglist[0]
|
||||||
|
: 0,
|
||||||
|
txn->mt_befree_pages ? txn->mt_befree_pages[0] : 0, befree_stored);
|
||||||
|
mdbx_error("audit @%" PRIaTXN ": %" PRIaPGNO "(pending) + %" PRIaPGNO
|
||||||
|
"(free) + %" PRIaPGNO "(count) = %" PRIaPGNO
|
||||||
|
"(total) <> %" PRIaPGNO "(next-pgno)",
|
||||||
|
txn->mt_txnid, pending, freecount, count + NUM_METAS,
|
||||||
|
pending + freecount + count + NUM_METAS, txn->mt_next_pgno);
|
||||||
|
return MDBX_PROBLEM;
|
||||||
|
}
|
||||||
|
|
||||||
/* Cleanup reclaimed GC records, than save the befree-list as of this
|
/* Cleanup reclaimed GC records, than save the befree-list as of this
|
||||||
* transaction to GC (aka freeDB). This recursive changes the reclaimed-list
|
* transaction to GC (aka freeDB). This recursive changes the reclaimed-list
|
||||||
* loose-list and befree-list. Keep trying until it stabilizes. */
|
* loose-list and befree-list. Keep trying until it stabilizes. */
|
||||||
@ -4431,8 +4462,11 @@ int mdbx_txn_commit(MDBX_txn *txn) {
|
|||||||
env->me_reclaimed_pglist = NULL;
|
env->me_reclaimed_pglist = NULL;
|
||||||
mdbx_pnl_shrink(&txn->mt_befree_pages);
|
mdbx_pnl_shrink(&txn->mt_befree_pages);
|
||||||
|
|
||||||
if (mdbx_audit_enabled())
|
if (mdbx_audit_enabled()) {
|
||||||
mdbx_audit(txn);
|
rc = mdbx_audit(txn, 0);
|
||||||
|
if (unlikely(rc != MDBX_SUCCESS))
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
rc = mdbx_page_flush(txn, 0);
|
rc = mdbx_page_flush(txn, 0);
|
||||||
if (likely(rc == MDBX_SUCCESS)) {
|
if (likely(rc == MDBX_SUCCESS)) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user