Skip to content

feat(#29): schema-cache reload via LISTEN/NOTIFY#55

Merged
milmazz merged 13 commits into
mainfrom
claude/issue-29-gap-ct5pye
Jul 7, 2026
Merged

feat(#29): schema-cache reload via LISTEN/NOTIFY#55
milmazz merged 13 commits into
mainfrom
claude/issue-29-gap-ct5pye

Conversation

@milmazz

@milmazz milmazz commented Jul 3, 2026

Copy link
Copy Markdown
Owner

Closes #29. Reload the schema cache without restarting, PostgREST-compatibly.

  • Bier.SchemaCache — introspection snapshot in one :persistent_term entry; reload is a single atomic swap. Swap happens inside the [:bier, :schema_cache, :load, *] span, so :stop ⇒ new snapshot visible.
  • Bier.SchemaCacheListener — dedicated LISTEN connection; NOTIFY <db_channel>, 'reload schema' coalesces and reloads; 'reload config' is a logged no-op. Internal backoff (500 ms → 30 s) — DB outages never crash-loop the supervisor; reconnects reload unconditionally.
  • New options db_channel ("pgrst") / db_channel_enabled (true), validated at boot, with PGRST_DB_CHANNEL[_ENABLED] CLI parity.
  • Bier.reload_schema_cache/1 — programmatic reload; failure keeps the old snapshot serving.

⚠️ Default adds one LISTEN connection per instance; set db_channel_enabled: false to opt out (required behind transaction-mode PgBouncer).

Design/plan docs in the first two commits. 19 new tests; full suite at the pre-existing 3-failure geojson/PostGIS baseline; all precommit gates clean.

🤖 Generated with Claude Code

claude and others added 13 commits July 3, 2026 23:10
…TEN/NOTIFY

Design doc and TDD implementation plan (superpowers spec/plan format) to
close the db-channel gap: single-key Bier.SchemaCache snapshot, config-gated
SchemaCacheListener with internal backoff, db_channel/db_channel_enabled
options with CLI parity, and Bier.reload_schema_cache/1.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0117wYTPtSBQPTpNiR8a1jEj
Reviewed against the real toolchain and a running fixture DB:

- The full suite's baseline is NOT zero failures: geojson cases
  1616/1617/1618 fail locally AND on every CI matrix leg (PostGIS-dependent
  test.shops fixture is deliberately commented out; CI gates on
  BASELINE_FAILURES, red only if failures increase). Corrected every
  'mix test -> 0 failures' expectation and replaced the unpassable
  'mix precommit' final gate with its individual steps.
- Hardened the Task 6 listener tests against a NOTIFY-vs-LISTEN race: the
  subscription is established in a handle_continue after boot, so tests now
  await it (:sys.get_state poll) before notifying; adjusted the expected
  red-phase output accordingly.
- Documented that the persistent_term snapshot entry is never erased on
  instance stop (pre-existing per-key behavior, now one key).

Every other claim verified exact: all ten read-site line refs, the existing
telemetry span shape (relation_count included), Registry/postgrex_opts/CLI
APIs, schema/config insertion points, test helpers, telemetry_test in the
locked dep, and RouterBuilder ignoring its relations argument.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
load!/3 (boot path) and reload/1 had different :stop guarantees: reload/1
put the new snapshot before the telemetry span closed, while load!/3 put
it after. Unify on load!/3 doing the swap inside its span so every
[:bier, :schema_cache, :load, :stop] observer sees the new snapshot,
and have reload/1 delegate to it instead of duplicating the span body.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
db_channel/db_channel_enabled pushed Bier.Config to 33 fields, tripping
Credo.Check.Warning.StructFieldAmount's default max of 31. Config is a flat
options struct that mirrors Bier.schema/0 and grows by one field per new
Bier.start_link/1 option, so raise the cap instead of restructuring it.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Reject null-byte db-channel values at boot (Postgrex.Notifications.listen/3
would otherwise raise and crash-loop the listener), reload on a first
connect that only succeeds after a retry (NOTIFYs fired during the failed
attempts were previously lost), and replace the hard match on listen/3's
result with a case that retries instead of crashing on {:eventually, ref}
or a connection that died in the window before the LISTEN call.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@milmazz milmazz changed the title docs(#29): spec + TDD implementation plan for schema-cache reload via LISTEN/NOTIFY feat(#29): schema-cache reload via LISTEN/NOTIFY (db-channel) Jul 4, 2026
@milmazz milmazz changed the title feat(#29): schema-cache reload via LISTEN/NOTIFY (db-channel) feat(#29): schema-cache reload via LISTEN/NOTIFY Jul 4, 2026
@milmazz
milmazz merged commit d014f50 into main Jul 7, 2026
3 checks passed
@milmazz
milmazz deleted the claude/issue-29-gap-ct5pye branch July 7, 2026 02:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[feature] Schema-cache reload via LISTEN/NOTIFY (db-channel)

2 participants