python
33 lines · 6 steps
Retrying deadlocked DB writes in Flask
A parametrized decorator that transparently retries a view when the database reports a deadlock, backing off exponentially between attempts.
Explained by
highlit
1import functools
2import time
3
4from flask import current_app
5from sqlalchemy.exc import OperationalError
6
7from app.extensions import db
8
9_DEADLOCK_CODES = {1205, 1213, 40001}
10
11
12def retry_on_deadlock(max_retries=3, base_delay=0.05):
13 def decorator(view):
14 @functools.wraps(view)
15 def wrapper(*args, **kwargs):
16 attempt = 0
17 while True:
18 try:
19 return view(*args, **kwargs)
20 except OperationalError as exc:
21 db.session.rollback()
22 code = exc.orig.args[0] if exc.orig and exc.orig.args else None
23 if code not in _DEADLOCK_CODES or attempt >= max_retries:
24 raise
25 attempt += 1
26 delay = base_delay * (2 ** (attempt - 1))
27 current_app.logger.warning(
28 "Deadlock on %s (code=%s), retry %d/%d in %.3fs",
29 view.__name__, code, attempt, max_retries, delay,
30 )
31 time.sleep(delay)
32 return wrapper
33 return decorator
01 / 01
STEP 01
‹ swipe to step through ›
Walkthrough
Space play
←→ step
click any line
Three takeaways
- 1Deadlocks are transient, so retrying the same transaction after a rollback often succeeds without any user-visible failure.
- 2A three-layer decorator lets you pass tuning parameters like retry count and delay while still wrapping the target cleanly.
- 3Exponential backoff spreads retries out over time, reducing the chance that competing transactions collide again immediately.
Related explainers
rust
use serde::Deserialize; #[derive(Debug, Deserialize)] #[serde(untagged)]
Parsing flexible JSON shapes with serde
deserialization
enums
json
Intermediate
6 steps
ruby
require "shellwords" require "open3" module Backup
Building safe shell commands in Ruby
shell-out
subprocess
command-injection
Intermediate
7 steps
python
from fastapi import FastAPI, WebSocket, WebSocketDisconnect app = FastAPI()
Building a WebSocket chat with FastAPI
websockets
broadcast
connection-management
Intermediate
9 steps
python
import time import uuid from django.utils.deprecation import MiddlewareMixin
Attaching per-request context in Django
middleware
request lifecycle
multi-tenancy
Intermediate
7 steps
python
import random from typing import Iterator, List
How reservoir sampling picks k items
reservoir-sampling
streaming
randomness
Intermediate
5 steps
rust
use chrono::{Duration, NaiveDate}; #[derive(Debug)] pub struct DateRange {
Parsing and iterating date ranges in Rust
error-handling
iterators
parsing
Intermediate
7 steps
Share this explainer
Here's the card — post it anywhere.
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code
Embed this explainer
Drop the interactive walkthrough into a blog or docs. Views never cost a credit.
<iframe src="https://highlit.co/explainers/retrying-deadlocked-db-writes-in-flask-explained-python-7daa/embed?autoplay=1" width="100%" height="520" loading="lazy" style="border:0"></iframe>
Autoplay is on by default — add ?autoplay=0 to start paused.