Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions Darling/Darling.Tests/CollectorStallProbeStoreTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public class CollectorStallProbeStoreTests
private const string TableName = "collector_stall_probes";

[Fact]
public void TheRungIsRegisteredAtTheTopOfADenseLadder()
public void TheRungIsRegisteredInADenseLadder()
{
var versions = PgMigrations.Scripts.Select(s => s.Version).ToList();

Expand All @@ -49,7 +49,13 @@ public void TheRungIsRegisteredAtTheTopOfADenseLadder()

Assert.Equal(StorageVersion.SchemaVersion, PgMigrations.Scripts[^1].Version);
Assert.Equal(StorageVersion.SchemaVersion, versions.Max());
Assert.Equal(RungVersion, StorageVersion.SchemaVersion);
/* NOT "this rung is the top" any more: V113 (#2138 phase 1) is, and it carries that guard in its
own suite. A rung that was top when its test was written cannot keep asserting it — the claim
belongs to whichever rung actually is, or every new rung breaks every older rung's suite. What
stays here is that this rung is IN the ladder and no higher than its head. */
Assert.True(
RungVersion <= StorageVersion.SchemaVersion,
$"V{RungVersion} sits above StorageVersion.SchemaVersion ({StorageVersion.SchemaVersion}), so it can never apply");

Assert.Equal(versions.Distinct().OrderBy(v => v), versions);
var above = versions.Where(v => v > 45).OrderBy(v => v).ToList();
Expand Down
39 changes: 23 additions & 16 deletions Darling/Darling.Tests/CollectorStallProbeViewerGateTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,20 @@ public class CollectorStallProbeViewerGateTests
/// <summary>The version a store one rung behind this one reports.</summary>
private const int PreviousVersion = 111;

/// <summary>The rung this suite is about. Read from the store-side suite so the two cannot disagree.</summary>
private const int RungVersion = CollectorStallProbeStoreTests.RungVersion;

/// <summary>The table the rung creates.</summary>
private const string TableName = "collector_stall_probes";

/// <summary>
/// The connect-time gate. A TABLE sentinel, because the table is the only object the rung creates. Being
/// the TOP rung, a fully-migrated store must map to exactly this version or the viewer refuses a store
/// that is perfectly current — permanently, because no later upgrade changes the answer.
/// The connect-time gate. A TABLE sentinel, because the table is the only object the rung creates.
///
/// <para>This rung is no longer the top one — V113 (#2138 phase 1) is — so the "a fully-migrated store
/// maps to exactly THIS version" clause has moved to that rung's suite, where it is true. Two things
/// here had to stop assuming it: the sentinel's ordinal is no longer the last one, and the
/// one-rung-behind check has to switch off every LATER sentinel too, or it measures the newest rung
/// instead of this one.</para>
/// </summary>
[Fact]
public void TheProbeAsksForTheTable_AndMapsAFullyMigratedStoreToThisRung()
Expand All @@ -56,22 +63,22 @@ public void TheProbeAsksForTheTable_AndMapsAFullyMigratedStoreToThisRung()
.GetMethod("MapProbedSchemaVersion", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static)!;
var arity = method.GetParameters().Length;

/* The sentinel count and the ordinal have to agree, or the ordinal literal above is pinning a
position that no longer exists. */
Assert.Equal(arity - 1, ProbeOrdinal);
/* The ordinal has to be a position that exists. It was arity - 1 while this was the top rung; a
later rung appends a sentinel and that equality would fail for every rung but the newest, which
is a pin about the ladder's length rather than about this rung. */
Assert.True(
ProbeOrdinal < arity,
$"sentinel ordinal {ProbeOrdinal} is outside the probe's {arity} parameters");

/* Every sentinel true = a fully-migrated store, which must map to THIS rung. As the top rung this is
also the "and no more than that" guard: a later rung appending a sentinel without its own arm
would leave this returning 112 for a store that is actually further along. Built by reflection so
the arity tracks the signature — the literal-true form silently defaults a newly added sentinel to
false and maps one version low. */
var all = Enumerable.Repeat((object)true, arity).ToArray();
Assert.Equal(StorageVersion.SchemaVersion, (int)method.Invoke(null, all)!);
/* A store migrated to exactly THIS rung: every sentinel up to and including this one true, every
later one false. Built by reflection so the arity tracks the signature — the literal-true form
silently defaults a newly added sentinel to false and maps one version low. */
var throughMine = Enumerable.Range(0, arity).Select(i => (object)(i <= ProbeOrdinal)).ToArray();
Assert.Equal(RungVersion, (int)method.Invoke(null, throughMine)!);

/* One rung behind: every sentinel present EXCEPT this one must report 111, not 112. Without this the
/* One rung behind: this rung's sentinel absent as well must report 111, not 112. Without it the
arm above could be satisfied by an unconditional return and nothing would notice. */
var allButMine = Enumerable.Repeat((object)true, arity).ToArray();
allButMine[ProbeOrdinal] = false;
var allButMine = Enumerable.Range(0, arity).Select(i => (object)(i < ProbeOrdinal)).ToArray();
Assert.Equal(PreviousVersion, (int)method.Invoke(null, allButMine)!);
}

