fix(ocr): round-4 review — defensive poll + drop dead tracking columns
Round 4 review (PR #910), no blockers: - poll(): a 2xx body with no `status` now fails fast (logged) instead of being treated as perpetually pending until the deadline; defensive page index (`p.get("index", i)`) so a malformed page degrades rather than KeyError-ing. - Document on poll() that job_id is namespaced (embeds "/") so the gateway route must be a path-capture param (GET /v1/ocr/batch/{job_id:path}). - Drop the vestigial `status` + `updated_at` columns from batch_ocr_jobs: a row only ever exists while pending (terminal jobs are deleted) and the live status comes from a fresh poll, so a stored mirror was permanently "pending" / redundant with submitted_at. Simplifies the migration, store, and dataclass. - Tests: submit() ValueError on missing job_id; poll() missing-status → failed. 1653 unit tests pass; ruff + ty green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
55630ba25c
commit
232684e881
@@ -28,10 +28,11 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class BatchOcrJob:
|
||||
"""A tracked in-flight batch OCR job."""
|
||||
"""A tracked in-flight batch OCR job. A row exists only while pending (terminal
|
||||
jobs are deleted), so there's no stored status — the live status comes from a
|
||||
fresh ``GatewayBatchOcrClient.poll``. ``submitted_at`` anchors the deadline."""
|
||||
|
||||
job_id: str
|
||||
status: str
|
||||
submitted_at: int
|
||||
|
||||
|
||||
@@ -67,14 +68,14 @@ class BatchOcrJobStore:
|
||||
"""The in-flight job for this document+version, or ``None``."""
|
||||
async with self._storage.acquire() as db:
|
||||
async with db.execute(
|
||||
"SELECT job_id, status, submitted_at FROM batch_ocr_jobs "
|
||||
"SELECT job_id, submitted_at FROM batch_ocr_jobs "
|
||||
"WHERE user_id = ? AND doc_id = ? AND doc_type = ? AND etag = ?",
|
||||
(user_id, doc_id, doc_type, etag),
|
||||
) as cursor:
|
||||
row = await cursor.fetchone()
|
||||
if row is None:
|
||||
return None
|
||||
return BatchOcrJob(job_id=row[0], status=row[1], submitted_at=int(row[2]))
|
||||
return BatchOcrJob(job_id=row[0], submitted_at=int(row[1]))
|
||||
|
||||
async def insert_pending(
|
||||
self,
|
||||
@@ -93,10 +94,10 @@ class BatchOcrJobStore:
|
||||
async with self._storage.acquire() as db:
|
||||
await db.execute(
|
||||
"INSERT INTO batch_ocr_jobs "
|
||||
"(user_id, doc_id, doc_type, etag, job_id, status, submitted_at, updated_at) "
|
||||
"VALUES (?, ?, ?, ?, ?, ?, ?, ?) "
|
||||
"(user_id, doc_id, doc_type, etag, job_id, submitted_at) "
|
||||
"VALUES (?, ?, ?, ?, ?, ?) "
|
||||
"ON CONFLICT (user_id, doc_id, doc_type, etag) DO NOTHING",
|
||||
(user_id, doc_id, doc_type, etag, job_id, "pending", now, now),
|
||||
(user_id, doc_id, doc_type, etag, job_id, now),
|
||||
)
|
||||
await db.commit()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user