What changed, and why it matters
This commit moves two database cleanup tasks in Electrum's Lightning Network gossip code onto a separate thread so they no longer freeze the main asyncio event loop. It is a performance and responsiveness fix, not a security patch. There is no indication it fixes a vulnerability or was triggered by a security report.
No security action required. Treat as a normal performance/responsiveness improvement. Reviewers may verify that thread-safety of ChannelDB state is preserved when prune operations run off the main event loop.
Security signals we found
No security-relevant signals in commit message or diff
Change addresses asyncio event-loop blocking, which can affect availability/responsiveness but is not an exploit primitive
No input validation, authentication, cryptography, or memory-safety changes
Evidence from the diff
The change wraps ChannelDB.prune_old_policies() and prune_orphaned_channels() in a synchronous function _maintain() and runs it via asyncio.to_thread() inside LNGossip.maintain_db(). This prevents long-running SQLite/channel_db operations from blocking the asyncio event loop. Two @profiler decorators were also added to help measure these slow operations. The commit is purely about event-loop responsiveness and observability.
Changed components
electrum/lnworker.pyelectrum/channel_db.pyLNGossip.maintain_db()ChannelDB.prune_old_policies()ChannelDB.prune_orphaned_channels()Inspect captured patch +6 / −2
diff --git a/electrum/channel_db.py b/electrum/channel_db.py
index 2159a4b..387b9a3 100644
--- a/electrum/channel_db.py
+++ b/electrum/channel_db.py
@@ -732,6 +732,7 @@ class ChannelDB(SqlDB):
now = int(time.time())
return list(k for k, v in _policies.items() if v.timestamp <= now - delta)
+ @profiler(min_threshold=0.2)
def prune_old_policies(self, delta):
old_policies = self.get_old_policies(delta)
if old_policies:
@@ -744,6 +745,7 @@ class ChannelDB(SqlDB):
self.update_counts()
self.logger.info(f'Deleting {len(old_policies)} old policies')
+ @profiler(min_threshold=0.2)
def prune_orphaned_channels(self):
with self.lock:
orphaned_chans = self._chans_with_0_policies.copy()
diff --git a/electrum/lnworker.py b/electrum/lnworker.py
index 92ae704..374350b 100644
--- a/electrum/lnworker.py
+++ b/electrum/lnworker.py
@@ -723,8 +723,10 @@ class LNGossip(Logger):
await self.channel_db.data_loaded.wait()
while True:
if len(self.unknown_ids) == 0:
- self.channel_db.prune_old_policies(self.max_age)
- self.channel_db.prune_orphaned_channels()
+ def _maintain():
+ self.channel_db.prune_old_policies(self.max_age)
+ self.channel_db.prune_orphaned_channels()
+ await asyncio.to_thread(_maintain)
await asyncio.sleep(120)
async def _maintain_forwarding_gossip(self):
Why this scored 19/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.