sterilize: search-then-create + retry-on-UNIQUE-400 + don't mark errored as applied
Job 1's bulk run apply'd 184 recipes and 182 of them failed with the same error: POST /api/foods -> 400 UNIQUE constraint failed: ingredient_foods.name, ingredient_foods.group_id. Cause: Mealie's name_normalized strips punctuation/whitespace/case more aggressively than our local _build_name_index's plain .lower(), so the cache misses, the create_food fires blindly, and Mealie's UNIQUE constraint kills the call. Whole-recipe apply was wrapped in try/except at the bulk runner so the recipe got marked errored — but applied_at was still set to NOW(), making the rerun think we'd already tried. We had, but the recipe's still unparsed. Two fixes: 1. sterilizer._resolve_food / _resolve_unit replace the inline create-on-miss block. Order: local cache → Mealie search-endpoint tie-break → create. On any UNIQUE-flavored 400 from create, fall back to one more search to adopt whatever Mealie has under the normalized form. Mealie's search endpoint applies its own name_normalized internally so we don't have to mirror its rules. _search_for_match takes "foods" or "units" and looks for an exact case-insensitive match against name or pluralName, with a fallback to "trust Mealie's ranker" when there's exactly one hit. 2. db.mark_proposal_applied no longer sets applied_at on error. On success: applied_at=NOW(), apply_error=NULL. On error: applied_at stays NULL, apply_error gets the message. list_approved_unapplied_ proposals keys off applied_at IS NULL, so a rerun naturally retries only the failed recipes. Net effect: rerun can now successfully apply the 182 failed recipes without re-walking them, and won't waste calls on the 2 that did go through.
This commit is contained in:
parent
9368b64a81
commit
f7b30d3b65
2 changed files with 136 additions and 19 deletions
|
|
@ -1069,13 +1069,26 @@ class DB:
|
|||
def mark_proposal_applied(
|
||||
self, job_id: int, recipe_slug: str, *, error: str | None = None
|
||||
) -> None:
|
||||
with self.conn() as c, c.cursor() as cur:
|
||||
cur.execute(
|
||||
"""UPDATE cauldron_sterilize_proposals
|
||||
SET applied_at=NOW(), apply_error=%s
|
||||
WHERE job_id=%s AND recipe_slug=%s""",
|
||||
((error or "")[:500] or None, job_id, recipe_slug),
|
||||
)
|
||||
"""On success: applied_at=NOW(), apply_error=NULL. On error: leave
|
||||
applied_at NULL so a rerun can retry, but record the error for
|
||||
review. The list_approved_unapplied_proposals query keys off
|
||||
applied_at IS NULL, so this directly drives retryability."""
|
||||
if error:
|
||||
with self.conn() as c, c.cursor() as cur:
|
||||
cur.execute(
|
||||
"""UPDATE cauldron_sterilize_proposals
|
||||
SET apply_error=%s
|
||||
WHERE job_id=%s AND recipe_slug=%s""",
|
||||
(error[:500], job_id, recipe_slug),
|
||||
)
|
||||
else:
|
||||
with self.conn() as c, c.cursor() as cur:
|
||||
cur.execute(
|
||||
"""UPDATE cauldron_sterilize_proposals
|
||||
SET applied_at=NOW(), apply_error=NULL
|
||||
WHERE job_id=%s AND recipe_slug=%s""",
|
||||
(job_id, recipe_slug),
|
||||
)
|
||||
|
||||
def list_approved_unapplied_proposals(self, job_id: int) -> list[dict]:
|
||||
with self.conn() as c, c.cursor() as cur:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue