From fb5b0af8d8f79cceeebd416a0ad0eca6c048dd9e Mon Sep 17 00:00:00 2001 From: Viktor Torstensson Date: Mon, 16 Feb 2026 20:58:33 +0100 Subject: [PATCH] accounts+sqlc: improve ListAccounts for SQL store Optimize SQL account listing by preloading linked invoices and payments for all accounts in bulk. Before this change, Accounts() queried ListAllAccounts and then did two extra queries per account (ListAccountInvoices/ListAccountPayments), which scales poorly as account count grows. Add ListAllAccountInvoices and ListAllAccountPayments queries, group their rows by account_id in memory, and marshal each account from the preloaded data. Keep conversion logic shared through marshalDBAccountWithLinkedData to preserve behavior between single-account and list-account paths. This reduces query count from 1 + 2N to 3 and improves list-path performance without changing external semantics. --- accounts/store_sql.go | 57 +++++++++++++++++++++++++++------ db/sqlc/accounts.sql.go | 61 ++++++++++++++++++++++++++++++++++++ db/sqlc/querier.go | 2 ++ db/sqlc/queries/accounts.sql | 8 +++++ 4 files changed, 118 insertions(+), 10 deletions(-) diff --git a/accounts/store_sql.go b/accounts/store_sql.go index 3e711a08..dba24dbf 100644 --- a/accounts/store_sql.go +++ b/accounts/store_sql.go @@ -44,6 +44,8 @@ type SQLQueries interface { GetAccountIndex(ctx context.Context, name string) (int64, error) GetAccountPayment(ctx context.Context, arg sqlc.GetAccountPaymentParams) (sqlc.AccountPayment, error) InsertAccount(ctx context.Context, arg sqlc.InsertAccountParams) (int64, error) + ListAllAccountInvoices(ctx context.Context) ([]sqlc.AccountInvoice, error) + ListAllAccountPayments(ctx context.Context) ([]sqlc.AccountPayment, error) ListAccountInvoices(ctx context.Context, id int64) ([]sqlc.AccountInvoice, error) ListAccountPayments(ctx context.Context, id int64) ([]sqlc.AccountPayment, error) ListAllAccounts(ctx context.Context) ([]sqlc.Account, error) @@ -200,6 +202,23 @@ func getAndMarshalAccount(ctx context.Context, db SQLQueries, id int64) ( func marshalDBAccount(ctx context.Context, db SQLQueries, dbAcct sqlc.Account) (*OffChainBalanceAccount, error) { + invoices, err := db.ListAccountInvoices(ctx, dbAcct.ID) + if err != nil { + return nil, err + } + + payments, err := db.ListAccountPayments(ctx, dbAcct.ID) + if err != nil { + return nil, err + } + + return marshalDBAccountWithLinkedData(dbAcct, invoices, payments) +} + +func marshalDBAccountWithLinkedData(dbAcct sqlc.Account, + invoices []sqlc.AccountInvoice, + payments []sqlc.AccountPayment) (*OffChainBalanceAccount, error) { + alias, err := AccountIDFromInt64(dbAcct.Alias) if err != nil { return nil, err @@ -217,21 +236,12 @@ func marshalDBAccount(ctx context.Context, db SQLQueries, Label: dbAcct.Label.String, } - invoices, err := db.ListAccountInvoices(ctx, dbAcct.ID) - if err != nil { - return nil, err - } for _, invoice := range invoices { var hash lntypes.Hash copy(hash[:], invoice.Hash) account.Invoices[hash] = struct{}{} } - payments, err := db.ListAccountPayments(ctx, dbAcct.ID) - if err != nil { - return nil, err - } - for _, payment := range payments { var hash lntypes.Hash copy(hash[:], payment.Hash) @@ -560,9 +570,36 @@ func (s *SQLStore) Accounts(ctx context.Context) ([]*OffChainBalanceAccount, return err } + dbInvoices, err := db.ListAllAccountInvoices(ctx) + if err != nil { + return err + } + + dbPayments, err := db.ListAllAccountPayments(ctx) + if err != nil { + return err + } + + accountInvoices := make(map[int64][]sqlc.AccountInvoice) + for _, invoice := range dbInvoices { + accountInvoices[invoice.AccountID] = append( + accountInvoices[invoice.AccountID], invoice, + ) + } + + accountPayments := make(map[int64][]sqlc.AccountPayment) + for _, payment := range dbPayments { + accountPayments[payment.AccountID] = append( + accountPayments[payment.AccountID], payment, + ) + } + accounts = make([]*OffChainBalanceAccount, len(dbAccounts)) for i, dbAccount := range dbAccounts { - account, err := marshalDBAccount(ctx, db, dbAccount) + account, err := marshalDBAccountWithLinkedData( + dbAccount, accountInvoices[dbAccount.ID], + accountPayments[dbAccount.ID], + ) if err != nil { return err } diff --git a/db/sqlc/accounts.sql.go b/db/sqlc/accounts.sql.go index 42f06010..6322e709 100644 --- a/db/sqlc/accounts.sql.go +++ b/db/sqlc/accounts.sql.go @@ -258,6 +258,67 @@ func (q *Queries) ListAccountPayments(ctx context.Context, accountID int64) ([]A return items, nil } +const listAllAccountInvoices = `-- name: ListAllAccountInvoices :many +SELECT account_id, hash +FROM account_invoices +` + +func (q *Queries) ListAllAccountInvoices(ctx context.Context) ([]AccountInvoice, error) { + rows, err := q.db.QueryContext(ctx, listAllAccountInvoices) + if err != nil { + return nil, err + } + defer rows.Close() + var items []AccountInvoice + for rows.Next() { + var i AccountInvoice + if err := rows.Scan(&i.AccountID, &i.Hash); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const listAllAccountPayments = `-- name: ListAllAccountPayments :many +SELECT account_id, hash, status, full_amount_msat +FROM account_payments +` + +func (q *Queries) ListAllAccountPayments(ctx context.Context) ([]AccountPayment, error) { + rows, err := q.db.QueryContext(ctx, listAllAccountPayments) + if err != nil { + return nil, err + } + defer rows.Close() + var items []AccountPayment + for rows.Next() { + var i AccountPayment + if err := rows.Scan( + &i.AccountID, + &i.Hash, + &i.Status, + &i.FullAmountMsat, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + const listAllAccounts = `-- name: ListAllAccounts :many SELECT id, alias, label, type, initial_balance_msat, current_balance_msat, last_updated, expiration FROM accounts diff --git a/db/sqlc/querier.go b/db/sqlc/querier.go index 2b2234ce..4b32259f 100644 --- a/db/sqlc/querier.go +++ b/db/sqlc/querier.go @@ -58,6 +58,8 @@ type Querier interface { InsertSessionPrivacyFlag(ctx context.Context, arg InsertSessionPrivacyFlagParams) error ListAccountInvoices(ctx context.Context, accountID int64) ([]AccountInvoice, error) ListAccountPayments(ctx context.Context, accountID int64) ([]AccountPayment, error) + ListAllAccountInvoices(ctx context.Context) ([]AccountInvoice, error) + ListAllAccountPayments(ctx context.Context) ([]AccountPayment, error) ListAllAccounts(ctx context.Context) ([]Account, error) ListAllKVStoresRecords(ctx context.Context) ([]Kvstore, error) ListSessions(ctx context.Context) ([]Session, error) diff --git a/db/sqlc/queries/accounts.sql b/db/sqlc/queries/accounts.sql index e2490b5e..4fff012a 100644 --- a/db/sqlc/queries/accounts.sql +++ b/db/sqlc/queries/accounts.sql @@ -76,11 +76,19 @@ SELECT * FROM account_payments WHERE account_id = $1; +-- name: ListAllAccountPayments :many +SELECT * +FROM account_payments; + -- name: ListAccountInvoices :many SELECT * FROM account_invoices WHERE account_id = $1; +-- name: ListAllAccountInvoices :many +SELECT * +FROM account_invoices; + -- name: GetAccountInvoice :one SELECT * FROM account_invoices