Skip to content

Execute both Availability Group reads against a real Postgres (#991) - #1697

Merged
erikdarlingdata merged 2 commits into
devfrom
feature/991-ag-live-read-test
Jul 26, 2026
Merged

Execute both Availability Group reads against a real Postgres (#991)#1697
erikdarlingdata merged 2 commits into
devfrom
feature/991-ag-live-read-test

Conversation

@erikdarlingdata

Copy link
Copy Markdown
Owner

Follow-up to #1692. The two Availability Group store reads shipped there with no test that runs their SQL, and that was the one gap that mattered.

Why this is not routine coverage

The defect those reads were fixed for is invisible to a unit test. Both grains were briefly read as two statements over a single command to save a round trip. PostgreSQL rejects that: Npgsql only splits multi-statement text into a batch when it parses the SQL for named placeholders, so with the positional ($1) parameters every read in that file uses, it sends one extended-protocol Parse and the server answers cannot insert multiple commands into a prepared statement.

Because every AG path is failure-isolated, it would not have crashed anything. It would have logged one error per server per sweep with the entire alert family silently dead -- the worst failure mode a monitoring product has, since the thing that is broken is the thing that tells you something is broken. It was caught in review, not by the suite. Only running the SQL catches that shape.

What it asserts

Seeds both collector tables and checks the things only real SQL can prove:

  • each query executes at all (the regression guard)
  • the MAX(collection_time) predicate keeps an older snapshot out
  • a row with a NULL identity column is dropped, not keyed under a placeholder
  • NULL lag and suspend-reason columns round-trip as NULL
  • the whole path reaches EvaluateStoreAlertsAsync and fires exactly one alert off freshly seeded rows (the disconnect and the suspend are first sightings, so they are silent baselines; the 900-second lag is past the 300-second default)

Added to the existing DARLING_TEST_PG-gated live section, so it skips locally and runs in CI's Darling PostgreSQL tests job. The cleanup helper issues one command per statement for the same reason as the code under test.

Testing

Darling suite green: 3250 passed, 156 skipped -- the new test is the +1 against the previous 155 skipped, i.e. it is correctly gated. It executes for real in the CI Postgres job on this PR.

🤖 Generated with Claude Code

erikdarlingdata and others added 2 commits July 26, 2026 14:39
The two AG store reads shipped in #1692 with no test that runs their SQL. That
is the one gap that mattered, because the defect they were fixed for is
invisible to a unit test: the grains were briefly read as two statements over a
SINGLE command to save a round trip, and PostgreSQL rejects that. Npgsql only
splits multi-statement text into a batch when it parses the SQL for NAMED
placeholders, so with the positional ($1) parameters these reads use it sends
one extended-protocol Parse and the server answers "cannot insert multiple
commands into a prepared statement".

Because every AG path is failure-isolated, that would not have crashed
anything. It would have logged one error per server per sweep with the entire
alert family silently dead - the worst failure mode a monitoring product has,
since the thing that is broken is the thing that tells you something is broken.
It was caught in review rather than by the suite.

Added to the existing DARLING_TEST_PG-gated live section (skipped locally, run
by CI's "Darling PostgreSQL tests" job), seeding both collector tables and
asserting the things only real SQL can prove: that each query executes at all,
that the MAX(collection_time) predicate keeps an older snapshot out, that a row
with a NULL identity column is dropped rather than keyed under a placeholder,
that NULL lag and suspend-reason columns round-trip, and that the whole thing
reaches EvaluateStoreAlertsAsync and fires exactly one alert off freshly seeded
rows.

The cleanup helper issues one command per statement for the same reason.

Darling suite green: 3250 passed, 156 skipped (the new test skips without a
store; it is the +1 against the previous 155).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@erikdarlingdata
erikdarlingdata merged commit a9ace0e into dev Jul 26, 2026
4 checks passed
@erikdarlingdata
erikdarlingdata deleted the feature/991-ag-live-read-test branch July 26, 2026 19:20
pull Bot pushed a commit to ehtick/PerformanceMonitor that referenced this pull request Jul 29, 2026
Concurrency groups for build.yml and sql-validation.yml, keyed per PR
number for pull_request events only. Push and release runs get a unique
per-run group (run_id), so dev/main integration builds, release builds,
and every push commit's required-check result are never queued behind,
replaced by, or cancelled through this mechanism.

Rationale: the shared Windows runner pool is what serializes everyone's
CI (erikdarlingdata#1697 sat queued behind two dev builds), so a stale PR run cancelled
at re-push time is reclaimed capacity for whatever sits queued behind
it. claude-review.yml already had exactly this shape; build.yml and
sql-validation.yml now match it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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