From 9f29073cdaee31db6fb7dd03fbb8ef378a99c9a9 Mon Sep 17 00:00:00 2001 From: giles Date: Wed, 25 Feb 2026 13:32:28 +0000 Subject: [PATCH] Fix Ghost sync race: advisory lock for multi-worker startup Two Hypercorn workers both run sync_all_content_from_ghost on startup, racing on PostAuthor/PostTag rows. Use pg_try_advisory_lock so only one worker runs the sync. Co-Authored-By: Claude Opus 4.6 --- blog/bp/blog/routes.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/blog/bp/blog/routes.py b/blog/bp/blog/routes.py index 2b788d9..570363e 100644 --- a/blog/bp/blog/routes.py +++ b/blog/bp/blog/routes.py @@ -48,10 +48,20 @@ def register(url_prefix, title): @blogs_bp.before_app_serving async def init(): from .ghost.ghost_sync import sync_all_content_from_ghost + from sqlalchemy import text + # Advisory lock prevents multiple Hypercorn workers from + # running the sync concurrently (which causes PK conflicts). async with get_session() as s: - await sync_all_content_from_ghost(s) - await s.commit() + got_lock = await s.scalar(text("SELECT pg_try_advisory_lock(900001)")) + if not got_lock: + return # another worker is syncing + try: + await sync_all_content_from_ghost(s) + await s.commit() + finally: + await s.execute(text("SELECT pg_advisory_unlock(900001)")) + await s.commit() @blogs_bp.before_request def route():