omq 0.13.0 → 0.14.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.
@@ -13,6 +13,7 @@ module OMQ
13
13
 
14
14
  EMPTY_FRAME = "".b.freeze
15
15
 
16
+
16
17
  # @param engine [Engine]
17
18
  #
18
19
  def initialize(engine)
@@ -24,6 +25,7 @@ module OMQ
24
25
  @tasks = []
25
26
  end
26
27
 
28
+
27
29
  # @return [FairQueue]
28
30
  #
29
31
  attr_reader :recv_queue
@@ -44,6 +46,7 @@ module OMQ
44
46
  @conn_send_tasks[connection] = ConnSendPump.start(@engine, connection, q, @tasks)
45
47
  end
46
48
 
49
+
47
50
  # @param connection [Connection]
48
51
  #
49
52
  def connection_removed(connection)
@@ -53,6 +56,7 @@ module OMQ
53
56
  @conn_send_tasks.delete(connection)&.stop
54
57
  end
55
58
 
59
+
56
60
  # Enqueues a reply. Routes to the connection that sent the matching
57
61
  # request by consuming the next pending_reply entry.
58
62
  #
@@ -65,12 +69,18 @@ module OMQ
65
69
  @conn_queues[conn]&.enqueue([*reply_info[:envelope], EMPTY_FRAME, *parts])
66
70
  end
67
71
 
72
+
73
+ # Stops all background tasks.
74
+ #
75
+ # @return [void]
76
+ #
68
77
  def stop
69
78
  @tasks.each(&:stop)
70
79
  @tasks.clear
71
80
  end
72
81
 
73
- # True when all per-connection send queues are empty.
82
+
83
+ # @return [Boolean] true when all per-connection send queues are empty
74
84
  #
75
85
  def send_queues_drained?
76
86
  @conn_queues.values.all?(&:empty?)
@@ -20,6 +20,7 @@ module OMQ
20
20
  init_round_robin(engine)
21
21
  end
22
22
 
23
+
23
24
  # @return [FairQueue]
24
25
  #
25
26
  attr_reader :recv_queue
@@ -36,6 +37,7 @@ module OMQ
36
37
  add_round_robin_send_connection(connection)
37
38
  end
38
39
 
40
+
39
41
  # @param connection [Connection]
40
42
  #
41
43
  def connection_removed(connection)
@@ -44,6 +46,7 @@ module OMQ
44
46
  remove_round_robin_send_connection(connection)
45
47
  end
46
48
 
49
+
47
50
  # @param parts [Array<String>]
48
51
  #
49
52
  def enqueue(parts)
@@ -52,6 +55,10 @@ module OMQ
52
55
  enqueue_round_robin(parts)
53
56
  end
54
57
 
58
+
59
+ # Stops all background tasks.
60
+ #
61
+ # @return [void]
55
62
  #
56
63
  def stop
57
64
  @tasks.each(&:stop)
@@ -15,8 +15,8 @@ module OMQ
15
15
  # their #initialize.
16
16
  #
17
17
  module RoundRobin
18
- # True when the staging queue and all per-connection send queues
19
- # are empty. Used by Engine#drain_send_queues during linger.
18
+ # @return [Boolean] true when the staging queue and all per-connection
19
+ # send queues are empty
20
20
  #
21
21
  def send_queues_drained?
22
22
  @staging_queue.empty? && @conn_queues.values.all?(&:empty?)
@@ -103,7 +103,7 @@ module OMQ
103
103
  @staging_queue.enqueue(parts)
104
104
  else
105
105
  conn = next_connection
106
- @conn_queues[conn]&.enqueue(parts)
106
+ @conn_queues[conn].enqueue(parts)
107
107
  end
108
108
  end
109
109
 
@@ -165,6 +165,7 @@ module OMQ
165
165
  @tasks << task
166
166
  end
167
167
 
168
+
168
169
  def write_batch(conn, batch)
169
170
  if batch.size == 1
170
171
  conn.send_message(transform_send(batch[0]))
@@ -24,6 +24,7 @@ module OMQ
24
24
  @tasks = []
25
25
  end
26
26
 
27
+
27
28
  # @return [FairQueue]
28
29
  #
29
30
  attr_reader :recv_queue
@@ -43,6 +44,7 @@ module OMQ
43
44
  @conn_send_tasks[connection] = ConnSendPump.start(@engine, connection, q, @tasks)
44
45
  end
45
46
 
47
+
46
48
  # @param connection [Connection]
47
49
  #
48
50
  def connection_removed(connection)
@@ -53,6 +55,7 @@ module OMQ
53
55
  @conn_send_tasks.delete(connection)&.stop
54
56
  end
55
57
 
58
+
56
59
  # Enqueues a message for sending. The first frame is the routing identity.
57
60
  #
58
61
  # @param parts [Array<String>]
@@ -69,12 +72,18 @@ module OMQ
69
72
  @conn_queues[conn]&.enqueue(parts[1..])
70
73
  end
71
74
 
75
+
76
+ # Stops all background tasks.
77
+ #
78
+ # @return [void]
79
+ #
72
80
  def stop
73
81
  @tasks.each(&:stop)
74
82
  @tasks.clear
75
83
  end
76
84
 
77
- # True when all per-connection send queues are empty.
85
+
86
+ # @return [Boolean] true when all per-connection send queues are empty
78
87
  #
79
88
  def send_queues_drained?
80
89
  @conn_queues.values.all?(&:empty?)
@@ -18,6 +18,7 @@ module OMQ
18
18
  @tasks = []
19
19
  end
20
20
 
21
+
21
22
  # @return [FairQueue]
22
23
  #
23
24
  attr_reader :recv_queue
@@ -36,6 +37,7 @@ module OMQ
36
37
  @tasks << task if task
37
38
  end
38
39
 
40
+
39
41
  # @param connection [Connection]
40
42
  #
41
43
  def connection_removed(connection)
@@ -43,12 +45,14 @@ module OMQ
43
45
  @recv_queue.remove_queue(connection)
44
46
  end
45
47
 
48
+
46
49
  # SUB is read-only.
47
50
  #
48
51
  def enqueue(_parts)
49
52
  raise "SUB sockets cannot send"
50
53
  end
51
54
 
55
+
52
56
  # Subscribes to a topic prefix.
53
57
  #
54
58
  # @param prefix [String]
@@ -60,6 +64,7 @@ module OMQ
60
64
  end
61
65
  end
62
66
 
67
+
63
68
  # Unsubscribes from a topic prefix.
64
69
  #
65
70
  # @param prefix [String]
@@ -71,6 +76,11 @@ module OMQ
71
76
  end
72
77
  end
73
78
 
79
+
80
+ # Stops all background tasks.
81
+ #
82
+ # @return [void]
83
+ #
74
84
  def stop
75
85
  @tasks.each(&:stop)
76
86
  @tasks.clear
@@ -23,6 +23,7 @@ module OMQ
23
23
  init_fan_out(engine)
24
24
  end
25
25
 
26
+
26
27
  # @return [Async::LimitedQueue]
27
28
  #
28
29
  attr_reader :recv_queue
@@ -36,6 +37,7 @@ module OMQ
36
37
  add_fan_out_send_connection(connection)
37
38
  end
38
39
 
40
+
39
41
  # @param connection [Connection]
40
42
  #
41
43
  def connection_removed(connection)
@@ -44,12 +46,17 @@ module OMQ
44
46
  remove_fan_out_send_connection(connection)
45
47
  end
46
48
 
49
+
47
50
  # @param parts [Array<String>]
48
51
  #
49
52
  def enqueue(parts)
50
53
  fan_out_enqueue(parts)
51
54
  end
52
55
 
56
+
57
+ # Stops all background tasks.
58
+ #
59
+ # @return [void]
53
60
  #
54
61
  def stop
55
62
  @tasks.each(&:stop)
@@ -65,6 +72,7 @@ module OMQ
65
72
  @recv_queue.enqueue(["\x01#{prefix}".b])
66
73
  end
67
74
 
75
+
68
76
  # Expose unsubscription to application as data message.
69
77
  #
70
78
  def on_cancel(conn, prefix)
@@ -21,6 +21,7 @@ module OMQ
21
21
  @tasks = []
22
22
  end
23
23
 
24
+
24
25
  # @return [FairQueue]
25
26
  #
26
27
  attr_reader :recv_queue
@@ -41,6 +42,7 @@ module OMQ
41
42
  start_conn_send_pump(connection, q)
42
43
  end
43
44
 
45
+
44
46
  # @param connection [Connection]
45
47
  #
46
48
  def connection_removed(connection)
@@ -50,6 +52,7 @@ module OMQ
50
52
  @conn_send_tasks.delete(connection)&.stop
51
53
  end
52
54
 
55
+
53
56
  # Enqueues a subscription command (fan-out to all connected PUBs).
54
57
  #
55
58
  # @param parts [Array<String>]
@@ -58,13 +61,18 @@ module OMQ
58
61
  @connections.each { |conn| @conn_queues[conn]&.enqueue(parts) }
59
62
  end
60
63
 
64
+
65
+ # Stops all background tasks.
66
+ #
67
+ # @return [void]
61
68
  #
62
69
  def stop
63
70
  @tasks.each(&:stop)
64
71
  @tasks.clear
65
72
  end
66
73
 
67
- # True when all per-connection send queues are empty.
74
+
75
+ # @return [Boolean] true when all per-connection send queues are empty
68
76
  #
69
77
  def send_queues_drained?
70
78
  @conn_queues.values.all?(&:empty?)
@@ -82,8 +90,10 @@ module OMQ
82
90
  prefix = frame.byteslice(1..) || "".b
83
91
  begin
84
92
  case flag
85
- when 0x01 then conn.send_command(Protocol::ZMTP::Codec::Command.subscribe(prefix))
86
- when 0x00 then conn.send_command(Protocol::ZMTP::Codec::Command.cancel(prefix))
93
+ when 0x01
94
+ conn.send_command(Protocol::ZMTP::Codec::Command.subscribe(prefix))
95
+ when 0x00
96
+ conn.send_command(Protocol::ZMTP::Codec::Command.cancel(prefix))
87
97
  end
88
98
  rescue Protocol::ZMTP::Error, *CONNECTION_LOST
89
99
  @engine.connection_lost(conn)
data/lib/omq/routing.rb CHANGED
@@ -18,6 +18,7 @@ module OMQ
18
18
  # Shared frozen empty binary string to avoid repeated allocations.
19
19
  EMPTY_BINARY = "".b.freeze
20
20
 
21
+
21
22
  # Plugin registry for socket types not built into omq.
22
23
  # Populated by sister gems via +Routing.register+.
23
24
  #
@@ -35,6 +36,7 @@ module OMQ
35
36
  end
36
37
  end
37
38
 
39
+
38
40
  # Builds a send or recv queue based on the mute strategy.
39
41
  #
40
42
  # @param hwm [Integer] high water mark
@@ -54,6 +56,7 @@ module OMQ
54
56
  end
55
57
  end
56
58
 
59
+
57
60
  # Drains all available messages from +queue+ into +batch+ without
58
61
  # blocking. Call after the initial blocking dequeue.
59
62
  #
@@ -73,6 +76,7 @@ module OMQ
73
76
  end
74
77
  end
75
78
 
79
+
76
80
  # Returns the routing strategy class for a socket type.
77
81
  #
78
82
  # @param socket_type [Symbol] e.g. :PAIR, :REQ
@@ -80,17 +84,28 @@ module OMQ
80
84
  #
81
85
  def self.for(socket_type)
82
86
  case socket_type
83
- when :PAIR then Pair
84
- when :REQ then Req
85
- when :REP then Rep
86
- when :DEALER then Dealer
87
- when :ROUTER then Router
88
- when :PUB then Pub
89
- when :SUB then Sub
90
- when :XPUB then XPub
91
- when :XSUB then XSub
92
- when :PUSH then Push
93
- when :PULL then Pull
87
+ when :PAIR
88
+ Pair
89
+ when :REQ
90
+ Req
91
+ when :REP
92
+ Rep
93
+ when :DEALER
94
+ Dealer
95
+ when :ROUTER
96
+ Router
97
+ when :PUB
98
+ Pub
99
+ when :SUB
100
+ Sub
101
+ when :XPUB
102
+ XPub
103
+ when :XSUB
104
+ XSub
105
+ when :PUSH
106
+ Push
107
+ when :PULL
108
+ Pull
94
109
  else
95
110
  @registry[socket_type] or raise ArgumentError, "unknown socket type: #{socket_type.inspect}"
96
111
  end
data/lib/omq/socket.rb CHANGED
@@ -62,7 +62,12 @@ module OMQ
62
62
  end
63
63
 
64
64
 
65
- def initialize(endpoints = nil, linger: 0); end
65
+ # @param endpoints [String, nil] optional endpoint with prefix convention
66
+ # (+@+ for bind, +>+ for connect, plain uses subclass default)
67
+ # @param linger [Integer] linger period in seconds (default 0)
68
+ #
69
+ def initialize(endpoints = nil, linger: 0)
70
+ end
66
71
 
67
72
 
68
73
  # Binds to an endpoint.
@@ -136,6 +141,8 @@ module OMQ
136
141
  # Signals end-of-stream on the receive side. A subsequent
137
142
  # +#receive+ call that would otherwise block returns +nil+.
138
143
  #
144
+ # @return [void]
145
+ #
139
146
  def close_read
140
147
  @engine.dequeue_recv_sentinel
141
148
  end
@@ -182,12 +189,18 @@ module OMQ
182
189
 
183
190
 
184
191
  # Disable auto-reconnect for connected endpoints.
192
+ #
193
+ # @param val [Boolean]
194
+ # @return [void]
195
+ #
185
196
  def reconnect_enabled=(val)
186
197
  @engine.reconnect_enabled = val
187
198
  end
188
199
 
189
200
 
190
- # Closes the socket.
201
+ # Closes the socket and releases all resources.
202
+ #
203
+ # @return [nil]
191
204
  #
192
205
  def close
193
206
  Reactor.run { @engine.close }
@@ -197,6 +210,8 @@ module OMQ
197
210
 
198
211
  # Set socket to use unbounded pipes (HWM=0).
199
212
  #
213
+ # @return [nil]
214
+ #
200
215
  def set_unbounded
201
216
  @options.send_hwm = 0
202
217
  @options.recv_hwm = 0
@@ -277,9 +292,12 @@ module OMQ
277
292
  @recv_buffer = []
278
293
  @recv_mutex = Mutex.new
279
294
  @engine = case backend
280
- when nil, :ruby then Engine.new(socket_type, @options)
281
- when :ffi then FFI::Engine.new(socket_type, @options)
282
- else raise ArgumentError, "unknown backend: #{backend}"
295
+ when nil, :ruby
296
+ Engine.new(socket_type, @options)
297
+ when :ffi
298
+ FFI::Engine.new(socket_type, @options)
299
+ else
300
+ raise ArgumentError, "unknown backend: #{backend}"
283
301
  end
284
302
  end
285
303
  end
@@ -64,6 +64,9 @@ module OMQ
64
64
  # Sets the direct recv queue. Drains any messages that were
65
65
  # buffered before the queue was available.
66
66
  #
67
+ # @param queue [Async::LimitedQueue, nil]
68
+ # @return [void]
69
+ #
67
70
  def direct_recv_queue=(queue)
68
71
  @direct_recv_queue = queue
69
72
  if queue && @pending_direct
@@ -93,8 +96,14 @@ module OMQ
93
96
  alias write_message send_message
94
97
 
95
98
 
99
+ # @return [Boolean] always false; inproc pipes are never encrypted
100
+ #
101
+ def encrypted? = false
102
+
96
103
  # No-op — inproc has no IO buffer to flush.
97
104
  #
105
+ # @return [nil]
106
+ #
98
107
  def flush = nil
99
108
 
100
109
 
@@ -143,7 +152,9 @@ module OMQ
143
152
  end
144
153
 
145
154
 
146
- # Closes this pipe end.
155
+ # Closes this pipe end and sends a nil sentinel to the peer.
156
+ #
157
+ # @return [void]
147
158
  #
148
159
  def close
149
160
  return if @closed
@@ -19,6 +19,7 @@ module OMQ
19
19
  #
20
20
  COMMAND_TYPES = %i[PUB SUB XPUB XSUB RADIO DISH].freeze
21
21
 
22
+
22
23
  # Global registry of bound inproc endpoints.
23
24
  #
24
25
  @registry = {}
@@ -105,11 +106,13 @@ module OMQ
105
106
  server_engine.connection_ready(server_pipe, endpoint: endpoint)
106
107
  end
107
108
 
109
+
108
110
  def needs_commands?(ce, se, ct, st)
109
111
  COMMAND_TYPES.include?(ct) || COMMAND_TYPES.include?(st) ||
110
112
  ce.options.qos >= 1 || se.options.qos >= 1
111
113
  end
112
114
 
115
+
113
116
  def make_pipe_pair(ce, se, ct, st, needs_cmds)
114
117
  if needs_cmds
115
118
  a_to_b = Async::Queue.new
@@ -126,6 +129,7 @@ module OMQ
126
129
  [client, server]
127
130
  end
128
131
 
132
+
129
133
  def await_bind(endpoint, engine)
130
134
  # Endpoint not bound yet — wait briefly then start background retry.
131
135
  # Matches ZMQ 4.x: connect to unbound inproc succeeds silently.
@@ -163,6 +167,7 @@ module OMQ
163
167
  end
164
168
  end
165
169
 
170
+
166
171
  # A bound inproc endpoint handle.
167
172
  #
168
173
  class Listener
@@ -30,6 +30,7 @@ module OMQ
30
30
  Listener.new(endpoint, server, path)
31
31
  end
32
32
 
33
+
33
34
  # Connects to an IPC endpoint.
34
35
  #
35
36
  # @param endpoint [String]
@@ -51,6 +52,7 @@ module OMQ
51
52
  endpoint.sub(%r{\Aipc://}, "")
52
53
  end
53
54
 
55
+
54
56
  # Converts @ prefix to \0 for abstract namespace.
55
57
  #
56
58
  def to_socket_path(path)
@@ -61,6 +63,7 @@ module OMQ
61
63
  end
62
64
  end
63
65
 
66
+
64
67
  # @return [Boolean] true if abstract namespace path
65
68
  #
66
69
  def abstract?(path)
@@ -68,6 +71,7 @@ module OMQ
68
71
  end
69
72
  end
70
73
 
74
+
71
75
  # A bound IPC listener.
72
76
  #
73
77
  class Listener
@@ -113,7 +117,9 @@ module OMQ
113
117
  end
114
118
 
115
119
 
116
- # Stops the listener.
120
+ # Stops the listener and removes the socket file.
121
+ #
122
+ # @return [void]
117
123
  #
118
124
  def stop
119
125
  @task&.stop
@@ -37,12 +37,7 @@ module OMQ
37
37
  Listener.new(resolved, servers, actual_port)
38
38
  end
39
39
 
40
- # Connects to a TCP endpoint.
41
- #
42
- # @param endpoint [String] e.g. "tcp://127.0.0.1:5555"
43
- # @param engine [Engine]
44
- # @return [void]
45
- #
40
+
46
41
  # Validates that the endpoint's host can be resolved.
47
42
  #
48
43
  # @param endpoint [String]
@@ -54,12 +49,19 @@ module OMQ
54
49
  end
55
50
 
56
51
 
52
+ # Connects to a TCP endpoint.
53
+ #
54
+ # @param endpoint [String] e.g. "tcp://127.0.0.1:5555"
55
+ # @param engine [Engine]
56
+ # @return [void]
57
+ #
57
58
  def connect(endpoint, engine)
58
59
  host, port = self.parse_endpoint(endpoint)
59
60
  sock = TCPSocket.new(host, port)
60
61
  engine.handle_connected(IO::Stream::Buffered.wrap(sock), endpoint: endpoint)
61
62
  end
62
63
 
64
+
63
65
  # Parses a TCP endpoint URI into host and port.
64
66
  #
65
67
  # @param endpoint [String]
@@ -71,6 +73,7 @@ module OMQ
71
73
  end
72
74
  end
73
75
 
76
+
74
77
  # A bound TCP listener.
75
78
  #
76
79
  class Listener
@@ -122,7 +125,9 @@ module OMQ
122
125
  end
123
126
 
124
127
 
125
- # Stops the listener.
128
+ # Stops the listener and closes all server sockets.
129
+ #
130
+ # @return [void]
126
131
  #
127
132
  def stop
128
133
  @tasks.each(&:stop)
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.13.0"
4
+ VERSION = "0.14.1"
5
5
  end
data/lib/omq/writable.rb CHANGED
@@ -19,6 +19,7 @@ module OMQ
19
19
  self
20
20
  end
21
21
 
22
+
22
23
  # Sends a message (chainable).
23
24
  #
24
25
  # @param message [String, Array<String>]
@@ -46,6 +47,7 @@ module OMQ
46
47
  parts.freeze
47
48
  end
48
49
 
50
+
49
51
  def frozen_binary(str)
50
52
  s = str.to_str
51
53
  return s if s.frozen? && s.encoding == Encoding::BINARY
data/lib/omq.rb CHANGED
@@ -16,7 +16,9 @@ module OMQ
16
16
  # Raised when an internal pump task crashes unexpectedly.
17
17
  # The socket is no longer usable; the original error is available via #cause.
18
18
  #
19
- class SocketDeadError < RuntimeError; end
19
+ class SocketDeadError < RuntimeError
20
+ end
21
+
20
22
 
21
23
  # Errors raised when a peer disconnects or resets the connection.
22
24
  # Not frozen at load time — transport plugins append to this before
@@ -42,6 +44,7 @@ module OMQ
42
44
  ]
43
45
  end
44
46
 
47
+
45
48
  # Transport
46
49
  require_relative "omq/transport/inproc"
47
50
  require_relative "omq/transport/tcp"
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.13.0
4
+ version: 0.14.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Patrik Wenger
@@ -13,16 +13,16 @@ dependencies:
13
13
  name: protocol-zmtp
14
14
  requirement: !ruby/object:Gem::Requirement
15
15
  requirements:
16
- - - ">="
16
+ - - "~>"
17
17
  - !ruby/object:Gem::Version
18
- version: '0'
18
+ version: '0.3'
19
19
  type: :runtime
20
20
  prerelease: false
21
21
  version_requirements: !ruby/object:Gem::Requirement
22
22
  requirements:
23
- - - ">="
23
+ - - "~>"
24
24
  - !ruby/object:Gem::Version
25
- version: '0'
25
+ version: '0.3'
26
26
  - !ruby/object:Gem::Dependency
27
27
  name: async
28
28
  requirement: !ruby/object:Gem::Requirement
@@ -69,6 +69,7 @@ files:
69
69
  - lib/omq/engine.rb
70
70
  - lib/omq/engine/connection_setup.rb
71
71
  - lib/omq/engine/heartbeat.rb
72
+ - lib/omq/engine/maintenance.rb
72
73
  - lib/omq/engine/reconnect.rb
73
74
  - lib/omq/engine/recv_pump.rb
74
75
  - lib/omq/monitor_event.rb