accounts: add database backend benchmark test

This commit is contained in:
Viktor Torstensson 2026-02-11 23:46:40 +01:00
parent f90f468a32
commit fe28ff7e60
No known key found for this signature in database
GPG key ID: 961CC8259AE675D4

405
accounts/benchmark_test.go Normal file
View file

@ -0,0 +1,405 @@
package accounts
import (
"context"
"encoding/binary"
"path/filepath"
"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/v2"
"github.com/stretchr/testify/require"
)
// NOTE: Set this to true if you also want to benchmark with a postgres backend.
var benchPostgres = false
// BenchmarkAccountStore benchmarks common account store operations across
// supported DB backends.
func BenchmarkAccountStore(b *testing.B) {
for _, backend := range accountBenchBackends(b) {
backend := backend
b.Run(backend.name, func(b *testing.B) {
runAccountBench(
b, backend, "NewAccount", benchmarkAccountNew,
)
runAccountBench(
b, backend, "UpdateBalance",
benchmarkAccountUpdateBalance,
)
runAccountBench(
b, backend, "AddInvoice",
benchmarkAccountAddInvoice,
)
runAccountBench(
b, backend, "CreditAccount",
benchmarkAccountCredit,
)
runAccountBench(
b, backend, "DebitAccount",
benchmarkAccountDebit,
)
runAccountBench(
b, backend, "UpsertPayment",
benchmarkAccountUpsertPayment,
)
runAccountBench(
b, backend, "DeletePayment",
benchmarkAccountDeletePayment,
)
runAccountBench(
b, backend, "RemoveAccount",
benchmarkAccountRemove,
)
runAccountBench(
b, backend, "GetAccount",
benchmarkAccountGet,
)
runAccountBench(
b, backend, "ListAccounts",
benchmarkAccountList,
)
runAccountBench(
b, backend, "StoreLastIndexes",
benchmarkAccountStoreLastIndexes,
)
runAccountBench(
b, backend, "LastIndexes",
benchmarkAccountLastIndexes,
)
})
}
}
type accountBenchBackend struct {
name string
newStore func(b *testing.B) Store
}
func runAccountBench(b *testing.B, backend accountBenchBackend, name string,
fn func(b *testing.B, store Store)) {
b.Run(name, func(b *testing.B) {
store := backend.newStore(b)
b.Cleanup(func() {
require.NoError(b, store.Close())
})
fn(b, store)
})
}
func accountBenchBackends(b *testing.B) []accountBenchBackend {
backends := []accountBenchBackend{
{
name: "kvdb-bbolt",
newStore: func(b *testing.B) Store {
clk := clock.NewTestClock(time.Now())
dir := b.TempDir()
store, err := NewBoltStore(dir, DBFilename, clk)
require.NoError(b, err)
return store
},
},
{
name: "native-sqlite",
newStore: func(b *testing.B) Store {
clk := clock.NewTestClock(time.Now())
dbFile := filepath.Join(
b.TempDir(), "accounts.sqlite",
)
sqlStore, err := sqldb.NewSqliteStore(
&sqldb.SqliteConfig{
SkipMigrations: false,
},
dbFile,
)
require.NoError(b, err)
require.NoError(
b,
sqldb.ApplyAllMigrations(
sqlStore,
db.MakeTestMigrationStreams(),
),
)
queries := sqlc.NewForType(
sqlStore.BaseDB,
sqlStore.BaseDB.BackendType,
)
return NewSQLStore(
sqlStore.BaseDB, queries, clk,
)
},
},
}
if benchPostgres {
backends = append(backends, accountBenchBackend{
name: "native-postgres",
newStore: func(b *testing.B) Store {
clk := clock.NewTestClock(time.Now())
fixture := sqldb.NewTestPgFixture(
b, db.DefaultPostgresFixtureLifetime,
)
b.Cleanup(func() {
fixture.TearDown(b)
})
sqlStore := sqldb.NewTestPostgresDB(
b, fixture,
db.MakeTestMigrationStreams(),
)
queries := sqlc.NewForType(
sqlStore.BaseDB,
sqlStore.BaseDB.BackendType,
)
return NewSQLStore(
sqlStore.BaseDB, queries, clk,
)
},
})
}
return backends
}
// benchmarkAccountNew measures account creation performance.
func benchmarkAccountNew(b *testing.B, store Store) {
ctx := context.Background()
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, err := store.NewAccount(
ctx, 1000, time.Time{}, "",
)
require.NoError(b, err)
}
}
// benchmarkAccountUpdateBalance measures balance update performance.
func benchmarkAccountUpdateBalance(b *testing.B, store Store) {
ctx := context.Background()
acct, err := store.NewAccount(ctx, 1000, time.Time{}, "")
require.NoError(b, err)
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
newBalance := fn.Some(int64(i))
err := store.UpdateAccount(
ctx, acct.ID, newBalance, fn.None[time.Time](),
fn.None[string](),
)
require.NoError(b, err)
}
}
// benchmarkAccountAddInvoice measures invoice association performance.
func benchmarkAccountAddInvoice(b *testing.B, store Store) {
ctx := context.Background()
acct, err := store.NewAccount(ctx, 1000, time.Time{}, "")
require.NoError(b, err)
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
var hash lntypes.Hash
binary.BigEndian.PutUint64(hash[:], uint64(i))
err := store.AddAccountInvoice(ctx, acct.ID, hash)
require.NoError(b, err)
}
}
// benchmarkAccountUpsertPayment measures payment upsert performance.
func benchmarkAccountUpsertPayment(b *testing.B, store Store) {
ctx := context.Background()
acct, err := store.NewAccount(ctx, 1000, time.Time{}, "")
require.NoError(b, err)
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
var hash lntypes.Hash
binary.BigEndian.PutUint64(hash[:], uint64(i))
_, err := store.UpsertAccountPayment(
ctx, acct.ID, hash, lnwire.MilliSatoshi(1000),
lnrpc.Payment_SUCCEEDED,
)
require.NoError(b, err)
}
}
// benchmarkAccountDeletePayment measures payment delete performance.
func benchmarkAccountDeletePayment(b *testing.B, store Store) {
ctx := context.Background()
acct, err := store.NewAccount(ctx, 1000, time.Time{}, "")
require.NoError(b, err)
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
var hash lntypes.Hash
binary.BigEndian.PutUint64(hash[:], uint64(i))
// As this function should benchmark the DeleteAccountPayment
// function, ensure that the timer isn't running when
// UpsertAccountPayment is executed.
b.StopTimer()
_, err := store.UpsertAccountPayment(
ctx, acct.ID, hash, lnwire.MilliSatoshi(1000),
lnrpc.Payment_SUCCEEDED,
)
require.NoError(b, err)
b.StartTimer()
err = store.DeleteAccountPayment(ctx, acct.ID, hash)
require.NoError(b, err)
}
}
// benchmarkAccountCredit measures credit performance.
func benchmarkAccountCredit(b *testing.B, store Store) {
ctx := context.Background()
acct, err := store.NewAccount(ctx, 1000, time.Time{}, "")
require.NoError(b, err)
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
err := store.CreditAccount(
ctx, acct.ID, lnwire.MilliSatoshi(1),
)
require.NoError(b, err)
}
}
// benchmarkAccountDebit measures debit performance.
func benchmarkAccountDebit(b *testing.B, store Store) {
ctx := context.Background()
acct, err := store.NewAccount(ctx, 1000000, time.Time{}, "")
require.NoError(b, err)
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
err := store.DebitAccount(
ctx, acct.ID, lnwire.MilliSatoshi(1),
)
require.NoError(b, err)
}
}
// benchmarkAccountGet measures account lookup performance.
func benchmarkAccountGet(b *testing.B, store Store) {
ctx := context.Background()
acct, err := store.NewAccount(ctx, 1000, time.Time{}, "")
require.NoError(b, err)
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, err := store.Account(ctx, acct.ID)
require.NoError(b, err)
}
}
// benchmarkAccountRemove measures account deletion performance.
func benchmarkAccountRemove(b *testing.B, store Store) {
ctx := context.Background()
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
// As this function should benchmark the RemoveAccount function,
// ensure that the timer isn't running when NewAccount is
// executed.
b.StopTimer()
acct, err := store.NewAccount(
ctx, 1000, time.Time{}, "",
)
require.NoError(b, err)
b.StartTimer()
err = store.RemoveAccount(ctx, acct.ID)
require.NoError(b, err)
}
}
// benchmarkAccountList measures listing performance with seeded data.
func benchmarkAccountList(b *testing.B, store Store) {
ctx := context.Background()
const seedCount = 100
for i := 0; i < seedCount; i++ {
_, err := store.NewAccount(
ctx, 1000, time.Time{}, "",
)
require.NoError(b, err)
}
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, err := store.Accounts(ctx)
require.NoError(b, err)
}
}
// benchmarkAccountStoreLastIndexes measures write performance.
func benchmarkAccountStoreLastIndexes(b *testing.B, store Store) {
ctx := context.Background()
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
err := store.StoreLastIndexes(
ctx, uint64(i), uint64(i+1),
)
require.NoError(b, err)
}
}
// benchmarkAccountLastIndexes measures read performance.
func benchmarkAccountLastIndexes(b *testing.B, store Store) {
ctx := context.Background()
require.NoError(b, store.StoreLastIndexes(ctx, 1, 2))
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, _, err := store.LastIndexes(ctx)
require.NoError(b, err)
}
}