omq 0.4.1 → 0.4.2

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: dd6af0ae414feae27d1119fe4f1144b680e7e039229d512fe2915cd0289fc4dc
4
- data.tar.gz: b68f9a321eb93350fc175bd603f8d4c29e23518b6b2dfd2a13072eade5904c8f
3
+ metadata.gz: 0dc74658712882f8d0eb8f58b0c9a37c959d50c87116677d23be39904f7002e7
4
+ data.tar.gz: 1ba17796cac6b1cd594ebbee874428e0675153d461d168e0f69f07abab608ef8
5
5
  SHA512:
6
- metadata.gz: 3e17e9b4d867a7d2972b8caa4eff31dfd3c77095c60e9e5d9fdb53bc2646458dbfff244228065c145f0eb818d32f68bc196a7d0099fa4a5f2f02142f4c9a94b2
7
- data.tar.gz: d0a7cc664076ea333433e5e14b5d1701219e4bf15200302cf0ab4420e928d50b2fe8d08d35889d69de6f35f7715b40f423195d33055a67f15af18404ee850536
6
+ metadata.gz: 3628a755a53010690ae407ac790eb83ef7592ac5cac34bddff8a62782c20cb6092c3dd300ae71a2dfff324eaf8457ba319305b6892ca259f8990ce63267c74e0
7
+ data.tar.gz: 76502cad1a484cf8224285fbf4259e9ba8e5daa396caa42ac3716b50ada2e72a4c31b8d256208ab7410f5e48727216fee526efbf6166e3a1a32bb7bc1701cacb
data/CHANGELOG.md CHANGED
@@ -1,5 +1,29 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.4.2 — 2026-03-27
4
+
5
+ ### Fixed
6
+
7
+ - Send pump dies permanently on connection loss — `rescue` was outside
8
+ the loop, so a single `CONNECTION_LOST` killed the pump and all
9
+ subsequent messages queued but never sent
10
+ - NULL handshake deadlocks with buffered IO — missing `io.flush` after
11
+ greeting and READY writes caused both peers to block on read
12
+ - Inproc DirectPipe drops messages when send pump runs before
13
+ `direct_recv_queue` is wired — now buffers to `@pending_direct` and
14
+ drains on assignment
15
+ - HWM and timeout options set after construction had no effect because
16
+ `Async::LimitedQueue` was already allocated with the default
17
+
18
+ ### Added
19
+
20
+ - `send_hwm:`, `send_timeout:` constructor kwargs for `PUSH`
21
+ - `recv_hwm:`, `recv_timeout:` constructor kwargs for `PULL`
22
+
23
+ ### Changed
24
+
25
+ - Use `Async::Clock.now` instead of `Process.clock_gettime` internally
26
+
3
27
  ## 0.4.1 — 2026-03-27
4
28
 
5
29
  ### Improved
@@ -137,4 +161,4 @@ Initial release. Pure Ruby implementation of ZMTP 3.1 (ZeroMQ) using Async.
137
161
  - Linger on close (drain send queue before closing)
138
162
  - `max_message_size` enforcement
139
163
  - Works inside Async reactors or standalone (shared IO thread)
