Skip to content

fix: resolve the outbox table through search_path in validate_schema - #232

Merged
lesnik512 merged 1 commit into
mainfrom
fix/validator-search-path
Oct 17, 2026
Merged

lesnik512 merged 1 commit into
mainfrom
fix/validator-search-path

Conversation

@lesnik512

Copy link
Copy Markdown
Member

For 0.15.1, no issue: validate_schema() now checks the table the app actually uses when an unqualified table name resolves through a later search_path entry.

The bug

Take a search_path of app, public where the app schema exists but the outbox table is in public. The app reads and writes public.outbox. The CHECK-constraint, partial-index and autovacuum probes looked only in current_schema(), which is app, so validation failed on a correct table:

  • a missing lease CHECK;
  • a missing autovacuum tuning, even after outbox_autovacuum_ddl() was applied;
  • a missing partial index would have been silently skipped, because the predicate probe found no indexes at all.

The Alembic diff wasn't affected: SQLAlchemy reflects an unqualified table through pg_table_is_visible, which follows the whole search_path.

The fix

validate_schema() asks Postgres once where the name lands: to_regclass(format('%I', :table)), joined to pg_namespace. format('%I') quotes the name the way an unqualified identifier in the app's SQL is resolved, so mixed-case names work too. The resolved schema is passed to every probe and to the Alembic comparison. The DLQ table is resolved separately.

  • A table with an explicit schema skips the lookup.
  • A table that doesn't resolve gives None, so the probes keep the old current_schema() fallback and the Alembic diff still reports "does not exist".

Tests

test_validate_schema_finds_a_table_through_a_later_search_path_entry builds that layout: an empty schema first on the search_path, the outbox and DLQ tables plus autovacuum tuning in public. Before the fix it failed with the missing CHECK and the untuned autovacuum. The unit tests that call the probes directly now pass the schema.

Verification

  • 693 tests pass in docker at 100% coverage.
  • ruff and ty are clean.
  • Perf: one catalog query per validate_schema() call, which runs at startup or in a health check. No effect on publish or fetch.

@github-actions

Copy link
Copy Markdown

Benchmark gate

✅ gate passed

scenario msg/s delete/msg WALrec/msg WALB/msg fpi upd del dead_tup
consumer/w1/b10 1285 1.000 6.69 912 242 5000 5000 10000
consumer/w1/b100 1396 1.000 7.09 1143 243 5000 5000 10000
consumer/w2/b10 1615 1.000 6.69 914 242 5000 5000 10000
consumer/w2/b100 1775 1.000 7.09 1143 243 5000 5000 10000
consumer/w4/b10 1657 1.000 6.73 942 242 5000 5000 10000
consumer/w4/b100 1885 1.000 7.14 1180 263 5000 5000 10000
consumer/w1/b100/tfbs100 5978 0.010 6.07 1119 244 5000 5000 10000
consumer/w1/b100/interval1-10 1400 1.000 7.09 1143 243 5000 5000 10000
producer/w1/b100 3025 0.000 3.04 597 1 0 0 0
producer/w1/b100/ppt1 734 0.000 4.04 637 1 0 0 0

Gated (fails the build): delete_calls + tuple counters (upd/del/ins) + the producer's insert_calls, exact; select_calls and other_calls within +2; wal_records within a 10% band; fetch_calls within 2x. msg/s, WAL bytes and total calls are informational (timing/FPI noise).

@lesnik512
lesnik512 merged commit 60fc26b into main Oct 17, 2026
13 checks passed
@lesnik512
lesnik512 deleted the fix/validator-search-path branch October 17, 2026 13:07
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.

1 participant