Skip to content

fix(ldk-node): reap finished connection tasks and cap concurrent inbound connections - #545

Merged
RaslaTL19 merged 5 commits into
mainfrom
fix/reap-finished-connection-tasks
Nov 9, 2026
Merged

RaslaTL19 merged 5 commits into
mainfrom
fix/reap-finished-connection-tasks

Conversation

@RaslaTL19

@RaslaTL19 RaslaTL19 commented Nov 9, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

  • Finished tasks are released. The vendored ldk-node runtime kept cancellable tasks in a JoinSet that was only drained at shutdown. Every finished inbound connection task therefore stayed tracked until the node stopped. Cancellable tasks are now tracked with tokio_util::task::TaskTracker and a CancellationToken, so a task is released when it ends. This follows upstream ldk-node's "Release completed cancellable tasks" (Release completed cancellable tasks lightningdevkit/ldk-node#997). That change landed on main after 0.7.1 and is not in a release yet.
  • Shutdown. Stop closes the tracker, cancels every task in it and waits until each has been dropped. Dropping the runtime also cancels them.
  • Late spawns are refused, as upstream does. A closed tracker refuses new cancellable tasks, so a task spawned while a stop is in progress or after it is dropped without being polled. Before, such a task went into a fresh set and could outlive the stop. Node::start opens a fresh set, so restart still works.
  • What stop does not cover. It covers tasks spawned through the runtime only. A task that one of them hands to Tokio directly, such as the socket task inside lightning_net_tokio::setup_inbound, is not tracked, as before. That task ends when Node::stop disconnects all peers.
  • Concurrent inbound connections are capped. The inbound accept loop moved to inbound.rs. At most MAX_CONCURRENT_INBOUND_CONNECTIONS (512) accepted connections are handled at a time across all listening addresses. A socket accepted beyond that is closed at once.
  • What the cap does not touch. It applies only to newly accepted sockets. Established connections and outbound connections, including reconnects to persisted peers, are unaffected.
  • Limitation of the cap. 512 is above LDK's own limit of 250 connected peers without a channel, but the extra slots are headroom, not reserved. The peer is not known when a socket is accepted. So while 512 accepted connections are open, a new inbound connection from a channel peer is refused too. A channel peer with a stored address is still reached by the outbound reconnect. One without a stored address has to retry until a slot frees.
  • Accept errors no longer stop the listener. A failed accept is logged and retried after 100 ms. A failed socket conversion ends only that connection.
  • Dependency. tokio-util (rt feature) is added to the vendored manifest. It is already in the root lock at 0.7.18, and the vendor lock pins the same version.
  • Docs. BITSOV-PATCH.md documents the delta.

Tests

  • inbound::tests::finished_inbound_connection_tasks_are_released: 200 connect/close cycles on an ephemeral loopback port. On the previous runtime, 201 tasks stayed tracked (200 finished connections plus the accept loop). Now only the accept loop remains.
  • inbound::tests::connections_beyond_the_limit_are_closed_and_slots_are_reused: with 4 slots, a 5th socket is closed, the 4 open ones stay up, and a freed slot is reused.
  • inbound::tests::stopping_cancels_open_inbound_connections
  • runtime::tests::task_spawned_while_stopping_does_not_outlive_the_stop: a running task spawns a child after the stop has begun. The child is never polled and nothing stays tracked after the stop. With the previous fresh-set behaviour, one task stayed tracked.
  • runtime::tests: finished tasks are released; stop and drop cancel live tasks; a task spawned after a stop is refused; spawning works again after a restart.
  • The inbound test fixture stops its tasks before dropping the runtime, so --nocapture output is clean.
cargo test --locked --manifest-path vendor/ldk-node/Cargo.toml --lib
cargo test --locked -p konsensus-lightning
cargo test --locked -p konsensus-node --bins
cargo clippy --locked -p konsensus-lightning -p konsensus-node --all-targets -- -D warnings

…und connections

Cancellable runtime tasks were kept in a JoinSet that was only drained at
shutdown, so every finished inbound connection task stayed tracked for the
node's lifetime. Track them with tokio-util's TaskTracker and a
CancellationToken instead, as upstream ldk-node does after 0.7.1: a task is
released when it ends, and stop still cancels and waits for every live task.

Move the inbound accept loop into its own module and handle at most
MAX_CONCURRENT_INBOUND_CONNECTIONS (512) accepted connections at a time; a
socket accepted beyond that is closed at once. Established and outbound
connections are unaffected. A failed accept is logged and retried instead of
ending the listener.

Tests run 200 short loopback connections and check that only the accept loop
remains tracked, that excess sockets are closed while open ones stay up and
freed slots are reused, and that stop closes open connections.
Stop used to swap in a fresh task set before cancelling the old one, so a
task spawned while the stop was in progress (or after it) landed in the new
set and could outlive the stop. As upstream ldk-node does, stop now closes
the tracker in place and a closed tracker refuses new cancellable tasks;
Node::start opens a fresh set, so restart still works.

Add a regression where a running task spawns a child after the stop has
begun: the child is never polled and nothing stays tracked. The inbound
test fixture now stops its tasks before dropping the runtime, so the
runtime is never dropped on one of its own workers. Patch notes describe
what stop covers and the inbound limit's behaviour while saturated.
@RaslaTL19
RaslaTL19 marked this pull request as ready for review November 9, 2026 12:36
@RaslaTL19
RaslaTL19 merged commit a4b49c0 into main Nov 9, 2026
9 checks passed
@RaslaTL19
RaslaTL19 deleted the fix/reap-finished-connection-tasks branch November 9, 2026 12:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant