[Date Prev][Date Next]
[Chronological]
[Thread]
[Top]
(ITS#7517) mdb_dbi_close(dbi updated in existing txn) breaks
Full_Name: Hallvard B Furuseth
Version: mdb.master 48dc782ea612f85e8356a50bfbafe22e5be121cf
OS: Linux x86_64
URL:
Submission from: (NULL) (193.69.163.163)
Submitted by: hallvard
If a thread modifies a named MDB database and closes it before
committing, the changes are lost. The mdb_dbi_close() doc only
says we must not close DBs which _other_ theads will reference.
I expect mdb_dbi_close() could check if the env has an active
write txn where the DB is dirty, and mdb_put() it first. And
in any parent txns which have it open, dunno if that's doable.
If not, in this case maybe _close() could stash away the DB info
so mdb_txn_commit()/mdb_txn_reset0() can save the updates.
Note that another mdb_dbi_open() can reuse the closed DBI, so
carelessness could e.g. save the changes with the wrong DB name.
Test case (no nested txns):
#include <lmdb.h>
#include <stdio.h>
#include <stdlib.h>
int main(void) {
char *dbname = "test.mdb";
MDB_env *env;
MDB_txn *txn;
MDB_dbi dbi;
int rdonly, rc;
MDB_val key = {3, "foo"}, data = {3, "abc"}, rdata;
# define E(e) { rc = (e); if (rc) { fprintf(stderr, "%s:%d: %s: %s\n", \
__FILE__, __LINE__, #e, mdb_strerror(rc)); abort(); } }
remove(dbname);
for (rdonly = 0; rdonly <= MDB_RDONLY; rdonly += MDB_RDONLY) {
E(mdb_env_create(&env));
E(mdb_env_set_maxdbs(env, 1));
E(mdb_env_open(env, dbname, MDB_NOSYNC|MDB_NOSUBDIR, 0666));
E(mdb_txn_begin(env, NULL, rdonly, &txn));
E(mdb_dbi_open(txn, NULL, rdonly ? 0 : MDB_CREATE, &dbi));
if (!rdonly) {
E(mdb_put(txn, dbi, &key, &data, 0));
mdb_dbi_close(env, dbi);
} else {
E(mdb_get(txn, dbi, &key, &rdata));
}
E(mdb_txn_commit(txn));
mdb_env_close(env);
}
return 0;
}