-
-
Notifications
You must be signed in to change notification settings - Fork 114
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
Use LND subscriptions #726
Use LND subscriptions #726
Conversation
Ok, we definitely need to make sure that our invoice code is idempotent when we want to run both methods in parallel for a while.
stacker.news/prisma/migrations/20221110224543_msats_funcs/migration.sql Lines 264 to 268 in 240ac34
Lines 34 to 39 in 240ac34
Lines 43 to 53 in 240ac34
Lines 55 to 63 in 240ac34
Lines 65 to 70 in 240ac34
Lines 72 to 74 in 240ac34
|
This is still a draft since I still think I am making too many assumptions here. For example, I am not entirely sure yet when However, the changes are small enough that a review at this stage also makes sense imo. I expect the code to not much change at this point. It just needs more testing, especially because I mentioned to run this in parallel with the old code. However, with enough testing, we don't even need to run this in parallel with the And maybe I can also go a step further and also convert |
If you haven't yet, you can check my Outer Space code which uses subscriptions instead of polling. The main difference IIRC is that you want to store an invoice cursor so that when the worker restarts it can get any invoices it missed while it was off. We should be able to switch completely off of polling but using both while migrating might make sense. Withdrawals can be subscribed too as well, but they don't have a cursor iirc so we'll continue to need polling as a fallback. |
49bac29
to
bb74b5f
Compare
Did that in bb74b5f
You're right, according to the However, I believe we don't need a cursor since iiuc, LND already does what we want:
-- https://lightning.engineering/api-docs/api/lnd/router/track-payments So since we don't need to get updates for every in-flight payment attempt, I think this subscription is fine. We only need to be subscribed to "every payment that is not in a terminal state". And So my current conclusion is that we might not even need polling for withdrawals. |
We don't need polling, but when the worker starts up, we will need to check the status of any pending payments in case any payments completed while the worker was down. |
Ah, makes sense! I was only thinking about pending payments that were not completed while the worker was down. |
7b8ce18
to
a429bf2
Compare
That's what nice about the cursor on receives. We can always resume where we left off. I asked Bos why there wasn't a cursor for withdrawals last year, "y no api symmetry?" I forget what he said, but he recommended still using subscriptions for withdrawals but checking for missed payment statuses if the connection is lost/restarted. |
I am doing this on worker startup in a429bf2 now: // queue status check of all pending withdrawals since they might have been paid by LND while worker was down
await models.$queryRaw`
INSERT INTO pgboss.job (name, data, retrylimit, retrybackoff, startafter)
SELECT 'checkWithdrawal', json_build_object('id', w.id, 'hash', w.hash), 21, true, now() + interval '10 seconds'
FROM "Withdrawl" w WHERE w.status IS NULL` However, since we do polling and subscription in parallel (so we still queue a job during I need to add some deduplication since else, there will always be two jobs running in parallel for these withdrawals until they are settled since they requeue themselves. So basically some JOIN with update: or don't use |
Alternatively, on startup:
This should allow you to get rid of the queuing all together. |
4c58384
to
ec25e58
Compare
Okay, I think I isolated the problem now. However, it does not get called if the invoice was already paid and is thus in the In that case, "expired" no longer exists. The invoice must be manually settled or canceled now. This does work in our happy path (when there is no error when the client uses the So I think I'll just insert a one time job for that case that gets run when the invoice would expire and then cancels it if it's not settled already. |
Okay, this should be it now. Timestamps: 00:00 - 00:10 Deposits 00:13 - 00:26 Withdrawals 00:27 - 00:38 Successful anon zap / HODL invoice 00:39 - 01:15 Expired anon zap / HODL invoice without paying 01:20 - 02:20 Simulate error after anon zap payment and wait for expiration to cancel invoice |
Oh man! So snappy! I should've done this literally years ago. I'll give it a review first thing tomorrow |
It could get even more snappier since we're still polling every second in the frontend but looks snappy enough for #715 so far :) And in production, payments will probably take a lot longer since this is just running locally. So the 1 second poll might be less significant in production. And one second is just the worst case. On average we would only wait 500 ms extra. |
Working on this locally. I noticed we don't have a good strategy for dealing with the For example:
This should be rare but it's a critical problem. AFAICT we have a few options:
(4) doesn't solve this problem for withdrawals and requires a restart, so I think (2) or (3) are among our best bets, but I think (3) is the better bet:
|
Would you mind explaining why exactly:
edit I think this answers (2):
So if (1) isn't doing that, what is (1) doing? |
(1) handles the transition from "unpaid" -> "is_held". |
Thanks! That makes a lot of sense. |
Ah, makes sense. Yes, I think (3) is the best option, too. |
023ddda
to
2ad9d8f
Compare
I made some very small readability changes as I reviewed, updated some of the comments to help someone like me who was less familiar with the code, and made sure we aren't suppressing some of the errors (which I might've overdone but better to be a little overzealous). This still needs another testing/review pass, but my polar network needs to be updated for TrackPayments I think. |
I'm not going to be pushing to this anymore tonight fyi. Just wanted to make sure I shared what I had in case you had other mods chambered. |
b04d65c
to
2ad9d8f
Compare
BTW you keep force pushing over your prs |
Oh no, I thought I disabled the mirroring from my repo on gitea. Will check after the call, thanks for letting me know update: Sorry, pushing from gitea to github was still enabled. I realized I don't need my forks anymore. I can push branches to this repo, probably since a long time lol |
Going to merge. I think this is rock solid now. @ekzyis I made some more significant changes so please look it over at your convenience and just in case. |
Understood, will try to break this in 8 hours :) |
This PR adds confirming invoices by subscribing to invoice updates using
subscribeToInvoices
since this will be relevant for good UX in #715.This should increase the speed since the worker only polls LND every 5 seconds but our frontend query polls every second. Difference in speed can also be seen in these two videos:
subscription
2024-01-02.01-26-03.mp4
polling
2024-01-02.01-27-21.mp4
We could run this for a few weeks in parallel with the old battle-tested polling as a fallback.
TODO since a429bf2:
payViaPaymentRequest
payViaPaymentRequest
payViaPaymentRequest
, start workerlncli payinvoice
payViaPaymentRequest
, start workerlncli payinvoice
I think these test cases should cover everything 🤔
And in general,
payViaPaymentRequest
should trigger the same behavior aslncli payinvoice
. But for 2d) and 2f), I will comment out thepayViaPaymentRequest
code lines and then pay the invoice manually vialncli payinvoice
to simulate a successful payment after the worker was restarted.update:
2d) and 2f) results in
unknown failure
since the payment is not known to LND (since I commented outpayViaPaymentRequest
). but this makes sense so is expected behavior. Both work when I simply return incheckWithdrawal
instead of failing the withdrawal if it wasn't found (this skip queueing a new check). Usinglncli payinvoice
then makes the withdrawal successful.