Expand Down
5 changes: 4 additions & 1 deletion Darling/Darling.Tests/DarlingManagedRolesTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,10 @@ public void ViewerRestrictedConfigTables_SecretAndNonSecretColumns_AreDisjointAn
{
var expectedSecrets = new Dictionary<string, string[]>(StringComparer.Ordinal)
{
["config_monitored_servers"] = new[] { "encrypted_password" },
/* V113 (#2138 phase 1): the remediation credential's blob is the same kind of thing as the
monitoring one beside it — a DPAPI secret — and it authenticates a WRITE, so if anything on
this table is secret it is. */
["config_monitored_servers"] = new[] { "encrypted_password", "remediation_encrypted_password" },
["config_command"] = new[] { "args_json" },
/* generic_url is a bearer secret like the sibling webhook URLs, and generic_headers holds the
Authorization token itself (#1506 / V26). pagerduty_routing_key is the Events API v2 integration
Expand Down
9 changes: 7 additions & 2 deletions Darling/Darling.Tests/DarlingRetentionTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -471,9 +471,13 @@ horizon in the store (90-day alert history, 60-day collection_log, 30-day base).
foreach (var (ageDays, decision) in new[] { (400, "would_force"), (100, "blocked") })
{
using var insert = new NpgsqlCommand(
/* actor is named because V113 (#2138 phase 1) dropped its DEFAULT: it is the column the
bot's own-forces-only invariant reads, and an INSERT that omits it must fail rather
than silently claim to be the bot. This raw INSERT is exactly the shape that would
have — it did, with 23502, which is the guard working. */
"INSERT INTO collect.plan_force_actions"
+ " (action_time, server_id, server_name, database_name, query_id, plan_id, action, mode, decision, outcome)"
+ " VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)", connection);
+ " (action_time, server_id, server_name, database_name, query_id, plan_id, action, mode, actor, decision, outcome)"
+ " VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)", connection);
insert.Parameters.AddWithValue(utcNow.AddDays(-ageDays));
insert.Parameters.AddWithValue(TestServerId);
insert.Parameters.AddWithValue("retention-e2e");
Expand All @@ -482,6 +486,7 @@ horizon in the store (90-day alert history, 60-day collection_log, 30-day base).
insert.Parameters.AddWithValue(2L);
insert.Parameters.AddWithValue("force");
insert.Parameters.AddWithValue("dry_run");
insert.Parameters.AddWithValue("bot");
insert.Parameters.AddWithValue(decision);
insert.Parameters.AddWithValue("journaled");
await insert.ExecuteNonQueryAsync(ct);
Expand Down
13 changes: 13 additions & 0 deletions Darling/Darling.Tests/MigrationDataMovingRungCensusPins.cs
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,19 @@ public sealed class MigrationDataMovingRungCensusPins
SetsTheFloor: true,
"CREATE INDEX over the populated collect.pg_deadlocks hypertable - index-only rung, so a "
+ "store that sat on V103 for a release pays the whole build here"),
new(
113,
SetsTheFloor: false,
"CREATE INDEX on collect.plan_force_actions (created V107), for the actor-filtered "
+ "pending-review read. Real DML shape on a pre-existing table, but the table's SIZE is "
+ "bounded by construction and the bound is small: one row per force/unforce decision per "
+ "server, capped by the bot's per-query cooldown and per-server daily budget (3), and purged "
+ "at 365 days - so its ceiling across a 42-server fleet is ~46k rows, and it is EMPTY on "
+ "every store today because the bot ships off. An index build at that scale is milliseconds, "
+ "which is the opposite of V104's case: that one is a hypertable carrying a real collected "
+ "series. The two ADD COLUMNs in the same rung are not findings and that was measured rather "
+ "than assumed - the volatile-DEFAULT shape needs DEFAULT <fn>( and this rung's default is "
+ "the literal 'bot', and ALTER COLUMN ... DROP DEFAULT is catalog-only"),
];

/// <summary>
Expand Down
Loading
Loading