Fix sendpay aggregation and ordering in listpays#4567
Fix sendpay aggregation and ordering in listpays#4567rustyrussell merged 25 commits intoElementsProject:masterfrom
sendpay aggregation and ordering in listpays#4567Conversation
listpayssendpay aggregation and ordering in listpays
| const struct pubkey *blinding, | ||
| bool am_origin, | ||
| u64 partid, | ||
| u64 groupid, |
There was a problem hiding this comment.
This is kinda true, except that we do not support more than one group in flight at a time, so this commit, while not wrong and a nice sanity check, should be unnecessary.
There was a problem hiding this comment.
(I wish we could comment on commits not just code!)
99bbf73 to
80aa906
Compare
80aa906 to
cb684e9
Compare
Also add `groupid` to the payment fields so we can retrieve them too.
cb684e9 to
eaebbb9
Compare
eaebbb9 to
c2871a9
Compare
When doing things like `waitsendpay` without specifying the `groupid` we likely want to use the latest `groupid` we created, since that's the one in flight. This adds a function to quickly retrieve that.
ef656e6 to
83d3551
Compare
So far we've always been deferring the deletion, retry and early abort logic to `sendonion` and `sendpay` which do not have the context to decide if a call is legitimate or not (they were mostly based on heuristics). By calling `listsendpays` for the invoice's `payment_hash` we can identify what our `groupid` should be, but more importantly we can also abort if another payment is pending or a prior attempt has already succeeded.
This was the main cause of the pay states flip-flopping, since we reset the status on each attempt any final status is not really final. Let's keep them around, and provide a stable history.
One of the fundamental constraints of the payment groups idea is that there may only ever be one group in flight at any point in time, so if we find a group that is in flight, any new `sendpay` or `sendonion` must match its `groupid`.
This re-establishes the prior behavior where a `sendpay` or `sendonion` that'd match a prior payment would cause the prior payment to be deleted. While we no longer delete prior attempts we now avoid a primary key collision by incrementing once. This helps us not having to touch all existing tests, and likely avoids breaking other users too.
We're about to suspend duplicate calls to `pay` and this will help us notify them if the original payment completes.
We want to avoid returning duplicate results when cross-completing, so mark them as completed when we return a result.
Fixes ElementsProject#4482 Fixes ElementsProject#4481 Changelog-Added: pay: Payment attempts are now grouped by the pay command that initiated them Changelog-Fixed: pay: `listpays` returns payments orderd by their creation date Changelog-Fixed: pay: `listpays` no longer groups attempts from multiple attempts to pay an invoice
SQL doesn't really allow `a OR 1` as a clause since `1` is not a boolean expression. Moving it into `a OR 1=1` however is valid again.
a42d89e to
71752df
Compare
| /* New group, reset what we collected. */ | ||
| if (last_group != groupid) { | ||
| completed = false; | ||
| pending = false; | ||
| last_group = groupid; | ||
| } |
There was a problem hiding this comment.
This assumes that listsendpays is listed in increasing groupid order? That assumption does not seem documented?
There was a problem hiding this comment.
This is a listsendpays call limited to the payment_hash of the invoice. There cannot be multiple groups in flight concurrently. So ordering by id which we do internally in lightningd/pay.c will result in the desired ordering of groupid. I'll add a comment to that effect 👍
| res = wait_payment(cmd->ld, cmd, rhash, *partid); | ||
| if (groupid == NULL) { | ||
| groupid = tal(cmd, u64); | ||
| *groupid = wallet_payment_get_groupid(cmd->ld->wallet, rhash); |
There was a problem hiding this comment.
Would prefer this function to be called "wallet_payment_get_last_groupid" to be clearer? (or max_groupid)?
| last_group = groupid; | ||
|
|
||
| parts = 1; | ||
| json_scan(tmpctx, buf, t, |
There was a problem hiding this comment.
Hmm, is there no group_id 0? Since you initialize last_group to 0, it seems you won't run this for the first group. Since msat is not initialized elsewhere, this means you will use it uninitialized below?
There was a problem hiding this comment.
The pay plugin Will indeed skip over groupid 0, but pure sendpay calls outside of pay may produce that group too
There was a problem hiding this comment.
Could either make it a pointer to make that explicit, but the msats field is only used if we detect that there was a prior success or failure and replicate the result for idempotency, so not having a prior attempt means that we'll not use msats at al
|
Ack 71752df |
This is still work in progress, but I wanted to share the current
state. The main goal is to address #4481 and #4482, i.e., fix the
ordering and address flip-flopping states on supposedly finished
payments. It takes a bit of a detour to get there. The idea is to add
a grouping key
groupidtosendpayinstances, such that allsendpayinstances that originate from the samepayinvocationshare the same
groupid. This waylistpayscan group them into thecorrect group when aggregating
sendpaysfor eachpayinvocation. However, while implementing this simple approach I noticed
we actually delete prior attempts automagically when retrying them,
which still results in flip-flopping, and is strange if you ask me: if
I called
pay2 times, I should see it 2 times inlistpays.So we now no longer delete prior attempts, but it also means we need
to add
groupidto any RPC call that may refer to a specificsendpaycall:waitsendpay,sendonion,sendpay, etc. Inaddition we now need to annotate HTLCs we have in the DB with which
(payment_hash, partid, groupid)triple they refer to in order tocorrectly route results back to the caller.
I'm still working on some failing tests, but it should just be a
matter of time now :-)
Fixes #4481
Fixes #4482
Fixes #4780