-
Notifications
You must be signed in to change notification settings - Fork 2.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[MPP] "slow settle" for TrackPayment #4142
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -117,19 +117,27 @@ func (p *controlTower) SettleAttempt(paymentHash lntypes.Hash, | |
return err | ||
} | ||
|
||
// Notify subscribers of success event. | ||
p.notifyFinalEvent( | ||
paymentHash, createSuccessResult(payment.HTLCs), | ||
) | ||
|
||
// If settling this attempt took the payment to a final state, we | ||
// should notify all subscribers of success event. | ||
p.notifyFinalEvent(payment) | ||
return nil | ||
} | ||
|
||
// FailAttempt marks the given payment attempt failed. | ||
func (p *controlTower) FailAttempt(paymentHash lntypes.Hash, | ||
attemptID uint64, failInfo *channeldb.HTLCFailInfo) error { | ||
|
||
return p.db.FailAttempt(paymentHash, attemptID, failInfo) | ||
payment, err := p.db.FailAttempt(paymentHash, attemptID, failInfo) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
// If failing this attempt took the payment to a final state, we | ||
// should notify all subscribers of the final payment status. This can | ||
// happen if the payment already had a settled attempt or terminal | ||
// failure recorded, and this attempt was the last one in-flight. | ||
p.notifyFinalEvent(payment) | ||
return nil | ||
} | ||
|
||
// createSuccessResult creates a success result to send to subscribers. | ||
|
@@ -173,13 +181,12 @@ func (p *controlTower) Fail(paymentHash lntypes.Hash, | |
return err | ||
} | ||
|
||
// Notify subscribers of fail event. | ||
p.notifyFinalEvent( | ||
paymentHash, createFailedResult( | ||
payment.HTLCs, reason, | ||
), | ||
) | ||
|
||
// If recording the failure reason took the payment to a final state, | ||
// we should notify all subscribers of the final payment status. This | ||
// can happen if the payment had no more attempts in flight at this | ||
// point, and recording this payment failure took the payment to a | ||
// final failure state. | ||
p.notifyFinalEvent(payment) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If we don't notify here, because there are still htlcs in-flight, is this There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No. That's why we also call it in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Aha, clear. |
||
return nil | ||
} | ||
|
||
|
@@ -249,9 +256,28 @@ func (p *controlTower) SubscribePayment(paymentHash lntypes.Hash) ( | |
} | ||
|
||
// notifyFinalEvent sends a final payment event to all subscribers of this | ||
// payment. The channel will be closed after this. | ||
func (p *controlTower) notifyFinalEvent(paymentHash lntypes.Hash, | ||
event *PaymentResult) { | ||
// payment if the payment is found in a state. The channel will be closed after | ||
// this. | ||
func (p *controlTower) notifyFinalEvent(payment *channeldb.MPPayment) { | ||
var event *PaymentResult | ||
switch payment.Status { | ||
|
||
// Notify subscribers of success event. | ||
case channeldb.StatusSucceeded: | ||
event = createSuccessResult(payment.HTLCs) | ||
|
||
// Notify subscribers of fail event. | ||
case channeldb.StatusFailed: | ||
event = createFailedResult( | ||
payment.HTLCs, *payment.FailureReason, | ||
) | ||
|
||
// Non-final status. | ||
default: | ||
return | ||
} | ||
|
||
paymentHash := payment.Info.PaymentHash | ||
|
||
// Get all subscribers for this hash. As there is only a single outcome, | ||
// the subscriber list can be cleared. | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't this causing a duplicate event?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Idea is that only the first event that take the payment to
Settle/failed
state will produce the notification. The second one will find the payment also in this state, but by that time the subscribers list will be empty (and cannot be filled again since we only add to it for in-flight payments)There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And there is no race condition possible there?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the first one that gets the lock on the subscribers will empty the list, and at that point it is impossible to add more to the list, so I think it is okay.
If we merge #3970 first I can add some more testcases here for MPP scenarios.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was actually thinking about a race between subscribe and notify, because the payment update in
FailAttempt
isn't inside the subscriber lock.I think the bottom line of this discussion is that the concurrent behavior isn't very clear. There is potentially a duplicate notification, but it is never broadcast because the subscriber lock makes sure the list is already empty.
Also control tower notification needs to be updated anyway to sends updates for non-final state transitions.
Isn't a single PR on top of #3970 that fixes both things a better idea?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
in order to be totally consistent i think, yes, there would need to be a lock around the db operation as well. this is preexisting tho, no?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the direction that I am thinking of atm: master...joostjager:notify-htlc-arrival-2