mirror of
https://github.com/lightninglabs/lightning-terminal.git
synced 2026-08-13 12:33:36 +02:00
db: skip SQL migration when no kvdb files exist
Avoid creating legacy bbolt database files when the kvdb-to-SQL programmatic migration runs on a node that never had LiT kvdb state. Note that we only skip the creating the database file if there is no single bbolt database file. If for example there's only 1 existing bbolt database file, we will still create the new bbolt database file for the other 2 bbolt databases, as we for example need to pass the accounts & session databases to the firewalldb migration, and we'd want to avoid having the migrations become more complex by having to handle the case where the other databases are missing.
This commit is contained in:
parent
27eba1e5c1
commit
059554474e
2 changed files with 188 additions and 0 deletions
|
|
@ -6,12 +6,14 @@ import (
|
|||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/golang-migrate/migrate/v4"
|
||||
"github.com/golang-migrate/migrate/v4/database"
|
||||
"github.com/lightninglabs/lightning-terminal/accounts"
|
||||
"github.com/lightninglabs/lightning-terminal/db/sqlcmig6"
|
||||
"github.com/lightninglabs/lightning-terminal/db/tombstone"
|
||||
"github.com/lightninglabs/lightning-terminal/firewalldb"
|
||||
"github.com/lightninglabs/lightning-terminal/session"
|
||||
"github.com/lightningnetwork/lnd/clock"
|
||||
|
|
@ -88,6 +90,40 @@ func kvdbToSqlProgrammaticMigration(ctx context.Context,
|
|||
_ *sqldb.BaseDB, clock clock.Clock, q *sqlcmig6.Queries) error {
|
||||
|
||||
start := time.Now()
|
||||
|
||||
accountsActive, err := tombstone.KVDBFileExists(
|
||||
filepath.Join(accountsDir, accounts.DBFilename),
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to inspect accounts kvdb: %w", err)
|
||||
}
|
||||
|
||||
sessionsActive, err := tombstone.KVDBFileExists(
|
||||
filepath.Join(networkDir, session.DBFilename),
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to inspect session kvdb: %w", err)
|
||||
}
|
||||
|
||||
firewallActive, err := tombstone.KVDBFileExists(
|
||||
filepath.Join(networkDir, firewalldb.DBFilename),
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to inspect rules kvdb: %w", err)
|
||||
}
|
||||
|
||||
if !accountsActive && !sessionsActive && !firewallActive {
|
||||
log.Infof("Skipping KVDB to SQL migration for all stores: " +
|
||||
"no legacy database files exist")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
if basicClient == nil {
|
||||
return errors.New("lightning client is required for " +
|
||||
"migration but was nil")
|
||||
}
|
||||
|
||||
log.Infof("Starting KVDB to SQL migration for all stores")
|
||||
|
||||
accountStore, err := accounts.NewBoltStoreForMigration(
|
||||
|
|
|
|||
152
db/migsets/programmatic_migrations_test.go
Normal file
152
db/migsets/programmatic_migrations_test.go
Normal file
|
|
@ -0,0 +1,152 @@
|
|||
package migsets
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/lightninglabs/lightning-terminal/accounts"
|
||||
"github.com/lightninglabs/lightning-terminal/db"
|
||||
"github.com/lightninglabs/lightning-terminal/db/sqlcmig6"
|
||||
"github.com/lightninglabs/lightning-terminal/firewalldb"
|
||||
"github.com/lightninglabs/lightning-terminal/session"
|
||||
"github.com/lightningnetwork/lnd/clock"
|
||||
"github.com/lightningnetwork/lnd/lnrpc"
|
||||
"github.com/lightningnetwork/lnd/sqldb/v2"
|
||||
"github.com/stretchr/testify/require"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials/insecure"
|
||||
"google.golang.org/grpc/test/bufconn"
|
||||
)
|
||||
|
||||
// TestKVDBToSQLProgrammaticMigrationSkipsMissingStores verifies that the kvdb
|
||||
// to SQL migration does not create missing legacy kvdb files while scanning for
|
||||
// stores to migrate.
|
||||
func TestKVDBToSQLProgrammaticMigrationSkipsMissingStores(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
accountsDir := t.TempDir()
|
||||
networkDir := t.TempDir()
|
||||
|
||||
sqlStore := sqldb.NewTestSqliteDB(t, db.MakeTestMigrationSets())
|
||||
queries := sqlcmig6.NewForType(
|
||||
sqlStore.BaseDB, sqlStore.BackendType,
|
||||
)
|
||||
|
||||
err := kvdbToSqlProgrammaticMigration(
|
||||
context.Background(), nil, accountsDir, networkDir,
|
||||
sqlStore.BaseDB, clock.NewDefaultClock(), queries,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
requireNoFile(t, filepath.Join(accountsDir, accounts.DBFilename))
|
||||
requireNoFile(t, filepath.Join(networkDir, session.DBFilename))
|
||||
requireNoFile(t, filepath.Join(networkDir, firewalldb.DBFilename))
|
||||
}
|
||||
|
||||
// TestKVDBToSQLProgrammaticMigrationRunsWithOneBBoltDBFiles verifies that the
|
||||
// migration still runs when only the accounts kvdb exists, creates the missing
|
||||
// legacy files, and migrates the existing account data to SQL.
|
||||
func TestKVDBToSQLProgrammaticMigrationRunsWithOneBBoltDBFiles(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ctx := context.Background()
|
||||
testClock := clock.NewDefaultClock()
|
||||
accountsDir := t.TempDir()
|
||||
networkDir := t.TempDir()
|
||||
|
||||
accountStore, err := accounts.NewBoltStore(
|
||||
accountsDir, accounts.DBFilename, testClock,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = accountStore.NewAccount(ctx, 1234, time.Time{}, "acct-1")
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, accountStore.Close())
|
||||
|
||||
sqlStore := sqldb.NewTestSqliteDB(t, db.MakeTestMigrationSets())
|
||||
queries := sqlcmig6.NewForType(
|
||||
sqlStore.BaseDB, sqlStore.BackendType,
|
||||
)
|
||||
|
||||
lndClient := newTestLightningClient(t)
|
||||
|
||||
err = kvdbToSqlProgrammaticMigration(
|
||||
ctx, lndClient, accountsDir, networkDir,
|
||||
sqlStore.BaseDB, testClock, queries,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
requireFileExists(t, filepath.Join(accountsDir, accounts.DBFilename))
|
||||
requireFileExists(t, filepath.Join(networkDir, session.DBFilename))
|
||||
requireFileExists(t, filepath.Join(networkDir, firewalldb.DBFilename))
|
||||
|
||||
dbAccounts, err := queries.ListAllAccounts(ctx)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, dbAccounts, 1)
|
||||
|
||||
dbSessions, err := queries.ListSessions(ctx)
|
||||
require.NoError(t, err)
|
||||
require.Empty(t, dbSessions)
|
||||
}
|
||||
|
||||
func requireNoFile(t *testing.T, path string) {
|
||||
t.Helper()
|
||||
|
||||
_, err := os.Stat(path)
|
||||
require.ErrorIs(t, err, os.ErrNotExist)
|
||||
}
|
||||
|
||||
func requireFileExists(t *testing.T, path string) {
|
||||
t.Helper()
|
||||
|
||||
_, err := os.Stat(path)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func newTestLightningClient(t *testing.T) lnrpc.LightningClient {
|
||||
t.Helper()
|
||||
|
||||
lis := bufconn.Listen(1024 * 1024)
|
||||
server := grpc.NewServer()
|
||||
lnrpc.RegisterLightningServer(server, &testLightningServer{})
|
||||
|
||||
go func() {
|
||||
_ = server.Serve(lis)
|
||||
}()
|
||||
|
||||
t.Cleanup(func() {
|
||||
server.Stop()
|
||||
require.NoError(t, lis.Close())
|
||||
})
|
||||
|
||||
conn, err := grpc.DialContext(
|
||||
context.Background(), "passthrough:///bufnet",
|
||||
grpc.WithTransportCredentials(insecure.NewCredentials()),
|
||||
grpc.WithContextDialer(
|
||||
func(context.Context, string) (net.Conn, error) {
|
||||
return lis.Dial()
|
||||
},
|
||||
),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
t.Cleanup(func() {
|
||||
require.NoError(t, conn.Close())
|
||||
})
|
||||
|
||||
return lnrpc.NewLightningClient(conn)
|
||||
}
|
||||
|
||||
type testLightningServer struct {
|
||||
lnrpc.UnimplementedLightningServer
|
||||
}
|
||||
|
||||
func (t *testLightningServer) ListMacaroonIDs(context.Context,
|
||||
*lnrpc.ListMacaroonIDsRequest) (*lnrpc.ListMacaroonIDsResponse, error) {
|
||||
|
||||
return &lnrpc.ListMacaroonIDsResponse{}, nil
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue