Fix page creation labels and webhook race condition
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 56s
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 56s
- Updated shared submodule: page-aware blog_new template labels - _upsert_post: handle concurrent webhook INSERTs via savepoint + retry Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -164,13 +164,8 @@ async def _upsert_tag(sess: AsyncSession, gt: Dict[str, Any]) -> Tag:
|
|||||||
return obj
|
return obj
|
||||||
|
|
||||||
|
|
||||||
async def _upsert_post(sess: AsyncSession, gp: Dict[str, Any], author_map: Dict[str, Author], tag_map: Dict[str, Tag]) -> Post:
|
def _apply_ghost_fields(obj: Post, gp: Dict[str, Any], author_map: Dict[str, Author], tag_map: Dict[str, Tag]) -> None:
|
||||||
res = await sess.execute(select(Post).where(Post.ghost_id == gp["id"]))
|
"""Apply Ghost API fields to a Post ORM object."""
|
||||||
obj = res.scalar_one_or_none()
|
|
||||||
if obj is None:
|
|
||||||
obj = Post(ghost_id=gp["id"]) # type: ignore[call-arg]
|
|
||||||
sess.add(obj)
|
|
||||||
|
|
||||||
obj.deleted_at = None # revive if soft-deleted
|
obj.deleted_at = None # revive if soft-deleted
|
||||||
|
|
||||||
obj.uuid = gp.get("uuid") or obj.uuid
|
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")
|
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]
|
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
|
# 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:
|
if obj.user_id is None and obj.primary_author_id is not None:
|
||||||
|
|||||||
2
shared
2
shared
Submodule shared updated: 9a1a4996bc...24432cd52a
Reference in New Issue
Block a user