Skip to content

Commit

Permalink
Default max_spare_txns on cpython impl to 0.
Browse files Browse the repository at this point in the history
Also, don't create spare transactions if the above parameter is true.

This prevents hanging open transactions across forks. This is exhibited
in a MDB_BAD_RSLOT error.

Fixes #346.
  • Loading branch information
jnwatson committed Jan 8, 2025
1 parent 8c143c3 commit ba544aa
Show file tree
Hide file tree
Showing 4 changed files with 38 additions and 6 deletions.
2 changes: 1 addition & 1 deletion ChangeLog
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
* Add Python 3.10 support.

* Fix crash relating to caching of transactions. The 'max_spare_txns'
parameter to Environment/open is currently ignored.
parameter to Environment/open is currently ignored in cpython.

2021-04-19 v1.2.1
* Resolve CI bug where non-Linux wheels were not being published to PyPI.
Expand Down
2 changes: 1 addition & 1 deletion lmdb/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,4 @@ def _reading_docs():
from lmdb.cffi import __all__
from lmdb.cffi import __doc__

__version__ = '1.6.2'
__version__ = '1.7.0.dev0'
12 changes: 8 additions & 4 deletions lmdb/cpython.c
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,9 @@ struct EnvObject {
int readonly;
/** Spare read-only transaction . */
struct MDB_txn *spare_txn;
/** Maximum number of spare transactions. In cpython only 0 and 1 are supported.
* If process will be forked, this must be set to 0. */
int max_spare_txns;
};

/** TransObject.flags bitfield values. */
Expand Down Expand Up @@ -1162,7 +1165,7 @@ env_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
int max_dbs;
int max_spare_txns;
int lock;
} arg = {NULL, 10485760, 1, 0, 1, 1, 0, 0755, 1, 1, 0, 1, 126, 0, 1, 1};
} arg = {NULL, 10485760, 1, 0, 1, 1, 0, 0755, 1, 1, 0, 1, 126, 0, 0, 1};

static const struct argspec argspec[] = {
{"path", ARG_OBJ, OFFSET(env_new, path)},
Expand All @@ -1180,7 +1183,7 @@ env_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{"max_readers", ARG_INT, OFFSET(env_new, max_readers)},
{"max_dbs", ARG_INT, OFFSET(env_new, max_dbs)},
{"max_spare_txns", ARG_INT, OFFSET(env_new, max_spare_txns)},
{"lock", ARG_BOOL, OFFSET(env_new, lock)}
{"lock", ARG_BOOL, OFFSET(env_new, lock)},
};

PyObject *fspath_obj = NULL;
Expand Down Expand Up @@ -1208,6 +1211,7 @@ env_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
self->main_db = NULL;
self->env = NULL;
self->spare_txn = NULL;
self->max_spare_txns = arg.max_spare_txns;

if((rc = mdb_env_create(&self->env))) {
err_set("mdb_env_create", rc);
Expand Down Expand Up @@ -3213,8 +3217,8 @@ trans_dealloc(TransObject *self)
PyObject_ClearWeakRefs((PyObject *) self);
}

if(txn && self->env && !self->env->spare_txn &&
(self->flags & TRANS_RDONLY)) {
if(txn && self->env && !self->env->spare_txn &&
self->env->max_spare_txns && (self->flags & TRANS_RDONLY)) {
MDEBUG("caching trans")
mdb_txn_reset(txn);
self->env->spare_txn = txn;
Expand Down
28 changes: 28 additions & 0 deletions tests/fork_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import lmdb
import os
import time

print(" python: Opening environment")
env = lmdb.Environment('db.lmdb')
print(" python: Opened environment %r" % env)
print()

print(" python: Starting transaction")
txn = env.begin(write=False)
print(" python: Started transaction %r" % txn)
print()

print(" python: Deleting transaction")
del txn
print(" python: Deleted transaction")
print()

if (os.fork() != 0):
os.wait()
print(" python: Exited forked process")
print()
print(" python: Starting transaction (no. 2)")
txn = env.begin(write=False)
print(" python: Started transaction (no. 2) %r" % txn)
else:
print(" python: Inside forked process")

0 comments on commit ba544aa

Please sign in to comment.