Skip to content
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions sqlite_minutils/db.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from typing import ( cast, Any, Callable, Dict, Generator, Iterable, Union, Optional, List, Tuple,)
from functools import cache
import uuid
import time

try: from sqlite_dump import iterdump
except ImportError: iterdump = None
Expand Down Expand Up @@ -427,6 +428,26 @@ def query(

def execute(
self, sql: str, parameters: Optional[Union[Iterable, dict]] = None
) -> sqlite3.Cursor:
while True:
try:
return self._orig_execute(sql=sql, parameters=parameters)
except sqlite3.OperationalError as e:
# Catch the unfortunately generic error sqlite3.OperationalError
# to determine if this is a concurrency issue
if "cannot start a transaction within a transaction" in str(e).lower():
# Probably a concurrency issue, retry
# We could make this more precise with time.perf_counter but
# imprecision here is a good thing as it adds jitter
# Yes, this avoids exponential backoff for REASONS
time.sleep(0.001)
continue
# Since this isn't a transaction failure, we re-raise the issue so
# other logic in this library can handle it
raise

def _orig_execute(
self, sql: str, parameters: Optional[Union[Iterable, dict]] = None
) -> sqlite3.Cursor:
"""
Execute SQL query and return a ``sqlite3.Cursor``.
Expand Down