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)) + }) + } +}