diff --git a/internal/migrations/019-digest-schema.sql b/internal/migrations/019-digest-schema.sql index c428b043..b15b4117 100644 --- a/internal/migrations/019-digest-schema.sql +++ b/internal/migrations/019-digest-schema.sql @@ -39,4 +39,24 @@ CREATE TABLE IF NOT EXISTS digest_config ( updated_at_height INT8 NOT NULL DEFAULT 0 ); +-- Seed the row rather than leaving it to whoever brings the network up. Leaving +-- it out is how a network reaches the state testnet was in: no row, so the +-- scheduler reads "disabled" and stops, which is indistinguishable in the logs +-- from digest being deliberately off. Nobody noticed until the queue had been +-- filling since 2010. +-- +-- Shipped disabled, so this creates the row without starting digest anywhere, +-- and ON CONFLICT leaves a network that already has one exactly as it is -- +-- including its own schedule. Turning digest on stays an operator decision made +-- through a signed exec-sql. +-- +-- digest_schedule is NOT NULL with no default, which is the reason a row could +-- not simply be conjured by the DEFAULTs the way duplicate_prune_config's is in +-- 056. The value below matches DefaultDigestSchedule in +-- extensions/tn_digest/constants.go, which is what the extension already falls +-- back to when the schedule comes back empty. +INSERT INTO digest_config (id, enabled, digest_schedule) +VALUES (1, false, '0 */6 * * *') +ON CONFLICT (id) DO NOTHING; + diff --git a/internal/migrations/056-duplicate-prune-schema.sql b/internal/migrations/056-duplicate-prune-schema.sql index 85da0e21..c6941543 100644 --- a/internal/migrations/056-duplicate-prune-schema.sql +++ b/internal/migrations/056-duplicate-prune-schema.sql @@ -30,7 +30,8 @@ CREATE TABLE IF NOT EXISTS duplicate_prune_config ( CONSTRAINT chk_dpc_cursor_not_negative CHECK (last_stream_ref >= 0) ); --- Seeded here on purpose. digest_config is not, and the consequence is a network --- where digest has simply never run because nobody noticed the row was missing. --- Creating it disabled costs nothing and removes that failure mode. +-- Seeded here on purpose. digest_config went unseeded for years, and the +-- consequence was a network where digest had simply never run because nobody +-- noticed the row was missing. Creating it disabled costs nothing and removes +-- that failure mode; 019 now seeds its own row the same way. INSERT INTO duplicate_prune_config (id) VALUES (1) ON CONFLICT (id) DO NOTHING; diff --git a/tests/streams/digest/digest_actions_test.go b/tests/streams/digest/digest_actions_test.go index 6ac83939..a25351de 100644 --- a/tests/streams/digest/digest_actions_test.go +++ b/tests/streams/digest/digest_actions_test.go @@ -17,6 +17,7 @@ import ( kwilTesting "github.com/trufnetwork/kwil-db/testing" + "github.com/trufnetwork/node/extensions/tn_digest" "github.com/trufnetwork/node/internal/migrations" testutils "github.com/trufnetwork/node/tests/streams/utils" "github.com/trufnetwork/node/tests/streams/utils/procedure" @@ -33,7 +34,7 @@ var idempotencyTestStreamId = util.GenerateStreamId(idempotencyTestStreamName) func TestDigestActions(t *testing.T) { testutils.RunSchemaTest(t, kwilTesting.SchemaTest{ - Name: "digest_actions_test", + Name: "digest_actions_test", SeedStatements: migrations.GetSeedScriptStatements(), FunctionTests: []kwilTesting.TestFunc{ WithDigestTestSetup(testDigestBasicOHLCCalculation(t)), @@ -60,6 +61,7 @@ func TestDigestActions(t *testing.T) { WithHighCloseTogetherSetup(testHighCloseTogether_Flag10(t)), WithAutoDigestZeroExpectedSetup(testAutoDigest_ValidatesExpectedRecordsInput(t)), WithSignerAndProvider(testAutoDigest_PreservesRecentDaysCutoff(t)), + WithSignerAndProvider(testDigestConfigShipsSeeded(t)), }, }, testutils.GetTestOptionsWithCache()) } @@ -67,7 +69,7 @@ func TestDigestActions(t *testing.T) { // Verifies leader-only authorization on digest actions using BlockContext.Proposer. func TestDigestActionsLeaderAuthorization(t *testing.T) { testutils.RunSchemaTest(t, kwilTesting.SchemaTest{ - Name: "digest_actions_leader_authorization", + Name: "digest_actions_leader_authorization", SeedStatements: migrations.GetSeedScriptStatements(), FunctionTests: []kwilTesting.TestFunc{ WithSignerAndProvider(func(ctx context.Context, platform *kwilTesting.Platform) error { @@ -2829,3 +2831,47 @@ func testAutoDigest_PreservesRecentDaysCutoff(t *testing.T) func(ctx context.Con return nil } } + +// digest_config had no row on testnet, so the scheduler read "disabled" and +// stopped — which looks in the logs exactly like digest being switched off on +// purpose. Nobody noticed for years, and the queue was 27,148 days deep by the +// time anyone did. 019 seeds the row now; this asserts it is there, that it is +// off, and that its schedule agrees with the fallback the extension applies when +// the schedule comes back empty. A seed that drifted from that constant would +// silently change a fresh network's cadence. +func testDigestConfigShipsSeeded(t *testing.T) func(context.Context, *kwilTesting.Platform) error { + return func(ctx context.Context, platform *kwilTesting.Platform) error { + kit, err := newCtxKit(ctx, platform, true) + if err != nil { + return errors.Wrap(err, "new ctx kit") + } + + var ( + rows int + enabled bool + schedule string + ) + if err := platform.Engine.Execute(kit.eng, platform.DB, + `SELECT enabled, digest_schedule FROM digest_config WHERE id = 1`, nil, + func(row *common.Row) error { + rows++ + enabled, _ = row.Values[0].(bool) + schedule, _ = row.Values[1].(string) + return nil + }); err != nil { + return errors.Wrap(err, "read digest_config") + } + + if rows != 1 { + return errors.Errorf("digest_config should hold exactly one seeded row, got %d", rows) + } + if enabled { + return errors.New("digest_config should ship disabled; enabling digest is an operator decision") + } + if schedule != tn_digest.DefaultDigestSchedule { + return errors.Errorf("seeded schedule %q should match DefaultDigestSchedule %q", + schedule, tn_digest.DefaultDigestSchedule) + } + return nil + } +} diff --git a/tests/streams/digest/prune_actions_test.go b/tests/streams/digest/prune_actions_test.go index 9d0201ff..e8445239 100644 --- a/tests/streams/digest/prune_actions_test.go +++ b/tests/streams/digest/prune_actions_test.go @@ -888,9 +888,10 @@ func testAutoPruneWrapsAtTheEndOfAPass(t *testing.T) func(context.Context, *kwil // Configuration // ============================================================================= -// The row is seeded by migration 056, disabled. digest_config is not seeded, and -// the consequence is a network where digest has never run because nobody noticed -// the row was missing — this asserts we did not repeat that. +// The row is seeded by migration 056, disabled. digest_config went unseeded for +// years, and the consequence was a network where digest had never run because +// nobody noticed the row was missing — this asserts we did not repeat that. +// 019 now seeds its own row, covered by testDigestConfigShipsSeeded. func testPruneConfigShipsDisabled(t *testing.T) func(context.Context, *kwilTesting.Platform) error { return func(ctx context.Context, platform *kwilTesting.Platform) error { res, err := callActionAsStrings(ctx, platform, "get_duplicate_prune_config", 4)