かなり長い間検索してきましたが、問題の解決策が見つかりません。私たちのプロジェクトでは SQLAlchemy を MySQL と組み合わせて使用していますが、次のような恐ろしいエラーが何度も発生します。
1213、「ロックを取得しようとしたときにデッドロックが見つかりました。トランザクションを再起動してください。」
この場合、トランザクションを最大 3 回再開してみます。
私はこれを実行するデコレータを書き始めましたが、失敗前のセッション状態を保存し、失敗後に同じトランザクションを再試行する方法がわかりません。(SQLAlchemy では例外が発生するたびにロールバックが必要になるため)
これまでの私の仕事は、
def retry_on_deadlock_decorator(func):
lock_messages_error = ['Deadlock found', 'Lock wait timeout exceeded']
@wraps(func)
def wrapper(*args, **kwargs):
attempt_count = 0
while attempt_count < settings.MAXIMUM_RETRY_ON_DEADLOCK:
try:
return func(*args, **kwargs)
except OperationalError as e:
if any(msg in e.message for msg in lock_messages_error) \
and attempt_count <= settings.MAXIMUM_RETRY_ON_DEADLOCK:
logger.error('Deadlock detected. Trying sql transaction once more. Attempts count: %s'
% (attempt_count + 1))
else:
raise
attempt_count += 1
return wrapper
ベストアンサー1
Session
実際には、外部からではこれを行うことはできません。Session
内部でこれをサポートする必要があります。多くのプライベート状態を保存する必要があるため、時間をかける価値がない可能性があります。
私はほとんどの ORM を完全に捨てて、より低レベルの SQLAlchemy Core インターフェイスを採用しました。これ (または任意の dbapi インターフェイス) を使用すると、retry_on_deadlock_decorator
デコレータ (上記の質問を参照) を簡単に使用して再試行対応db.execute
ラッパーを作成できます。
@retry_on_deadlock_decorator
def deadlock_safe_execute(db, stmt, *args, **kw):
return db.execute(stmt, *args, **kw)
そして代わりに
db.execute("UPDATE users SET active=0")
あなたがやる
deadlock_safe_execute(db, "UPDATE users SET active=0")
デッドロックが発生した場合に自動的に再試行します。