PostgreSQL Concurrent Schema Init Deadlock
SkillDatabases & dataFix PostgreSQL deadlock errors caused by concurrent schema initialization in worker processes. Use when: (1) psycopg2.errors.DeadlockDetected during CREATE INDEX/TABLE IF NOT EXISTS, (2) Multiple Cloud Run jobs, Kubernetes pods, or worker processes start simultaneously, (3) Error shows "Process X waits for RowExclusiveLock... blocked by process Y", (4) init_schema() or migration code runs at worker startup. The key insight: "IF NOT EXISTS" is NOT truly concurrent-safe - PostgreSQL still acquires locks that can deadlock.
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the PostgreSQL Concurrent Schema Init Deadlock skill
What this skill tells your AI
The instructions your AI receives, as published by divinevideo/divine-mobile in .agents/skills/postgres-concurrent-schema-init-deadlock/SKILL.md and read by ahel’s review.
Problem
Multiple worker processes (Cloud Run jobs, K8s pods, serverless functions) starting simultaneously all try to run schema initialization code, causing PostgreSQL deadlocks even when using "IF NOT EXISTS" clauses.
Context / Trigger Conditions
- Error:
psycopg2.errors.DeadlockDetected: deadlock detected - Log shows:
Process X waits for RowExclusiveLock on relation... blocked by process Y - Multiple workers/jobs starting at roughly the same time
- Each worker calls
init_schema()or runs migrations at startup - Using
CREATE TABLE IF NOT EXISTSorCREATE INDEX IF NOT EXISTS
Why This Happens
PostgreSQL's IF NOT EXISTS is not concurrent-safe:
CREATE INDEX IF NOT EXISTSstill acquires locks before checking existence- Multiple processes acquiring locks on different objects can deadlock
- Even "safe" DDL can conflict when executed concurrently
Solution
Option 1: Skip Init in Production (Recommended)
Schema already exists - don't run init_schema() in workers:
with Database() as db:
# Schema already exists in production - skip to avoid deadlocks
# db.init_schema()
# ... worker code
Option 2: Use Advisory Locks
Serialize schema init with PostgreSQL advisory locks:
def init_schema_safe(self):
cursor = self._cursor()
# Acquire advisory lock (blocks other processes)
cursor.execute("SELECT pg_advisory_lock(12345)")
try:
self.init_schema()
finally:
cursor.execute("SELECT pg_advisory_unlock(12345)")
self.conn.commit()
Option 3: Separate Migration Step
Run migrations as a separate job before starting workers:
# In deployment pipeline
python -m src.migrate # Single process, runs first
# Then start workers
gcloud run jobs execute worker-job
Option 4: Lock Timeout + Retry
Set lock timeout and retry on deadlock:
def init_schema_with_retry(self, max_retries=3):
for attempt in range(max_retries):
try:
cursor = self._cursor()
cursor.execute("SET lock_timeout = '5s'")
self.init_schema()
return
except psycopg2.errors.DeadlockDetected:
self.conn.rollback()
if attempt == max_retries - 1:
raise
time.sleep(random.uniform(1, 3))
Verification
After applying fix:
- Start multiple workers simultaneously
- Check logs for absence of deadlock errors
- Verify all workers start successfully
Example
Before (deadlocks with 6 concurrent Cloud Run jobs):
# src/download.py
with VineDatabase() as db:
db.init_schema() # DEADLOCK when multiple jobs start!
# ... download logic
After (no deadlocks):
# src/download.py
with VineDatabase() as db:
# Schema already exists in production - skip to avoid deadlocks
# db.init_schema()
# ... download logic
Notes
- This applies to any concurrent worker pattern: Cloud Run, Celery, Kubernetes, Lambda
- The deadlock can be intermittent - depends on exact timing of worker starts
CREATE TABLE IF NOT EXISTSis generally safer thanCREATE INDEX IF NOT EXISTS- Cloud Run jobs often start simultaneously when triggered, making this common
- Consider using database migration tools (Alembic, Flyway) with proper locking
References
Signals
- GitHub stars
- 264
- Forks
- 55
- Last commit
- Sep 2026
ahel review
S4info
community integration — published by divinevideo, not postgres
Automated review, not a security audit. Ruleset v1.
Advanced
- Catalog kind
- skill
- Gateway key
postgres-concurrent-schema-init-deadlock- Source
- github.com/divinevideo/divine-mobile