diff --git a/bp/blog/ghost/ghost_sync.py b/bp/blog/ghost/ghost_sync.py index 669dd80..4bb799b 100644 --- a/bp/blog/ghost/ghost_sync.py +++ b/bp/blog/ghost/ghost_sync.py @@ -164,13 +164,8 @@ async def _upsert_tag(sess: AsyncSession, gt: Dict[str, Any]) -> Tag: return obj -async def _upsert_post(sess: AsyncSession, gp: Dict[str, Any], author_map: Dict[str, Author], tag_map: Dict[str, Tag]) -> Post: - res = await sess.execute(select(Post).where(Post.ghost_id == gp["id"])) - obj = res.scalar_one_or_none() - if obj is None: - obj = Post(ghost_id=gp["id"]) # type: ignore[call-arg] - sess.add(obj) - +def _apply_ghost_fields(obj: Post, gp: Dict[str, Any], author_map: Dict[str, Author], tag_map: Dict[str, Tag]) -> None: + """Apply Ghost API fields to a Post ORM object.""" obj.deleted_at = None # revive if soft-deleted obj.uuid = gp.get("uuid") or obj.uuid @@ -213,7 +208,29 @@ async def _upsert_post(sess: AsyncSession, gp: Dict[str, Any], author_map: Dict[ pt = gp.get("primary_tag") obj.primary_tag_id = tag_map[pt["id"].strip()].id if (pt and pt["id"] in tag_map) else None # type: ignore[index] - await sess.flush() + +async def _upsert_post(sess: AsyncSession, gp: Dict[str, Any], author_map: Dict[str, Author], tag_map: Dict[str, Tag]) -> Post: + from sqlalchemy.exc import IntegrityError + + res = await sess.execute(select(Post).where(Post.ghost_id == gp["id"])) + obj = res.scalar_one_or_none() + if obj is None: + obj = Post(ghost_id=gp["id"]) # type: ignore[call-arg] + sess.add(obj) + + _apply_ghost_fields(obj, gp, author_map, tag_map) + + try: + async with sess.begin_nested(): + await sess.flush() + except IntegrityError: + # Race condition: another request inserted this ghost_id concurrently. + # Expunge the failed object, re-select the existing row, and update it. + sess.expunge(obj) + res = await sess.execute(select(Post).where(Post.ghost_id == gp["id"])) + obj = res.scalar_one() + _apply_ghost_fields(obj, gp, author_map, tag_map) + await sess.flush() # Backfill user_id from primary author email if not already set if obj.user_id is None and obj.primary_author_id is not None: diff --git a/shared b/shared index 9a1a499..24432cd 160000 --- a/shared +++ b/shared @@ -1 +1 @@ -Subproject commit 9a1a4996bcf0b9ae44d1e718816c0e5a84e23101 +Subproject commit 24432cd52acae183416d8e198206923bddb815b8