While working on a fix for #39, I noticed three tests in tests/test_dbm.py fail under Python 3.7-3.12.
The failing tests are:
tests/store/test_dbm.py::DbmQueueTest::test_sync_checkpoint_ops
tests/store/test_dbm.py::DbmQueueTest::test_sync_checkpoint_timeout
tests/store/test_dbm.py::DbmQueueTest::test_sync_loss
All three tests (essentially) do:
store = DbmQueue(dest_dir)
store.enqueue(...)
store2 = DbmQueue(dest_dir)
assert store2.size() == N
DbmQueue uses shelve to manage two dbm databases. The Restrictions section of the shelve docs says the following:
The shelve module does not support concurrent read/write access to shelved objects. (Multiple simultaneous read accesses are safe.) When a program has a shelf open for writing, no other program should have it open for reading or writing. Unix file locking can be used to solve this, but this differs across Unix versions and requires knowledge about the database implementation used.
The failures I'm seeing appear to be the result of using shelve in violation of the above restriction.
In Python < 3.13 with the dbm.gnu backend, all three tests fail on the store2 = line with a message like,
_gdbm.error: [Errno 11] Resource temporarily unavailable: '/tmp/coilmq-dbm-test6magd2ek/metadata'
The dbm.gnu backend locks the database file by default. Locking can be disabled by adding n to the shelve.open flag arg, like flag="cn", but that will cause problems when other backends are used.
In Python < 3.13 without the dbm.gnu backend (i.e.dbm.ndbm becomes the first choice), the first two tests fail on the assert because store2.size() is 0, not N. It looks like shelve.Shelf::sync() isn't enough to persist a dbm.ndbm database to disk because the file changes between the last sync() and close(). I think test_sync_loss passes by accident.
In Python >= 3.13, the tests pass. Python 3.13 introduced the dbm.sqlite3 backend and made it the the first choice backend for dbm.open(). dbm.sqlite3 seems to behave the way these tests expect.
My recommendation for the first two tests would be to remove the second DbmQueue and instead use mocks and/or monkeypatching to count the number of times the shelf was synced to the disk. I think it's more important to verify that the shelf is synced after the expected ops/after the right amount of time has elapsed, than it is to make sure the content is readable with a second DbmQueue without closing the first one.
I'm not sure what test_sync_loss is trying to verify so I can't recommend a fix that would work for every situation.
While working on a fix for #39, I noticed three tests in
tests/test_dbm.pyfail under Python 3.7-3.12.The failing tests are:
tests/store/test_dbm.py::DbmQueueTest::test_sync_checkpoint_opstests/store/test_dbm.py::DbmQueueTest::test_sync_checkpoint_timeouttests/store/test_dbm.py::DbmQueueTest::test_sync_lossAll three tests (essentially) do:
DbmQueueusesshelveto manage two dbm databases. The Restrictions section of the shelve docs says the following:The failures I'm seeing appear to be the result of using shelve in violation of the above restriction.
In Python < 3.13 with the
dbm.gnubackend, all three tests fail on thestore2 =line with a message like,The
dbm.gnubackend locks the database file by default. Locking can be disabled by addingnto theshelve.openflagarg, likeflag="cn", but that will cause problems when other backends are used.In Python < 3.13 without the
dbm.gnubackend (i.e.dbm.ndbmbecomes the first choice), the first two tests fail on theassertbecausestore2.size()is 0, notN. It looks likeshelve.Shelf::sync()isn't enough to persist adbm.ndbmdatabase to disk because the file changes between the lastsync()andclose(). I thinktest_sync_losspasses by accident.In Python >= 3.13, the tests pass. Python 3.13 introduced the
dbm.sqlite3backend and made it the the first choice backend fordbm.open().dbm.sqlite3seems to behave the way these tests expect.My recommendation for the first two tests would be to remove the second
DbmQueueand instead use mocks and/or monkeypatching to count the number of times the shelf was synced to the disk. I think it's more important to verify that the shelf is synced after the expected ops/after the right amount of time has elapsed, than it is to make sure the content is readable with a secondDbmQueuewithout closing the first one.I'm not sure what
test_sync_lossis trying to verify so I can't recommend a fix that would work for every situation.