From a9ab1fcf714330ac7441421f74d65a486635ed8d Mon Sep 17 00:00:00 2001 From: Suriyakumarvijayanayagam Date: Tue, 25 Aug 2026 15:23:06 +0530 Subject: [PATCH] Declare tenacity - undeclared import crashed the container on boot app/services/enrichment/barcode/retry.py imports tenacity at module scope, and that module is on app/main.py's import path (main -> store_catalog router -> store_catalog_pipeline -> barcode enrichment -> sources -> retry). tenacity was in no requirements file, so the deployed image exited 1 during startup: File "/app/app/main.py", line 33, in from app.api.routers import store_catalog ... File "/app/app/services/enrichment/barcode/retry.py", line 16 from tenacity import ( ModuleNotFoundError: No module named 'tenacity' This surfaced as "100% CPU", not as a crash, which is why it was mis-read. uvicorn never bound a socket, Swarm restarted the task, and each restart re-ran the ~21s of eager pandas/scipy/sklearn imports that the analytics, recommendations and nutrition routers pull in at module scope. On this 1-vCPU host that loop pins the only core indefinitely. retry.py's own docstring asserted tenacity was "already a project dependency (see requirements.txt)". It never was - corrected to say the opposite, and to record that it is a hard startup dependency rather than an optional extra. Verified on the deployed image, not just locally: with tenacity present the container reaches health=healthy with restarts=0, and /, /api/health, /api/brands, /api/system/status and /docs all return 200. Idle cost is 0.16% CPU / 168MiB. An AST scan of every import in app/, cli/, scripts/ and serve.py against the image finds no other missing module (playwright is the one remaining absence and is deliberate - commented out in requirements.txt and imported lazily inside a function). Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01N7bVBpxH4AbJtK7Kp3MzDR --- app/services/enrichment/barcode/retry.py | 6 ++++-- requirements.txt | 10 ++++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/app/services/enrichment/barcode/retry.py b/app/services/enrichment/barcode/retry.py index ddf2515..82c98fd 100644 --- a/app/services/enrichment/barcode/retry.py +++ b/app/services/enrichment/barcode/retry.py @@ -1,8 +1,10 @@ """ Configurable retry-with-exponential-backoff for barcode source calls. -Built on `tenacity`, already a project dependency (see requirements.txt - -used elsewhere for httpx-based retries). Only retries on genuinely +Built on `tenacity`, declared in requirements.txt. The import below is at +module scope and this module sits on app/main.py's import path, so tenacity +is a hard startup dependency, not an optional extra - without it the +container exits 1 before uvicorn binds. Only retries on genuinely transient failures (network/timeout errors); a malformed response or a "no results" outcome is not retried, since retrying those wastes the source's rate-limit budget for no benefit (relevant for UPCItemDB's diff --git a/requirements.txt b/requirements.txt index 6647884..a3d4784 100644 --- a/requirements.txt +++ b/requirements.txt @@ -42,6 +42,16 @@ httpx>=0.27.2 # POST /api/catalog/generate to pull in NEW brands. If you only ever use # the bundled seed data (scripts/seed_sample_data.py) + RAG chat/search, # you can skip everything below this line. +# Retry/backoff for the barcode enrichment sources +# (app/services/enrichment/barcode/retry.py). This is NOT an optional extra: +# retry.py imports it at module scope, and that module is reached from +# app/main.py's own import of the store_catalog router - so a missing tenacity +# is not a degraded feature, it is the container exiting 1 on boot with +# ModuleNotFoundError before uvicorn ever binds a socket. Swarm then restarts +# it, and each restart re-runs ~21s of pandas/scipy/sklearn imports, which on a +# 1-vCPU host reads as pinned-at-100% CPU rather than as a crash. +tenacity>=8.2.3 + beautifulsoup4>=4.12.3 lxml>=4.9.3 python-slugify>=8.0.4