Commit db2ef91
authored
Fix event handler leaks, reduce allocations, and refactor Network layer (#10753)
* Fix event handler leaks, reduce allocations, and refactor Network layer
- Fix event handler leaks in ProtocolsManager, PeerManager, RlpxHost,
DiscoveryManager, DiscoveryApp, CompositeDiscoveryApp,
NodeLifecycleManager, and NettyDiscoveryHandler by tracking
subscriptions and unsubscribing on disposal
- Implement IDisposable on ProtocolsManager with
SessionSubscription/ProtocolHandlerSubscription wrappers for
deterministic event handler cleanup
- Cache event handler and Func<> delegates in fields to avoid repeated
allocations on subscribe/unsubscribe
- Replace Action<T> delegates in MessageQueue/MessageDictionary with
IMessageSender<T> interface to eliminate per-construction allocations
- Rewrite PacketSender from async to synchronous with ContinueWith,
removing async state machine overhead on the hot send path
- Add custom DisconnectEventHandlers collection enabling safe
unsubscribe during dispatch
- Extract channel initializer classes in RlpxHost, replace
closure-capturing lambdas with cached delegates and named methods
- Convert NodeTable sort lambda to struct comparer
- Improve shutdown ordering in PeerManager
- Add RLP deserialization collection-size limits to Snap and Eth message
serializers
- Apply [MethodImpl(NoInlining)] to logging local functions with
interpolated strings
- Apply [DoesNotReturn, StackTraceHidden] to throw helpers
- Replace ArgumentNullException constructor with ThrowIfNull
* Add tests for event handler cleanup, disposal, shutdown, and deserialization limits
- Add tests for event handler cleanup on session disposal
- Add tests for ProtocolsManager.Dispose
- Add tests for PeerPool shutdown and SessionMonitor stop
- Add tests for SnapCapabilitySwitcher lifecycle
- Add tests for CompositeNodeSource event forwarding
- Add tests for ZeroFrameDecoder oversized frame rejection
- Add tests for deserialization limit validation in Snap/Eth serializers
- Add tests for MessageDictionary and MessageQueue with IMessageSender
- Deduplicate test setup across Eth protocol handler tests
(EthProtocolTestHelper)
- Refactor NettyDiscoveryHandlerTests, HelloMessageSerializerTests, and
GetTrieNodesMessageSerializerTests
* Fix RetryCache use-after-return race, CTS leak, and shutdown ordering
- Replace TryGetValue/TryAdd with GetOrAdd in RetryCache.Announced to
eliminate TOCTOU race where timer thread could remove and pool-return
a ConcurrentHashSet while another thread still holds a reference
- Add using to CancellationTokenSource in RlpxHost.ConnectAsync to
prevent timer resource leak on repeated outbound connect attempts
- Move subscription detachment in RlpxHost.Shutdown to after channel
close and event loop shutdown so Disconnected handlers fire during
shutdown and sessions are properly disposed
* Fix P2PMessageKey label caching on record struct
Record struct copies lose mutable field state, so the `_labels ??=`
pattern silently re-computed labels on every access. Replace with a
static ConcurrentDictionary keyed by the struct value — the key space is bounded by (protocol versions × SpaceSize) which is a few dozen entries total.
* Await PeerManager loop task in StopAsync and fix StartNew anti-pattern
Task.Factory.StartNew(LongRunning) with an async delegate returns
Task<Task> — the outer task completes at the first await, making
LongRunning meaningless and ContinueWith premature. Replace with
async Task + Task.Yield().
StopAsync previously returned Task.CompletedTask while the loop was
still running. Now it awaits the loop task for clean shutdown.
Add regression tests: Stop_does_not_hang, Disconnect_triggers_refill,
No_slot_available_before_deadline_does_not_deadlock_refill.
* Fix double-stop of DiscoveryApp during shutdown
DiscoveryApp and DiscoveryV5App implement IStoppableService via
IDiscoveryApp. When registered as container-owned singletons,
ServiceStopperMiddleware calls StopAsync on them directly AND
CompositeDiscoveryApp also calls StopAsync on them — producing
the duplicate "Discovery shutdown complete" log message.
Mark both as ExternallyOwned so only CompositeDiscoveryApp
manages their lifecycle.
fx
Propagate cancellation through discovery node location
LocateNodesAsync did not pass its CancellationToken into SendFindNode
or WasMessageReceived. During shutdown, the discovery loop had to wait
for all in-flight FindNode requests to time out (up to 8 rounds × N
nodes × 500ms each ≈ 70 seconds) before the task completed.
Thread the token through LocateNodesAsync → SendFindNodes →
SendFindNode → WasMessageReceived, and add ThrowIfCancellationRequested
at the top of each discovery round. WasMessageReceived now creates a
linked CTS so the Task.Delay is cancelled immediately on shutdown.
* fix: address Claude reviewer feedback on network PR
- RetryCache: document that first announcer is not added to retry bag
(intentional — it receives RequestRequired and requests directly)
- RlpxHost.Shutdown: dispose orphaned sessions via DetachAndDispose
(sessions whose Disconnected event never fired were leaked)
- DisconnectEventHandlers: document unordered invocation due to
swap-and-null removal pattern
- PacketSender: document thread-safety of lazy-init (Netty guarantees
single-threaded channel event delivery)
- ZeroFrameDecoder: document 12 MiB frame size cap rationale
- RlpxHost: remove commented-out logging setup code
- PeerManager: replace var with explicit KeyValuePair type,
add locking comment on _isStopping field
* fix: CI failures from review feedback changes
- DetachAndDispose: mark session as disconnected before disposing to
avoid InvalidOperationException on sessions still in Initialized state
- PeerManagerTests: relax assertion from exact count (1) to > 0 since
ConnectTimeoutMs=0 allows multiple connections to be queued before
the slot count updates
* fix: dispose CancellationTokenSource in RlpxHost.Shutdown
The delayCancellation CTS in the shutdown path was not disposed.
Add using declaration to match the ConnectAsync path.
* fix: address PR review feedback (round 2)
- Move IsIPv4Multicast to NodeFilter as a shared helper, delegate
from DiscoveryV5App
- Rename LogTrace to TraceNodeFilteredByRateLimit for clarity
- Replace magic number 4097/1025 in snap serializer tests with
SnapMessageLimits constants + 1
* fix: use ArrayPoolListRef instead of allocating Task[] in WhenAllDiscoveryApps1 parent f40ae6d commit db2ef91
File tree
80 files changed
+3547
-1054
lines changed- src/Nethermind
- Nethermind.Core.Test/Modules
- Nethermind.Core/Collections
- Nethermind.Crypto
- Nethermind.Init
- Modules
- Steps
- Nethermind.Network.Discovery.Test
- Nethermind.Network.Discovery
- Discv5
- Lifecycle
- RoutingTable
- Serializers
- Nethermind.Network.Stats
- Nethermind.Network.Test
- P2P
- Subprotocols
- Eth
- V62
- V63
- Snap/Messages
- Rlpx
- TestWrappers
- Nethermind.Network
- P2P
- Analyzers
- Messages
- ProtocolHandlers
- Subprotocols
- Eth
- V62
- Messages
- V63/Messages
- V69/Messages
- Snap
- Messages
- Rlpx
- Handshake
- Nethermind.Serialization.Rlp
- Nethermind.TxPool
Some content is hidden
Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
80 files changed
+3547
-1054
lines changedLines changed: 1 addition & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
45 | 45 | | |
46 | 46 | | |
47 | 47 | | |
| 48 | + | |
48 | 49 | | |
49 | 50 | | |
50 | 51 | | |
| |||
Lines changed: 8 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
| 3 | + | |
3 | 4 | | |
4 | 5 | | |
5 | 6 | | |
| |||
138 | 139 | | |
139 | 140 | | |
140 | 141 | | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
141 | 149 | | |
142 | 150 | | |
143 | 151 | | |
| |||
Lines changed: 7 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
145 | 145 | | |
146 | 146 | | |
147 | 147 | | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
148 | 155 | | |
149 | 156 | | |
150 | 157 | | |
| |||
Lines changed: 1 addition & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
67 | 67 | | |
68 | 68 | | |
69 | 69 | | |
| 70 | + | |
70 | 71 | | |
71 | 72 | | |
72 | 73 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | | - | |
| 1 | + | |
2 | 2 | | |
3 | 3 | | |
4 | 4 | | |
5 | 5 | | |
6 | | - | |
7 | 6 | | |
8 | | - | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
9 | 10 | | |
10 | | - | |
11 | | - | |
12 | | - | |
13 | | - | |
| 11 | + | |
| 12 | + | |
14 | 13 | | |
15 | | - | |
16 | | - | |
17 | | - | |
18 | | - | |
19 | | - | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
20 | 19 | | |
21 | | - | |
22 | | - | |
23 | | - | |
24 | | - | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
25 | 24 | | |
26 | | - | |
27 | | - | |
28 | | - | |
29 | | - | |
30 | | - | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
31 | 28 | | |
32 | | - | |
| 29 | + | |
| 30 | + | |
33 | 31 | | |
34 | | - | |
35 | | - | |
36 | | - | |
37 | | - | |
38 | | - | |
39 | | - | |
40 | | - | |
41 | | - | |
42 | | - | |
43 | | - | |
44 | | - | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
45 | 36 | | |
46 | | - | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
47 | 40 | | |
48 | | - | |
| 41 | + | |
| 42 | + | |
49 | 43 | | |
50 | | - | |
| 44 | + | |
51 | 45 | | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
52 | 54 | | |
53 | 55 | | |
54 | 56 | | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
2 | 2 | | |
3 | 3 | | |
4 | 4 | | |
| 5 | + | |
5 | 6 | | |
6 | 7 | | |
7 | 8 | | |
| |||
105 | 106 | | |
106 | 107 | | |
107 | 108 | | |
108 | | - | |
109 | 109 | | |
110 | 110 | | |
111 | 111 | | |
| |||
114 | 114 | | |
115 | 115 | | |
116 | 116 | | |
117 | | - | |
118 | 117 | | |
119 | 118 | | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
120 | 125 | | |
121 | 126 | | |
122 | 127 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
140 | 140 | | |
141 | 141 | | |
142 | 142 | | |
| 143 | + | |
143 | 144 | | |
144 | 145 | | |
145 | 146 | | |
| |||
Lines changed: 158 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
6 | 6 | | |
7 | 7 | | |
8 | 8 | | |
| 9 | + | |
9 | 10 | | |
10 | 11 | | |
11 | 12 | | |
12 | 13 | | |
13 | 14 | | |
14 | 15 | | |
| 16 | + | |
| 17 | + | |
15 | 18 | | |
16 | 19 | | |
17 | 20 | | |
18 | 21 | | |
| 22 | + | |
19 | 23 | | |
20 | 24 | | |
21 | 25 | | |
| |||
94 | 98 | | |
95 | 99 | | |
96 | 100 | | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
97 | 115 | | |
98 | 116 | | |
99 | 117 | | |
| |||
152 | 170 | | |
153 | 171 | | |
154 | 172 | | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
| 192 | + | |
| 193 | + | |
| 194 | + | |
| 195 | + | |
| 196 | + | |
| 197 | + | |
| 198 | + | |
| 199 | + | |
| 200 | + | |
155 | 201 | | |
156 | 202 | | |
157 | 203 | | |
| |||
190 | 236 | | |
191 | 237 | | |
192 | 238 | | |
| 239 | + | |
| 240 | + | |
| 241 | + | |
| 242 | + | |
| 243 | + | |
| 244 | + | |
| 245 | + | |
| 246 | + | |
| 247 | + | |
| 248 | + | |
| 249 | + | |
| 250 | + | |
| 251 | + | |
| 252 | + | |
| 253 | + | |
| 254 | + | |
| 255 | + | |
| 256 | + | |
| 257 | + | |
| 258 | + | |
| 259 | + | |
| 260 | + | |
| 261 | + | |
| 262 | + | |
| 263 | + | |
| 264 | + | |
| 265 | + | |
193 | 266 | | |
194 | 267 | | |
195 | 268 | | |
| |||
209 | 282 | | |
210 | 283 | | |
211 | 284 | | |
| 285 | + | |
| 286 | + | |
| 287 | + | |
| 288 | + | |
| 289 | + | |
| 290 | + | |
| 291 | + | |
| 292 | + | |
| 293 | + | |
| 294 | + | |
| 295 | + | |
| 296 | + | |
| 297 | + | |
| 298 | + | |
| 299 | + | |
| 300 | + | |
| 301 | + | |
| 302 | + | |
| 303 | + | |
| 304 | + | |
| 305 | + | |
| 306 | + | |
| 307 | + | |
| 308 | + | |
| 309 | + | |
| 310 | + | |
| 311 | + | |
| 312 | + | |
| 313 | + | |
| 314 | + | |
| 315 | + | |
| 316 | + | |
| 317 | + | |
| 318 | + | |
| 319 | + | |
| 320 | + | |
| 321 | + | |
| 322 | + | |
| 323 | + | |
| 324 | + | |
| 325 | + | |
| 326 | + | |
| 327 | + | |
| 328 | + | |
| 329 | + | |
| 330 | + | |
| 331 | + | |
| 332 | + | |
| 333 | + | |
| 334 | + | |
| 335 | + | |
| 336 | + | |
| 337 | + | |
| 338 | + | |
| 339 | + | |
| 340 | + | |
| 341 | + | |
| 342 | + | |
| 343 | + | |
| 344 | + | |
| 345 | + | |
| 346 | + | |
| 347 | + | |
| 348 | + | |
| 349 | + | |
| 350 | + | |
| 351 | + | |
| 352 | + | |
| 353 | + | |
| 354 | + | |
| 355 | + | |
| 356 | + | |
| 357 | + | |
| 358 | + | |
| 359 | + | |
| 360 | + | |
| 361 | + | |
| 362 | + | |
| 363 | + | |
| 364 | + | |
| 365 | + | |
| 366 | + | |
| 367 | + | |
| 368 | + | |
| 369 | + | |
212 | 370 | | |
213 | 371 | | |
0 commit comments