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
20 changes: 20 additions & 0 deletions internal/migrations/019-digest-schema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -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;


7 changes: 4 additions & 3 deletions internal/migrations/056-duplicate-prune-schema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -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;
50 changes: 48 additions & 2 deletions tests/streams/digest/digest_actions_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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)),
Expand All @@ -60,14 +61,15 @@ func TestDigestActions(t *testing.T) {
WithHighCloseTogetherSetup(testHighCloseTogether_Flag10(t)),
WithAutoDigestZeroExpectedSetup(testAutoDigest_ValidatesExpectedRecordsInput(t)),
WithSignerAndProvider(testAutoDigest_PreservesRecentDaysCutoff(t)),
WithSignerAndProvider(testDigestConfigShipsSeeded(t)),
},
}, testutils.GetTestOptionsWithCache())
}

// 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 {
Expand Down Expand Up @@ -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
}
}
7 changes: 4 additions & 3 deletions tests/streams/digest/prune_actions_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Loading