Skip to content

Commit 341fa28

Browse files
committed
Preserve trust DB on failed reload
Trust database reload treated every update_database failure as fatal. Under concurrent reload and SIGHUP stress, LMDB can report MDB_MAP_FULL while committing the drop transaction, and the daemon exited even though the old trust database was still the published database. The delete path now reports map exhaustion with the same code used by write failures, retries once after growing auto-sized maps, and returns a preserved-database status when the drop transaction never becomes visible. The reload path records the failure and keeps serving the previous trust database for that preserved state, while retaining the existing fatal behavior for rebuild failures after the database has been cleared.
1 parent 3365e1b commit 341fa28

1 file changed

Lines changed: 44 additions & 4 deletions

File tree

src/library/database.c

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ typedef enum { DB_NO_OP, ONE_FILE, RELOAD_DB, FLUSH_CACHE, RELOAD_RULES } db_ops
6464
#define MAX_DELIMS 3 // Trustdb has 4 fields - therefore 3 delimiters
6565
#define DEFAULT_DB_MAX_SIZE_MB 100
6666
#define WRITE_DB_MAP_FULL 6
67+
#define UPDATE_DB_PRESERVED 7
6768
/*
6869
* The decision worker configuration is added later in the worker-pool
6970
* roadmap. Size LMDB readers now for that planned cap plus maintenance users
@@ -1195,6 +1196,18 @@ static int database_empty(void)
11951196
}
11961197

11971198

1199+
/*
1200+
* delete_all_entries_db - Clear all trust records in one LMDB transaction.
1201+
*
1202+
* The old trust database remains published until the drop transaction
1203+
* commits. Any error reported here means the delete did not become visible to
1204+
* readers, so callers can preserve the current trust database instead of
1205+
* treating the reload as a partially applied update. MDB_MAP_FULL is returned
1206+
* as WRITE_DB_MAP_FULL so auto-sizing paths can grow the live map and retry.
1207+
*
1208+
* Returns 0 on success, WRITE_DB_MAP_FULL for map exhaustion, and a non-zero
1209+
* stage code for other failures.
1210+
*/
11981211
static int delete_all_entries_db()
11991212
{
12001213
int rc = 0;
@@ -1212,7 +1225,7 @@ static int delete_all_entries_db()
12121225
if ((rc = mdb_drop(txn, dbi, 0))) {
12131226
msg(LOG_DEBUG, "mdb_drop -> %s", mdb_strerror(rc));
12141227
abort_transaction(txn);
1215-
return 3;
1228+
return rc == MDB_MAP_FULL ? WRITE_DB_MAP_FULL : 3;
12161229
}
12171230

12181231
if ((rc = mdb_txn_commit(txn))) {
@@ -1221,7 +1234,7 @@ static int delete_all_entries_db()
12211234
else
12221235
msg(LOG_DEBUG, "mdb_txn_commit -> %s",
12231236
mdb_strerror(rc));
1224-
return 4;
1237+
return rc == MDB_MAP_FULL ? WRITE_DB_MAP_FULL : 4;
12251238
}
12261239

12271240
return 0;
@@ -2160,6 +2173,7 @@ void unlock_rule(void) {
21602173
static int update_database(conf_t *config)
21612174
{
21622175
int rc;
2176+
int retries = 0;
21632177

21642178
msg(LOG_INFO, "Updating trust database");
21652179
msg(LOG_DEBUG, "Loading trust database backends");
@@ -2172,10 +2186,28 @@ static int update_database(conf_t *config)
21722186

21732187
lock_update_thread();
21742188

2175-
if ((rc = delete_all_entries_db())) {
2189+
for (;;) {
2190+
rc = delete_all_entries_db();
2191+
if (rc == 0)
2192+
break;
2193+
2194+
// The existing DB is still active because the drop did not
2195+
// commit; grow auto-sized maps once before giving up.
2196+
if (rc == WRITE_DB_MAP_FULL && config->do_audit_db_sizing &&
2197+
retries == 0 && grow_map_after_full(config) == 0) {
2198+
retries++;
2199+
continue;
2200+
}
2201+
21762202
msg(LOG_ERR, "Cannot delete database (%d)", rc);
21772203
unlock_update_thread();
2178-
return rc;
2204+
/*
2205+
* delete_all_entries_db() failed before a transaction commit
2206+
* completed. The current LMDB contents are still the active
2207+
* trust database, so report the failed reload without killing
2208+
* the daemon.
2209+
*/
2210+
return UPDATE_DB_PRESERVED;
21792211
}
21802212

21812213
if (stop) {
@@ -2344,6 +2376,14 @@ static void do_reload_db(conf_t* config)
23442376
goto out;
23452377

23462378
record_trust_reload_failure();
2379+
if (rc == UPDATE_DB_PRESERVED) {
2380+
// update_database() failed before clearing the live DB.
2381+
// Keep running with the last successfully built trust set.
2382+
msg(LOG_ERR,
2383+
"Previous trust database preserved after reload failure");
2384+
goto out;
2385+
}
2386+
23472387
close(ffd[0].fd);
23482388
backend_close();
23492389
unlink_fifo();

0 commit comments

Comments
 (0)