From 5c09652e1a8aec53a6521ec3d2d816941d7b5082 Mon Sep 17 00:00:00 2001 From: Elle Mouton Date: Fri, 4 Jul 2025 10:18:46 +0200 Subject: [PATCH] graph/db: add dev migration test helper This commit was inspired by the invoices package TestMigrationWithChannelDB test helper. This test is purely for running locally to test the graph SQL migration. It allows a dev to run the migration against a local `channel.sqlite` or even `channel.db` file. --- graph/db/sql_migration_test.go | 127 +++++++++++++++++++++++++++++++++ 1 file changed, 127 insertions(+) diff --git a/graph/db/sql_migration_test.go b/graph/db/sql_migration_test.go index e21eb2002..10e070c0f 100644 --- a/graph/db/sql_migration_test.go +++ b/graph/db/sql_migration_test.go @@ -4,10 +4,18 @@ package graphdb import ( "context" + "os" + "path" + "strings" "testing" + "time" "github.com/btcsuite/btcd/chaincfg" + "github.com/btcsuite/btclog/v2" "github.com/lightningnetwork/lnd/kvdb" + "github.com/lightningnetwork/lnd/kvdb/sqlbase" + "github.com/lightningnetwork/lnd/kvdb/sqlite" + "github.com/lightningnetwork/lnd/sqldb" "github.com/stretchr/testify/require" ) @@ -80,3 +88,122 @@ func setUpKVStore(t *testing.T) *KVStore { return kvStore } + +// TestMigrationWithChannelDB tests the migration of the graph store from a +// bolt backed channel.db or a kvdb channel.sqlite to a SQL database. Note that +// this test does not attempt to be a complete migration test for all graph +// store types but rather is added as a tool for developers and users to debug +// graph migration issues with an actual channel.db/channel.sqlite file. +// +// NOTE: To use this test, place either of those files in the graph/db/testdata +// directory, uncomment the "Skipf" line, and set "chain" variable appropriately +// and set the "fileName" variable to the name of the channel database file you +// want to use for the migration test. +func TestMigrationWithChannelDB(t *testing.T) { + ctx := context.Background() + + // NOTE: comment this line out to run the test. + t.Skipf("skipping test meant for local debugging only") + + // NOTE: set this to the genesis hash of the chain that the store + // was created on. + chain := *chaincfg.MainNetParams.GenesisHash + + // NOTE: set this to the name of the channel database file you want + // to use for the migration test. This may be either a bbolt ".db" file + // or a SQLite ".sqlite" file. If you want to migrate from a + // bbolt channel.db file, set this to "channel.db". + const fileName = "channel.sqlite" + + // Set up logging for the test. + UseLogger(btclog.NewSLogger(btclog.NewDefaultHandler(os.Stdout))) + + // migrate runs the migration from the kvdb store to the SQL store. + migrate := func(t *testing.T, kvBackend kvdb.Backend) { + graphStore := newBatchQuerier(t) + + err := graphStore.ExecTx( + ctx, sqldb.WriteTxOpt(), func(tx SQLQueries) error { + return MigrateGraphToSQL( + ctx, kvBackend, tx, chain, + ) + }, sqldb.NoOpReset, + ) + require.NoError(t, err) + } + + connectBBolt := func(t *testing.T, dbPath string) kvdb.Backend { + cfg := &kvdb.BoltBackendConfig{ + DBPath: dbPath, + DBFileName: fileName, + NoFreelistSync: true, + AutoCompact: false, + AutoCompactMinAge: kvdb.DefaultBoltAutoCompactMinAge, + DBTimeout: kvdb.DefaultDBTimeout, + } + + kvStore, err := kvdb.GetBoltBackend(cfg) + require.NoError(t, err) + + return kvStore + } + + connectSQLite := func(t *testing.T, dbPath string) kvdb.Backend { + const ( + timeout = 10 * time.Second + maxConns = 50 + ) + sqlbase.Init(maxConns) + + cfg := &sqlite.Config{ + Timeout: timeout, + BusyTimeout: timeout, + MaxConnections: maxConns, + } + + kvStore, err := kvdb.Open( + kvdb.SqliteBackendName, ctx, cfg, + dbPath, fileName, + // NOTE: we use the raw string here else we get an + // import cycle if we try to import lncfg.NSChannelDB. + "channeldb", + ) + require.NoError(t, err) + + return kvStore + } + + tests := []struct { + name string + dbPath string + }{ + { + name: "empty", + dbPath: t.TempDir(), + }, + { + name: "testdata", + dbPath: "testdata", + }, + } + + // Determine if we are using a SQLite file or a Bolt DB file. + var isSqlite bool + if strings.HasSuffix(fileName, ".sqlite") { + isSqlite = true + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + chanDBPath := path.Join(test.dbPath, fileName) + t.Logf("Connecting to channel DB at: %s", chanDBPath) + + connectDB := connectBBolt + if isSqlite { + connectDB = connectSQLite + } + + migrate(t, connectDB(t, test.dbPath)) + }) + } +}