nnq 0.6.0 → 0.6.1

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: 0a336ac1e24bc6210ddeac6731163baab6f8980d9eebbe3235fac13157416fcf
4
- data.tar.gz: f049bf9038235487966cae1c8920b452abf9984e3776b2b93cbbd95e067fb485
3
+ metadata.gz: 19bceda0339689e8850efe39120590002821f5c859eaf33e4ea1065af317bcaa
4
+ data.tar.gz: 2a15dd79b5cf4f8a41c564e89cf40af7296de8765e8e070de12057b4bc0dedf9
5
5
  SHA512:
6
- metadata.gz: dcef45943a41f1bc53bbcf8ecc0c34c2779e4a16dea04e8f78854cf6a9debe11168596b47b764728e49393f61c319d41e2d79e489b37dec809adc59cbc0741f0
7
- data.tar.gz: fd7aaa30c57d5b8fbfd6279aba8b96423e201837ca4ef0144a315ec020da5e0183aaede0c7327fc49bf1bd318894099e4fe02657a12aebf98c1d895582583c62
6
+ metadata.gz: 1e14a21e6d620df7770c27868f5fdb8ed945fb04137b8817c12fdaa2fdfca1e36a952e6fc4afc5be60562dfd1ff8f43d9d1781ce9a4fb6a1c3a8963cd3738233
7
+ data.tar.gz: 13aae01f03df0b0eca53aa0329d0f2d35b036b4f52744f83033cc46297eae39a60b2c639ad144c9ee3af63cee7d3780c3fff036d30f418e078ccb89253f70761
data/CHANGELOG.md CHANGED
@@ -1,5 +1,31 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.6.1 — 2026-04-15
4
+
5
+ - **Verbose trace (`-vvv`) now fires for cooked REQ/REP/RESPONDENT
6
+ sends.** Cooked `Req#send_request`, `Rep#send_reply`, and
7
+ `Respondent#send_reply` bypass `send_pump` and write to the
8
+ connection directly, so they were never emitting `:message_sent`
9
+ monitor events — `-vvv` only ever showed the `<<` recv side. Each
10
+ now calls `emit_verbose_msg_sent(body)` after the write. Raw
11
+ REQ/REP/RESPONDENT sends get the same treatment (raw surveyor
12
+ already emitted via its per-peer send pump).
13
+ - **Verbose recv previews strip the SP backtrace header.** The recv
14
+ loop used to emit the raw wire body, so `-vvv` traces for
15
+ REQ/REP/SURVEYOR/RESPONDENT showed the 4-byte request/survey id
16
+ (or a multi-word backtrace stack) in front of the payload. Routing
17
+ strategies now expose an optional `preview_body(wire)` hook; the
18
+ engine calls it before emitting `:message_received` so the trace
19
+ shows just the payload.
20
+ - **`Engine#close` drains the monitor queue before cancelling
21
+ tasks.** The monitor consumer fiber lives under the socket-level
22
+ barrier, so `barrier.stop` used to `Async::Stop` it before it had
23
+ a chance to drain trailing events. `close` now emits `:closed`,
24
+ enqueues the nil sentinel, and awaits the stored `monitor_task`
25
+ before stopping the barrier. Fixes flaky `-vvv` traces on
26
+ short-lived sockets where the last `:message_received` event
27
+ would occasionally be lost.
28
+
3
29
  ## 0.6.0 — 2026-04-15
4
30
 
5
31
  - **NNG-style raw mode for REQ/REP and SURVEYOR/RESPONDENT.** Constructing
data/lib/nnq/engine.rb CHANGED
@@ -70,6 +70,10 @@ module NNQ
70
70
  attr_accessor :monitor_queue
71
71
 
72
72
 
73
+ # @return [Async::Task, nil] the monitor consumer task, if any
74
+ attr_accessor :monitor_task
75
+
76
+
73
77
  # @return [Boolean] when true, {#emit_verbose_monitor_event} forwards
74
78
  # per-message traces (:message_sent / :message_received) to the
75
79
  # monitor queue. Set by {Socket#monitor} via its +verbose:+ kwarg.
@@ -286,6 +290,15 @@ module NNQ
286
290
  # collection mutates during iteration, so snapshot the values.
287
291
  @connections.values.each(&:close!)
288
292
 
293
+ # Emit :closed, seal the monitor queue, and wait for the monitor
294
+ # fiber to drain it before cancelling tasks. Without this join,
295
+ # trailing :message_received events that the recv pump enqueued
296
+ # just before close would be lost when the barrier.stop below
297
+ # Async::Stops the monitor fiber mid-dequeue.
298
+ emit_monitor_event(:closed)
299
+ close_monitor_queue
300
+ @monitor_task&.wait
301
+
289
302
  # Cascade-cancel every remaining task (reconnect loops, accept
290
303
  # loops, supervisors) in one shot.
291
304
  @lifecycle.barrier&.stop
@@ -295,8 +308,6 @@ module NNQ
295
308
  # Unblock anyone waiting on peer_connected when the socket is
296
309
  # closed before a peer ever arrived.
297
310
  @lifecycle.peer_connected.resolve(nil) unless @lifecycle.peer_connected.resolved?
298
- emit_monitor_event(:closed)
299
- close_monitor_queue
300
311
  end
301
312
 
302
313
 
@@ -337,7 +348,10 @@ module NNQ
337
348
  @connections[conn].barrier.async(annotation: "nnq recv #{conn.endpoint}") do
338
349
  loop do
339
350
  body = conn.receive_message
