Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Make nested transaction thread aware
Previously, if create_transaction() was called from different threads or
greenthreads, always a single transaction was returned even when the
thread was different.
This patch makes a map between thread ID and a transaction therefore
nested transaction is returned only for the same threads.
Change-Id: I884e7973d05434d798e0aeaa0f1f826905a8113e
Closes-bug: 1793499
Closes-bug: 1735154
Loading branch information
Showing
2 changed files
with
55 additions
and
6 deletions .
+14
−6
ovsdbapp/api.py
+41
−0
ovsdbapp/tests/unit/test_api.py
@@ -16,6 +16,11 @@
import contextlib
import six
try :
# Python 3 no longer has thread module
import thread # noqa
except ImportError :
import threading as thread
@six .add_metaclass (abc .ABCMeta )
@@ -67,7 +72,8 @@ def __exit__(self, exc_type, exc_val, tb):
@six .add_metaclass (abc .ABCMeta )
class API (object ):
def __init__ (self ):
self ._nested_txn = None
# Mapping between a (green)thread and its transaction.
self ._nested_txns_map = {}
@abc .abstractmethod
def create_transaction (self , check_error = False , log_errors = True , ** kwargs ):
@@ -92,16 +98,18 @@ def transaction(self, check_error=False, log_errors=True, **kwargs):
:returns: Either a new transaction or an existing one.
:rtype: :class:`Transaction`
"""
if self ._nested_txn :
yield self ._nested_txn
else :
cur_thread_id = thread .get_ident ()
try :
yield self ._nested_txns_map [cur_thread_id ]
except KeyError :
with self .create_transaction (
check_error , log_errors , ** kwargs ) as txn :
self ._nested_txn = txn
self ._nested_txns_map [ cur_thread_id ] = txn
try :
yield txn
finally :
self ._nested_txn = None
del self ._nested_txns_map [ cur_thread_id ]
@abc .abstractmethod
def db_create (self , table , ** col_values ):
@@ -14,10 +14,24 @@
import mock
import testtools
import time
from ovsdbapp import api
from ovsdbapp .tests import base
try :
import eventlet
def create_thread (executable ):
eventlet .spawn_n (executable )
except ImportError :
import threading
def create_thread (executable ):
thread = threading .Thread (target = executable )
thread .start ()
class FakeTransaction (object ):
def __enter__ (self ):
@@ -60,3 +74,30 @@ class TestException(Exception):
with self .api .transaction () as txn2 :
self .assertIsNot (txn1 , txn2 )
def test_transaction_nested_multiple_threads (self ):
shared_resource = []
def thread1 ():
with self .api .transaction () as txn :
shared_resource .append (txn )
while len (shared_resource ) == 1 :
time .sleep (0.1 )
shared_resource .append (0 )
def thread2 ():
while len (shared_resource ) != 1 :
time .sleep (0.1 )
with self .api .transaction () as txn :
shared_resource .append (txn )
shared_resource .append (0 )
create_thread (thread1 )
create_thread (thread2 )
while len (shared_resource ) != 4 :
time .sleep (0.1 )
txn1 , txn2 = shared_resource [:2 ]
self .assertNotEqual (txn1 , txn2 )
Toggle all file notes