fix: address PR #836 round-3 review (lock-key invariant, single open)

🟡 Document the _doc_queueing_lock ":" delimiter invariant (user_id and the
   controlled doc_type enum are colon-free, so the key is collision-safe; a
   future doc_type with ":" must not be added).
🟡 API pod no longer opens the procrastinate connector twice on startup: add
   ProcrastinateTaskProducer.ensure_schema() (applies the schema on the
   already-open pool) and have both lifespan branches build the producer then
   ensure_schema — one open/close cycle, matching the worker. build_producer now
   returns the concrete producer type.
🟢 Document in ports.py that a long-lived-connection producer may optionally
   provide drain() (lifespan probes via getattr).
🟢 Add a unit test that a non-credential pipeline error propagates (for
   procrastinate's RetryStrategy) and still closes the client via finally.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Chris Coutinho
2026-06-03 15:32:16 +02:00
co-authored by Claude Opus 4.8
parent 820b98dac1
commit b10ce15032
5 changed files with 75 additions and 25 deletions
+12 -16
View File
@@ -1676,14 +1676,12 @@ def get_app(transport: str = "streamable-http", enabled_apps: list[str] | None =
receive_stream = None
task_producer: TaskProducer
if use_postgres:
# Create procrastinate's tables before the scanner can defer.
# Lazy import: the procrastinate lib is a Postgres-only extra.
from nextcloud_mcp_server.vector.queue.procrastinate import ( # noqa: PLC0415
apply_ingest_queue_schema,
)
await apply_ingest_queue_schema()
task_producer = await build_producer(settings)
# Open the connector once (build_producer) and reuse it to create
# procrastinate's tables before the scanner can defer — a single
# open/close cycle, matching the worker command.
producer = await build_producer(settings)
await producer.ensure_schema()
task_producer = producer
logger.info("Ingest queue: postgres (procrastinate); worker drains it")
else:
send_stream, receive_stream = anyio.create_memory_object_stream[
@@ -1895,14 +1893,12 @@ def get_app(transport: str = "streamable-http", enabled_apps: list[str] | None =
receive_stream = None
task_producer: TaskProducer
if use_postgres:
# Create procrastinate's tables before any scanner defers.
# Lazy import: procrastinate is a Postgres-only extra.
from nextcloud_mcp_server.vector.queue.procrastinate import ( # noqa: PLC0415
apply_ingest_queue_schema,
)
await apply_ingest_queue_schema()
task_producer = await build_producer(settings)
# Single open/close cycle: build_producer opens the connector
# and ensure_schema reuses it to create procrastinate's tables
# before any scanner defers (matches the worker command).
producer = await build_producer(settings)
await producer.ensure_schema()
task_producer = producer
logger.info(
"Ingest queue: postgres (procrastinate); worker drains it"
)