omq 0.15.3 → 0.15.4

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d1aa0586915986b6147a3433605657386ba586a8457e1ef2e0542b89f15da8d5
4
- data.tar.gz: 7d6507a015ca32b74ee3b449f29982f3d4766975628bfea13966f77d14ec8d2f
3
+ metadata.gz: 3357d4050a4f44b5c440026bc6ec9100cd34a64307f04fe8eab88ab219617cbc
4
+ data.tar.gz: b507113e3df663fbb27b805370be466795d00f0fd483e62a7936156a74047a5e
5
5
  SHA512:
6
- metadata.gz: f1dfb171e7aaa086e08a4f8ea0a30e4f13883e7e1879302edfbb0ca12ecf70176369f7aabe1861efaad5867bd8dd0ea935fd8e37cd52df5974c17391145cc8eb
7
- data.tar.gz: 5c33383958134a31830b4449b7de15b92ab7221597c00537ae2cea8a53bb029bf289022043acede991459a3a64d74b2cc90ed97494e7961d98a7b6b9259fdbe7
6
+ metadata.gz: 7d8dbffe8678966f753c0679543b7933cfe986dfd0fb7cb15be2aa824b99064f7a42e45fe73dd1e9d63e59fbaeeafe8b03f14e7a75078858d4f80d636cb3001e
7
+ data.tar.gz: 4ff71015d8434c0199b5c0f55312d8ee55defa7b2ae1d8dd526c6938cb20c77332a207848fbeae3e383a7d832cc305bd3030e2d36685c65483ddc1155e58bea3
data/CHANGELOG.md CHANGED
@@ -1,6 +1,21 @@
1
1
  # Changelog
2
2
 
3
- ## Unreleased
3
+ ## 0.15.4 — 2026-04-08
4
+
5
+ - **Lazy routing initialization** — the routing strategy is now created on
6
+ first use (bind, connect, send, or receive) instead of eagerly in the
7
+ constructor. This allows socket option setters (`send_hwm=`, `recv_hwm=`)
8
+ to take effect before internal queue sizing.
9
+ - **Prefetch byte limit** — `dequeue_recv_batch` now stops at 1 MB total,
10
+ not just 64 messages. Prevents large messages from filling the prefetch
11
+ buffer with hundreds of megabytes.
12
+ - **Bound staging queue `@head`** — `StagingQueue#prepend` now drops messages
13
+ when at capacity, preventing unbounded growth during reconnect cycles.
14
+ - **Bound monitor queue** — `Socket#monitor` uses a `LimitedQueue(64)` instead
15
+ of an unbounded queue, preventing memory growth when verbose monitoring
16
+ can't keep up with message rate.
17
+
18
+ ### Unreleased
4
19
 
5
20
  - **Auto-freeze on bind/connect** — `#bind` and `#connect` now call
6
21
  `OMQ.freeze_for_ractors!` automatically, freezing `CONNECTION_LOST`,
data/lib/omq/engine.rb CHANGED
@@ -42,9 +42,11 @@ module OMQ
42
42
  attr_reader :options
43
43
 
44
44
 
45
- # @return [Routing] routing strategy
45
+ # @return [Routing] routing strategy (created lazily on first access)
46
46
  #
47
- attr_reader :routing
47
+ def routing
48
+ @routing ||= Routing.for(@socket_type).new(self)
49
+ end
48
50
 
49
51
 
50
52
  # @return [String, nil] last bound endpoint
@@ -63,7 +65,7 @@ module OMQ
63
65
  def initialize(socket_type, options)
64
66
  @socket_type = socket_type
65
67
  @options = options
66
- @routing = Routing.for(socket_type).new(self)
68
+ @routing = nil
67
69
  @connections = {} # connection => ConnectionRecord
68
70
  @dialed = Set.new # endpoints we called connect() on (reconnect intent)
69
71
  @listeners = []
@@ -225,7 +227,7 @@ module OMQ
225
227
  pipe = @connection_wrapper.call(pipe) if @connection_wrapper
226
228
  @connections[pipe] = ConnectionRecord.new(endpoint: endpoint, done: nil)
227
229
  emit_monitor_event(:handshake_succeeded, endpoint: endpoint)
228
- @routing.connection_added(pipe)
230
+ routing.connection_added(pipe)
229
231
  @peer_connected.resolve(pipe)
230
232
  end
231
233
 
@@ -237,28 +239,31 @@ module OMQ
237
239
  #
238
240
  def dequeue_recv
239
241
  raise @fatal_error if @fatal_error
240
- msg = @routing.recv_queue.dequeue
242
+ msg = routing.recv_queue.dequeue
241
243
  raise @fatal_error if msg.nil? && @fatal_error
242
244
  msg
243
245
  end
244
246
 
245
247
 
246
- # Dequeues up to +max+ messages. Blocks on the first, then
247
- # drains non-blocking.
248
+ # Dequeues up to +max+ messages or +max_bytes+ total. Blocks
249
+ # on the first, then drains non-blocking.
248
250
  #
249
- # @param max [Integer]
251
+ # @param max [Integer] message count limit
252
+ # @param max_bytes [Integer] byte size limit
250
253
  # @return [Array<Array<String>>]
251
254
  #
252
- def dequeue_recv_batch(max)
255
+ def dequeue_recv_batch(max, max_bytes: 1 << 20)
253
256
  raise @fatal_error if @fatal_error
254
- queue = @routing.recv_queue
257
+ queue = routing.recv_queue
255
258
  msg = queue.dequeue
256
259
  raise @fatal_error if msg.nil? && @fatal_error
257
260
  batch = [msg]
258
- while batch.size < max
261
+ bytes = msg.sum(&:bytesize)
262
+ while batch.size < max && bytes < max_bytes
259
263
  msg = queue.dequeue(timeout: 0)
260
264
  break unless msg
261
265
  batch << msg
266
+ bytes += msg.sum(&:bytesize)
262
267
  end
263
268
  batch
264
269
  end
@@ -268,7 +273,7 @@ module OMQ
268
273
  # pending {#dequeue_recv} with a nil return value.
269
274
  #
270
275
  def dequeue_recv_sentinel
271
- @routing.recv_queue.push(nil)
276
+ routing.recv_queue.push(nil)
272
277
  end
273
278
 
274
279
 
@@ -280,7 +285,7 @@ module OMQ
280
285
  #
281
286
  def enqueue_send(parts)
282
287
  raise @fatal_error if @fatal_error
283
- @routing.enqueue(parts)
288
+ routing.enqueue(parts)
284
289
  end
285
290
 
286
291
 
@@ -305,7 +310,7 @@ module OMQ
305
310
  #
306
311
  def connection_lost(connection)
307
312
  entry = @connections.delete(connection)
308
- @routing.connection_removed(connection)
313
+ routing.connection_removed(connection)
309
314
  connection.close
310
315
  emit_monitor_event(:disconnected, endpoint: entry&.endpoint)
311
316
  entry&.done&.resolve(true)
@@ -369,7 +374,7 @@ module OMQ
369
374
  rescue => wrapped
370
375
  wrapped
371
376
  end
372
- @routing.recv_queue.push(nil) rescue nil
377
+ routing.recv_queue.push(nil) rescue nil
373
378
  @peer_connected.resolve(nil) rescue nil
374
379
  end
375
380
 
@@ -502,14 +507,14 @@ module OMQ
502
507
  conns = @connections.filter_map { |conn, e| conn if e.endpoint == endpoint }
503
508
  conns.each do |conn|
504
509
  @connections.delete(conn)
505
- @routing.connection_removed(conn)
510
+ routing.connection_removed(conn)
506
511
  conn.close
507
512
  end
508
513
  end
509
514
 
510
515
 
511
516
  def stop_tasks
512
- @routing.stop rescue nil
517
+ routing.stop rescue nil
513
518
  @tasks.each { |t| t.stop rescue nil }
514
519
  @tasks.clear
515
520
  end
@@ -12,7 +12,8 @@ module OMQ
12
12
  # @param max [Integer, nil] capacity (nil or 0 = unbounded)
13
13
  #
14
14
  def initialize(max = nil)
15
- @queue = (max && max > 0) ? Async::LimitedQueue.new(max) : Async::Queue.new
15
+ @max = (max && max > 0) ? max : nil
16
+ @queue = @max ? Async::LimitedQueue.new(@max) : Async::Queue.new
16
17
  @head = []
17
18
  @mu = Mutex.new
18
19
  end
@@ -30,13 +31,18 @@ module OMQ
30
31
 
31
32
 
32
33
  # Inserts a message at the front (for re-staging after a
33
- # failed drain).
34
+ # failed drain). Drops the message if the staging queue is
35
+ # already at capacity (messages sent to a peer that disconnected
36
+ # may be lost -- same as ZMQ).
34
37
  #
35
38
  # @param msg [Array<String>]
36
39
  # @return [void]
37
40
  #
38
41
  def prepend(msg)
39
- @mu.synchronize { @head.push(msg) }
42
+ @mu.synchronize do
43
+ return if @max && @head.size >= @max
44
+ @head.push(msg)
45
+ end
40
46
  end
41
47
 
42
48
 
data/lib/omq/socket.rb CHANGED
@@ -174,7 +174,7 @@ module OMQ
174
174
  #
175
175
  def monitor(verbose: false, &block)
176
176
  ensure_parent_task
177
- queue = Async::Queue.new
177
+ queue = Async::LimitedQueue.new(64)
178
178
  @engine.monitor_queue = queue
179
179
  @engine.verbose_monitor = verbose
180
180
  Reactor.run do
data/lib/omq/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module OMQ
4
- VERSION = "0.15.3"
4
+ VERSION = "0.15.4"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: omq
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.15.3
4
+ version: 0.15.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Patrik Wenger