Skip to content

Commit fec1474

Browse files
committed
Add LMDB reload diagnostics
The trust database logs only reported the active named database page count and a generic db_max_size warning. During reload stress that was not enough to distinguish real trust entry growth from LMDB map high-water growth caused by old reader transactions delaying page reuse. Add grouped LMDB diagnostics for map-full and write/delete failures. The logs now include actual map size, map pages, allocated high-water pages, last page, transaction id, named database entries and pages, reader slots, and stale reader cleanup. Size checks also log active pages versus allocated pages and warn when the high-water mark approaches the map limit while the live database remains smaller.
1 parent 341fa28 commit fec1474

1 file changed

Lines changed: 140 additions & 6 deletions

File tree

src/library/database.c

Lines changed: 140 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -561,6 +561,109 @@ static void close_env(int do_close_dbi)
561561
memset(&walk_read, 0, sizeof(walk_read));
562562
}
563563

564+
/*
565+
* log_lmdb_state - log LMDB map, named DB, and reader-table state.
566+
* @priority: syslog priority used for the diagnostic lines.
567+
* @context: short description of the operation being diagnosed.
568+
* @lmdb_rc: LMDB return code that triggered the diagnostic, or 0.
569+
*
570+
* LMDB map pressure follows the environment high-water mark, not just the
571+
* current named database page count. Rebuilding a steady set of trust entries
572+
* can still run out of map space when old reader transactions keep deleted
573+
* pages from being reused. These diagnostics are intentionally grouped around
574+
* failures and size checks so the logs show active DB pages, allocated pages,
575+
* the last transaction id, and reader-table pressure together.
576+
*
577+
* Returns: none.
578+
*/
579+
static void log_lmdb_state(int priority, const char *context, int lmdb_rc)
580+
{
581+
MDB_envinfo info;
582+
MDB_stat stat;
583+
MDB_txn *txn = NULL;
584+
const char *where = context ? context : "unknown operation";
585+
size_t page_size = 4096;
586+
size_t map_pages = 0;
587+
size_t allocated_pages = 0;
588+
size_t db_pages = 0;
589+
int stale_readers = 0;
590+
int reader_rc;
591+
int stat_rc = 0;
592+
int rc;
593+
594+
if (env == NULL) {
595+
msg(priority, "LMDB state after %s: environment is closed",
596+
where);
597+
return;
598+
}
599+
600+
rc = mdb_env_info(env, &info);
601+
if (rc) {
602+
msg(priority, "LMDB state after %s: mdb_env_info failed: %s",
603+
where, mdb_strerror(rc));
604+
return;
605+
}
606+
607+
reader_rc = mdb_reader_check(env, &stale_readers);
608+
609+
if (dbi_init) {
610+
stat_rc = mdb_txn_begin(env, NULL, MDB_RDONLY, &txn);
611+
if (stat_rc == 0) {
612+
stat_rc = mdb_stat(txn, dbi, &stat);
613+
mdb_txn_abort(txn);
614+
}
615+
} else {
616+
stat_rc = EINVAL;
617+
}
618+
619+
if (stat_rc == 0) {
620+
page_size = stat.ms_psize;
621+
db_pages = stat.ms_branch_pages + stat.ms_leaf_pages +
622+
stat.ms_overflow_pages;
623+
}
624+
625+
if (page_size)
626+
map_pages = info.me_mapsize / page_size;
627+
allocated_pages = info.me_last_pgno + 1;
628+
629+
if (lmdb_rc)
630+
msg(priority, "LMDB state after %s: error=%s (%d)",
631+
where, mdb_strerror(lmdb_rc), lmdb_rc);
632+
633+
msg(priority,
634+
"LMDB env after %s: map=%zu MiB pages=%zu "
635+
"allocated=%zu (%zu%%) last_pgno=%zu txnid=%zu",
636+
where, info.me_mapsize / MEGABYTE, map_pages, allocated_pages,
637+
map_pages ? (100 * allocated_pages) / map_pages : 0,
638+
info.me_last_pgno, info.me_last_txnid);
639+
640+
if (stat_rc == 0) {
641+
msg(priority,
642+
"LMDB db after %s: entries=%zu pages=%zu (%zu%%) "
643+
"branch=%zu leaf=%zu overflow=%zu depth=%u page_size=%zu",
644+
where, stat.ms_entries, db_pages,
645+
map_pages ? (100 * db_pages) / map_pages : 0,
646+
stat.ms_branch_pages, stat.ms_leaf_pages,
647+
stat.ms_overflow_pages, stat.ms_depth, page_size);
648+
} else {
649+
msg(priority, "LMDB db after %s: stat unavailable: %s",
650+
where, mdb_strerror(stat_rc));
651+
}
652+
653+
if (reader_rc) {
654+
msg(priority,
655+
"LMDB readers after %s: reader_check failed: %s slots_used=%u max=%u",
656+
where, mdb_strerror(reader_rc), info.me_numreaders,
657+
info.me_maxreaders);
658+
} else {
659+
msg(priority,
660+
"LMDB readers after %s: slots_used=%u max=%u "
661+
"stale_cleared=%d configured_max=%u",
662+
where, info.me_numreaders, info.me_maxreaders,
663+
stale_readers, db_max_readers);
664+
}
665+
}
666+
564667
static void close_db(int do_report)
565668
{
566669
if (do_report) {
@@ -600,6 +703,23 @@ static void check_db_size(const conf_t *config)
600703
mdb_env_info(env, &st);
601704
max_pages = st.me_mapsize / size;
602705
unsigned long percent = max_pages ? (100*pages)/max_pages : 0;
706+
unsigned long allocated = st.me_last_pgno + 1;
707+
unsigned long allocated_percent =
708+
max_pages ? (100*allocated)/max_pages : 0;
709+
710+
// Active DB pages can stay steady while LMDB's high-water mark grows
711+
// if readers pin old pages across repeated rebuilds.
712+
msg(LOG_DEBUG,
713+
"Trust database active pages: %lu (%lu%%), allocated pages: %lu (%lu%%)",
714+
pages, percent, allocated, allocated_percent);
715+
if (allocated_percent > 85 && allocated_percent > percent) {
716+
msg(LOG_WARNING,
717+
"Trust database LMDB map high-water at %lu%% capacity "
718+
"while active DB pages are at %lu%%",
719+
allocated_percent, percent);
720+
log_lmdb_state(LOG_WARNING, "trust database size check", 0);
721+
}
722+
603723
if (percent > 85) {
604724
if (config->do_audit_db_sizing)
605725
msg(LOG_WARNING, "Trust database at %lu%% capacity - "
@@ -920,14 +1040,19 @@ static int write_db(const char *idx, size_t idx_len, const char *data)
9201040
value.mv_size = strlen(data);
9211041

9221042
if ((rc = mdb_put(txn, dbi, &key, &value, 0))) {
923-
msg(LOG_ERR, "%s", mdb_strerror(rc));
9241043
abort_transaction(txn);
1044+
msg(LOG_ERR, "mdb_put failed while writing trust DB: %s",
1045+
mdb_strerror(rc));
1046+
log_lmdb_state(LOG_ERR, "trust DB write", rc);
9251047
ret_val = (rc == MDB_MAP_FULL) ? WRITE_DB_MAP_FULL : 3;
9261048
goto out;
9271049
}
9281050

9291051
if ((rc = mdb_txn_commit(txn))) {
930-
msg(LOG_ERR, "%s", mdb_strerror(rc));
1052+
msg(LOG_ERR,
1053+
"mdb_txn_commit failed after writing trust DB: %s",
1054+
mdb_strerror(rc));
1055+
log_lmdb_state(LOG_ERR, "trust DB write commit", rc);
9311056
ret_val = (rc == MDB_MAP_FULL) ? WRITE_DB_MAP_FULL : 4;
9321057
goto out;
9331058
}
@@ -1213,8 +1338,13 @@ static int delete_all_entries_db()
12131338
int rc = 0;
12141339
MDB_txn *txn;
12151340

1216-
if (mdb_txn_begin(env, NULL, 0, &txn))
1341+
rc = mdb_txn_begin(env, NULL, 0, &txn);
1342+
if (rc) {
1343+
msg(LOG_ERR, "mdb_txn_begin failed before trust DB delete: %s",
1344+
mdb_strerror(rc));
1345+
log_lmdb_state(LOG_ERR, "trust DB delete begin", rc);
12171346
return 1;
1347+
}
12181348

12191349
if (open_dbi(txn)) {
12201350
abort_transaction(txn);
@@ -1223,17 +1353,21 @@ static int delete_all_entries_db()
12231353

12241354
// 0 -> delete , 1 -> delete and close
12251355
if ((rc = mdb_drop(txn, dbi, 0))) {
1226-
msg(LOG_DEBUG, "mdb_drop -> %s", mdb_strerror(rc));
12271356
abort_transaction(txn);
1357+
msg(LOG_ERR, "mdb_drop failed while clearing trust DB: %s",
1358+
mdb_strerror(rc));
1359+
log_lmdb_state(LOG_ERR, "trust DB delete", rc);
12281360
return rc == MDB_MAP_FULL ? WRITE_DB_MAP_FULL : 3;
12291361
}
12301362

12311363
if ((rc = mdb_txn_commit(txn))) {
12321364
if (rc == MDB_MAP_FULL)
1233-
msg(LOG_ERR, "db_max_size needs to be increased");
1365+
msg(LOG_ERR,
1366+
"mdb_txn_commit hit MDB_MAP_FULL while clearing trust DB");
12341367
else
1235-
msg(LOG_DEBUG, "mdb_txn_commit -> %s",
1368+
msg(LOG_ERR, "mdb_txn_commit while clearing trust DB: %s",
12361369
mdb_strerror(rc));
1370+
log_lmdb_state(LOG_ERR, "trust DB delete commit", rc);
12371371
return rc == MDB_MAP_FULL ? WRITE_DB_MAP_FULL : 4;
12381372
}
12391373

0 commit comments

Comments
 (0)