Skip to content

Drop the never-written warning/critical_behind_threshold columns (#5173) - #5184

Merged
jeremydmiller merged 1 commit into
masterfrom
fix/5173-drop-behind-thresholds
Aug 4, 2026
Merged

Drop the never-written warning/critical_behind_threshold columns (#5173)#5184
jeremydmiller merged 1 commit into
masterfrom
fix/5173-drop-behind-thresholds

Conversation

@jeremydmiller

Copy link
Copy Markdown
Member

Closes #5173.

The state of things

warning_behind_threshold and critical_behind_threshold had a complete read path and no write path:

  • created in EventProgressionTable when EnableExtendedProgressionTracking is on;
  • listed in ProjectionProgressStatement's extendedColumns;
  • hydrated into ShardState.WarningBehindThreshold / .CriticalBehindThreshold by ShardStateSelector.

And written by nothing, in any repo. They are absent from WriteExtendedProgressionAsync's UPDATE … FROM unnest(…) and from the legacy mt_mark_event_progression_extended.sql, and nothing else writes extended columns. Every read returned NULL, always.

Direction: drop, not wire

Nothing ever owned the value. In JasperFx both ShardState properties are declared and never assigned or read — a repo-wide grep returns only the two declarations, no tests, no callers. In CritterWatch the only threshold code path is PushAgentThresholdsHandler, a stub that logs and discards (its computed agentUri is never used); every lag threshold lives console-side in ProjectionAlertDefaults / AlertThresholdResolver.

Wiring them would mean designing who owns the value, how it is pushed and what reads it — a real design question with no current demand. Dropping them removes two columns of storage, two entries in every extended-tracking SELECT, and two selector ordinals that carry nothing.

Migration note

Existing deployments with extended tracking on get an alter table {schema}.mt_event_progression drop column … on their next ApplyAllConfiguredChangesToDatabaseAsync(). That is lossless: the columns were provably always NULL, and no query anywhere selects them any more.

Ordinals

The issue flags this correctly — ShardStateSelector walks positionally, and the failure_* columns are deliberately kept trailing (ProjectionProgressStatement.cs:45-46) so their ordinals stay stable. Removing two columns from the middle of the extended block shifts everything after them, so the existing #5048 round-trip tests are the real guard here. They stay green (12/12 across Bug_5048 + extended_progression_* + the new test, net10.0).

New test Bug_5173_behind_threshold_columns_removed asserts the two columns are no longer created and that the eight that carry something still are.

Hand-off

ShardState.WarningBehindThreshold (Projections/ShardState.cs:116) and .CriticalBehindThreshold (:122) remain in JasperFx, still unassigned and unread. Removing them is a JasperFx change, and harmless to leave in place meanwhile — nothing sets them now.

🤖 Generated with Claude Code

https://claude.ai/code/session_017CTtw2kVRSZKp1p5RTxgAy

`warning_behind_threshold` and `critical_behind_threshold` were created in DDL, listed in
every extended-tracking SELECT, and hydrated into `ShardState` — and written by nothing, in
any repo. Every read of them returned NULL, always.

Nothing ever owned the value. In JasperFx the two `ShardState` properties are declared and
never assigned or read; in CritterWatch the only threshold code path is a stub that logs and
discards, and every lag threshold lives console-side. So this drops them rather than wiring
them: two columns of storage, two entries in every extended-tracking SELECT and two selector
ordinals, all carrying nothing.

Existing deployments get an `alter table ... drop column` on their next
`ApplyAllConfiguredChangesToDatabaseAsync()`, which is lossless because the columns were
provably always NULL.

The `failure_*` columns deliberately trail the extended block so `ShardStateSelector`'s
positional ordinals stay stable; removing two columns from the middle of that block shifts
them, so the existing #5048 round-trip tests are the guard and stay green.

The two `ShardState` properties remain in JasperFx (unassigned and unread there too) — a
hand-off, not something Marten can remove.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017CTtw2kVRSZKp1p5RTxgAy
@jeremydmiller
jeremydmiller merged commit 383f916 into master Aug 4, 2026
10 checks passed
@jeremydmiller
jeremydmiller deleted the fix/5173-drop-behind-thresholds branch August 4, 2026 14:55
jeremydmiller added a commit that referenced this pull request Aug 4, 2026
…chema (#5173) (#5188)

#5184 removed warning_behind_threshold / critical_behind_threshold from three places: the
DDL, the extended-tracking SELECT, and ShardStateSelector's ordinals. The reasoning holds
-- nothing in any repo ever wrote or read them, and every lag threshold lives in the
monitoring tool -- and the read-side half of it stands. Only the DDL is restored.

The cost of removing them was never the two always-NULL columns, it is the schema CHANGE.
Measured against PostgreSQL, on a store with event sourcing registered, an
mt_event_progression carrying a column the configuration no longer declares gives:

    CreateMigrationAsync().Difference   = Update   (mt_event_progression=Update)
    AssertDatabaseMatchesConfigurationAsync()      -> throws
    ApplyAllConfiguredChangesToDatabaseAsync()     -> drops the column

So an upgrade is not the no-op #5184's commit message implied. Two things happen to
existing deployments. A host running AutoCreate.None with a schema assertion at boot --
the standard production posture -- does not migrate, it refuses to start until someone
migrates it. And a host that does apply runs `alter table ... drop column`, which in
PostgreSQL is O(1) metadata but needs ACCESS EXCLUSIVE on a small, hot table that every
running daemon writes; while that lock waits it blocks every reader and writer behind it.

Two unused columns are much cheaper than asking every deployment to absorb that, and
keeping them also means a freshly created database and an upgraded one have the same
shape rather than diverging on a detail nothing reads.

If they are ever removed for real it has to be a documented migration step rather than a
silent consequence of upgrading a package.

The tests pin both halves and both fail with the columns taken back out. Note the
`AddEventType` in each: without an event type registered the event store's tables are not
part of the schema migration at all, every delta comes back None, and any assertion about
this table passes vacuously -- which is how the original version of this test was green
against both shapes.


Claude-Session: https://claude.ai/code/session_016v2Aijyo8MX2AdPUZL5VtG

Co-authored-by: Claude Opus 5 (1M context) <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.

warning_behind_threshold / critical_behind_threshold are created, selected and hydrated but never written

1 participant