fix(ocr): wire batch settings into _field_map + review nits

Round 1 review (PR #910):
- BLOCKING: add document_ocr_mode / _batch_poll_seconds / _batch_max_wait_seconds
  to config._field_map — without it dynaconf silently ignored the env vars and
  DOCUMENT_OCR_MODE=batch could never be enabled in production. Add a regression
  test asserting the three round-trip from env.
- migration 008: give batch_ocr_jobs a composite PRIMARY KEY on
  (user_id, doc_id, doc_type, etag) instead of a bare UniqueConstraint (N1).
- OcrProcessor: use a dedicated _batch_client_lock instead of sharing the sync
  backend lock (N3).
- tests: use https:// gateway URLs in the new fixtures to clear SonarCloud's
  "insecure http" security hotspots (all 14 were test-only http://gw literals).

1653 unit tests pass; ruff + ty green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Chris Coutinho
2026-06-15 10:36:44 +02:00
co-authored by Claude Opus 4.8
parent 3b7e8d779b
commit 2b7dfc8535
6 changed files with 48 additions and 24 deletions
@@ -272,6 +272,7 @@ class OcrProcessor(DocumentProcessor):
# using sync" warning to once per pod.
self._batch_client_resolved = False
self._batch_client: Any = None
self._batch_client_lock: anyio.Lock | None = None
self._batch_fallback_warned = False
@property
@@ -377,9 +378,9 @@ class OcrProcessor(DocumentProcessor):
provider=mistral / no gateway). Resolved once under the backend lock so the
token provider's M2M cache survives across documents."""
if not self._batch_client_resolved:
if self._backend_lock is None:
self._backend_lock = anyio.Lock()
async with self._backend_lock:
if self._batch_client_lock is None:
self._batch_client_lock = anyio.Lock()
async with self._batch_client_lock:
if not self._batch_client_resolved: # double-checked
self._batch_client = build_gateway_batch_client(get_settings())
self._batch_client_resolved = True