Commit bdacfb23 authored by Aurélien Campéas's avatar Aurélien Campéas
Browse files

tsio: assert that the write operations happen within a transaction context

Crashing within such an operation *must* be safe.
parent 71272278551a
......@@ -43,6 +43,7 @@ def tsh(request, engine):
if namespace == 'zzz':
Snapshot._max_bucket_size = 5
tsh = tsio.TimeSerie(namespace)
tsh._testing = True
yield tsh
......
......@@ -40,6 +40,7 @@ def test_rename(engine, cli, datadir, tsh):
namespace=tsh.namespace)
tsh = TimeSerie(tsh.namespace)
tsh._testing = True
assert tsh.get(engine, 'afoo') is None
assert tsh.get(engine, 'abar') is None
......@@ -59,6 +60,7 @@ def test_delete(engine, cli, datadir, tsh):
namespace=tsh.namespace)
tsh = TimeSerie(tsh.namespace)
tsh._testing = True
assert not tsh.exists(engine, 'bfoo')
assert tsh.get(engine, 'bfoo') is None
assert tsh.get(engine, 'bbar') is None
......@@ -67,6 +69,7 @@ def test_delete(engine, cli, datadir, tsh):
tsh.insert(engine, serie, 'bbq', 'Babar')
tsh = TimeSerie(tsh.namespace)
tsh._testing = True
r = cli('delete', engine.url,
series='bbq',
namespace=tsh.namespace)
......
......@@ -27,6 +27,24 @@ def utcdt(*dt):
return pd.Timestamp(datetime(*dt), tz='UTC')
def test_in_tx(engine):
tsh = TimeSerie()
with pytest.raises(TypeError) as err:
tsh.insert(engine, 0, 0, 0)
assert err.value.args[0] == 'You must use a transaction object'
with engine.connect() as cn:
with pytest.raises(TypeError) as err:
tsh.insert(cn, 0, 0, 0)
assert err.value.args[0] == 'You must use a transaction object'
ts = genserie(datetime(2017, 10, 28, 23),
'H', 4, tz='UTC')
with engine.begin() as cn:
tsh.insert(cn, ts, 'test_tx', 'Babar')
def test_tstamp_roundtrip(engine, tsh):
assert_structures(engine, tsh)
ts = genserie(datetime(2017, 10, 28, 23),
......
......@@ -49,6 +49,12 @@ class TimeSerie(SeriesServices):
self.serie_tablename = {}
self.create_lock_id = sum(ord(c) for c in namespace)
def _check_tx(self, cn):
# safety belt to make sure important api points are tx-safe
if isinstance(cn, Engine) or not cn.in_transaction():
if not getattr(self, '_testing', False):
raise TypeError('You must use a transaction object')
def insert(self, cn, newts, seriename, author,
metadata=None,
_insertion_date=None):
......@@ -59,6 +65,7 @@ class TimeSerie(SeriesServices):
author: str free-form author name
metadata: optional dict for changeset metadata
"""
self._check_tx(cn)
assert isinstance(newts, pd.Series), 'Not a pd.Series'
assert isinstance(seriename, str), 'Name not a string'
assert isinstance(author, str), 'Author not a string'
......@@ -129,6 +136,7 @@ class TimeSerie(SeriesServices):
return meta
def update_metadata(self, cn, seriename, metadata):
self._check_tx(cn)
assert isinstance(metadata, dict)
assert not set(metadata.keys()) & self.metakeys
meta = self.metadata(cn, seriename)
......@@ -243,7 +251,6 @@ class TimeSerie(SeriesServices):
`delta` time after the insertion dates and where we
keep the most recent ones
"""
histo = self.get_history(
cn, seriename, deltabefore=-delta,
from_value_date=from_value_date,
......@@ -294,6 +301,7 @@ class TimeSerie(SeriesServices):
return cn.execute(sql).scalar()
def delete(self, cn, seriename):
self._check_tx(cn)
assert not isinstance(cn, Engine), 'use a transaction object'
assert self.exists(cn, seriename)
# changeset will keep ghost entries
......@@ -328,6 +336,7 @@ class TimeSerie(SeriesServices):
self._resetcaches()
def strip(self, cn, seriename, csid):
self._check_tx(cn)
logs = self.log(cn, fromrev=csid, names=(seriename,))
assert logs
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment