sqldb+payments: add payment_duplicates for legacy duplicate payments
Older LND versions could create multiple payments for the same hash. We need to preserve those historical records during KV→SQL migration, but they don’t fit the normal payment schema because we enforce a unique payment hash constraint. Introduce a lean payment_duplicates table to store only the essential fields (identifier, amount, timestamps, settle/fail data). This keeps the primary payment records stable and makes the migration deterministic even when duplicate records lack attempt info. The table is intentionally minimal and can be dropped after migration if no duplicate payments exist. For now there is no logic in place which allows the noderunner to fetch duplicate payments after the migration.
This commit is contained in:
7 files changed
+195
-1
No files matched your search
@@ -479,6 +479,53 @@ func (q *Queries) FetchPayment(ctx context.Context, paymentIdentifier []byte) (F
|
||||
return i, err
|
||||
}
|
||||
|
||||
const fetchPaymentDuplicates = `-- name: FetchPaymentDuplicates :many
|
||||
SELECT
|
||||
id,
|
||||
payment_id,
|
||||
amount_msat,
|
||||
created_at,
|
||||
fail_reason,
|
||||
settle_preimage,
|
||||
settle_time
|
||||
FROM payment_duplicates
|
||||
WHERE payment_id = $1
|
||||
ORDER BY id ASC
|
||||
`
|
||||
|
||||
// Fetch all duplicate payment records from the payment_duplicates table for
|
||||
// a given payment ID.
|
||||
func (q *Queries) FetchPaymentDuplicates(ctx context.Context, paymentID int64) ([]PaymentDuplicate, error) {
|
||||
rows, err := q.db.QueryContext(ctx, fetchPaymentDuplicates, paymentID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []PaymentDuplicate
|
||||
for rows.Next() {
|
||||
var i PaymentDuplicate
|
||||
if err := rows.Scan(
|
||||
&i.ID,
|
||||
&i.PaymentID,
|
||||
&i.AmountMsat,
|
||||
&i.CreatedAt,
|
||||
&i.FailReason,
|
||||
&i.SettlePreimage,
|
||||
&i.SettleTime,
|
||||
); 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 fetchPaymentLevelFirstHopCustomRecords = `-- name: FetchPaymentLevelFirstHopCustomRecords :many
|
||||
SELECT
|
||||
l.id,
|
||||
@@ -909,6 +956,51 @@ func (q *Queries) InsertPaymentAttemptFirstHopCustomRecord(ctx context.Context,
|
||||
return err
|
||||
}
|
||||
|
||||
const insertPaymentDuplicateMig = `-- name: InsertPaymentDuplicateMig :one
|
||||
INSERT INTO payment_duplicates (
|
||||
payment_id,
|
||||
amount_msat,
|
||||
created_at,
|
||||
fail_reason,
|
||||
settle_preimage,
|
||||
settle_time
|
||||
)
|
||||
VALUES (
|
||||
$1,
|
||||
$2,
|
||||
$3,
|
||||
$4,
|
||||
$5,
|
||||
$6
|
||||
)
|
||||
RETURNING id
|
||||
`
|
||||
|
||||
type InsertPaymentDuplicateMigParams struct {
|
||||
PaymentID int64
|
||||
AmountMsat int64
|
||||
CreatedAt time.Time
|
||||
FailReason sql.NullInt32
|
||||
SettlePreimage []byte
|
||||
SettleTime sql.NullTime
|
||||
}
|
||||
|
||||
// Insert a duplicate payment record into the payment_duplicates table and
|
||||
// return its ID.
|
||||
func (q *Queries) InsertPaymentDuplicateMig(ctx context.Context, arg InsertPaymentDuplicateMigParams) (int64, error) {
|
||||
row := q.db.QueryRowContext(ctx, insertPaymentDuplicateMig,
|
||||
arg.PaymentID,
|
||||
arg.AmountMsat,
|
||||
arg.CreatedAt,
|
||||
arg.FailReason,
|
||||
arg.SettlePreimage,
|
||||
arg.SettleTime,
|
||||
)
|
||||
var id int64
|
||||
err := row.Scan(&id)
|
||||
return id, err
|
||||
}
|
||||
|
||||
const insertPaymentFirstHopCustomRecord = `-- name: InsertPaymentFirstHopCustomRecord :exec
|
||||
INSERT INTO payment_first_hop_custom_records (
|
||||
payment_id,
|
||||
|
||||
Reference in new issue
Block a user