Fix SQLite named_locks
The named_lock context manager rely on READ_COMMITED isolation layer to create locks in the database. Unfortunately sqlite does not support sur isolation layer. Since years the tests were running fine but the new eventlet release (0.38.2) seems to order the green threads differently, leading to race conditions. To fix this, let's introduce a threading semaphore lock inside the named_lock when sqlite is used. Change-Id: I6d0e7e3b2fb7498becd637477a9ccd60cd0da515 Signed-off-by: Arnaud M <arnaud.morin@gmail.com>
This commit is contained in:
parent
f74f45903d
commit
add8a2eb97
@ -33,6 +33,14 @@ def acquire_lock(obj_id, session):
|
||||
_locks[obj_id] = (session, tup[1])
|
||||
|
||||
|
||||
def release_lock(obj_id, session):
|
||||
with _mutex:
|
||||
if obj_id in _locks:
|
||||
tup = _locks.get(obj_id)
|
||||
tup[1].release()
|
||||
del _locks[obj_id]
|
||||
|
||||
|
||||
def release_locks(session):
|
||||
with _mutex:
|
||||
for obj_id, tup in _locks.items():
|
||||
|
@ -2080,6 +2080,13 @@ def delete_event_triggers(session=None, **kwargs):
|
||||
|
||||
@b.session_aware()
|
||||
def create_named_lock(name, session=None):
|
||||
if b.get_driver_name() == 'sqlite':
|
||||
# In case of 'sqlite' we need to apply a manual lock because sqlite
|
||||
# is not READ_COMMITED able
|
||||
# This is useful for testing purpose as sqlite is not supposed to be
|
||||
# used for prod
|
||||
sqlite_lock.acquire_lock(name, session)
|
||||
|
||||
# This method has to work not through SQLAlchemy session because
|
||||
# session may not immediately issue an SQL query to a database
|
||||
# and instead just schedule it whereas we need to make sure to
|
||||
@ -2103,7 +2110,7 @@ def get_named_locks(session=None, **kwargs):
|
||||
|
||||
|
||||
@b.session_aware()
|
||||
def delete_named_lock(lock_id, session=None):
|
||||
def delete_named_lock(lock_id, name, session=None):
|
||||
# This method has to work without SQLAlchemy session because
|
||||
# session may not immediately issue an SQL query to a database
|
||||
# and instead just schedule it whereas we need to make sure to
|
||||
@ -2118,6 +2125,9 @@ def delete_named_lock(lock_id, session=None):
|
||||
|
||||
session.flush()
|
||||
|
||||
if b.get_driver_name() == 'sqlite':
|
||||
sqlite_lock.release_lock(name, session)
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def named_lock(name):
|
||||
@ -2131,7 +2141,7 @@ def named_lock(name):
|
||||
|
||||
yield
|
||||
|
||||
delete_named_lock(lock_id)
|
||||
delete_named_lock(lock_id, name)
|
||||
|
||||
|
||||
@b.session_aware()
|
||||
|
@ -3536,20 +3536,20 @@ class LockTest(SQLAlchemyTest):
|
||||
|
||||
self.assertEqual('lock1', locks[0].name)
|
||||
|
||||
db_api.delete_named_lock('invalid_lock_id')
|
||||
db_api.delete_named_lock('invalid_lock_id', 'fake')
|
||||
|
||||
locks = db_api.get_named_locks()
|
||||
|
||||
self.assertEqual(1, len(locks))
|
||||
|
||||
db_api.delete_named_lock(locks[0].id)
|
||||
db_api.delete_named_lock(locks[0].id, 'lock1')
|
||||
|
||||
locks = db_api.get_named_locks()
|
||||
|
||||
self.assertEqual(0, len(locks))
|
||||
|
||||
def test_with_named_lock(self):
|
||||
name = 'lock1'
|
||||
name = 'lock42'
|
||||
|
||||
with db_api.named_lock(name):
|
||||
# Make sure that within 'with' section the lock record exists.
|
||||
|
Loading…
x
Reference in New Issue
Block a user