Skip to content

perf(catalog): release DB pool lease during hierarchy provider I/O #1077

Description

@seonghobae

Finding

The canonical corporate-entity catalog path correctly delays its explicit PostgreSQL transaction and pg_advisory_xact_lock until after hierarchy inference / external corroboration. However, get_or_create_corporate_entity(...) still receives a borrowed asyncpg.Connection and retains that pool lease while it performs potentially long asyncio.to_thread(...) inference and relation-verification calls, including recursive parent resolution.

The process pool is currently bounded at max_size=10. With enough concurrent catalog misses, provider latency can therefore consume all DB pool slots even though no transaction or explicit lock is held, delaying unrelated buyer read/write paths. #1055 adds Customer Master as another online caller of the same catalog owner, in addition to existing Keyman/post-summary ingestion paths, so this is now a buyer-visible resilience/performance boundary rather than a leaf implementation detail.

This is not a request to move catalog semantics into Customer Master, copy contextual-orchestrator routing, or weaken ADR 0010/0012/0160. The corporate-entity catalog remains the single canonical owner.

Realistic RED

Add a deterministic concurrency regression using the real pool contract or an equivalent integration harness:

  1. configure the pool at its production bound (max_size=10) or a proportionally equivalent explicit test bound;
  2. start enough unresolved organization resolutions to occupy every catalog path while inference/verification is deliberately blocked on a controllable provider barrier;
  3. while those provider calls are blocked, issue an unrelated DB-only read through the same pool;
  4. RED if the unrelated DB operation cannot obtain a connection within a bounded local latency solely because catalog calls retain idle leases during provider I/O.

Do not fake success by enlarging the pool, shortening the provider barrier, excluding the wait from measurement, or warming a cache that bypasses the miss path.

Causal architecture

Refactor the canonical catalog service boundary so database ownership is short-lived:

  • acquire briefly to load candidate/alias snapshots required for deterministic matching;
  • release the DB lease before hierarchy inference, search corroboration, or other network/provider I/O;
  • preserve tie fail-closed behavior, SKOS alias handling, recursion/cycle/depth invariants and parent corroboration outside an explicit transaction;
  • reacquire only for the fresh candidate/alias re-check, named pg_advisory_xact_lock, idempotent creation/update, and commit;
  • on recursion, do not retain a parent connection while child/parent provider work executes;
  • cancellation, timeout and provider exceptions must return every lease and leave no open transaction.

If a versioned repository/service abstraction is needed to express snapshot → external decision → short commit safely, keep that abstraction in the corporate-entity catalog bounded context. Callers consume it; they must not reproduce matching/creation SQL or provider selection.

Acceptance

  • the concurrency RED above becomes GREEN without increasing the pool bound;
  • no provider/network call executes while a DB connection is retained solely for later mutation, and no provider/network call executes inside an explicit transaction/advisory-lock scope;
  • the final write still performs a fresh under-lock catalog check so concurrent duplicate creation remains prevented;
  • existing ADR 0010/0012/0160 tie, alias, hierarchy, cycle/depth and idempotency tests remain GREEN;
  • Keyman, post-summary and Customer Master callers preserve their current domain contracts and consume the same canonical catalog service;
  • connection cleanup is proven for success, provider timeout/error and task cancellation;
  • add an async buyer-path measurement (deterministic integration test and, where practical, k6/API evidence) showing an unrelated DB-only path remains serviceable while catalog provider calls are blocked;
  • record the connection-ownership decision in ARCHITECTURE/ADR/OPERABILITY/TEST_STRATEGY as appropriate.

Serialization

Do not modify #1055 merely to start this follow-on while its current exact-head Strix admission is still running. Preserve single-writer/evidence boundaries: settle #1055 first, then implement this owner-level catalog repair on the then-current protected ancestry or through an already-existing verified successor if one appears.

No pool-size workaround, provider/model override, cross-service SQL, source-neutral validation commit, warning suppression, force push, destructive rebase, or gate weakening.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions