You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Getting to data a Database opened with with Env::open_database on a process without a previous Env opened as Read Write held by the process lead to error "22 EINVAL", due to Env metadata not sync'ed in the Env at the end of the RoTxn openning transaction.
Open a RoTxn with Env::read_txn let's call it ro_txn_0
Open databases db_0 with open_database with ro_txn_0
ro_txn_0 is aborted through the drop
Fix involve commit() ro_txn_0` who was used for openning.
Then
open an other ro_txn_1 with Env::read_txn
get data on db_0 with ro_txn_1 -> Io(Os { code: 22, kind: InvalidInput, message: "Invalid argument" })' (EINVAL)
How to fix?
Committing the RoTxn that open of databases fix the issue. LMDB log an empty commit but not so empty!
Why it fixes? The committing like said below synchronize the lmdb Env internal data about opened databases with the currents openings done with the RoTxn, the RoTxn is Read only but only in respect to the data in the databases not the Env in memory or the process.
Possible work around? Force RwTxn for database opening, maybe not desirable.
How it escaped Heed test suite?
The problem arise if the database is opened without prior Env about that database opened previously.
Context
I am in a setup with one process doing writes and read call it the Producer (no problem it's perfect) and an other doing only read the Reader.
The issue also apply when doing read-only access without other Env held inside the process.
/** Number of DBs in metapage (free and main) - also hardcoded elsewhere */#defineCORE_DBS 2
Where is set txn->mt_numdbs.
Here at least for my code-path in the reader: mdb_txn_renew0
/* Setup db info */txn->mt_numdbs=env->me_numdbs;
for (i=CORE_DBS; i<txn->mt_numdbs; i++) {
x=env->me_dbflags[i];
txn->mt_dbs[i].md_flags=x&PERSISTENT_FLAGS;
txn->mt_dbflags[i] = (x&MDB_VALID) ? DB_VALID|DB_USRVALID|DB_STALE : 0;
}
txn->mt_dbflags[MAIN_DBI] =DB_VALID|DB_USRVALID;
txn->mt_dbflags[FREE_DBI] =DB_VALID;
Ok but it should be right no? env->me_numdbs shall have the right value 3 here!
It turns out the open_dbi does not update it, it updates the txn->mt_numdbs and on commit it will update the env->me_numdbs back!! ;)
staticvoidmdb_txn_end(MDB_txn*txn, unsignedmode)
{
MDB_env*env=txn->mt_env;
#ifMDB_DEBUGstaticconstchar*constnames[] =MDB_END_NAMES;
#endif/* Export or close DBI handles opened in this txn */mdb_dbis_update(txn, mode&MDB_END_UPDATE);
And since RoTxn only expose mdb_txn_abort we cannot update the database number of the environment.
voidmdb_txn_abort(MDB_txn*txn)
{
if (txn==NULL)
return;
if (txn->mt_child)
mdb_txn_abort(txn->mt_child);
mdb_txn_end(txn, MDB_END_ABORT|MDB_END_SLOT|MDB_END_FREE);
}
Using mdb_txn_commit even empty looks definitely better since it set correctly the parameters for synchronizing database openned to the Env LMDB struct.
Screenshot of the debugging session
Here a screenshot of a debugging session showing the issue:
In this session I opened 1 database (+ the 2 code DB managed by lmdb itself) so n = 3.
And since RoTxn only mdb_txn_abort we cannot update the database number of the environment.
The logs
Added the following patch for debugging lmdb and producing the log
diff --git a/libraries/liblmdb/Makefile b/libraries/liblmdb/Makefile
index 942d0db..142989a 100644
--- a/libraries/liblmdb/Makefile+++ b/libraries/liblmdb/Makefile@@ -22,7 +22,7 @@ CC = gcc
AR = ar
W = -W -Wall -Wno-unused-parameter -Wbad-function-cast -Wuninitialized
THREADS = -pthread
-OPT = -O2 -g+OPT = -O0 -g
CFLAGS = $(THREADS) $(OPT) $(W) $(XCFLAGS)
LDLIBS =
SOLIBS =
diff --git a/libraries/liblmdb/mdb.c b/libraries/liblmdb/mdb.c
index 26b7cba..a3a40da 100644
--- a/libraries/liblmdb/mdb.c+++ b/libraries/liblmdb/mdb.c@@ -566,6 +566,7 @@ typedef MDB_ID pgno_t;
* See struct MDB_txn.mt_txnid for details.
*/
typedef MDB_ID txnid_t;
+#define MDB_DEBUG 3
/** @defgroup debug Debug Macros
* @{
@@ -575,12 +576,12 @@ typedef MDB_ID txnid_t;
* Set this to 1 for copious tracing. Set to 2 to add dumps of all IDLs
* read from and written to the database (used for free space management).
*/
-#define MDB_DEBUG 0+#define MDB_DEBUG 3
#endif
#if MDB_DEBUG
-static int mdb_debug;-static txnid_t mdb_debug_start;+static int mdb_debug = 1;+static txnid_t mdb_debug_start = 1;
/** Print a debug message with printf formatting.
* Requires double parenthesis around 2 or more args.
@@ -6817,6 +6818,7 @@ mdb_get(MDB_txn *txn, MDB_dbi dbi,
DKBUF;
DPRINTF(("===> get db %u key [%s]", dbi, DKEY(key)));
+ DPRINTF(("READ txn %"Yu" mt_numdbs %u ", txn->mt_txnid, (txn)->mt_numdbs));
if (!key || !data || !TXN_DBI_EXIST(txn, dbi, DB_USRVALID))
return EINVAL;
Full logs including LMDB logs
cargo test -- env::tests::open_already_existing_database_multiprocess
Compiling heed v0.20.0-alpha.2(/heed/heed)Finished test [unoptimized + debuginfo] target(s) in 0.54s
Running unittests src/lib.rs(target/debug/deps/heed-9bbbf71c6c2f5ce8)
running 1 test
mdb_env_open2:4955 new mdbenv
mdb_env_init_meta:4266 writing new meta page
mdb_env_open2:5033 opened database version 1, pagesize 4096
mdb_env_open2:5035 using meta page 0
mdb_env_open2:5036 depth:0
mdb_env_open2:5037 entries:0
mdb_env_open2:5038 branch pages:0
mdb_env_open2:5039 leaf pages:0
mdb_env_open2:5040 overflow pages:0
mdb_env_open2:5041 root:18446744073709551615
mdb_env_open:5669 opened dbenv 0x7f43e8000ed0
mdb_txn_begin:3283 begin txn 1w 0x7f43e8002750 on mdbenv 0x7f43e8000ed0, root page 18446744073709551615
mdb_page_search:6655 tree is empty
mdb_cursor_put:7637 ==> put db 1 key [73686172656430], size 7, data size 48
mdb_cursor_put:7698 allocating new root leaf page
mdb_page_new:8235 allocated new mpage 2, page size 4096
mdb_cursor_push:6114 pushing page 2 on db 1 cursor 0x7f43ef762840
mdb_cursor_put:7720 inserting key at index 0
mdb_node_add:8336 add to leaf page 2 index 0, data size 48 key size 7[73686172656430]
mdb_txn_commit:4096 committing txn 10x7f43e8002750 on mdbenv 0x7f43e8000ed0, root page 2
mdb_cursor_put:7637 ==> put db 1 key [73686172656430], size 7, data size 48
mdb_page_search:6680 db 1 root page 2 has flags 0x12
mdb_page_search_root:6565 found leaf page 2for key [73686172656430]
mdb_node_search:6000 searching 1 keys in leaf page 2
mdb_node_search:6045 found leaf index 0[73686172656430], rc = 0
mdb_cursor_set:7269 ==> cursor placed on key [73686172656430]
mdb_page_search:6655 tree is empty
mdb_page_flush:3861 committing page 2
mdb_page_flush:3792 committing page 2
mdb_env_write_meta:4315 writing meta page 1for root page 2
mdb_txn_end:3350 committed txn 1w 0x7f43e8002750 on mdbenv 0x7f43e8000ed0, root page 2
mdb_txn_begin:3283 begin txn 2w 0x7f43e8002750 on mdbenv 0x7f43e8000ed0, root page 2
mdb_page_search:6680 db 1 root page 2 has flags 0x2
mdb_page_search_root:6565 found leaf page 2 for key [73686172656430]
mdb_node_search:6000 searching 1 keys in leaf page 2
mdb_node_search:6045 found leaf index 0[73686172656430], rc = 0
mdb_page_search:6655 tree is empty
mdb_cursor_put:7637 ==> put db 2 key [73686172656430], size 7, data size 17
mdb_cursor_put:7698 allocating new root leaf page
mdb_page_new:8235 allocated new mpage 3, page size 4096
mdb_cursor_push:6114 pushing page 3 on db 2 cursor 0x7f43ef7628b0
mdb_cursor_put:7720 inserting key at index 0
mdb_node_add:8336 add to leaf page 3 index 0, data size 17 key size 7[73686172656430]
mdb_txn_commit:4096 committing txn 20x7f43e8002750 on mdbenv 0x7f43e8000ed0, root page 2
mdb_cursor_put:7637 ==> put db 1 key [73686172656430], size 7, data size 48
mdb_page_search:6680 db 1 root page 2 has flags 0x2
mdb_page_search_root:6565 found leaf page 2 for key [73686172656430]
mdb_node_search:6000 searching 1 keys in leaf page 2
mdb_node_search:6045 found leaf index 0[73686172656430], rc = 0
mdb_cursor_set:7269 ==> cursor placed on key [73686172656430]
mdb_page_touch:2766 touched db 1 page 2 -> 4
mdb_page_search:6655 tree is empty
mdb_cursor_put:7637 ==> put db 0 key [0200000000000000], size 8, data size 16
mdb_cursor_put:7698 allocating new root leaf page
mdb_page_new:8235 allocated new mpage 5, page size 4096
mdb_cursor_push:6114 pushing page 5 on db 0 cursor 0x7f43ef762b70
mdb_cursor_put:7720 inserting key at index 0
mdb_node_add:8336 add to leaf page 5 index 0, data size 16 key size 8[0200000000000000]
mdb_freelist_save:3582IDL write txn 2 root 5 num 1
mdb_freelist_save:3585IDL2
mdb_page_search:6680 db 0 root page 5 has flags 0x12
mdb_page_search_root:6565 found leaf page 5 for key [null]
mdb_cursor_next:6946 cursor_next: top page is 5in cursor 0x7f43ef762970
mdb_cursor_next:6954 =====> move to next sibling page
mdb_page_flush:3861 committing page 3
mdb_page_flush:3861 committing page 4
mdb_page_flush:3861 committing page 5
mdb_page_flush:3792 committing page 5
mdb_env_write_meta:4315 writing meta page 0for root page 4
mdb_txn_end:3350 committed txn 2w 0x7f43e8002750 on mdbenv 0x7f43e8000ed0, root page 4
mdb_env_open2:5033 opened database version 1, pagesize 4096
mdb_env_open2:5035 using meta page 0
mdb_env_open2:5036 depth:1
mdb_env_open2:5037 entries:1
mdb_env_open2:5038 branch pages:0
mdb_env_open2:5039 leaf pages:1
mdb_env_open2:5040 overflow pages:0
mdb_env_open2:5041 root:4
mdb_env_open:5669 opened dbenv 0x7f43e8000ed0
mdb_txn_begin:3283 begin txn 2r 0x7f43e8003350 on mdbenv 0x7f43e8000ed0, root page 4
mdb_page_search:6680 db 1 root page 4 has flags 0x2
mdb_page_search_root:6565 found leaf page 4 for key [73686172656430]
mdb_node_search:6000 searching 1 keys in leaf page 4
mdb_node_search:6045 found leaf index 0[73686172656430], rc = 0
mdb_cursor_set:7269 ==> cursor placed on key [73686172656430]
mdb_txn_end:3350 abort txn 2r 0x7f43e8003350 on mdbenv 0x7f43e8000ed0, root page 4
mdb_txn_begin:3283 begin txn 2r 0x7f43e8003350 on mdbenv 0x7f43e8000ed0, root page 4
mdb_get:6820 ===> get db 2 key [73686172656430]
mdb_get:6821READ txn 2 mt_numdbs 2
mdb_txn_end:3350 abort txn 2r 0x7f43e8003350 on mdbenv 0x7f43e8000ed0, root page 4
test env::tests::open_already_existing_database_multiprocess ... FAILED
failures:
---- env::tests::open_already_existing_database_multiprocess stdout ----
thread 'env::tests::open_already_existing_database_multiprocess' panicked at 'called `Result::unwrap()` on an `Err` value:Io(Os{ code:22, kind:InvalidInput, message:"Invalid argument"})', heed/src/env.rs:985:65
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
failures:
env::tests::open_already_existing_database_multiprocess
test result:FAILED.0 passed;1failed;0 ignored;0 measured;16 filtered out; finished in 0.00s
error: test failed, to rerun pass `-p heed --lib`
The text was updated successfully, but these errors were encountered:
Getting to data a
Database
opened with withEnv::open_database
on a process without a previousEnv
opened as Read Write held by the process lead to error "22 EINVAL", due to Env metadata not sync'ed in the Env at the end of theRoTxn
openning transaction.Steps to reproduce
Env
on a databaseRoTxn
withEnv::read_txn
let's call itro_txn_0
db_0
with open_database withro_txn_0
ro_txn_0
is aborted through thedrop
commit()
ro_txn_0` who was used for openning.Then
ro_txn_1
withEnv::read_txn
db_0
withro_txn_1
-> Io(Os { code: 22, kind: InvalidInput, message: "Invalid argument" })' (EINVAL)How to fix?
Committing the
RoTxn
that open of databases fix the issue. LMDB log an empty commit but not so empty!Why it fixes? The committing like said below synchronize the lmdb
Env
internal data about opened databases with the currents openings done with theRoTxn
, theRoTxn
is Read only but only in respect to the data in the databases not the Env in memory or the process.Possible work around? Force
RwTxn
for database opening, maybe not desirable.How it escaped Heed test suite?
The problem arise if the database is opened without prior
Env
about that database opened previously.Context
I am in a setup with one process doing writes and read call it the Producer (no problem it's perfect) and an other doing only read the Reader.
The issue also apply when doing read-only access without other
Env
held inside the process.In both case used the open_database
It leads to the following error:
The investigation
mdb_get
checkout thetxn->mt_numdbs
on the reader it will be 2 because it's the stock value due toDB_CORE
LMDB macro.mdb_get L6821
define TXN_DBI_EXIST
define CORE_DBS
Where is set
txn->mt_numdbs
.Here at least for my code-path in the reader: mdb_txn_renew0
Ok but it should be right no?
env->me_numdbs
shall have the right value 3 here!It turns out the
open_dbi
does not update it, it updates thetxn->mt_numdbs
and on commit it will update theenv->me_numdbs
back!! ;)Leading us to this little piece of code: mdb_dbis_update
Called back mdb_txn_end
And since
RoTxn
only expose mdb_txn_abort we cannot update the database number of the environment.RoTxn::drop
Using mdb_txn_commit even empty looks definitely better since it set correctly the parameters for synchronizing database openned to the Env LMDB struct.
Screenshot of the debugging session
Here a screenshot of a debugging session showing the issue:
In this session I opened 1 database (+ the 2 code DB managed by lmdb itself) so n = 3.
And since
RoTxn
only mdb_txn_abort we cannot update the database number of the environment.The logs
Added the following patch for debugging lmdb and producing the log
Full logs including LMDB logs
The text was updated successfully, but these errors were encountered: