From 1368898e92c1c5a9ecda52f55ff04eece390dcb8 Mon Sep 17 00:00:00 2001 From: Ben Weeks Date: Sun, 12 Apr 2026 12:53:33 +0100 Subject: [PATCH] fix: offload bolt11_decode in list_transactions to worker thread Closes #35 `_on_list_transactions` decodes each payment's BOLT11 invoice with the synchronous `bolt11_decode()` inside the asyncio event loop. On wallets with non-trivial history this blocks the event loop long enough that NWC clients hit their reply timeout (~60s) and LNbits' other async work (relay keep-alives, publishes, concurrent NWC requests) stalls. Move the decode call to a worker thread via `asyncio.to_thread` so the event loop keeps running while each invoice is parsed. Sibling event-loop-starvation fixes: - lnbits/lnbits#3918 (IN_FLIGHT payment polling backoff) - lnbits/lnbits#3925 (non-blocking relay publishes in send_nostr_dm) Together these three changes restore NWC responsiveness on busy LNDRest-backed wallets. Co-Authored-By: Claude Opus 4.6 (1M context) --- tasks.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tasks.py b/tasks.py index a19a0d8..3a59dab 100644 --- a/tasks.py +++ b/tasks.py @@ -410,7 +410,13 @@ async def _on_list_transactions( transactions: list[dict] = [] p: Payment for p in history: - invoice_data = bolt11_decode(p.bolt11) + # bolt11_decode is synchronous and CPU-bound. Running it directly inside + # this async handler blocks the asyncio event loop for the duration of + # the whole batch, long enough that NWC clients hit their reply timeout + # and other extensions (nostrclient relay keep-alives, publishes, + # concurrent requests) are starved. Offload to a worker thread so the + # event loop stays responsive. + invoice_data = await asyncio.to_thread(bolt11_decode, p.bolt11) is_settled = not p.pending timestamp = int(p.time.timestamp()) or invoice_data.date transactions.append(