omq 0.5.1 → 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.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +184 -0
  3. data/README.md +21 -19
  4. data/exe/omq +6 -0
  5. data/lib/omq/cli/base_runner.rb +423 -0
  6. data/lib/omq/cli/channel.rb +8 -0
  7. data/lib/omq/cli/client_server.rb +106 -0
  8. data/lib/omq/cli/config.rb +51 -0
  9. data/lib/omq/cli/formatter.rb +75 -0
  10. data/lib/omq/cli/pair.rb +31 -0
  11. data/lib/omq/cli/peer.rb +8 -0
  12. data/lib/omq/cli/pipe.rb +249 -0
  13. data/lib/omq/cli/pub_sub.rb +14 -0
  14. data/lib/omq/cli/push_pull.rb +14 -0
  15. data/lib/omq/cli/radio_dish.rb +27 -0
  16. data/lib/omq/cli/req_rep.rb +77 -0
  17. data/lib/omq/cli/router_dealer.rb +70 -0
  18. data/lib/omq/cli/scatter_gather.rb +14 -0
  19. data/lib/omq/cli.rb +468 -0
  20. data/lib/omq/pub_sub.rb +2 -2
  21. data/lib/omq/radio_dish.rb +2 -2
  22. data/lib/omq/socket.rb +74 -27
  23. data/lib/omq/version.rb +1 -1
  24. data/lib/omq/zmtp/connection.rb +24 -3
  25. data/lib/omq/zmtp/engine.rb +179 -17
  26. data/lib/omq/zmtp/options.rb +4 -3
  27. data/lib/omq/zmtp/reactor.rb +10 -5
  28. data/lib/omq/zmtp/routing/channel.rb +8 -2
  29. data/lib/omq/zmtp/routing/fan_out.rb +38 -8
  30. data/lib/omq/zmtp/routing/pair.rb +8 -2
  31. data/lib/omq/zmtp/routing/peer.rb +7 -1
  32. data/lib/omq/zmtp/routing/push.rb +14 -7
  33. data/lib/omq/zmtp/routing/radio.rb +32 -11
  34. data/lib/omq/zmtp/routing/rep.rb +11 -7
  35. data/lib/omq/zmtp/routing/req.rb +1 -2
  36. data/lib/omq/zmtp/routing/round_robin.rb +35 -1
  37. data/lib/omq/zmtp/routing/router.rb +7 -1
  38. data/lib/omq/zmtp/routing/scatter.rb +16 -3
  39. data/lib/omq/zmtp/routing/server.rb +7 -1
  40. data/lib/omq/zmtp/routing/xsub.rb +7 -1
  41. data/lib/omq/zmtp/transport/inproc.rb +40 -5
  42. data/lib/omq/zmtp/transport/ipc.rb +9 -7
  43. data/lib/omq/zmtp/transport/tcp.rb +14 -7
  44. data/lib/omq/zmtp/writable.rb +21 -4
  45. data/lib/omq.rb +7 -0
  46. metadata +18 -3
  47. data/exe/omqcat +0 -532
data/lib/omq/socket.rb CHANGED
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "forwardable"
4
+
3
5
  module OMQ
4
6
  # Socket base class.
5
7
  #
@@ -8,32 +10,34 @@ module OMQ
8
10
  #
9
11
  attr_reader :options
10
12
 
13
+
11
14
  # @return [Integer, nil] last auto-selected TCP port
12
15
  #
13
16
  attr_reader :last_tcp_port
14
17
 
18
+
15
19
  # Delegate socket option accessors to @options.
16
20
  #
17
- %i[
18
- send_hwm send_hwm=
19
- recv_hwm recv_hwm=
20
- linger linger=
21
- identity identity=
22
- recv_timeout recv_timeout=
23
- send_timeout send_timeout=
24
- read_timeout read_timeout=
25
- write_timeout write_timeout=
26
- router_mandatory router_mandatory=
27
- router_mandatory?
28
- reconnect_interval reconnect_interval=
29
- heartbeat_interval heartbeat_interval=
30
- heartbeat_ttl heartbeat_ttl=
31
- heartbeat_timeout heartbeat_timeout=
32
- max_message_size max_message_size=
33
- mechanism mechanism=
34
- ].each do |method|
35
- define_method(method) { |*args| @options.public_send(method, *args) }
36
- end
21
+ extend Forwardable
22
+
23
+ def_delegators :@options,
24
+ :send_hwm, :send_hwm=,
25
+ :recv_hwm, :recv_hwm=,
26
+ :linger, :linger=,
27
+ :identity, :identity=,
28
+ :recv_timeout, :recv_timeout=,
29
+ :send_timeout, :send_timeout=,
30
+ :read_timeout, :read_timeout=,
31
+ :write_timeout, :write_timeout=,
32
+ :router_mandatory, :router_mandatory=,
33
+ :router_mandatory?,
34
+ :reconnect_interval, :reconnect_interval=,
35
+ :heartbeat_interval, :heartbeat_interval=,
36
+ :heartbeat_ttl, :heartbeat_ttl=,
37
+ :heartbeat_timeout, :heartbeat_timeout=,
38
+ :max_message_size, :max_message_size=,
39
+ :mechanism, :mechanism=
40
+
37
41
 
38
42
  # Creates a new socket and binds it to the given endpoint.
39
43
  #
@@ -45,6 +49,7 @@ module OMQ
45
49
  new(nil, **opts).tap { |s| s.bind(endpoint) }
46
50
  end
47
51
 
52
+
48
53
  # Creates a new socket and connects it to the given endpoint.
49
54
  #
50
55
  # @param endpoint [String]
@@ -55,8 +60,10 @@ module OMQ
55
60
  new(nil, **opts).tap { |s| s.connect(endpoint) }
56
61
  end
57
62
 
63
+
58
64
  def initialize(endpoints = nil, linger: 0); end
59
65
 
66
+
60
67
  # Binds to an endpoint.
61
68
  #
62
69
  # @param endpoint [String]
@@ -67,6 +74,7 @@ module OMQ
67
74
  @last_tcp_port = @engine.last_tcp_port
68
75
  end
69
76
 
77
+
70
78
  # Connects to an endpoint.
71
79
  #
72
80
  # @param endpoint [String]
@@ -76,6 +84,7 @@ module OMQ
76
84
  @engine.connect(endpoint)
77
85
  end
78
86
 
87
+
79
88
  # Disconnects from an endpoint.
80
89
  #
81
90
  # @param endpoint [String]
@@ -85,6 +94,7 @@ module OMQ
85
94
  @engine.disconnect(endpoint)
86
95
  end
87
96
 
97
+
88
98
  # Unbinds from an endpoint.
89
99
  #
90
100
  # @param endpoint [String]
@@ -94,21 +104,52 @@ module OMQ
94
104
  @engine.unbind(endpoint)
95
105
  end
96
106
 
107
+
97
108
  # @return [String, nil] last bound endpoint
98
109
  #
99
110
  def last_endpoint
100
111
  @engine.last_endpoint
101
112
  end
102
113
 
103
- # Closes the socket.
114
+
115
+ # @return [Async::Promise] resolves when first peer completes handshake
116
+ def peer_connected = @engine.peer_connected
117
+
118
+
119
+ # @return [Async::Promise] resolves when first subscriber joins (PUB/XPUB only)
120
+ def subscriber_joined = @engine.routing.subscriber_joined
121
+
122
+
123
+ # @return [Async::Promise] resolves when all peers disconnect (after having had peers)
124
+ def all_peers_gone = @engine.all_peers_gone
125
+
126
+
127
+ # @return [Integer] current number of peer connections
128
+ def connection_count = @engine.connections.size
129
+
130
+
131
+ # Signals end-of-stream on the receive side. A subsequent
132
+ # +#receive+ call that would otherwise block returns +nil+.
104
133
  #
105
- # @return [void]
134
+ def close_read
135
+ @engine.dequeue_recv_sentinel
136
+ end
137
+
138
+
139
+ # Disable auto-reconnect for connected endpoints.
140
+ def reconnect_enabled=(val)
141
+ @engine.reconnect_enabled = val
142
+ end
143
+
144
+
145
+ # Closes the socket.
106
146
  #
107
147
  def close
108
148
  @engine.close
109
149
  nil
110
150
  end
111
151
 
152
+
112
153
  # Set socket to use unbounded pipes (HWM=0).
113
154
  #
114
155
  def set_unbounded
@@ -117,14 +158,17 @@ module OMQ
117
158
  nil
118
159
  end
119
160
 
161
+
120
162
  # @return [String]
121
163
  #
122
164
  def inspect
123
165
  format("#<%s last_endpoint=%p>", self.class, last_endpoint)
124
166
  end
125
167
 
168
+
126
169
  private
127
170
 
171
+
128
172
  # Runs a block with a timeout. Uses Async's with_timeout if inside
129
173
  # a reactor, otherwise falls back to Timeout.timeout.
130
174
  #
@@ -142,6 +186,7 @@ module OMQ
142
186
  raise IO::TimeoutError, "timed out"
143
187
  end
144
188
 
189
+
145
190
  # Connects or binds based on endpoint prefix convention.
146
191
  #
147
192
  # @param endpoints [String, nil]
@@ -159,18 +204,20 @@ module OMQ
159
204
  end
160
205
  end
161
206
 
207
+
162
208
  # Initializes engine and options for a socket type.
163
209
  #
164
210
  # @param socket_type [Symbol]
165
211
  # @param linger [Integer]
166
212
  #
167
213
  def _init_engine(socket_type, linger:, send_hwm: nil, recv_hwm: nil,
168
- send_timeout: nil, recv_timeout: nil)
214
+ send_timeout: nil, recv_timeout: nil, conflate: false)
169
215
  @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
216
+ @options.send_hwm = send_hwm if send_hwm
217
+ @options.recv_hwm = recv_hwm if recv_hwm
218
+ @options.send_timeout = send_timeout if send_timeout
219
+ @options.recv_timeout = recv_timeout if recv_timeout
220
+ @options.conflate = conflate
174
221
  @engine = ZMTP::Engine.new(socket_type, @options)
175
222
  end
176
223
  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.5.1"
4
+ VERSION = "0.6.1"
5
5
  end
@@ -13,14 +13,17 @@ module OMQ
13
13
  #
14
14
  attr_reader :peer_socket_type
15
15
 
16
+
16
17
  # @return [String] peer's identity (from READY handshake)
17
18
  #
18
19
  attr_reader :peer_identity
19
20
 
21
+
20
22
  # @return [Object] transport IO (#read, #write, #close)
21
23
  #
22
24
  attr_reader :io
23
25
 
26
+
24
27
  # @param io [#read, #write, #close] transport IO
25
28
  # @param socket_type [String] our socket type name (e.g. "REQ")
26
29
  # @param identity [String] our identity
@@ -51,6 +54,7 @@ module OMQ
51
54
  @max_message_size = max_message_size
52
55
  end
53
56
 
57
+
54
58
  # Performs the full ZMTP handshake via the configured mechanism.
55
59
  #
56
60
  # @return [void]
@@ -77,6 +81,7 @@ module OMQ
77
81
  end
78
82
  end
79
83
 
84
+
80
85
  # Sends a multi-frame message (write + flush).
81
86
  #
82
87
  # @param parts [Array<String>] message frames
@@ -89,6 +94,7 @@ module OMQ
89
94
  end
90
95
  end
91
96
 
97
+
92
98
  # Writes a multi-frame message to the buffer without flushing.
93
99
  # Call {#flush} after batching writes.
94
100
  #
@@ -101,6 +107,7 @@ module OMQ
101
107
  end
102
108
  end
103
109
 
110
+
104
111
  # Flushes the write buffer to the underlying IO.
105
112
  #
106
113
  # @return [void]
@@ -111,6 +118,7 @@ module OMQ
111
118
  end
112
119
  end
113
120
 
121
+
114
122
  # Receives a multi-frame message.
115
123
  # PING/PONG commands are handled automatically by #read_frame.
116
124
  #
@@ -125,12 +133,13 @@ module OMQ
125
133
  yield frame if block_given?
126
134
  next
127
135
  end
128
- frames << frame.body
136
+ frames << frame.body.freeze
129
137
  break unless frame.more?
130
138
  end
131
- frames
139
+ frames.freeze
132
140
  end
133
141
 
142
+
134
143
  # Starts the heartbeat sender task. Call after handshake.
135
144
  #
136
145
  # @return [#stop, nil] the heartbeat task, or nil if disabled
@@ -138,7 +147,7 @@ module OMQ
138
147
  def start_heartbeat
139
148
  return nil unless @heartbeat_interval
140
149
  @last_received_at = monotonic_now
141
- @heartbeat_task = Reactor.spawn_pump do
150
+ @heartbeat_task = Reactor.spawn_pump(annotation: "heartbeat") do
142
151
  loop do
143
152
  sleep @heartbeat_interval
144
153
  # Send PING with TTL
@@ -160,6 +169,7 @@ module OMQ
160
169
  end
161
170
  end
162
171
 
172
+
163
173
  # Sends a command.
164
174
  #
165
175
  # @param command [Codec::Command]
@@ -176,6 +186,7 @@ module OMQ
176
186
  end
177
187
  end
178
188
 
189
+
179
190
  # Reads one frame from the wire. Handles PING/PONG automatically.
180
191
  # When using an encrypted mechanism, MESSAGE commands are decrypted
181
192
  # back to ZMTP frames transparently.
@@ -215,6 +226,7 @@ module OMQ
215
226
  end
216
227
  end
217
228
 
229
+
218
230
  # Closes the connection.
219
231
  #
220
232
  # @return [void]
@@ -226,8 +238,14 @@ module OMQ
226
238
  # already closed
227
239
  end
228
240
 
241
+
229
242
  private
230
243
 
244
+
245
+ # Writes message parts as ZMTP frames, encrypting if needed.
246
+ #
247
+ # @param parts [Array<String>] message frames
248
+ #
231
249
  def write_frames(parts)
232
250
  parts.each_with_index do |part, i|
233
251
  more = i < parts.size - 1
@@ -239,14 +257,17 @@ module OMQ
239
257
  end
240
258
  end
241
259
 
260
+
242
261
  def touch_heartbeat
243
262
  @last_received_at = monotonic_now if @heartbeat_interval
244
263
  end
245
264
 
265
+
246
266
  def monotonic_now
247
267
  Async::Clock.now
248
268
  end
249
269
 
270
+
250
271
  # Sends one frame to the wire.
251
272
  #
252
273
  # @param frame [Codec::Frame]