mirror of
https://github.com/lightninglabs/lightning-terminal.git
synced 2026-08-13 12:33:36 +02:00
562 lines
12 KiB
Go
562 lines
12 KiB
Go
package firewalldb
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"path/filepath"
|
|
"strconv"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/lightninglabs/lightning-terminal/accounts"
|
|
"github.com/lightninglabs/lightning-terminal/db"
|
|
"github.com/lightninglabs/lightning-terminal/db/sqlc"
|
|
"github.com/lightninglabs/lightning-terminal/session"
|
|
"github.com/lightningnetwork/lnd/clock"
|
|
"github.com/lightningnetwork/lnd/sqldb/v2"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
// NOTE: Set this to true if also you want to benchmark with a postgres backend.
|
|
var benchPostgres = false
|
|
|
|
// BenchmarkFirewallDB benchmarks common firewall DB operations across
|
|
// supported DB backends.
|
|
func BenchmarkFirewallDB(b *testing.B) {
|
|
for _, backend := range firewallBenchBackends(b) {
|
|
backend := backend
|
|
b.Run(backend.name, func(b *testing.B) {
|
|
runFirewallBench(
|
|
b, backend, "AddAction",
|
|
benchmarkAddAction,
|
|
)
|
|
runFirewallBench(
|
|
b, backend, "SetActionState",
|
|
benchmarkSetActionState,
|
|
)
|
|
runFirewallBench(
|
|
b, backend, "ListActions",
|
|
benchmarkListActions,
|
|
)
|
|
runFirewallBench(
|
|
b, backend, "KVStoreSet",
|
|
benchmarkKVStoreSet,
|
|
)
|
|
runFirewallBench(
|
|
b, backend, "KVStoreGet",
|
|
benchmarkKVStoreGet,
|
|
)
|
|
runFirewallBench(
|
|
b, backend, "KVStoreDel",
|
|
benchmarkKVStoreDel,
|
|
)
|
|
runFirewallBench(
|
|
b, backend, "DeleteTempKVStores",
|
|
benchmarkDeleteTempKVStores,
|
|
)
|
|
runFirewallBench(
|
|
b, backend, "PrivacyNewPair",
|
|
benchmarkPrivacyNewPair,
|
|
)
|
|
runFirewallBench(
|
|
b, backend, "PrivacyRealToPseudo",
|
|
benchmarkPrivacyRealToPseudo,
|
|
)
|
|
runFirewallBench(
|
|
b, backend, "PrivacyPseudoToReal",
|
|
benchmarkPrivacyPseudoToReal,
|
|
)
|
|
runFirewallBench(
|
|
b, backend, "PrivacyFetchAllPairs",
|
|
benchmarkPrivacyFetchAllPairs,
|
|
)
|
|
})
|
|
}
|
|
}
|
|
|
|
type firewallBenchBackend struct {
|
|
name string
|
|
newStores func(b *testing.B) (FirewallDBs, session.Store, func())
|
|
}
|
|
|
|
func runFirewallBench(b *testing.B, backend firewallBenchBackend, name string,
|
|
fn func(b *testing.B, store FirewallDBs, sessStore session.Store)) {
|
|
|
|
b.Run(name, func(b *testing.B) {
|
|
store, sessStore, cleanup := backend.newStores(b)
|
|
b.Cleanup(cleanup)
|
|
|
|
fn(b, store, sessStore)
|
|
})
|
|
}
|
|
|
|
func firewallBenchBackends(b *testing.B) []firewallBenchBackend {
|
|
backends := []firewallBenchBackend{
|
|
{
|
|
name: "kvdb-bbolt",
|
|
newStores: func(b *testing.B) (FirewallDBs,
|
|
session.Store, func()) {
|
|
|
|
clk := clock.NewTestClock(time.Now())
|
|
dir := b.TempDir()
|
|
|
|
acctStore, err := accounts.NewBoltStore(
|
|
dir, accounts.DBFilename, clk,
|
|
)
|
|
require.NoError(b, err)
|
|
|
|
sessStore, err := session.NewDB(
|
|
dir, session.DBFilename, clk, acctStore,
|
|
)
|
|
require.NoError(b, err)
|
|
|
|
firewallStore, err := NewBoltDB(
|
|
dir, DBFilename, sessStore, acctStore,
|
|
clk,
|
|
)
|
|
require.NoError(b, err)
|
|
|
|
cleanup := func() {
|
|
require.NoError(
|
|
b, firewallStore.Close(),
|
|
)
|
|
require.NoError(b, sessStore.Close())
|
|
require.NoError(b, acctStore.Close())
|
|
}
|
|
|
|
return firewallStore, sessStore, cleanup
|
|
},
|
|
},
|
|
{
|
|
name: "native-sqlite",
|
|
newStores: func(b *testing.B) (FirewallDBs,
|
|
session.Store, func()) {
|
|
|
|
clk := clock.NewTestClock(time.Now())
|
|
dbFile := filepath.Join(
|
|
b.TempDir(), "litd.sqlite",
|
|
)
|
|
|
|
sqlStore, err := sqldb.NewSqliteStore(
|
|
&sqldb.SqliteConfig{
|
|
SkipMigrations: false,
|
|
},
|
|
dbFile,
|
|
)
|
|
require.NoError(b, err)
|
|
|
|
require.NoError(
|
|
b,
|
|
sqldb.ApplyAllMigrations(
|
|
sqlStore,
|
|
db.MakeTestMigrationSets(),
|
|
),
|
|
)
|
|
|
|
queries := sqlc.NewForType(
|
|
sqlStore.BaseDB,
|
|
sqlStore.BaseDB.BackendType,
|
|
)
|
|
sessStore := session.NewSQLStore(
|
|
sqlStore.BaseDB, queries, clk,
|
|
)
|
|
firewallStore := NewSQLDB(
|
|
sqlStore.BaseDB, queries, clk,
|
|
)
|
|
|
|
cleanup := func() {
|
|
require.NoError(
|
|
b, firewallStore.Close(),
|
|
)
|
|
}
|
|
|
|
return firewallStore, sessStore, cleanup
|
|
},
|
|
},
|
|
}
|
|
|
|
if benchPostgres {
|
|
backends = append(backends, firewallBenchBackend{
|
|
name: "native-postgres",
|
|
newStores: func(b *testing.B) (FirewallDBs,
|
|
session.Store, func()) {
|
|
|
|
clk := clock.NewTestClock(time.Now())
|
|
fixture := sqldb.NewTestPgFixture(
|
|
b, db.DefaultPostgresFixtureLifetime,
|
|
)
|
|
b.Cleanup(func() {
|
|
fixture.TearDown(b)
|
|
})
|
|
|
|
sqlStore := sqldb.NewTestPostgresDB(
|
|
b, fixture,
|
|
db.MakeTestMigrationSets(),
|
|
)
|
|
|
|
queries := sqlc.NewForType(
|
|
sqlStore.BaseDB,
|
|
sqlStore.BaseDB.BackendType,
|
|
)
|
|
sessStore := session.NewSQLStore(
|
|
sqlStore.BaseDB, queries, clk,
|
|
)
|
|
firewallStore := NewSQLDB(
|
|
sqlStore.BaseDB, queries, clk,
|
|
)
|
|
|
|
cleanup := func() {
|
|
require.NoError(
|
|
b, firewallStore.Close(),
|
|
)
|
|
}
|
|
|
|
return firewallStore, sessStore, cleanup
|
|
},
|
|
})
|
|
}
|
|
|
|
return backends
|
|
}
|
|
|
|
// benchmarkAddAction measures action insertion performance.
|
|
func benchmarkAddAction(b *testing.B, store FirewallDBs,
|
|
_ session.Store) {
|
|
|
|
ctx := context.Background()
|
|
|
|
b.ReportAllocs()
|
|
b.ResetTimer()
|
|
|
|
for i := 0; i < b.N; i++ {
|
|
_, err := store.AddAction(ctx, &AddActionReq{
|
|
RPCMethod: "/lnrpc.Lightning/GetInfo",
|
|
})
|
|
require.NoError(b, err)
|
|
}
|
|
}
|
|
|
|
// benchmarkSetActionState measures state update performance.
|
|
func benchmarkSetActionState(b *testing.B, store FirewallDBs,
|
|
_ session.Store) {
|
|
|
|
ctx := context.Background()
|
|
|
|
b.ReportAllocs()
|
|
b.ResetTimer()
|
|
|
|
for i := 0; i < b.N; i++ {
|
|
b.StopTimer()
|
|
locator, err := store.AddAction(ctx, &AddActionReq{
|
|
RPCMethod: "/lnrpc.Lightning/GetInfo",
|
|
})
|
|
require.NoError(b, err)
|
|
b.StartTimer()
|
|
|
|
err = store.SetActionState(
|
|
ctx, locator, ActionStateDone, "",
|
|
)
|
|
require.NoError(b, err)
|
|
}
|
|
}
|
|
|
|
// benchmarkListActions measures list performance with seeded data.
|
|
func benchmarkListActions(b *testing.B, store FirewallDBs,
|
|
_ session.Store) {
|
|
|
|
ctx := context.Background()
|
|
|
|
const seedCount = 100
|
|
for i := 0; i < seedCount; i++ {
|
|
_, err := store.AddAction(ctx, &AddActionReq{
|
|
RPCMethod: "/lnrpc.Lightning/GetInfo",
|
|
})
|
|
require.NoError(b, err)
|
|
}
|
|
|
|
query := &ListActionsQuery{
|
|
CountAll: true,
|
|
}
|
|
|
|
b.ReportAllocs()
|
|
b.ResetTimer()
|
|
|
|
for i := 0; i < b.N; i++ {
|
|
_, _, _, err := store.ListActions(ctx, query)
|
|
require.NoError(b, err)
|
|
}
|
|
}
|
|
|
|
// benchmarkKVStoreSet measures KV store Set performance.
|
|
func benchmarkKVStoreSet(b *testing.B, store FirewallDBs,
|
|
_ session.Store) {
|
|
|
|
ctx := context.Background()
|
|
groupID := session.ID{1, 2, 3, 4}
|
|
|
|
kvStores := store.GetKVStores("bench", groupID, "feature")
|
|
|
|
b.ReportAllocs()
|
|
b.ResetTimer()
|
|
|
|
for i := 0; i < b.N; i++ {
|
|
key := strconv.Itoa(i)
|
|
value := []byte("value")
|
|
|
|
err := kvStores.Update(ctx, func(ctx context.Context,
|
|
tx KVStoreTx) error {
|
|
|
|
return tx.Global().Set(ctx, key, value)
|
|
})
|
|
require.NoError(b, err)
|
|
}
|
|
}
|
|
|
|
// benchmarkKVStoreGet measures KV store Get performance.
|
|
func benchmarkKVStoreGet(b *testing.B, store FirewallDBs,
|
|
_ session.Store) {
|
|
|
|
ctx := context.Background()
|
|
groupID := session.ID{1, 2, 3, 4}
|
|
|
|
kvStores := store.GetKVStores("bench", groupID, "feature")
|
|
|
|
err := kvStores.Update(ctx, func(ctx context.Context,
|
|
tx KVStoreTx) error {
|
|
|
|
return tx.Global().Set(ctx, "key", []byte("value"))
|
|
})
|
|
require.NoError(b, err)
|
|
|
|
b.ReportAllocs()
|
|
b.ResetTimer()
|
|
|
|
for i := 0; i < b.N; i++ {
|
|
err := kvStores.View(ctx, func(ctx context.Context,
|
|
tx KVStoreTx) error {
|
|
|
|
_, err := tx.Global().Get(ctx, "key")
|
|
return err
|
|
})
|
|
require.NoError(b, err)
|
|
}
|
|
}
|
|
|
|
// benchmarkKVStoreDel measures KV store Del performance.
|
|
func benchmarkKVStoreDel(b *testing.B, store FirewallDBs,
|
|
_ session.Store) {
|
|
|
|
ctx := context.Background()
|
|
groupID := session.ID{1, 2, 3, 4}
|
|
|
|
kvStores := store.GetKVStores("bench", groupID, "feature")
|
|
|
|
const keyCount = 128
|
|
keys := make([]string, keyCount)
|
|
for i := 0; i < keyCount; i++ {
|
|
keys[i] = fmt.Sprintf("key-%d", i)
|
|
}
|
|
|
|
reseed := func() {
|
|
for _, key := range keys {
|
|
err := kvStores.Update(ctx,
|
|
func(ctx context.Context, tx KVStoreTx) error {
|
|
return tx.Global().Set(
|
|
ctx, key, []byte("value"),
|
|
)
|
|
},
|
|
)
|
|
require.NoError(b, err)
|
|
}
|
|
}
|
|
|
|
reseed()
|
|
|
|
b.ReportAllocs()
|
|
b.ResetTimer()
|
|
|
|
for i := 0; i < b.N; i++ {
|
|
key := keys[i%keyCount]
|
|
err := kvStores.Update(ctx, func(ctx context.Context,
|
|
tx KVStoreTx) error {
|
|
|
|
return tx.Global().Del(ctx, key)
|
|
})
|
|
require.NoError(b, err)
|
|
|
|
if i%keyCount == keyCount-1 {
|
|
b.StopTimer()
|
|
reseed()
|
|
b.StartTimer()
|
|
}
|
|
}
|
|
}
|
|
|
|
// benchmarkDeleteTempKVStores measures temp kv store cleanup.
|
|
func benchmarkDeleteTempKVStores(b *testing.B, store FirewallDBs,
|
|
_ session.Store) {
|
|
|
|
ctx := context.Background()
|
|
groupID := session.ID{9, 9, 9, 9}
|
|
|
|
kvStores := store.GetKVStores("bench", groupID, "feature")
|
|
|
|
const seedCount = 100
|
|
|
|
b.ReportAllocs()
|
|
b.ResetTimer()
|
|
|
|
for i := 0; i < b.N; i++ {
|
|
b.StopTimer()
|
|
for j := 0; j < seedCount; j++ {
|
|
key := fmt.Sprintf("tmp-%d", j)
|
|
|
|
err := kvStores.Update(ctx,
|
|
func(ctx context.Context, tx KVStoreTx) error {
|
|
return tx.GlobalTemp().Set(
|
|
ctx, key, []byte("v"),
|
|
)
|
|
},
|
|
)
|
|
require.NoError(b, err)
|
|
}
|
|
b.StartTimer()
|
|
|
|
err := store.DeleteTempKVStores(ctx)
|
|
require.NoError(b, err)
|
|
}
|
|
}
|
|
|
|
// benchmarkPrivacyNewPair measures privacy mapper inserts.
|
|
func benchmarkPrivacyNewPair(b *testing.B, store FirewallDBs,
|
|
sessStore session.Store) {
|
|
|
|
ctx := context.Background()
|
|
groupID := newPrivacyGroupID(ctx, b, sessStore)
|
|
|
|
privacyDB := store.PrivacyDB(groupID)
|
|
|
|
b.ReportAllocs()
|
|
b.ResetTimer()
|
|
|
|
for i := 0; i < b.N; i++ {
|
|
real := fmt.Sprintf("real-%d", i)
|
|
pseudo := fmt.Sprintf("pseudo-%d", i)
|
|
|
|
err := privacyDB.Update(ctx,
|
|
func(ctx context.Context, tx PrivacyMapTx) error {
|
|
return tx.NewPair(ctx, real, pseudo)
|
|
},
|
|
)
|
|
require.NoError(b, err)
|
|
}
|
|
}
|
|
|
|
// benchmarkPrivacyRealToPseudo measures privacy mapper lookups by real value.
|
|
func benchmarkPrivacyRealToPseudo(b *testing.B, store FirewallDBs,
|
|
sessStore session.Store) {
|
|
|
|
ctx := context.Background()
|
|
groupID := newPrivacyGroupID(ctx, b, sessStore)
|
|
|
|
privacyDB := store.PrivacyDB(groupID)
|
|
|
|
err := privacyDB.Update(ctx,
|
|
func(ctx context.Context, tx PrivacyMapTx) error {
|
|
return tx.NewPair(ctx, "real", "pseudo")
|
|
},
|
|
)
|
|
require.NoError(b, err)
|
|
|
|
b.ReportAllocs()
|
|
b.ResetTimer()
|
|
|
|
for i := 0; i < b.N; i++ {
|
|
err := privacyDB.View(ctx,
|
|
func(ctx context.Context, tx PrivacyMapTx) error {
|
|
_, err := tx.RealToPseudo(ctx, "real")
|
|
return err
|
|
},
|
|
)
|
|
require.NoError(b, err)
|
|
}
|
|
}
|
|
|
|
// benchmarkPrivacyPseudoToReal measures privacy mapper lookups by pseudo.
|
|
func benchmarkPrivacyPseudoToReal(b *testing.B, store FirewallDBs,
|
|
sessStore session.Store) {
|
|
|
|
ctx := context.Background()
|
|
groupID := newPrivacyGroupID(ctx, b, sessStore)
|
|
|
|
privacyDB := store.PrivacyDB(groupID)
|
|
|
|
err := privacyDB.Update(ctx,
|
|
func(ctx context.Context, tx PrivacyMapTx) error {
|
|
return tx.NewPair(ctx, "real", "pseudo")
|
|
},
|
|
)
|
|
require.NoError(b, err)
|
|
|
|
b.ReportAllocs()
|
|
b.ResetTimer()
|
|
|
|
for i := 0; i < b.N; i++ {
|
|
err := privacyDB.View(ctx,
|
|
func(ctx context.Context, tx PrivacyMapTx) error {
|
|
_, err := tx.PseudoToReal(ctx, "pseudo")
|
|
return err
|
|
},
|
|
)
|
|
require.NoError(b, err)
|
|
}
|
|
}
|
|
|
|
// benchmarkPrivacyFetchAllPairs measures privacy mapper list performance.
|
|
func benchmarkPrivacyFetchAllPairs(b *testing.B, store FirewallDBs,
|
|
sessStore session.Store) {
|
|
|
|
ctx := context.Background()
|
|
groupID := newPrivacyGroupID(ctx, b, sessStore)
|
|
|
|
privacyDB := store.PrivacyDB(groupID)
|
|
|
|
const seedCount = 100
|
|
for i := 0; i < seedCount; i++ {
|
|
real := fmt.Sprintf("real-%d", i)
|
|
pseudo := fmt.Sprintf("pseudo-%d", i)
|
|
|
|
err := privacyDB.Update(ctx,
|
|
func(ctx context.Context, tx PrivacyMapTx) error {
|
|
return tx.NewPair(ctx, real, pseudo)
|
|
},
|
|
)
|
|
require.NoError(b, err)
|
|
}
|
|
|
|
b.ReportAllocs()
|
|
b.ResetTimer()
|
|
|
|
for i := 0; i < b.N; i++ {
|
|
err := privacyDB.View(ctx,
|
|
func(ctx context.Context, tx PrivacyMapTx) error {
|
|
_, err := tx.FetchAllPairs(ctx)
|
|
return err
|
|
},
|
|
)
|
|
require.NoError(b, err)
|
|
}
|
|
}
|
|
|
|
func newPrivacyGroupID(ctx context.Context, b *testing.B,
|
|
sessStore session.Store) session.ID {
|
|
|
|
expiry := time.Now().Add(time.Hour)
|
|
sess, err := sessStore.NewSession(
|
|
ctx, "bench", session.TypeMacaroonAdmin, expiry,
|
|
"mailbox.test",
|
|
)
|
|
require.NoError(b, err)
|
|
|
|
return sess.GroupID
|
|
}
|