3
0
Fork 0
mirror of https://github.com/YosysHQ/sby.git synced 2025-08-11 15:50:56 +00:00

Better transaction control

Use context manager to handle commit/rollback.
Use `sqlite3.Connection.in_transaction` property instead of rolling our own.
Read-only methods don't need the transaction wrapper and we never read-update-write.
This commit is contained in:
Krystine Sherwin 2025-07-08 15:44:01 +12:00
parent 03244c4f96
commit f84e648391
No known key found for this signature in database

View file

@ -17,61 +17,52 @@ Fn = TypeVar("Fn", bound=Callable[..., Any])
def transaction(method: Fn) -> Fn: def transaction(method: Fn) -> Fn:
@wraps(method) @wraps(method)
def wrapper(self: SbyStatusDb, *args: Any, **kwargs: Any) -> Any: def wrapper(self: SbyStatusDb, *args: Any, **kwargs: Any) -> Any:
if self._transaction_active: if self.con.in_transaction:
return method(self, *args, **kwargs) return method(self, *args, **kwargs)
try: try:
self.log_debug(f"begin {method.__name__!r} transaction") with self.con:
self.db.execute("begin") self.log_debug(f"begin {method.__name__!r} transaction")
self._transaction_active = True self.db.execute("begin")
result = method(self, *args, **kwargs) result = method(self, *args, **kwargs)
self.db.execute("commit")
self._transaction_active = False
self.log_debug(f"comitted {method.__name__!r} transaction")
return result
except sqlite3.OperationalError as err:
self.log_debug(f"failed {method.__name__!r} transaction {err}")
self.db.rollback()
self._transaction_active = False
except Exception as err: except Exception as err:
self.log_debug(f"failed {method.__name__!r} transaction {err}") self.log_debug(f"failed {method.__name__!r} transaction {err}")
self.db.rollback() if not isinstance(err, sqlite3.OperationalError):
self._transaction_active = False raise
raise else:
self.log_debug(f"comitted {method.__name__!r} transaction")
return result
try: try:
self.log_debug( with self.con:
f"retrying {method.__name__!r} transaction once in immediate mode" self.log_debug(
) f"retrying {method.__name__!r} transaction once in immediate mode"
self.db.execute("begin immediate") )
self._transaction_active = True self.db.execute("begin immediate")
result = method(self, *args, **kwargs) result = method(self, *args, **kwargs)
self.db.execute("commit")
self._transaction_active = False
self.log_debug(f"comitted {method.__name__!r} transaction")
return result
except Exception as err: except Exception as err:
self.log_debug(f"failed {method.__name__!r} transaction {err}") self.log_debug(f"failed {method.__name__!r} transaction {err}")
self.db.rollback()
self._transaction_active = False
raise raise
else:
self.log_debug(f"comitted {method.__name__!r} transaction")
return result
return wrapper # type: ignore return wrapper # type: ignore
class SbyStatusDb: class SbyStatusDb:
def __init__(self, path: Path, task, timeout: float = 5.0): def __init__(self, path: Path, task, timeout: float = 5.0):
self.debug = False self.debug = True
self.task = task self.task = task
self._transaction_active = False
setup = not os.path.exists(path) setup = not os.path.exists(path)
self.db = sqlite3.connect(path, isolation_level=None, timeout=timeout) self.con = sqlite3.connect(path, isolation_level=None, timeout=timeout)
self.db = self.con.cursor()
self.db.row_factory = sqlite3.Row self.db.row_factory = sqlite3.Row
cur = self.db.cursor() with self.con:
cur.execute("PRAGMA journal_mode=WAL") self.db.execute("PRAGMA journal_mode=WAL")
cur.execute("PRAGMA synchronous=0") self.db.execute("PRAGMA synchronous=0")
self.db.commit()
if setup: if setup:
self._setup() self._setup()
@ -245,7 +236,6 @@ class SbyStatusDb:
), ),
) )
@transaction
def all_tasks(self): def all_tasks(self):
rows = self.db.execute( rows = self.db.execute(
""" """
@ -255,7 +245,6 @@ class SbyStatusDb:
return {row["id"]: dict(row) for row in rows} return {row["id"]: dict(row) for row in rows}
@transaction
def all_task_properties(self): def all_task_properties(self):
rows = self.db.execute( rows = self.db.execute(
""" """
@ -271,7 +260,6 @@ class SbyStatusDb:
return {row["id"]: get_result(row) for row in rows} return {row["id"]: get_result(row) for row in rows}
@transaction
def all_task_property_statuses(self): def all_task_property_statuses(self):
rows = self.db.execute( rows = self.db.execute(
""" """
@ -287,7 +275,6 @@ class SbyStatusDb:
return {row["id"]: get_result(row) for row in rows} return {row["id"]: get_result(row) for row in rows}
@transaction
def all_status_data(self): def all_status_data(self):
return ( return (
self.all_tasks(), self.all_tasks(),