perf(backend): install defaults and sync the add-on catalog once per process

↗ view on GitHub · Amal · 2026-08-12 · bdb2b4b6

WHY THIS MATTERS
Two hot-path costs shipped with the workflows restructure, measured live:

1. ensureDefaultWorkflows() ran on EVERY workflow list, quick-action list,
   and chat message. After first install the RPC is a pure no-op, yet each
   call shipped the full five-default payload (multi-KB of prompt text) to
   Postgres and took pg_advisory_xact_lock(user), serializing all of one
   user's concurrent requests through that transaction forever.

2. syncWorkflowAddonCatalog() ran on every GET /workflow-addons and upserted
   all ~129 catalog rows (142 KB response, full prompt bodies) with a fresh
   updated_at on each request. One browser refresh of the Workflows page
   rewrote every row - verified by watching min/max(updated_at) advance in
   the database on each reload. The content_hash column existed precisely to
   prevent this but was computed, stored, and never compared.

WHAT IS A PER-PROCESS LATCH?
The catalog is derived from a generated module that only changes when new
code deploys, and a deploy restarts the process. So "sync once per process
lifetime" is exactly as fresh as "sync on every request" - module-level
state (a cached promise) makes the first request do the work and every
later caller await the same result. Concurrent first requests share one
sync instead of racing duplicate upserts; a failed sync clears the latch so
the next request retries rather than caching the failure.

HOW THE FIX WORKS
- ensureDefaultWorkflows() remembers per process which users it has already
  ensured and skips the RPC afterwards. Failures are not cached.
- syncWorkflowAddonCatalog() runs behind the latch, reads the stored
  (addon_key, content_hash, active) rows first, and only upserts seeds whose
  hash differs - so updated_at now changes only when content actually
  changes, and the steady-state sync is a single SELECT.
- New unit tests pin all of this: RPC called once per user, failures retried,
  hash match skips the upsert, concurrent callers share one sync, and the
  latch clears after an error.

Found by review of PR #309 (catalog rewrite measured via psql before/after
page reloads).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E4PXCdenNH5Mqhm5Sre9Zs
Repository open-legal-products/mike
Author Amal <mamalanand3@gmail.com>
Authored
Committed
Parents 94f2536a
Stats 2 files changed , +149 , -11
Part of Refactor workflows into defaults, add-ons, and quick actions

Capture this commit into my fork

Download a Markdown prompt that tells Claude how to port this exact commit into your working tree. Run it via claude -p < capture-commit-bdb2b4b6.md from inside the repo you want the change in.

⬇ Download capture-commit-bdb2b4b6.md