paymentsdb: implement DeleteFailedAttempts for sql backend
This commit is contained in:
4 files changed
+126
No files matched your search
@@ -291,6 +291,8 @@ func (p *KVStore) InitPayment(paymentHash lntypes.Hash,
|
|||||||
// DeleteFailedAttempts deletes all failed htlcs for a payment if configured
|
// DeleteFailedAttempts deletes all failed htlcs for a payment if configured
|
||||||
// by the KVStore db.
|
// by the KVStore db.
|
||||||
func (p *KVStore) DeleteFailedAttempts(hash lntypes.Hash) error {
|
func (p *KVStore) DeleteFailedAttempts(hash lntypes.Hash) error {
|
||||||
|
// TODO(ziggie): Refactor to not mix application logic with database
|
||||||
|
// logic. This decision should be made in the application layer.
|
||||||
if !p.keepFailedPaymentAttempts {
|
if !p.keepFailedPaymentAttempts {
|
||||||
const failedHtlcsOnly = true
|
const failedHtlcsOnly = true
|
||||||
err := p.DeletePayment(hash, failedHtlcsOnly)
|
err := p.DeletePayment(hash, failedHtlcsOnly)
|
||||||
|
|||||||
@@ -712,3 +712,123 @@ func (s *SQLStore) FetchPayment(paymentHash lntypes.Hash) (*MPPayment, error) {
|
|||||||
|
|
||||||
return mpPayment, nil
|
return mpPayment, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeleteFailedAttempts removes all failed HTLC attempts from the database for
|
||||||
|
// the specified payment, while preserving the payment record itself and any
|
||||||
|
// successful or in-flight attempts.
|
||||||
|
//
|
||||||
|
// The method performs the following validations before deletion:
|
||||||
|
// - StatusInitiated: Can delete failed attempts
|
||||||
|
// - StatusInFlight: Cannot delete, returns ErrPaymentInFlight (active HTLCs
|
||||||
|
// still on the network)
|
||||||
|
// - StatusSucceeded: Can delete failed attempts (payment completed)
|
||||||
|
// - StatusFailed: Can delete failed attempts (payment permanently failed)
|
||||||
|
//
|
||||||
|
// If the keepFailedPaymentAttempts configuration flag is enabled, this method
|
||||||
|
// returns immediately without deleting anything, allowing failed attempts to
|
||||||
|
// be retained for debugging or auditing purposes.
|
||||||
|
//
|
||||||
|
// This method is idempotent - calling it multiple times on the same payment
|
||||||
|
// has no adverse effects.
|
||||||
|
//
|
||||||
|
// This method is part of the PaymentControl interface, which is embedded in
|
||||||
|
// the PaymentWriter interface and ultimately the DB interface. It represents
|
||||||
|
// the final step (step 5) in the payment lifecycle control flow and should be
|
||||||
|
// called after a payment reaches a terminal state (succeeded or permanently
|
||||||
|
// failed) to clean up historical failed attempts.
|
||||||
|
func (s *SQLStore) DeleteFailedAttempts(paymentHash lntypes.Hash) error {
|
||||||
|
ctx := context.TODO()
|
||||||
|
|
||||||
|
// In case we are configured to keep failed payment attempts, we exit
|
||||||
|
// early.
|
||||||
|
//
|
||||||
|
// TODO(ziggie): Refactor to not mix application logic with database
|
||||||
|
// logic. This decision should be made in the application layer.
|
||||||
|
if s.keepFailedPaymentAttempts {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
err := s.db.ExecTx(ctx, sqldb.WriteTxOpt(), func(db SQLQueries) error {
|
||||||
|
dbPayment, err := db.FetchPayment(ctx, paymentHash[:])
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to fetch payment: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
paymentStatus, err := computePaymentStatusFromDB(
|
||||||
|
ctx, db, dbPayment,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to compute payment "+
|
||||||
|
"status: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := paymentStatus.removable(); err != nil {
|
||||||
|
return fmt.Errorf("cannot delete failed "+
|
||||||
|
"attempts for payment %v: %w", paymentHash, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then we delete the failed attempts for this payment.
|
||||||
|
return db.DeleteFailedAttempts(ctx, dbPayment.Payment.ID)
|
||||||
|
}, sqldb.NoOpReset)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to delete failed attempts for "+
|
||||||
|
"payment %v: %w", paymentHash, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// computePaymentStatusFromDB computes the payment status by fetching minimal
|
||||||
|
// data from the database. This is a lightweight query optimized for SQL that
|
||||||
|
// doesn't load route data, making it significantly more efficient than
|
||||||
|
// FetchPayment when only the status is needed.
|
||||||
|
func computePaymentStatusFromDB(ctx context.Context, db SQLQueries,
|
||||||
|
dbPayment sqlc.PaymentAndIntent) (PaymentStatus, error) {
|
||||||
|
|
||||||
|
payment := dbPayment.GetPayment()
|
||||||
|
|
||||||
|
resolutionTypes, err := db.FetchHtlcAttemptResolutionsForPayment(
|
||||||
|
ctx, payment.ID,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return 0, fmt.Errorf("failed to fetch htlc resolutions: %w",
|
||||||
|
err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build minimal HTLCAttempt slice with only resolution info.
|
||||||
|
htlcs := make([]HTLCAttempt, len(resolutionTypes))
|
||||||
|
for i, resType := range resolutionTypes {
|
||||||
|
if !resType.Valid {
|
||||||
|
// NULL resolution_type means in-flight (no Settle, no
|
||||||
|
// Failure).
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
switch HTLCAttemptResolutionType(resType.Int32) {
|
||||||
|
case HTLCAttemptResolutionSettled:
|
||||||
|
// Mark as settled (preimage details not needed for
|
||||||
|
// status).
|
||||||
|
htlcs[i].Settle = &HTLCSettleInfo{}
|
||||||
|
|
||||||
|
case HTLCAttemptResolutionFailed:
|
||||||
|
// Mark as failed (failure details not needed for
|
||||||
|
// status).
|
||||||
|
htlcs[i].Failure = &HTLCFailInfo{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert fail reason to FailureReason pointer.
|
||||||
|
var failureReason *FailureReason
|
||||||
|
if payment.FailReason.Valid {
|
||||||
|
reason := FailureReason(payment.FailReason.Int32)
|
||||||
|
failureReason = &reason
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use the existing status decision logic.
|
||||||
|
status, err := decidePaymentStatus(htlcs, failureReason)
|
||||||
|
if err != nil {
|
||||||
|
return 0, fmt.Errorf("failed to decide payment status: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return status, nil
|
||||||
|
}
|
||||||
@@ -29,6 +29,8 @@ DELETE FROM payment_htlc_attempts WHERE payment_id = $1 AND attempt_index IN (
|
|||||||
)
|
)
|
||||||
`
|
`
|
||||||
|
|
||||||
|
// Delete all failed HTLC attempts for the given payment. Resolution type 2
|
||||||
|
// indicates a failed attempt.
|
||||||
func (q *Queries) DeleteFailedAttempts(ctx context.Context, paymentID int64) error {
|
func (q *Queries) DeleteFailedAttempts(ctx context.Context, paymentID int64) error {
|
||||||
_, err := q.db.ExecContext(ctx, deleteFailedAttempts, paymentID)
|
_, err := q.db.ExecContext(ctx, deleteFailedAttempts, paymentID)
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ type Querier interface {
|
|||||||
DeleteChannelPolicyExtraTypes(ctx context.Context, channelPolicyID int64) error
|
DeleteChannelPolicyExtraTypes(ctx context.Context, channelPolicyID int64) error
|
||||||
DeleteChannels(ctx context.Context, ids []int64) error
|
DeleteChannels(ctx context.Context, ids []int64) error
|
||||||
DeleteExtraNodeType(ctx context.Context, arg DeleteExtraNodeTypeParams) error
|
DeleteExtraNodeType(ctx context.Context, arg DeleteExtraNodeTypeParams) error
|
||||||
|
// Delete all failed HTLC attempts for the given payment. Resolution type 2
|
||||||
|
// indicates a failed attempt.
|
||||||
DeleteFailedAttempts(ctx context.Context, paymentID int64) error
|
DeleteFailedAttempts(ctx context.Context, paymentID int64) error
|
||||||
DeleteInvoice(ctx context.Context, arg DeleteInvoiceParams) (sql.Result, error)
|
DeleteInvoice(ctx context.Context, arg DeleteInvoiceParams) (sql.Result, error)
|
||||||
DeleteNode(ctx context.Context, id int64) error
|
DeleteNode(ctx context.Context, id int64) error
|
||||||
|
|||||||
Reference in new issue
Block a user