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:
parent
03244c4f96
commit
f84e648391
1 changed files with 26 additions and 39 deletions
|
@ -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(),
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue