Повторите попытку в тупике для MySQL/SQLAlchemy
Я искал довольно давно и не могу найти решение моей проблемы. Мы используем SQLAlchemy в сочетании с MySQL для нашего проекта, и мы сталкиваемся с несколькими опасными ошибками:
1213, "Тупик обнаружен при попытке получить блокировку; попробуйте перезапустить транзакцию".
В этом случае мы попытаемся перезапустить транзакцию не более трех раз.
Я начал писать декоратор, который делает это, но я не знаю, как сохранить состояние сеанса перед сбоем и повторить одну и ту же транзакцию после него? (Поскольку 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 нижнего уровня. Используя этот (или даже любой интерфейс 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")
который автоматически повторит попытку, если произойдет тупик.
Ответ 2
Вы использовали такой код?
try:
Perform table transaction
break
except:
rollback
delay
try again to perform table transaction
Единственный способ по-настоящему справиться с блокировками - написать свой код, чтобы ожидать их. Это, как правило, не очень сложно, если ваш код базы данных хорошо написан. Часто вы можете просто попробовать/поймать запрос выполнение логики и поиск тупика при возникновении ошибок. если ты поймать один, нормальная вещь, которую нужно сделать, это просто попытаться выполнить неудачный запрос.
Полезные ссылки: