multi: use sqldb/v2 in accounts package

Update the accounts package to use the `sqldb/v2` package instead of the
older version.
This commit is contained in:
Viktor Torstensson 2025-07-22 12:20:04 +02:00
parent 7265ae2a85
commit b7ee351cb0
No known key found for this signature in database
GPG key ID: 961CC8259AE675D4
7 changed files with 160 additions and 60 deletions

View file

@ -2,18 +2,17 @@ package accounts
import (
"context"
"database/sql"
"fmt"
"testing"
"time"
"github.com/lightninglabs/lightning-terminal/db"
"github.com/lightninglabs/lightning-terminal/db/sqlc"
"github.com/lightningnetwork/lnd/clock"
"github.com/lightningnetwork/lnd/fn"
"github.com/lightningnetwork/lnd/lnrpc"
"github.com/lightningnetwork/lnd/lntypes"
"github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/sqldb"
"github.com/lightningnetwork/lnd/sqldb/v2"
"github.com/stretchr/testify/require"
"golang.org/x/exp/rand"
"pgregory.net/rapid"
@ -36,7 +35,7 @@ func TestAccountStoreMigration(t *testing.T) {
}
makeSQLDB := func(t *testing.T) (*SQLStore,
*db.TransactionExecutor[SQLQueries]) {
*sqlQueriesExecutor[SQLQueries]) {
testDBStore := NewTestDB(t, clock)
@ -45,13 +44,9 @@ func TestAccountStoreMigration(t *testing.T) {
baseDB := store.BaseDB
genericExecutor := db.NewTransactionExecutor(
baseDB, func(tx *sql.Tx) SQLQueries {
return baseDB.WithTx(tx)
},
)
queries := sqlc.NewForType(baseDB, baseDB.BackendType)
return store, genericExecutor
return store, newSQLQueriesExecutor(baseDB, queries)
}
assertMigrationResults := func(t *testing.T, sqlStore *SQLStore,
@ -344,7 +339,7 @@ func TestAccountStoreMigration(t *testing.T) {
return MigrateAccountStoreToSQL(
ctx, kvStore.db, tx,
)
},
}, sqldb.NoOpReset,
)
require.NoError(t, err)

View file

@ -15,6 +15,7 @@ import (
"github.com/lightningnetwork/lnd/lnrpc"
"github.com/lightningnetwork/lnd/lntypes"
"github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/sqldb/v2"
)
const (
@ -32,6 +33,8 @@ const (
//
//nolint:ll
type SQLQueries interface {
sqldb.BaseQuerier
AddAccountInvoice(ctx context.Context, arg sqlc.AddAccountInvoiceParams) error
DeleteAccount(ctx context.Context, id int64) error
DeleteAccountPayment(ctx context.Context, arg sqlc.DeleteAccountPaymentParams) error
@ -56,12 +59,13 @@ type SQLQueries interface {
GetAccountInvoice(ctx context.Context, arg sqlc.GetAccountInvoiceParams) (sqlc.AccountInvoice, error)
}
// BatchedSQLQueries is a version of the SQLQueries that's capable
// of batched database operations.
// BatchedSQLQueries combines the SQLQueries interface with the BatchedTx
// interface, allowing for multiple queries to be executed in single SQL
// transaction.
type BatchedSQLQueries interface {
SQLQueries
db.BatchedTx[SQLQueries]
sqldb.BatchedTx[SQLQueries]
}
// SQLStore represents a storage backend.
@ -71,19 +75,37 @@ type SQLStore struct {
db BatchedSQLQueries
// BaseDB represents the underlying database connection.
*db.BaseDB
*sqldb.BaseDB
clock clock.Clock
}
// NewSQLStore creates a new SQLStore instance given an open BatchedSQLQueries
// storage backend.
func NewSQLStore(sqlDB *db.BaseDB, clock clock.Clock) *SQLStore {
executor := db.NewTransactionExecutor(
sqlDB, func(tx *sql.Tx) SQLQueries {
return sqlDB.WithTx(tx)
type sqlQueriesExecutor[T sqldb.BaseQuerier] struct {
*sqldb.TransactionExecutor[T]
SQLQueries
}
func newSQLQueriesExecutor(baseDB *sqldb.BaseDB,
queries *sqlc.Queries) *sqlQueriesExecutor[SQLQueries] {
executor := sqldb.NewTransactionExecutor(
baseDB, func(tx *sql.Tx) SQLQueries {
return queries.WithTx(tx)
},
)
return &sqlQueriesExecutor[SQLQueries]{
TransactionExecutor: executor,
SQLQueries: queries,
}
}
// NewSQLStore creates a new SQLStore instance given an open BatchedSQLQueries
// storage backend.
func NewSQLStore(sqlDB *sqldb.BaseDB, queries *sqlc.Queries,
clock clock.Clock) *SQLStore {
executor := newSQLQueriesExecutor(sqlDB, queries)
return &SQLStore{
db: executor,
@ -155,7 +177,7 @@ func (s *SQLStore) NewAccount(ctx context.Context, balance lnwire.MilliSatoshi,
}
return nil
})
}, sqldb.NoOpReset)
if err != nil {
return nil, err
}
@ -297,7 +319,7 @@ func (s *SQLStore) AddAccountInvoice(ctx context.Context, alias AccountID,
}
return s.markAccountUpdated(ctx, db, acctID)
})
}, sqldb.NoOpReset)
}
func getAccountIDByAlias(ctx context.Context, db SQLQueries, alias AccountID) (
@ -422,7 +444,7 @@ func (s *SQLStore) UpdateAccount(ctx context.Context,
}
return s.markAccountUpdated(ctx, db, id)
})
}, sqldb.NoOpReset)
}
// CreditAccount increases the balance of the account with the given alias by
@ -457,7 +479,7 @@ func (s *SQLStore) CreditAccount(ctx context.Context, alias AccountID,
}
return s.markAccountUpdated(ctx, db, id)
})
}, sqldb.NoOpReset)
}
// DebitAccount decreases the balance of the account with the given alias by the
@ -498,7 +520,7 @@ func (s *SQLStore) DebitAccount(ctx context.Context, alias AccountID,
}
return s.markAccountUpdated(ctx, db, id)
})
}, sqldb.NoOpReset)
}
// Account retrieves an account from the SQL store and un-marshals it. If the
@ -520,7 +542,7 @@ func (s *SQLStore) Account(ctx context.Context, alias AccountID) (
account, err = getAndMarshalAccount(ctx, db, id)
return err
})
}, sqldb.NoOpReset)
return account, err
}
@ -552,7 +574,7 @@ func (s *SQLStore) Accounts(ctx context.Context) ([]*OffChainBalanceAccount,
}
return nil
})
}, sqldb.NoOpReset)
return accounts, err
}
@ -569,7 +591,7 @@ func (s *SQLStore) RemoveAccount(ctx context.Context, alias AccountID) error {
}
return db.DeleteAccount(ctx, id)
})
}, sqldb.NoOpReset)
}
// UpsertAccountPayment updates or inserts a payment entry for the given
@ -679,7 +701,7 @@ func (s *SQLStore) UpsertAccountPayment(ctx context.Context, alias AccountID,
}
return s.markAccountUpdated(ctx, db, id)
})
}, sqldb.NoOpReset)
}
// DeleteAccountPayment removes a payment entry from the account with the given
@ -722,7 +744,7 @@ func (s *SQLStore) DeleteAccountPayment(ctx context.Context, alias AccountID,
}
return s.markAccountUpdated(ctx, db, id)
})
}, sqldb.NoOpReset)
}
// LastIndexes returns the last invoice add and settle index or
@ -749,7 +771,7 @@ func (s *SQLStore) LastIndexes(ctx context.Context) (uint64, uint64, error) {
}
return err
})
}, sqldb.NoOpReset)
return uint64(addIndex), uint64(settleIndex), err
}
@ -774,7 +796,7 @@ func (s *SQLStore) StoreLastIndexes(ctx context.Context, addIndex,
Name: settleIndexName,
Value: int64(settleIndex),
})
})
}, sqldb.NoOpReset)
}
// Close closes the underlying store.

View file

@ -16,7 +16,7 @@ var ErrDBClosed = errors.New("database is closed")
// NewTestDB is a helper function that creates an SQLStore database for testing.
func NewTestDB(t *testing.T, clock clock.Clock) Store {
return createStore(t, db.NewTestPostgresDB(t).BaseDB, clock)
return createStore(t, db.NewTestPostgresV2DB(t).BaseDB, clock)
}
// NewTestDBFromPath is a helper function that creates a new SQLStore with a
@ -24,5 +24,5 @@ func NewTestDB(t *testing.T, clock clock.Clock) Store {
func NewTestDBFromPath(t *testing.T, dbPath string,
clock clock.Clock) Store {
return createStore(t, db.NewTestPostgresDB(t).BaseDB, clock)
return createStore(t, db.NewTestPostgresV2DB(t).BaseDB, clock)
}

View file

@ -5,15 +5,20 @@ package accounts
import (
"testing"
"github.com/lightninglabs/lightning-terminal/db"
"github.com/lightninglabs/lightning-terminal/db/sqlc"
"github.com/lightningnetwork/lnd/clock"
"github.com/lightningnetwork/lnd/sqldb/v2"
"github.com/stretchr/testify/require"
)
// createStore is a helper function that creates a new SQLStore and ensure that
// it is closed when during the test cleanup.
func createStore(t *testing.T, sqlDB *db.BaseDB, clock clock.Clock) *SQLStore {
store := NewSQLStore(sqlDB, clock)
func createStore(t *testing.T, sqlDB *sqldb.BaseDB,
clock clock.Clock) *SQLStore {
queries := sqlc.NewForType(sqlDB, sqlDB.BackendType)
store := NewSQLStore(sqlDB, queries, clock)
t.Cleanup(func() {
require.NoError(t, store.Close())
})

View file

@ -8,6 +8,7 @@ import (
"github.com/lightninglabs/lightning-terminal/db"
"github.com/lightningnetwork/lnd/clock"
"github.com/lightningnetwork/lnd/sqldb/v2"
)
// ErrDBClosed is an error that is returned when a database operation is
@ -16,7 +17,10 @@ var ErrDBClosed = errors.New("database is closed")
// NewTestDB is a helper function that creates an SQLStore database for testing.
func NewTestDB(t *testing.T, clock clock.Clock) Store {
return createStore(t, db.NewTestSqliteDB(t).BaseDB, clock)
return createStore(
t, sqldb.NewTestSqliteDB(t, db.LitdMigrationStreams).BaseDB,
clock,
)
}
// NewTestDBFromPath is a helper function that creates a new SQLStore with a
@ -24,7 +28,7 @@ func NewTestDB(t *testing.T, clock clock.Clock) Store {
func NewTestDBFromPath(t *testing.T, dbPath string,
clock clock.Clock) Store {
return createStore(
t, db.NewTestSqliteDbHandleFromPath(t, dbPath).BaseDB, clock,
)
tDb := sqldb.NewTestSqliteDBFromPath(t, dbPath, db.LitdMigrationStreams)
return createStore(t, tDb.BaseDB, clock)
}

View file

@ -8,9 +8,11 @@ import (
"github.com/lightninglabs/lightning-terminal/accounts"
"github.com/lightninglabs/lightning-terminal/db"
"github.com/lightninglabs/lightning-terminal/db/sqlc"
"github.com/lightninglabs/lightning-terminal/firewalldb"
"github.com/lightninglabs/lightning-terminal/session"
"github.com/lightningnetwork/lnd/clock"
"github.com/lightningnetwork/lnd/sqldb/v2"
)
const (
@ -101,14 +103,41 @@ func NewStores(cfg *Config, clock clock.Clock) (*stores, error) {
return stores, err
}
sqlStore, err := db.NewSqliteStore(cfg.Sqlite)
// Until we have fully added support for sqldb/v2 in all of our
// stores, we need to use the db packages definition of the
// SQLite store for the packages that still haven't added
// support for sqldb/v2. This is only temporary and will be
// removed once all stores have been updated to use sqldb/v2.
legacySqlStore, err := db.NewSqliteStore(cfg.Sqlite)
sqlStore, err := sqldb.NewSqliteStore(&sqldb.SqliteConfig{
SkipMigrations: cfg.Sqlite.SkipMigrations,
SkipMigrationDbBackup: cfg.Sqlite.SkipMigrationDbBackup,
}, cfg.Sqlite.DatabaseFileName)
if err != nil {
return stores, err
}
acctStore := accounts.NewSQLStore(sqlStore.BaseDB, clock)
sessStore := session.NewSQLStore(sqlStore.BaseDB, clock)
firewallStore := firewalldb.NewSQLDB(sqlStore.BaseDB, clock)
if !cfg.Sqlite.SkipMigrations {
err = sqldb.ApplyAllMigrations(
sqlStore, db.LitdMigrationStreams,
)
if err != nil {
return stores, fmt.Errorf("error applying "+
"migrations to SQLite store: %w", err,
)
}
}
queries := sqlc.NewForType(sqlStore, sqlStore.BackendType)
acctStore := accounts.NewSQLStore(
sqlStore.BaseDB, queries, clock,
)
sessStore := session.NewSQLStore(legacySqlStore.BaseDB, clock)
firewallStore := firewalldb.NewSQLDB(
legacySqlStore.BaseDB, clock,
)
stores.accounts = acctStore
stores.sessions = sessStore
@ -116,14 +145,46 @@ func NewStores(cfg *Config, clock clock.Clock) (*stores, error) {
stores.closeFns["sqlite"] = sqlStore.BaseDB.Close
case DatabaseBackendPostgres:
sqlStore, err := db.NewPostgresStore(cfg.Postgres)
// Until we have fully added support for sqldb/v2 in all of our
// stores, we need to use the db packages definition of the
// Postgres store for the packages that still haven't added
// support for sqldb/v2. This is only temporary and will be
// removed once all stores have been updated to use sqldb/v2.
legacySqlStore, err := db.NewPostgresStore(cfg.Postgres)
sqlStore, err := sqldb.NewPostgresStore(&sqldb.PostgresConfig{
Dsn: cfg.Postgres.DSN(false),
MaxOpenConnections: cfg.Postgres.MaxOpenConnections,
MaxIdleConnections: cfg.Postgres.MaxIdleConnections,
ConnMaxLifetime: cfg.Postgres.ConnMaxLifetime,
ConnMaxIdleTime: cfg.Postgres.ConnMaxIdleTime,
RequireSSL: cfg.Postgres.RequireSSL,
SkipMigrations: cfg.Postgres.SkipMigrations,
})
if err != nil {
return stores, err
}
acctStore := accounts.NewSQLStore(sqlStore.BaseDB, clock)
sessStore := session.NewSQLStore(sqlStore.BaseDB, clock)
firewallStore := firewalldb.NewSQLDB(sqlStore.BaseDB, clock)
if !cfg.Postgres.SkipMigrations {
err = sqldb.ApplyAllMigrations(
sqlStore, db.LitdMigrationStreams,
)
if err != nil {
return stores, fmt.Errorf("error applying "+
"migrations to Postgres store: %w", err,
)
}
}
queries := sqlc.NewForType(sqlStore, sqlStore.BackendType)
acctStore := accounts.NewSQLStore(
sqlStore.BaseDB, queries, clock,
)
sessStore := session.NewSQLStore(legacySqlStore.BaseDB, clock)
firewallStore := firewalldb.NewSQLDB(
legacySqlStore.BaseDB, clock,
)
stores.accounts = acctStore
stores.sessions = sessStore

View file

@ -538,8 +538,6 @@ func TestFirewallDBMigration(t *testing.T) {
// the sql version of the kv stores that we'll create
// in test, without also needing to migrate it.
accountStore := accounts.NewTestDB(t, clock)
acctSQLStore, ok := accountStore.(*accounts.SQLStore)
require.True(t, ok)
sessionsStore := session.NewTestDBWithAccounts(
t, clock, accountStore,
@ -573,9 +571,12 @@ func TestFirewallDBMigration(t *testing.T) {
// Perform the migration.
err = txEx.ExecTx(ctx, sqldb.WriteTxOpt(),
func(tx SQLQueries) error {
qs, ok := tx.(*sqlc.Queries)
require.True(t, ok)
return MigrateFirewallDBToSQL(
ctx, firewallStore.DB, tx,
acctSQLStore, sessSQLStore,
ctx, firewallStore.DB, tx, qs,
sessSQLStore,
rootKeyStore.getAllRootKeys(),
)
},
@ -1557,6 +1558,8 @@ func actionWithMultipleAccounts(t *testing.T, ctx context.Context,
acctSqlStore, ok := acctStore.(*accounts.SQLStore)
require.True(t, ok)
queries := sqlc.NewForType(acctSqlStore, acctSqlStore.BackendType)
// To ensure that the two accounts do collide, we modify the alias
// of the second account to match the first 4 bytes of acct1's ID.
var newAcctAlias [8]byte
@ -1567,7 +1570,7 @@ func actionWithMultipleAccounts(t *testing.T, ctx context.Context,
acctAlias, err := newAcct2ID.ToInt64()
require.NoError(t, err)
_, err = acctSqlStore.UpdateAccountAliasForTests(
_, err = queries.UpdateAccountAliasForTests(
ctx, sqlc.UpdateAccountAliasForTestsParams{
Alias: acctAlias,
ID: acctID2,
@ -1610,6 +1613,8 @@ func actionWithSessionAndAccount(t *testing.T, ctx context.Context,
acctSqlStore, ok := acctStore.(*accounts.SQLStore)
require.True(t, ok)
queries := sqlc.NewForType(acctSqlStore, acctSqlStore.BackendType)
// Modify the first 4 bytes of the account alias to match the session
// ID, to ensure that they collide.
var newAcctAlias [8]byte
@ -1619,7 +1624,7 @@ func actionWithSessionAndAccount(t *testing.T, ctx context.Context,
acctAlias, err := accounts.AccountID(newAcctAlias).ToInt64()
require.NoError(t, err)
_, err = acctSqlStore.UpdateAccountAliasForTests(
_, err = queries.UpdateAccountAliasForTests(
ctx, sqlc.UpdateAccountAliasForTestsParams{
Alias: acctAlias,
ID: acctID,
@ -1676,6 +1681,8 @@ func actionWithSessionWithLinkedAccountAndAccount(t *testing.T,
acctSqlStore, ok := acctStore.(*accounts.SQLStore)
require.True(t, ok)
queries := sqlc.NewForType(acctSqlStore, acctSqlStore.BackendType)
// Modify the first 4 bytes of the second account alias to match the
// session ID, to ensure that they collide.
var newAcct2Alias [8]byte
@ -1685,7 +1692,7 @@ func actionWithSessionWithLinkedAccountAndAccount(t *testing.T,
acctAlias, err := accounts.AccountID(newAcct2Alias).ToInt64()
require.NoError(t, err)
_, err = acctSqlStore.UpdateAccountAliasForTests(
_, err = queries.UpdateAccountAliasForTests(
ctx, sqlc.UpdateAccountAliasForTestsParams{
Alias: acctAlias,
ID: acct2ID,
@ -1734,6 +1741,8 @@ func randomActions(t *testing.T, ctx context.Context, boltDB *BoltDB,
acctSqlStore, ok := acctStore.(*accounts.SQLStore)
require.True(t, ok)
queries := sqlc.NewForType(acctSqlStore, acctSqlStore.BackendType)
for i := 0; i < numActions; i++ {
rJson, err := randomJSON(rand.Intn(20))
require.NoError(t, err)
@ -1866,7 +1875,7 @@ func randomActions(t *testing.T, ctx context.Context, boltDB *BoltDB,
require.NoError(t, err)
// nolint:ll
_, err = acctSqlStore.UpdateAccountAliasForTests(
_, err = queries.UpdateAccountAliasForTests(
ctx, sqlc.UpdateAccountAliasForTestsParams{
Alias: acctAlias,
ID: acctID,
@ -2059,10 +2068,12 @@ func testAccountWithExpiry(t *testing.T, ctx context.Context,
acctSqlStore, ok := acctStore.(*accounts.SQLStore)
require.True(t, ok)
queries := sqlc.NewForType(acctSqlStore, acctSqlStore.BackendType)
aliasInt, err := acct.ID.ToInt64()
require.NoError(t, err)
acctSqlID, err := acctSqlStore.GetAccountIDByAlias(ctx, aliasInt)
acctSqlID, err := queries.GetAccountIDByAlias(ctx, aliasInt)
require.NoError(t, err)
return acct, acctSqlID
@ -2107,10 +2118,12 @@ func testSessionWithAccount(t *testing.T, ctx context.Context,
acctSqlStore, ok := acctStore.(*accounts.SQLStore)
require.True(t, ok)
queries := sqlc.NewForType(acctSqlStore, acctSqlStore.BackendType)
aliasInt, err := acct.ID.ToInt64()
require.NoError(t, err)
acctSqlID, err := acctSqlStore.GetAccountIDByAlias(ctx, aliasInt)
acctSqlID, err := queries.GetAccountIDByAlias(ctx, aliasInt)
require.NoError(t, err)
return sess, acct, acctSqlID