legion-transport 1.4.25 → 1.4.26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +7 -0
- data/lib/legion/transport/connection.rb +58 -1
- data/lib/legion/transport/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 3ef4fe3fdbefc12a9efd791c71b41422435c5ad5401e5ba0c51040649360f0b0
|
|
4
|
+
data.tar.gz: 8162f75c0d55ec521e8e1f3e2eaa1ff367b16bd0479a5c4f46471d9a6a92f8db
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 577f95777048496f3155dc4e22a9fd865df45fe1869c076e7297dcd2390352d3929952b0fa849e5864974ad84b2595a28d3788f8bdfe50a9a3f55084f3fb535c
|
|
7
|
+
data.tar.gz: 96cda6bf47f5bfa71ef6e3684b1ea68ebe91573159d42e00adba7f6f664d9f1bc0a587a185abfd5108dbe60f77bbe6e598c61a7a4929b971aca24c7ccf128707
|
data/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [1.4.26] - 2026-05-16
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
- Fixed channel leak caused by concurrent-ruby's IO-pool thread lifecycle: `TimerTask` (used by `Every` actors) dispatches onto `global_io_executor` threads that idle-timeout after 60s; thread-local Bunny channels were never closed when those threads died, eventually exhausting RabbitMQ's `channel_max` (1024). Added a `@channel_registry` that tracks publisher channels by owning thread and sweeps orphaned channels from dead threads before creating new ones.
|
|
9
|
+
- `Connection.shutdown` now explicitly closes all tracked publisher channels before tearing down the session.
|
|
10
|
+
- `Connection.force_reconnect` resets the channel registry to prevent stale references after reconnection.
|
|
11
|
+
|
|
5
12
|
## [1.4.25] - 2026-05-15
|
|
6
13
|
|
|
7
14
|
### Added
|
|
@@ -11,6 +11,7 @@ module Legion
|
|
|
11
11
|
module Connection
|
|
12
12
|
RECOVERY_WINDOW = 60
|
|
13
13
|
MAX_RECOVERIES_PER_WINDOW = 5
|
|
14
|
+
MAX_PUBLISHER_CHANNELS = 128
|
|
14
15
|
|
|
15
16
|
class << self
|
|
16
17
|
include Legion::Logging::Helper
|
|
@@ -36,6 +37,7 @@ module Legion
|
|
|
36
37
|
def reconnect(connection_name: 'Legion', **)
|
|
37
38
|
@session = nil
|
|
38
39
|
@channel_thread = Concurrent::ThreadLocalVar.new(nil)
|
|
40
|
+
@channel_registry = Concurrent::Hash.new
|
|
39
41
|
setup(connection_name: connection_name)
|
|
40
42
|
end
|
|
41
43
|
|
|
@@ -83,9 +85,18 @@ module Legion
|
|
|
83
85
|
s = session
|
|
84
86
|
raise IOError, 'transport session unavailable (recovery in progress)' unless s&.open?
|
|
85
87
|
|
|
88
|
+
sweep_dead_thread_channels
|
|
89
|
+
|
|
90
|
+
current_size = channel_registry_size
|
|
91
|
+
if current_size >= MAX_PUBLISHER_CHANNELS
|
|
92
|
+
log.warn "Channel registry at capacity (size=#{current_size}, max=#{MAX_PUBLISHER_CHANNELS}); " \
|
|
93
|
+
'RabbitMQ channel_max exhaustion risk — investigate thread lifecycle'
|
|
94
|
+
end
|
|
95
|
+
|
|
86
96
|
@channel_thread.value = s.create_channel(nil, settings[:channel][:default_worker_pool_size], false, 10)
|
|
87
97
|
@channel_thread.value.prefetch(settings[:prefetch])
|
|
88
|
-
|
|
98
|
+
track_channel(Thread.current, @channel_thread.value)
|
|
99
|
+
log.debug "Channel created for thread #{Thread.current.object_id} (tracked=#{channel_registry_size})"
|
|
89
100
|
@channel_thread.value
|
|
90
101
|
end
|
|
91
102
|
|
|
@@ -127,6 +138,7 @@ module Legion
|
|
|
127
138
|
@shutting_down = true
|
|
128
139
|
pre_mark_sessions_closing
|
|
129
140
|
close_build_session
|
|
141
|
+
close_all_tracked_channels
|
|
130
142
|
|
|
131
143
|
if @pool
|
|
132
144
|
@pool.shutdown
|
|
@@ -150,6 +162,7 @@ module Legion
|
|
|
150
162
|
ensure
|
|
151
163
|
@log_channel = nil
|
|
152
164
|
@session = nil
|
|
165
|
+
@channel_registry = Concurrent::Hash.new
|
|
153
166
|
@shutting_down = false
|
|
154
167
|
end
|
|
155
168
|
|
|
@@ -163,6 +176,7 @@ module Legion
|
|
|
163
176
|
reset_pool if pool_mode
|
|
164
177
|
@session = nil
|
|
165
178
|
@channel_thread = Concurrent::ThreadLocalVar.new(nil)
|
|
179
|
+
@channel_registry = Concurrent::Hash.new
|
|
166
180
|
@recovery_timestamps = []
|
|
167
181
|
|
|
168
182
|
tear_down_session(old) if old && !pool_mode
|
|
@@ -272,8 +286,50 @@ module Legion
|
|
|
272
286
|
sess
|
|
273
287
|
end
|
|
274
288
|
|
|
289
|
+
def channel_registry_size
|
|
290
|
+
(@channel_registry ||= Concurrent::Hash.new).size
|
|
291
|
+
end
|
|
292
|
+
|
|
275
293
|
private
|
|
276
294
|
|
|
295
|
+
def track_channel(thread, channel)
|
|
296
|
+
@channel_registry ||= Concurrent::Hash.new
|
|
297
|
+
@channel_registry[thread] = channel
|
|
298
|
+
end
|
|
299
|
+
|
|
300
|
+
def close_all_tracked_channels
|
|
301
|
+
@channel_registry ||= Concurrent::Hash.new
|
|
302
|
+
return if @channel_registry.empty?
|
|
303
|
+
|
|
304
|
+
@channel_registry.each_value do |channel|
|
|
305
|
+
channel.close if channel&.open?
|
|
306
|
+
rescue StandardError => e
|
|
307
|
+
handle_exception(e, level: :warn, handled: true, operation: 'transport.connection.close_tracked_channel')
|
|
308
|
+
end
|
|
309
|
+
@channel_registry.clear
|
|
310
|
+
end
|
|
311
|
+
|
|
312
|
+
def sweep_dead_thread_channels
|
|
313
|
+
@channel_registry ||= Concurrent::Hash.new
|
|
314
|
+
return if @channel_registry.empty?
|
|
315
|
+
|
|
316
|
+
swept = 0
|
|
317
|
+
@channel_registry.each do |thread, channel|
|
|
318
|
+
next if thread&.alive?
|
|
319
|
+
|
|
320
|
+
if channel&.open?
|
|
321
|
+
channel.close
|
|
322
|
+
swept += 1
|
|
323
|
+
end
|
|
324
|
+
@channel_registry.delete(thread)
|
|
325
|
+
rescue StandardError => e
|
|
326
|
+
@channel_registry.delete(thread)
|
|
327
|
+
handle_exception(e, level: :warn, handled: true, operation: 'transport.connection.sweep_channel')
|
|
328
|
+
end
|
|
329
|
+
|
|
330
|
+
log.info "Swept #{swept} orphaned channel(s) from dead threads (remaining=#{@channel_registry.size})" if swept.positive?
|
|
331
|
+
end
|
|
332
|
+
|
|
277
333
|
def pre_mark_sessions_closing
|
|
278
334
|
candidates = [
|
|
279
335
|
session,
|
|
@@ -493,6 +549,7 @@ module Legion
|
|
|
493
549
|
@log_channel = nil
|
|
494
550
|
@session = Concurrent::AtomicReference.new(create_session_with_failover(connection_name: connection_name))
|
|
495
551
|
@channel_thread = Concurrent::ThreadLocalVar.new(nil)
|
|
552
|
+
@channel_registry = Concurrent::Hash.new
|
|
496
553
|
start_session(session)
|
|
497
554
|
qos_channel = session.create_channel(nil, settings[:channel][:session_worker_pool_size])
|
|
498
555
|
apply_qos_and_close(qos_channel)
|