multi: thread context through Fail payment functions

This commit is contained in:
ziggie committed 2026-02-25 08:02:24 +01:00
1 parent 446300984e
commit 635a67c94a
10 files changed
+41 -23

No files matched your search

+1 -1
View File
@@ -99,7 +99,7 @@ type PaymentControl interface {
// invoking this method, InitPayment should return nil on its next call // invoking this method, InitPayment should return nil on its next call
// for this payment hash, allowing the user to make a subsequent // for this payment hash, allowing the user to make a subsequent
// payment. // payment.
Fail(lntypes.Hash, FailureReason) (*MPPayment, error) Fail(context.Context, lntypes.Hash, FailureReason) (*MPPayment, error)
// DeleteFailedAttempts removes all failed HTLCs from the db. It should // DeleteFailedAttempts removes all failed HTLCs from the db. It should
// be called for a given payment whenever all inflight htlcs are // be called for a given payment whenever all inflight htlcs are
+1 -1
View File
@@ -528,7 +528,7 @@ func (p *KVStore) updateHtlcKey(paymentHash lntypes.Hash,
// payment failed. After invoking this method, InitPayment should return nil on // payment failed. After invoking this method, InitPayment should return nil on
// its next call for this payment hash, allowing the switch to make a // its next call for this payment hash, allowing the switch to make a
// subsequent payment. // subsequent payment.
func (p *KVStore) Fail(paymentHash lntypes.Hash, func (p *KVStore) Fail(_ context.Context, paymentHash lntypes.Hash,
reason FailureReason) (*MPPayment, error) { reason FailureReason) (*MPPayment, error) {
var ( var (
+1 -1
View File
@@ -112,7 +112,7 @@ func TestKVStoreDeleteNonInFlight(t *testing.T) {
// Fail the payment, which should moved it to Failed. // Fail the payment, which should moved it to Failed.
failReason := FailureReasonNoRoute failReason := FailureReasonNoRoute
_, err = paymentDB.Fail( _, err = paymentDB.Fail(
info.PaymentIdentifier, failReason, ctx, info.PaymentIdentifier, failReason,
) )
if err != nil { if err != nil {
t.Fatalf("unable to fail payment hash: %v", err) t.Fatalf("unable to fail payment hash: %v", err)
+7 -6
View File
@@ -191,8 +191,9 @@ func createTestPayments(t *testing.T, p DB, payments []*payment) {
require.NoError(t, err, "unable to fail htlc") require.NoError(t, err, "unable to fail htlc")
failReason := FailureReasonNoRoute failReason := FailureReasonNoRoute
_, err = p.Fail(info.PaymentIdentifier, _, err = p.Fail(
failReason) ctx, info.PaymentIdentifier, failReason,
)
require.NoError(t, err, "unable to fail payment hash") require.NoError(t, err, "unable to fail payment hash")
// Settle the attempt // Settle the attempt
@@ -1667,7 +1668,7 @@ func TestFailsWithoutInFlight(t *testing.T) {
// Calling Fail should return an error. // Calling Fail should return an error.
_, err = paymentDB.Fail( _, err = paymentDB.Fail(
info.PaymentIdentifier, FailureReasonNoRoute, t.Context(), info.PaymentIdentifier, FailureReasonNoRoute,
) )
require.ErrorIs(t, err, ErrPaymentNotInitiated) require.ErrorIs(t, err, ErrPaymentNotInitiated)
} }
@@ -1843,7 +1844,7 @@ func TestSwitchFail(t *testing.T) {
// Fail the payment, which should moved it to Failed. // Fail the payment, which should moved it to Failed.
failReason := FailureReasonNoRoute failReason := FailureReasonNoRoute
_, err = paymentDB.Fail(info.PaymentIdentifier, failReason) _, err = paymentDB.Fail(ctx, info.PaymentIdentifier, failReason)
require.NoError(t, err, "unable to fail payment hash") require.NoError(t, err, "unable to fail payment hash")
// Verify the status is indeed Failed. // Verify the status is indeed Failed.
@@ -2139,7 +2140,7 @@ func TestMultiShard(t *testing.T) {
// a terminal state. // a terminal state.
failReason := FailureReasonNoRoute failReason := FailureReasonNoRoute
_, err = paymentDB.Fail( _, err = paymentDB.Fail(
info.PaymentIdentifier, failReason, ctx, info.PaymentIdentifier, failReason,
) )
if err != nil { if err != nil {
t.Fatalf("unable to fail payment hash: %v", err) t.Fatalf("unable to fail payment hash: %v", err)
@@ -2232,7 +2233,7 @@ func TestMultiShard(t *testing.T) {
// syncing. // syncing.
failReason := FailureReasonPaymentDetails failReason := FailureReasonPaymentDetails
_, err = paymentDB.Fail( _, err = paymentDB.Fail(
info.PaymentIdentifier, failReason, ctx, info.PaymentIdentifier, failReason,
) )
require.NoError(t, err, "unable to fail") require.NoError(t, err, "unable to fail")
} }
+1 -3
View File
@@ -1782,11 +1782,9 @@ func (s *SQLStore) FailAttempt(ctx context.Context, paymentHash lntypes.Hash,
// This method is part of the PaymentControl interface, which is embedded in // This method is part of the PaymentControl interface, which is embedded in
// the PaymentWriter interface and ultimately the DB interface. It represents // the PaymentWriter interface and ultimately the DB interface. It represents
// step 4 in the payment lifecycle control flow. // step 4 in the payment lifecycle control flow.
func (s *SQLStore) Fail(paymentHash lntypes.Hash, func (s *SQLStore) Fail(ctx context.Context, paymentHash lntypes.Hash,
reason FailureReason) (*MPPayment, error) { reason FailureReason) (*MPPayment, error) {
ctx := context.TODO()
var mpPayment *MPPayment var mpPayment *MPPayment
err := s.db.ExecTx(ctx, sqldb.WriteTxOpt(), func(db SQLQueries) error { err := s.db.ExecTx(ctx, sqldb.WriteTxOpt(), func(db SQLQueries) error {
+5 -4
View File
@@ -66,7 +66,8 @@ type ControlTower interface {
// payment. // payment.
// //
// NOTE: Subscribers should be notified by the new state of the payment. // NOTE: Subscribers should be notified by the new state of the payment.
FailPayment(lntypes.Hash, paymentsdb.FailureReason) error FailPayment(context.Context, lntypes.Hash,
paymentsdb.FailureReason) error
// FetchInFlightPayments returns all payments with status InFlight. // FetchInFlightPayments returns all payments with status InFlight.
FetchInFlightPayments(ctx context.Context) ([]*paymentsdb.MPPayment, FetchInFlightPayments(ctx context.Context) ([]*paymentsdb.MPPayment,
@@ -272,13 +273,13 @@ func (p *controlTower) FetchPayment(ctx context.Context,
// //
// NOTE: This method will overwrite the failure reason if the payment is already // NOTE: This method will overwrite the failure reason if the payment is already
// failed. // failed.
func (p *controlTower) FailPayment(paymentHash lntypes.Hash, func (p *controlTower) FailPayment(ctx context.Context,
reason paymentsdb.FailureReason) error { paymentHash lntypes.Hash, reason paymentsdb.FailureReason) error {
p.paymentsMtx.Lock(paymentHash) p.paymentsMtx.Lock(paymentHash)
defer p.paymentsMtx.Unlock(paymentHash) defer p.paymentsMtx.Unlock(paymentHash)
payment, err := p.db.Fail(paymentHash, reason) payment, err := p.db.Fail(ctx, paymentHash, reason)
if err != nil { if err != nil {
return err return err
} }
+2 -1
View File
@@ -516,7 +516,8 @@ func testKVStoreSubscribeFail(t *testing.T, registerAttempt,
// Mark the payment as failed. // Mark the payment as failed.
err = pControl.FailPayment( err = pControl.FailPayment(
info.PaymentIdentifier, paymentsdb.FailureReasonTimeout, t.Context(), info.PaymentIdentifier,
paymentsdb.FailureReasonTimeout,
) )
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
+2 -2
View File
@@ -491,7 +491,7 @@ func (m *mockControlTowerOld) FailAttempt(_ context.Context, phash lntypes.Hash,
return nil, fmt.Errorf("pid not found") return nil, fmt.Errorf("pid not found")
} }
func (m *mockControlTowerOld) FailPayment(phash lntypes.Hash, func (m *mockControlTowerOld) FailPayment(_ context.Context, phash lntypes.Hash,
reason paymentsdb.FailureReason) error { reason paymentsdb.FailureReason) error {
m.Lock() m.Lock()
@@ -782,7 +782,7 @@ func (m *mockControlTower) FailAttempt(_ context.Context, phash lntypes.Hash,
return attempt.(*paymentsdb.HTLCAttempt), args.Error(1) return attempt.(*paymentsdb.HTLCAttempt), args.Error(1)
} }
func (m *mockControlTower) FailPayment(phash lntypes.Hash, func (m *mockControlTower) FailPayment(_ context.Context, phash lntypes.Hash,
reason paymentsdb.FailureReason) error { reason paymentsdb.FailureReason) error {
args := m.Called(phash, reason) args := m.Called(phash, reason)
+18 -3
View File
@@ -364,11 +364,18 @@ func (p *paymentLifecycle) checkContext(ctx context.Context) error {
p.identifier.String()) p.identifier.String())
} }
// The context is already cancelled at this point, so we create
// a new context so the payment can successfully be marked as
// failed.
cleanupCtx := context.WithoutCancel(ctx)
// By marking the payment failed, depending on whether it has // By marking the payment failed, depending on whether it has
// inflight HTLCs or not, its status will now either be // inflight HTLCs or not, its status will now either be
// `StatusInflight` or `StatusFailed`. In either case, no more // `StatusInflight` or `StatusFailed`. In either case, no more
// HTLCs will be attempted. // HTLCs will be attempted.
err := p.router.cfg.Control.FailPayment(p.identifier, reason) err := p.router.cfg.Control.FailPayment(
cleanupCtx, p.identifier, reason,
)
if err != nil { if err != nil {
return fmt.Errorf("FailPayment got %w", err) return fmt.Errorf("FailPayment got %w", err)
} }
@@ -389,6 +396,8 @@ func (p *paymentLifecycle) checkContext(ctx context.Context) error {
func (p *paymentLifecycle) requestRoute( func (p *paymentLifecycle) requestRoute(
ps *paymentsdb.MPPaymentState) (*route.Route, error) { ps *paymentsdb.MPPaymentState) (*route.Route, error) {
ctx := context.TODO()
remainingFees := p.calcFeeBudget(ps.FeesPaid) remainingFees := p.calcFeeBudget(ps.FeesPaid)
// Query our payment session to construct a route. // Query our payment session to construct a route.
@@ -430,7 +439,9 @@ func (p *paymentLifecycle) requestRoute(
log.Warnf("Marking payment %v permanently failed with no route: %v", log.Warnf("Marking payment %v permanently failed with no route: %v",
p.identifier, failureCode) p.identifier, failureCode)
err = p.router.cfg.Control.FailPayment(p.identifier, failureCode) err = p.router.cfg.Control.FailPayment(
ctx, p.identifier, failureCode,
)
if err != nil { if err != nil {
return nil, fmt.Errorf("FailPayment got: %w", err) return nil, fmt.Errorf("FailPayment got: %w", err)
} }
@@ -800,6 +811,8 @@ func (p *paymentLifecycle) failPaymentAndAttempt(
attemptID uint64, reason *paymentsdb.FailureReason, attemptID uint64, reason *paymentsdb.FailureReason,
sendErr error) (*attemptResult, error) { sendErr error) (*attemptResult, error) {
ctx := context.TODO()
log.Errorf("Payment %v failed: final_outcome=%v, raw_err=%v", log.Errorf("Payment %v failed: final_outcome=%v, raw_err=%v",
p.identifier, *reason, sendErr) p.identifier, *reason, sendErr)
@@ -808,7 +821,9 @@ func (p *paymentLifecycle) failPaymentAndAttempt(
// NOTE: we must fail the payment first before failing the attempt. // NOTE: we must fail the payment first before failing the attempt.
// Otherwise, once the attempt is marked as failed, another goroutine // Otherwise, once the attempt is marked as failed, another goroutine
// might make another attempt while we are failing the payment. // might make another attempt while we are failing the payment.
err := p.router.cfg.Control.FailPayment(p.identifier, *reason) err := p.router.cfg.Control.FailPayment(
ctx, p.identifier, *reason,
)
if err != nil { if err != nil {
log.Errorf("Unable to fail payment: %v", err) log.Errorf("Unable to fail payment: %v", err)
return nil, err return nil, err
+3 -1
View File
@@ -1088,7 +1088,9 @@ func (r *ChannelRouter) sendToRoute(htlcHash lntypes.Hash, rt *route.Route,
return nil return nil
} }
return r.cfg.Control.FailPayment(paymentIdentifier, reason) return r.cfg.Control.FailPayment(
ctx, paymentIdentifier, reason,
)
} }
log.Debugf("SendToRoute for payment %v with skipTempErr=%v", log.Debugf("SendToRoute for payment %v with skipTempErr=%v",