Skip to content

Scrub the legacy plan-force-actions detail state_unavailable line (#4346) - #4384

Merged
erikdarlingdata merged 13 commits into
devfrom
fix/4346-plan-force-legacy-detail-scrub
Sep 26, 2026
Merged

erikdarlingdata merged 13 commits into
devfrom
fix/4346-plan-force-legacy-detail-scrub

Conversation

@erikdarlingdata

@erikdarlingdata erikdarlingdata commented Sep 26, 2026 •

Copy link
Copy Markdown
Owner

Refs #4346.

What this does

  • A one-time startup job, PlanForceActionDetailScrub, rewrites legacy collect.plan_force_actions.detail audit lines written by older builds to the same safe form that PgPlanForceActionStore.SanitizeDetailForAudit already applies on every read.
    • It uses that same sanitizer (no second copy of the rules), and updates only rows whose text changes, in keyed batches.
    • Its completion marker lives in collect.collector_state and is written only when every row succeeded. A failed batch logs its SQLSTATE and retries at the next start.
    • It runs after migrations, never blocks startup, and returns and logs counts only.
  • The raw-detail census (PlanForceActionDetailCensus) pins two exact per-file maps over production code: every string literal naming plan_force_actions, and every literal naming it together with detail. Any other file must count zero. This job is the one exempted raw reader, and its candidate filter constant is used exactly once.

Test plan

  • Live tests (PlanForceActionDetailScrubLiveTests, gated on DARLING_TEST_PG):
    • a legacy row is rewritten;
    • a current row is byte-identical;
    • a second start is a no-op;
    • a failed batch leaves the marker unset and the next start completes.
  • Census pins, with positive and negative controls for every literal form (regular, verbatim, raw, interpolated, $$ raw, +-joined).

CHANGELOG entry

SECTION: Security
ENTRY:

)

One-time startup scrub: PlanForceActionDetailScrub reads collect.plan_force_actions.detail
raw (the one named exemption in PlanForceActionAuditRedactionTests' RawDetailReaderExemptions,
#4377), runs it through PgPlanForceActionStore.SanitizeDetailForAudit (no second copy of the
allow-list), and rewrites only rows whose sanitized text differs. Marker in
collect.collector_state (V44, fleet-sentinel server_id) - no new migration rung. Batched by
row count (plan_force_actions is a plain table, not a hypertable); a batch failure is logged
(SQLSTATE only) and the marker withheld so the next start retries. Wired into DarlingWorker
alongside the #4348 setting scrub.
Pins: legacy row rewrite, post-#4326 row byte-identical, second-start no-op, and a batch failure leaves the marker unset for retry. Live PG tests gated on DARLING_TEST_PG, own-store (#1776) scratch database convention.
…4346)

PlanForceActionAuditRedactionTests.EnclosingMethodFqName picked the
enclosing type/method by the textually nearest preceding class/method
keyword, not by which declaration's braces actually enclose the SQL
site. PlanForceActionDetailScrub declares a nested Summary class above
RunAsync, so the scrub's raw SELECT was attributed to
Summary.Summary and the one exemption in RawDetailReaderExemptions
never matched.

Rewritten to use CSharpSourceWalker.BraceBalanced to find the
innermost type/method whose body actually contains the SQL position,
with a fallback for a query built as a const string field (this
scrub's own shape) that finds the next method declared in the same
type. Added a round-trip self-check pin.
…unrelated CandidateSql fields (#4346/#4376)

- PlanForceActionDetailScrub's raw-detail SQL field is renamed from the
  bare CandidateSql to LegacyDetailCandidateSql. PgSettingScrub.cs and
  QueryStoreBackfill.cs each declare their own unrelated CandidateSql
  field, so a guard on the bare name failed on the real tree before the
  census even ran; the new name cannot collide with either.
- PlanForceActionAuditRedactionTests' exemption list, its round-trip
  pin, and its synthetic positive/negative controls are updated to the
  new field name.
- Fixed the scrub's remarks: a failed batch STOPS the run rather than
  continuing with the next batch, matching the actual break in
  RunAsync.
- Fixed the scrub's type remarks to state plainly that JournalAsync
  does not sanitize at write time; a new row is safe only because the
  #4326/#4363 producers that build Detail are, not because the store
  enforces it on the way in.
- DarlingWorker's catch around the scrub now logs the exception's type
  and SQLSTATE only, matching the type-and-SQLSTATE-only shape used
  elsewhere in this area, rather than the exception's own message.

Darling.Tests builds clean (-p:EnableWindowsTargeting=true, 0
Warning(s), 0 Error(s)).
…l count

Deletes the #4346/#4376 enclosing-method-attribution scan
(NoOtherProductionCode_ReadsTheDetailColumnDirectly and its helpers) and
its now-unused controls, and replaces it with PlanForceActionDetailCensus:
a small C# tokenizer that joins consecutive string literals (regular,
verbatim, raw, interpolated) the way the compiler folds a +-concatenation,
and counts joined literals naming both plan_force_actions and the whole
word detail. PlanForceActionDetailCensusTests pins the exact real counts
per file (PgPlanForceActionStore.cs, PlanForceActionDetailScrub.cs, and
PgMigrations.cs' V107 DDL) plus synthetic controls for every literal shape
and an M1 check that LegacyDetailCandidateSql is declared once and used
exactly once. Refs #4346, #4376.
…-string holes, L2 path-relative map + exemption doc (#4384, cites #4346)
@erikdarlingdata
erikdarlingdata marked this pull request as ready for review September 26, 2026 06:20
@erikdarlingdata
erikdarlingdata merged commit bb04415 into dev Sep 26, 2026
17 of 18 checks passed
@erikdarlingdata
erikdarlingdata deleted the fix/4346-plan-force-legacy-detail-scrub branch September 26, 2026 06:20
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