Merge pull request #1326 from ViktorT-11/2026-06-dont-create-kvdb-databases-on-migration
Some checks failed
CI / frontend tests on macOS-latest (push) Has been cancelled
CI / frontend tests on ubuntu-latest (push) Has been cancelled
CI / frontend tests on windows-latest (push) Has been cancelled
CI / backend build on macOS-latest (push) Has been cancelled
CI / backend build on ubuntu-latest (push) Has been cancelled
CI / backend build on windows-latest (push) Has been cancelled
CI / cross compilation (push) Has been cancelled
CI / cross compilation-1 (push) Has been cancelled
CI / cross compilation-2 (push) Has been cancelled
CI / RPC proto compilation check (push) Has been cancelled
CI / check commits (push) Has been cancelled
CI / Sqlc check (push) Has been cancelled
CI / lint (push) Has been cancelled
CI / run unit tests (push) Has been cancelled
CI / run unit tests-1 (push) Has been cancelled
CI / run unit tests-2 (push) Has been cancelled
CI / run unit tests-3 (push) Has been cancelled
CI / build itest binaries (push) Has been cancelled
CI / check release notes updated (push) Has been cancelled
CI / integration test (push) Has been cancelled
CI / integration test-1 (push) Has been cancelled
CI / integration test-2 (push) Has been cancelled

[sql-75] Don't create kvdb database files in kvdb -> SQL migration if they don't exist
This commit is contained in:
Viktor Torstensson 2026-06-17 00:59:20 +02:00 committed by GitHub
commit f78be1d0bd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 221 additions and 4 deletions

View file

@ -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(

View 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
}

View file

@ -34,10 +34,31 @@ const (
dbFilePermission = 0600
)
// KVDBFileExists reports whether the legacy bbolt database file exists at the
// given path. This only checks file presence and intentionally ignores any
// tombstone state inside the database.
func KVDBFileExists(path string) (bool, error) {
fi, err := os.Stat(path)
switch {
case err == nil:
return !fi.IsDir(), nil
case os.IsNotExist(err):
return false, nil
default:
return false, err
}
}
// DeprecateKVDB marks the given legacy bbolt database as deprecated by
// writing the migration tombstone marker into the specified top-level bucket.
func DeprecateKVDB(path string, timeout time.Duration, bucketKey []byte) error {
if _, err := os.Stat(path); os.IsNotExist(err) {
exists, err := KVDBFileExists(path)
if err != nil {
return err
}
if !exists {
return nil
}
@ -59,7 +80,11 @@ func DeprecateKVDB(path string, timeout time.Duration, bucketKey []byte) error {
func CheckKVDBDeprecated(path string, bucketKey []byte,
timeout time.Duration) error {
if _, err := os.Stat(path); os.IsNotExist(err) {
exists, err := KVDBFileExists(path)
if err != nil {
return err
}
if !exists {
return nil
}
@ -93,11 +118,15 @@ func CheckKVDBDeprecated(path string, bucketKey []byte,
func HasActiveKVDB(path string, bucketKey []byte,
timeout time.Duration) (bool, error) {
if _, err := os.Stat(path); os.IsNotExist(err) {
exists, err := KVDBFileExists(path)
if err != nil {
return false, err
}
if !exists {
return false, nil
}
err := CheckKVDBDeprecated(path, bucketKey, timeout)
err = CheckKVDBDeprecated(path, bucketKey, timeout)
switch {
case errors.Is(err, ErrKVDBDeprecated):
return false, nil