340
- emit_verbose_msg_received(body)
351
+ if @verbose_monitor
352
+ preview = @routing.respond_to?(:preview_body) ? @routing.preview_body(body) : body
353
+ emit_verbose_msg_received(preview)
354
+ end
341
355
  @routing.enqueue(body, conn)
342
356
  rescue *CONNECTION_LOST, Async::Stop
343
357
  break
@@ -70,6 +70,14 @@ module NNQ
70
70
 
71
71
  return if conn.closed?
72
72
  conn.send_message(body, header: btrace)
73
+ @engine.emit_verbose_msg_sent(body)
74
+ end
75
+
76
+
77
+ # Strips the backtrace header for verbose trace previews.
78
+ def preview_body(wire)
79
+ _, payload = parse_backtrace(wire)
80
+ payload || wire
73
81
  end
74
82
 
75
83
 
@@ -36,11 +36,18 @@ module NNQ
36
36
  return if to.closed?
37
37
  return if Backtrace.too_many_hops?(header)
38
38
  to.send_message(body, header: header)
39
+ @engine.emit_verbose_msg_sent(body)
39
40
  rescue ClosedError
40
41
  # peer went away between receive and send — drop
41
42
  end
42
43
 
43
44
 
45
+ def preview_body(wire)
46
+ _, payload = parse_backtrace(wire)
47
+ payload || wire
48
+ end
49
+
50
+
44
51
  # Called by the engine recv loop.
45
52
  def enqueue(wire_bytes, conn)
46
53
  header, payload = parse_backtrace(wire_bytes)
@@ -56,6 +56,7 @@ module NNQ
56
56
  conn = pick_peer
57
57
  header = [id].pack("N")
58
58
  conn.send_message(body, header: header)
59
+ @engine.emit_verbose_msg_sent(body)
59
60
  promise.wait
60
61
  ensure
61
62
  @mutex.synchronize do
@@ -81,6 +82,12 @@ module NNQ
81
82
  end
82
83
 
83
84
 
85
+ # Strips the 4-byte request id for verbose trace previews.
86
+ def preview_body(wire)
87
+ wire.byteslice(4..) || wire
88
+ end
89
+
90
+
84
91
  def close
85
92
  @mutex.synchronize do
86
93
  @outstanding&.last&.reject(NNQ::Error.new("REQ socket closed"))
@@ -26,6 +26,13 @@ module NNQ
26
26
  def send(body, header:)
27
27
  conn = pick_peer
28
28
  conn.send_message(body, header: header)
29
+ @engine.emit_verbose_msg_sent(body)
30
+ end
31
+
32
+
33
+ def preview_body(wire)
34
+ _, payload = parse_backtrace(wire)
35
+ payload || wire
29
36
  end
30
37
 
31
38
 
@@ -52,6 +52,14 @@ module NNQ
52
52
 
53
53
  return if conn.closed?
54
54
  conn.send_message(body, header: btrace)
55
+ @engine.emit_verbose_msg_sent(body)
56
+ end
57
+
58
+
59
+ # Strips the backtrace header for verbose trace previews.
60
+ def preview_body(wire)
61
+ _, payload = parse_backtrace(wire)
62
+ payload || wire
55
63
  end
56
64
 
57
65
 
@@ -29,10 +29,17 @@ module NNQ
29
29
  return if to.closed?
30
30
  return if Backtrace.too_many_hops?(header)
31
31
  to.send_message(body, header: header)
32
+ @engine.emit_verbose_msg_sent(body)
32
33
  rescue ClosedError
33
34
  end
34
35
 
35
36
 
37
+ def preview_body(wire)
38
+ _, payload = parse_backtrace(wire)
39
+ payload || wire
40
+ end
41
+
42
+
36
43
  def enqueue(wire_bytes, conn)
37
44
  header, payload = parse_backtrace(wire_bytes)
38
45
  return unless header
@@ -78,6 +78,12 @@ module NNQ
78
78
  end
79
79
 
80
80
 
81
+ # Strips the 4-byte survey id for verbose trace previews.
82
+ def preview_body(wire)
83
+ wire.byteslice(4..) || wire
84
+ end
85
+
86
+
81
87
  def connection_added(conn)
82
88
  queue = Async::LimitedQueue.new(@engine.options.send_hwm)
83
89
  @queues[conn] = queue
@@ -47,6 +47,12 @@ module NNQ
47
47
  end
48
48
 
49
49
 
50
+ def preview_body(wire)
51
+ _, payload = parse_backtrace(wire)
52
+ payload || wire
53
+ end
54
+
55
+
50
56
  def connection_added(conn)
51
57
  queue = Async::LimitedQueue.new(@engine.options.send_hwm)
52
58
  @queues[conn] = queue
data/lib/nnq/socket.rb CHANGED
@@ -126,13 +126,14 @@ module NNQ
126
126
  @engine.verbose_monitor = verbose
127
127
 
128
128
  Reactor.run do
129
- @engine.spawn_task(annotation: "nnq monitor") do
129
+ @engine.monitor_task = @engine.spawn_task(annotation: "nnq monitor") do
130
130
  while (event = queue.dequeue)
131
131
  block.call(event)
132
132
  end
133
133
  rescue Async::Stop
134
134
  ensure
135
135
  @engine.monitor_queue = nil
136
+ @engine.monitor_task = nil
136
137
  block.call(MonitorEvent.new(type: :monitor_stopped))
137
138
  end
138
139
  end
data/lib/nnq/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module NNQ
4
- VERSION = "0.6.0"
4
+ VERSION = "0.6.1"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: nnq
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.0
4
+ version: 0.6.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Patrik Wenger