@@ 7,27 7,32 @@ import pytz
class dbcache:
+ __slots__ = ('ns', 'engine')
- def __init__(self, uri):
+ def __init__(self, uri, namespace='cache'):
self.engine = create_engine(uri)
+ self.ns = namespace
def _remove_expired(self, cn):
cn.execute(
- 'delete from cache.things '
- 'where idate + validity < now()'
+ f'delete from "{self.ns}".things '
+ f'where idate + validity < now()'
+ )
+
+ def _txlock(self, cn, key):
+ lockid = hash(key + self.ns.encode('utf-8'))
+ cn.execute(
+ f'select pg_advisory_xact_lock({lockid})'
)
def get(self, key):
with self.engine.begin() as cn:
- lockid = hash(key)
- cn.execute(
- f'select pg_advisory_xact_lock({lockid})'
- )
+ self._txlock(cn, key)
self._remove_expired(cn)
q = select(
'value'
).table(
- 'cache.things'
+ f'{self.ns}.things'
).where(
key=sha1(key).hexdigest()
)
@@ 37,7 42,7 @@ class dbcache:
def _set(self, cn, hkey, value, lifetime):
sql = (
- 'insert into cache.things (key, value, validity) '
+ f'insert into {self.ns}.things (key, value, validity) '
'values (%(key)s, %(value)s, %(validity)s) '
'on conflict (key) do update set '
' value = %(value)s, '
@@ 54,10 59,7 @@ class dbcache:
def set(self, key, value, lifetime=timedelta(minutes=10)):
with self.engine.begin() as cn:
- lockid = hash(key)
- cn.execute(
- f'select pg_advisory_xact_lock({lockid})'
- )
+ self._txlock(cn, key)
self._remove_expired(cn)
hkey = sha1(key).hexdigest()
self._set(cn, hkey, value, lifetime)
@@ 65,16 67,13 @@ class dbcache:
def getorset(self, key, valuemaker, lifetime=timedelta(minutes=10)):
assert callable(valuemaker)
with self.engine.begin() as cn:
- lockid = hash(key)
+ self._txlock(cn, key)
+ self._remove_expired(cn)
hkey = sha1(key).hexdigest()
- cn.execute(
- f'select pg_advisory_xact_lock({lockid})'
- )
- self._remove_expired(cn)
q = select(
'value'
).table(
- 'cache.things'
+ f'{self.ns}.things'
).where(
key=hkey
)
@@ 5,17 5,23 @@ from sqlalchemy import create_engine
from pytest_sa_pg import db
from dbcache import schema
+from dbcache.api import dbcache
DATADIR = Path(__file__).parent / 'data'
PORT = 2346
-@pytest.fixture(scope='session')
-def dburi(request):
+
+@pytest.fixture(
+ scope='session',
+ params=('cache', 'fancyns')
+)
+def cache(request):
db.setup_local_pg_cluster(request, DATADIR, PORT, {
'timezone': 'UTC',
'log_timezone': 'UTC'
})
+ ns = request.param
e = create_engine('postgresql://localhost:{}/postgres'.format(PORT))
- schema.init(e, drop=True)
- return e.url
+ schema.init(e, ns=ns, drop=True)
+ yield dbcache(e.url, ns)
@@ 5,9 5,7 @@ import pickle
from dbcache.api import dbcache
-def test_cache(dburi):
- cache = dbcache(dburi)
-
+def test_cache(cache):
assert cache.get(b'a') is None
cache.set(b'a', b'aaa')
assert cache.get(b'a') == b'aaa'
@@ 15,18 13,14 @@ def test_cache(dburi):
assert cache.get(b'a') == b'newvalue'
-def test_invalidation(dburi):
- cache = dbcache(dburi)
-
+def test_invalidation(cache):
cache.set(b'b', b'bbb', lifetime=timedelta(seconds=1))
assert cache.get(b'b') == b'bbb'
sleep(1)
assert cache.get(b'b') is None
-def test_getorset(dburi):
- cache = dbcache(dburi)
-
+def test_getorset(cache):
def stuff(x):
return x