From 9ae3c4f9713f2819c6a873ed7316a96f62a18490 Mon Sep 17 00:00:00 2001 From: Ihor Aleksandrychiev Date: Tue, 14 Jul 2026 11:56:19 +0300 Subject: [PATCH] Fix DBPrivClean() for LMDB 1.0.0 mdb_drop() semantics LMDB 1.0.0 changed mdb_drop(): emptying the main DBI marks the transaction MDB_TXN_DROPPED, so any subsequent read/write on that same transaction fails with MDB_BAD_TXN. CFEngine reuses one write transaction per thread and callers typically repopulate the DB right after cleaning it, which triggered the failure. Signed-off-by: Ihor Aleksandrychiev --- libpromises/dbm_lmdb.c | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/libpromises/dbm_lmdb.c b/libpromises/dbm_lmdb.c index b062774092..340f84b107 100644 --- a/libpromises/dbm_lmdb.c +++ b/libpromises/dbm_lmdb.c @@ -955,7 +955,33 @@ bool DBPrivClean(DBPriv *db) assert(txn != NULL); assert(!txn->cursor_open); - return (mdb_drop(txn->txn, db->dbi, EMPTY_DB) != 0); + const int drop_rc = mdb_drop(txn->txn, db->dbi, EMPTY_DB); + if (drop_rc != MDB_SUCCESS) + { + Log(LOG_LEVEL_ERR, "Could not empty database '%s': %s", + (char *) mdb_env_get_userctx(db->env), mdb_strerror(drop_rc)); + return false; + } + + /* LMDB 1.0.0 changed mdb_drop() semantics: emptying the main DBI marks the + * transaction with MDB_TXN_DROPPED, after which it may only be committed or + * aborted -- any further read/write on it returns MDB_BAD_TXN. CFEngine + * reuses a single write transaction per thread, and callers (e.g. the + * package module in cf-agent) repopulate the DB immediately after cleaning + * it, so we must release the transaction here; the next write opens a fresh + * one. On LMDB 0.9.x this is simply an earlier commit of the same data. */ + const int commit_rc = mdb_txn_commit(txn->txn); + CheckLMDBUsable(commit_rc, db->env); + txn->txn = NULL; + txn->rw_txn = false; + if (commit_rc != MDB_SUCCESS) + { + Log(LOG_LEVEL_ERR, "Could not commit emptied database '%s': %s", + (char *) mdb_env_get_userctx(db->env), mdb_strerror(commit_rc)); + return false; + } + + return true; } int DBPrivGetDBUsagePercentage(const char *db_path)