140
- - Optional CURVE encryption via the [omq-curve](https://github.com/paddor/omq-curve) gem
164
+ - Optional CURVE encryption via the [omq-curve](https://github.com/zeromq/omq-curve) gem
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # OMQ! Where did the C dependency go?!
2
2
 
3
- [![CI](https://github.com/paddor/omq/actions/workflows/ci.yml/badge.svg)](https://github.com/paddor/omq/actions/workflows/ci.yml)
3
+ [![CI](https://github.com/zeromq/omq/actions/workflows/ci.yml/badge.svg)](https://github.com/zeromq/omq/actions/workflows/ci.yml)
4
4
  [![Gem Version](https://img.shields.io/gem/v/omq?color=e9573f)](https://rubygems.org/gems/omq)
5
5
  [![License: ISC](https://img.shields.io/badge/License-ISC-blue.svg)](LICENSE)
6
6
  [![Ruby](https://img.shields.io/badge/Ruby-%3E%3D%203.3-CC342D?logo=ruby&logoColor=white)](https://www.ruby-lang.org)
data/lib/omq/push_pull.rb CHANGED
@@ -4,8 +4,8 @@ module OMQ
4
4
  class PUSH < Socket
5
5
  include ZMTP::Writable
6
6
 
7
- def initialize(endpoints = nil, linger: 0)
8
- _init_engine(:PUSH, linger: linger)
7
+ def initialize(endpoints = nil, linger: 0, send_hwm: nil, send_timeout: nil)
8
+ _init_engine(:PUSH, linger: linger, send_hwm: send_hwm, send_timeout: send_timeout)
9
9
  _attach(endpoints, default: :connect)
10
10
  end
11
11
  end
@@ -13,8 +13,8 @@ module OMQ
13
13
  class PULL < Socket
14
14
  include ZMTP::Readable
15
15
 
16
- def initialize(endpoints = nil, linger: 0)
17
- _init_engine(:PULL, linger: linger)
16
+ def initialize(endpoints = nil, linger: 0, recv_hwm: nil, recv_timeout: nil)
17
+ _init_engine(:PULL, linger: linger, recv_hwm: recv_hwm, recv_timeout: recv_timeout)
18
18
  _attach(endpoints, default: :bind)
19
19
  end
20
20
  end
data/lib/omq/socket.rb CHANGED
@@ -164,8 +164,13 @@ module OMQ
164
164
  # @param socket_type [Symbol]
165
165
  # @param linger [Integer]
166
166
  #
167
- def _init_engine(socket_type, linger:)
167
+ def _init_engine(socket_type, linger:, send_hwm: nil, recv_hwm: nil,
168
+ send_timeout: nil, recv_timeout: nil)
168
169
  @options = ZMTP::Options.new(linger: linger)
170
+ @options.send_hwm = send_hwm if send_hwm
171
+ @options.recv_hwm = recv_hwm if recv_hwm
172
+ @options.send_timeout = send_timeout if send_timeout
173
+ @options.recv_timeout = recv_timeout if recv_timeout
169
174
  @engine = ZMTP::Engine.new(socket_type, @options)
170
175
  end
171
176
  end
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.4.1"
4
+ VERSION = "0.4.2"
5
5
  end
@@ -218,7 +218,7 @@ module OMQ
218
218
  end
219
219
 
220
220
  def monotonic_now
221
- Process.clock_gettime(Process::CLOCK_MONOTONIC)
221
+ Async::Clock.now
222
222
  end
223
223
 
224
224
  # Sends one frame to the wire.
@@ -243,11 +243,11 @@ module OMQ
243
243
  #
244
244
  def drain_send_queues(timeout)
245
245
  return unless @routing.respond_to?(:send_queue)
246
- deadline = timeout ? Process.clock_gettime(Process::CLOCK_MONOTONIC) + timeout : nil
246
+ deadline = timeout ? Async::Clock.now + timeout : nil
247
247
 
248
248
  until @routing.send_queue.empty?
249
249
  if deadline
250
- remaining = deadline - Process.clock_gettime(Process::CLOCK_MONOTONIC)
250
+ remaining = deadline - Async::Clock.now
251
251
  break if remaining <= 0
252
252
  end
253
253
  sleep 0.001
@@ -26,6 +26,7 @@ module OMQ
26
26
  def handshake!(io, as_server:, socket_type:, identity:)
27
27
  # Send our greeting
28
28
  io.write(Codec::Greeting.encode(mechanism: MECHANISM_NAME, as_server: as_server))
29
+ io.flush
29
30
 
30
31
  # Read peer greeting
31
32
  greeting_data = io.read_exactly(Codec::Greeting::SIZE)
@@ -38,6 +39,7 @@ module OMQ
38
39
  # Send our READY command
39
40
  ready_cmd = Codec::Command.ready(socket_type: socket_type, identity: identity)
40
41
  io.write(ready_cmd.to_frame.to_wire)
42
+ io.flush
41
43
 
42
44
  # Read peer READY command
43
45
  frame = Codec::Frame.read_from(io)
@@ -56,13 +56,18 @@ module OMQ
56
56
  @tasks << Reactor.spawn_pump do
57
57
  loop do
58
58
  parts = @send_queue.dequeue
59
- conn = next_connection
60
- conn.send_message(transform_send(parts))
59
+ send_with_retry(parts)
61
60
  end
62
- rescue *ZMTP::CONNECTION_LOST
63
- # connection lost mid-write
64
61
  end
65
62
  end
63
+
64
+ def send_with_retry(parts)
65
+ conn = next_connection
66
+ conn.send_message(transform_send(parts))
67
+ rescue *ZMTP::CONNECTION_LOST
68
+ @engine.connection_lost(conn)
69
+ retry
70
+ end
66
71
  end
67
72
  end
68
73
  end
@@ -202,7 +202,7 @@ module OMQ
202
202
  # @return [Async::LimitedQueue, nil] when set, {#send_message}
203
203
  # enqueues directly here instead of using the internal queue
204
204
  #
205
- attr_accessor :direct_recv_queue
205
+ attr_reader :direct_recv_queue
206
206
 
207
207
  # @return [Proc, nil] optional transform applied before
208
208
  # enqueuing into {#direct_recv_queue}
@@ -224,6 +224,20 @@ module OMQ
224
224
  @peer = nil
225
225
  @direct_recv_queue = nil
226
226
  @direct_recv_transform = nil
227
+ @pending_direct = nil
228
+ end
229
+
230
+ # Sets the direct recv queue. Drains any messages that were
231
+ # buffered before the queue was available.
232
+ #
233
+ def direct_recv_queue=(queue)
234
+ @direct_recv_queue = queue
235
+ if queue && @pending_direct
236
+ @pending_direct.each do |msg|
237
+ queue.enqueue(msg)
238
+ end
239
+ @pending_direct = nil
240
+ end
227
241
  end
228
242
 
229
243
  # Sends a multi-frame message.
@@ -240,8 +254,11 @@ module OMQ
240
254
  if @direct_recv_queue
241
255
  msg = @direct_recv_transform ? @direct_recv_transform.call(parts) : parts
242
256
  @direct_recv_queue.enqueue(msg)
243
- else
257
+ elsif @send_queue
244
258
  @send_queue.enqueue(parts)
259
+ else
260
+ msg = @direct_recv_transform ? @direct_recv_transform.call(parts) : parts
261
+ (@pending_direct ||= []) << msg
245
262
  end
246
263
  end
247
264
 
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.4.1
4
+ version: 0.4.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Patrik Wenger
@@ -88,7 +88,7 @@ files:
88
88
  - lib/omq/zmtp/transport/tcp.rb
89
89
  - lib/omq/zmtp/valid_peers.rb
90
90
  - lib/omq/zmtp/writable.rb
91
- homepage: https://github.com/paddor/omq
91
+ homepage: https://github.com/zeromq/omq
92
92
  licenses:
93
93
  - ISC
94
94
  metadata: {}