fix(vision): /run del worker non blocca più l'event loop, timeout e semaforo (#34)
Decodifica + run_graph girano in asyncio.to_thread sotto asyncio.wait_for (VISION_RUN_TIMEOUT_S, default 100s, sotto i 120s del backend) -> 504 del worker allo scadere. /health resta leggero e risponde durante un calcolo in corso. asyncio.Semaphore(VISION_MAX_CONCURRENCY, default 2): oltre -> 503 "worker busy". vision_service._call_worker inoltra 422/503/504 del worker con il loro status e motivo invece di appiattirli su 502. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -21,6 +21,14 @@ from src.backend.models.orm.task import RecipeSubtask, RecipeTask
|
||||
from src.backend.models.orm.vision import VisionReferenceImage, VisionResult
|
||||
from src.backend.services import measurement_service
|
||||
|
||||
# Worker responses whose status and reason are forwarded as-is rather than
|
||||
# flattened into a generic 502 - see `_call_worker`.
|
||||
_FORWARDED_WORKER_STATUSES = frozenset({
|
||||
status.HTTP_422_UNPROCESSABLE_CONTENT,
|
||||
status.HTTP_503_SERVICE_UNAVAILABLE,
|
||||
status.HTTP_504_GATEWAY_TIMEOUT,
|
||||
})
|
||||
|
||||
|
||||
def _is_measurable(value: object) -> bool:
|
||||
"""A worker output counts as a quote only if it is a finite, non-bool number.
|
||||
@@ -69,17 +77,18 @@ async def _call_worker(image_bytes: bytes, graph: dict) -> dict:
|
||||
detail=f"the vision worker is unreachable: {exc}",
|
||||
) from exc
|
||||
|
||||
if response.status_code == status.HTTP_422_UNPROCESSABLE_CONTENT:
|
||||
# The worker already diagnosed a bad request (undecodable image,
|
||||
# malformed graph) - forward its reason instead of flattening it
|
||||
# into a generic 502.
|
||||
# The worker already gave this fault a name - forward its status and
|
||||
# reason instead of flattening every non-200 into a generic 502: 422 is a
|
||||
# bad request (undecodable image, malformed graph), 503 is "busy, try
|
||||
# again" (VISION_MAX_CONCURRENCY exceeded), 504 is its own run timeout
|
||||
# (VISION_RUN_TIMEOUT_S, always tighter than this client's own 120s so
|
||||
# the worker answers first).
|
||||
if response.status_code in _FORWARDED_WORKER_STATUSES:
|
||||
try:
|
||||
detail = response.json().get("detail", response.text)
|
||||
except ValueError:
|
||||
detail = response.text
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_422_UNPROCESSABLE_CONTENT, detail=detail,
|
||||
)
|
||||
raise HTTPException(status_code=response.status_code, detail=detail)
|
||||
|
||||
if response.status_code != 200:
|
||||
raise HTTPException(
|
||||
|
||||
Reference in New Issue
Block a user