Do not reuse sqlite connection

Prior to this patch, `PersistentDict` has been reusing sqlite
connection for all subsequent calls. This proved to fail in
MT environments.

With this change, `PersistentDict` reopens sqlite connection from
each object and closes it down in hope to make it MT-safe.

Change-Id: Ic3e4618f44236c86e59c62bebfd817b40b5235aa
This commit is contained in:
Ilya Etingof 2019-08-27 14:29:15 +02:00
parent 43f33479f7
commit a0e3057b9a
2 changed files with 22 additions and 25 deletions

View File

@ -19,6 +19,7 @@ try:
except ImportError:
import collections
import contextlib
from functools import wraps
import os
import pickle
@ -73,18 +74,16 @@ def memoize(permanent_cache=None):
class PersistentDict(MutableMapping):
DBPATH = os.path.join(tempfile.gettempdir(), 'sushy-emulator')
_connection = None
_dbpath = None
def make_permanent(self, dbpath, dbfile):
dbpath = dbpath or self.DBPATH
if not os.path.exists(dbpath):
os.makedirs(dbpath)
dbpath = os.path.join(dbpath, dbfile) + '.sqlite'
self._connection = sqlite3.connect(dbpath)
self._dbpath = os.path.join(dbpath, dbfile) + '.sqlite'
with self.connection as connection:
cursor = connection.cursor()
with self.connection() as cursor:
cursor.execute(
'create table if not exists cache '
'(key blob primary key not null, value blob not null)'
@ -100,18 +99,25 @@ class PersistentDict(MutableMapping):
def decode(blob):
return pickle.loads(blob)
@property
@contextlib.contextmanager
def connection(self):
if not self._connection:
if not self._dbpath:
raise TypeError('Dict is not yet persistent')
return self._connection
connection = sqlite3.connect(self._dbpath)
try:
yield connection.cursor()
connection.commit()
finally:
connection.close()
def __getitem__(self, key):
key = self.encode(key)
with self.connection as connection:
cursor = connection.cursor()
with self.connection() as cursor:
cursor.execute(
'select value from cache where key=?',
(key,)
@ -127,8 +133,7 @@ class PersistentDict(MutableMapping):
key = self.encode(key)
value = self.encode(value)
with self.connection as connection:
cursor = connection.cursor()
with self.connection() as cursor:
cursor.execute(
'insert or replace into cache values (?, ?)',
(key, value)
@ -137,9 +142,7 @@ class PersistentDict(MutableMapping):
def __delitem__(self, key):
key = self.encode(key)
with self.connection as connection:
cursor = connection.cursor()
with self.connection() as cursor:
cursor.execute(
'select count(*) from cache where key=?',
(key,)
@ -154,8 +157,7 @@ class PersistentDict(MutableMapping):
)
def __iter__(self):
with self.connection as connection:
cursor = connection.cursor()
with self.connection() as cursor:
cursor.execute(
'select key from cache'
)
@ -165,9 +167,9 @@ class PersistentDict(MutableMapping):
yield self.decode(r[0])
def __len__(self):
with self.connection as connection:
cursor = connection.cursor()
with self.connection() as cursor:
cursor.execute(
'select count(*) from cache'
)
return cursor.fetchone()[0]
count = cursor.fetchone()[0]
return count

View File

@ -116,7 +116,6 @@ class PersistentDictTestCase(base.BaseTestCase):
mock_pickle.dumps.return_value = 'pickled-key'
mock_connection = mock_sqlite3.connect.return_value
mock_connection = mock_connection.__enter__.return_value
mock_cursor = mock_connection.cursor.return_value
mock_cursor.fetchone.return_value = ['pickled-value']
@ -136,7 +135,6 @@ class PersistentDictTestCase(base.BaseTestCase):
'pickled-key', 'pickled-value']
mock_connection = mock_sqlite3.connect.return_value
mock_connection = mock_connection.__enter__.return_value
mock_cursor = mock_connection.cursor.return_value
pd[1] = 2
@ -153,7 +151,6 @@ class PersistentDictTestCase(base.BaseTestCase):
mock_pickle.dumps.return_value = 'pickled-key'
mock_connection = mock_sqlite3.connect.return_value
mock_connection = mock_connection.__enter__.return_value
mock_cursor = mock_connection.cursor.return_value
del pd[1]
@ -169,7 +166,6 @@ class PersistentDictTestCase(base.BaseTestCase):
mock_pickle.dumps.return_value = 'pickled-key'
mock_connection = mock_sqlite3.connect.return_value
mock_connection = mock_connection.__enter__.return_value
mock_cursor = mock_connection.cursor.return_value
mock_cursor.fetchall.return_value = [['pickled-key']]
@ -186,7 +182,6 @@ class PersistentDictTestCase(base.BaseTestCase):
pd.make_permanent('/', 'file')
mock_connection = mock_sqlite3.connect.return_value
mock_connection = mock_connection.__enter__.return_value
mock_cursor = mock_connection.cursor.return_value
expected = 1