omq 0.9.0 → 0.11.0

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 (82) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +129 -0
  3. data/README.md +28 -3
  4. data/lib/omq/channel.rb +5 -5
  5. data/lib/omq/client_server.rb +10 -10
  6. data/lib/omq/engine.rb +702 -0
  7. data/lib/omq/options.rb +48 -0
  8. data/lib/omq/pair.rb +4 -4
  9. data/lib/omq/peer.rb +5 -5
  10. data/lib/omq/pub_sub.rb +18 -18
  11. data/lib/omq/push_pull.rb +6 -6
  12. data/lib/omq/queue_interface.rb +73 -0
  13. data/lib/omq/radio_dish.rb +6 -6
  14. data/lib/omq/reactor.rb +128 -0
  15. data/lib/omq/readable.rb +44 -0
  16. data/lib/omq/req_rep.rb +8 -8
  17. data/lib/omq/router_dealer.rb +8 -8
  18. data/lib/omq/routing/channel.rb +83 -0
  19. data/lib/omq/routing/client.rb +56 -0
  20. data/lib/omq/routing/dealer.rb +57 -0
  21. data/lib/omq/routing/dish.rb +78 -0
  22. data/lib/omq/routing/fan_out.rb +140 -0
  23. data/lib/omq/routing/gather.rb +46 -0
  24. data/lib/omq/routing/pair.rb +86 -0
  25. data/lib/omq/routing/peer.rb +101 -0
  26. data/lib/omq/routing/pub.rb +60 -0
  27. data/lib/omq/routing/pull.rb +46 -0
  28. data/lib/omq/routing/push.rb +81 -0
  29. data/lib/omq/routing/radio.rb +150 -0
  30. data/lib/omq/routing/rep.rb +101 -0
  31. data/lib/omq/routing/req.rb +65 -0
  32. data/lib/omq/routing/round_robin.rb +168 -0
  33. data/lib/omq/routing/router.rb +110 -0
  34. data/lib/omq/routing/scatter.rb +82 -0
  35. data/lib/omq/routing/server.rb +101 -0
  36. data/lib/omq/routing/sub.rb +78 -0
  37. data/lib/omq/routing/xpub.rb +72 -0
  38. data/lib/omq/routing/xsub.rb +83 -0
  39. data/lib/omq/routing.rb +66 -0
  40. data/lib/omq/scatter_gather.rb +8 -8
  41. data/lib/omq/single_frame.rb +18 -0
  42. data/lib/omq/socket.rb +32 -11
  43. data/lib/omq/transport/inproc.rb +355 -0
  44. data/lib/omq/transport/ipc.rb +117 -0
  45. data/lib/omq/transport/tcp.rb +111 -0
  46. data/lib/omq/transport/tls.rb +146 -0
  47. data/lib/omq/version.rb +1 -1
  48. data/lib/omq/writable.rb +66 -0
  49. data/lib/omq.rb +64 -4
  50. metadata +34 -33
  51. data/lib/omq/zmtp/engine.rb +0 -551
  52. data/lib/omq/zmtp/options.rb +0 -48
  53. data/lib/omq/zmtp/reactor.rb +0 -131
  54. data/lib/omq/zmtp/readable.rb +0 -29
  55. data/lib/omq/zmtp/routing/channel.rb +0 -81
  56. data/lib/omq/zmtp/routing/client.rb +0 -56
  57. data/lib/omq/zmtp/routing/dealer.rb +0 -57
  58. data/lib/omq/zmtp/routing/dish.rb +0 -80
  59. data/lib/omq/zmtp/routing/fan_out.rb +0 -131
  60. data/lib/omq/zmtp/routing/gather.rb +0 -48
  61. data/lib/omq/zmtp/routing/pair.rb +0 -84
  62. data/lib/omq/zmtp/routing/peer.rb +0 -100
  63. data/lib/omq/zmtp/routing/pub.rb +0 -62
  64. data/lib/omq/zmtp/routing/pull.rb +0 -48
  65. data/lib/omq/zmtp/routing/push.rb +0 -80
  66. data/lib/omq/zmtp/routing/radio.rb +0 -139
  67. data/lib/omq/zmtp/routing/rep.rb +0 -101
  68. data/lib/omq/zmtp/routing/req.rb +0 -65
  69. data/lib/omq/zmtp/routing/round_robin.rb +0 -143
  70. data/lib/omq/zmtp/routing/router.rb +0 -109
  71. data/lib/omq/zmtp/routing/scatter.rb +0 -81
  72. data/lib/omq/zmtp/routing/server.rb +0 -100
  73. data/lib/omq/zmtp/routing/sub.rb +0 -80
  74. data/lib/omq/zmtp/routing/xpub.rb +0 -74
  75. data/lib/omq/zmtp/routing/xsub.rb +0 -86
  76. data/lib/omq/zmtp/routing.rb +0 -65
  77. data/lib/omq/zmtp/single_frame.rb +0 -20
  78. data/lib/omq/zmtp/transport/inproc.rb +0 -359
  79. data/lib/omq/zmtp/transport/ipc.rb +0 -118
  80. data/lib/omq/zmtp/transport/tcp.rb +0 -117
  81. data/lib/omq/zmtp/writable.rb +0 -61
  82. data/lib/omq/zmtp.rb +0 -81
@@ -1,551 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "async"
4
-
5
- module OMQ
6
- module ZMTP
7
- # Per-socket orchestrator.
8
- #
9
- # Manages connections, transports, and the routing strategy for one
10
- # OMQ::Socket instance. Each socket type creates one Engine.
11
- #
12
- class Engine
13
- # @return [Symbol] socket type (e.g. :REQ, :PAIR)
14
- #
15
- attr_reader :socket_type
16
-
17
-
18
- # @return [Options] socket options
19
- #
20
- attr_reader :options
21
-
22
-
23
- # @return [Routing] routing strategy
24
- #
25
- attr_reader :routing
26
-
27
-
28
- # @return [String, nil] last bound endpoint
29
- #
30
- attr_reader :last_endpoint
31
-
32
-
33
- # @return [Integer, nil] last auto-selected TCP port
34
- #
35
- attr_reader :last_tcp_port
36
-
37
-
38
- # @param socket_type [Symbol] e.g. :REQ, :REP, :PAIR
39
- # @param options [Options]
40
- #
41
- def initialize(socket_type, options)
42
- @socket_type = socket_type
43
- @options = options
44
- @routing = Routing.for(socket_type).new(self)
45
- @connections = []
46
- @connection_endpoints = {} # connection => endpoint (for reconnection)
47
- @connected_endpoints = [] # endpoints we connected to (not bound)
48
- @listeners = []
49
- @tasks = []
50
- @closed = false
51
- @closing = false
52
- @last_endpoint = nil
53
- @last_tcp_port = nil
54
- @peer_connected = Async::Promise.new
55
- @all_peers_gone = Async::Promise.new
56
- @reconnect_enabled = true
57
- @parent_task = nil
58
- @connection_promises = {} # connection => Async::Promise
59
- @fatal_error = nil
60
- end
61
-
62
-
63
- attr_reader :peer_connected, :all_peers_gone, :connections, :parent_task
64
-
65
-
66
- attr_writer :reconnect_enabled
67
-
68
-
69
- # Binds to an endpoint.
70
- #
71
- # @param endpoint [String] e.g. "tcp://127.0.0.1:5555", "inproc://foo"
72
- # @return [void]
73
- # @raise [ArgumentError] on unsupported transport
74
- #
75
- def bind(endpoint)
76
- capture_parent_task
77
- transport = transport_for(endpoint)
78
- listener = transport.bind(endpoint, self)
79
- @listeners << listener
80
- @last_endpoint = listener.endpoint
81
- @last_tcp_port = extract_tcp_port(listener.endpoint)
82
- end
83
-
84
-
85
- # Connects to an endpoint.
86
- #
87
- # @param endpoint [String]
88
- # @return [void]
89
- #
90
- def connect(endpoint)
91
- capture_parent_task
92
- validate_endpoint!(endpoint)
93
- @connected_endpoints << endpoint
94
- if endpoint.start_with?("inproc://")
95
- # Inproc connect is synchronous and instant
96
- transport = transport_for(endpoint)
97
- transport.connect(endpoint, self)
98
- else
99
- # TCP/IPC connect in background — never blocks the caller
100
- schedule_reconnect(endpoint, delay: 0)
101
- end
102
- end
103
-
104
-
105
- # Disconnects from an endpoint. Closes connections to that endpoint
106
- # and stops auto-reconnection for it.
107
- #
108
- # @param endpoint [String]
109
- # @return [void]
110
- #
111
- def disconnect(endpoint)
112
- @connected_endpoints.delete(endpoint)
113
- conns = @connection_endpoints.select { |_, ep| ep == endpoint }.keys
114
- conns.each do |conn|
115
- @connection_endpoints.delete(conn)
116
- @connections.delete(conn)
117
- @routing.connection_removed(conn)
118
- conn.close
119
- end
120
- end
121
-
122
-
123
- # Unbinds from an endpoint. Stops the listener and closes all
124
- # connections that were accepted on it.
125
- #
126
- # @param endpoint [String]
127
- # @return [void]
128
- #
129
- def unbind(endpoint)
130
- listener = @listeners.find { |l| l.endpoint == endpoint }
131
- return unless listener
132
- listener.stop
133
- @listeners.delete(listener)
134
-
135
- # Close connections accepted on this endpoint
136
- conns = @connection_endpoints.select { |_, ep| ep == endpoint }.keys
137
- conns.each do |conn|
138
- @connection_endpoints.delete(conn)
139
- @connections.delete(conn)
140
- @routing.connection_removed(conn)
141
- conn.close
142
- end
143
- end
144
-
145
-
146
- # Called by a transport when an incoming connection is accepted.
147
- #
148
- # @param io [#read, #write, #close]
149
- # @param endpoint [String, nil] the endpoint this was accepted on
150
- # @return [void]
151
- #
152
- def handle_accepted(io, endpoint: nil)
153
- spawn_connection(io, as_server: true, endpoint: endpoint)
154
- end
155
-
156
-
157
- # Called by a transport when an outgoing connection is established.
158
- #
159
- # @param io [#read, #write, #close]
160
- # @return [void]
161
- #
162
- def handle_connected(io, endpoint: nil)
163
- spawn_connection(io, as_server: false, endpoint: endpoint)
164
- end
165
-
166
-
167
- # Called by inproc transport with a pre-validated DirectPipe.
168
- # Skips ZMTP handshake — just registers with routing strategy.
169
- #
170
- # @param pipe [Transport::Inproc::DirectPipe]
171
- # @return [void]
172
- #
173
- def connection_ready(pipe, endpoint: nil)
174
- @connections << pipe
175
- @connection_endpoints[pipe] = endpoint if endpoint
176
- @routing.connection_added(pipe)
177
- @peer_connected.resolve(pipe)
178
- end
179
-
180
-
181
- # Dequeues the next received message. Blocks until available.
182
- #
183
- # @return [Array<String>] message parts
184
- # @raise if a background pump task crashed
185
- #
186
- def dequeue_recv
187
- raise @fatal_error if @fatal_error
188
- msg = @routing.recv_queue.dequeue
189
- raise @fatal_error if msg.nil? && @fatal_error
190
- msg
191
- end
192
-
193
-
194
- # Pushes a nil sentinel into the recv queue, unblocking a
195
- # pending {#dequeue_recv} with a nil return value.
196
- #
197
- def dequeue_recv_sentinel
198
- @routing.recv_queue.push(nil)
199
- end
200
-
201
-
202
- # Enqueues a message for sending. Blocks at HWM.
203
- #
204
- # @param parts [Array<String>]
205
- # @return [void]
206
- # @raise if a background pump task crashed
207
- #
208
- def enqueue_send(parts)
209
- raise @fatal_error if @fatal_error
210
- @routing.enqueue(parts)
211
- end
212
-
213
-
214
- # Starts a recv pump for a connection, or wires the inproc
215
- # fast path when the connection is a DirectPipe.
216
- #
217
- # @param conn [Connection, Transport::Inproc::DirectPipe]
218
- # Starts a recv pump that dequeues messages from a connection
219
- # and enqueues them into the routing strategy's recv queue.
220
- #
221
- # When a block is given, each message is yielded for transformation
222
- # before enqueueing. The block is compiled at the call site, giving
223
- # YJIT a monomorphic call per routing strategy instead of a shared
224
- # megamorphic `transform.call` dispatch.
225
- #
226
- # @param conn [Connection, Transport::Inproc::DirectPipe]
227
- # @param recv_queue [Async::LimitedQueue] routing strategy's recv queue
228
- # @yield [msg] optional per-message transform
229
- # @return [#stop, nil] pump task handle, or nil for DirectPipe bypass
230
- #
231
- def start_recv_pump(conn, recv_queue, &transform)
232
- if conn.is_a?(Transport::Inproc::DirectPipe) && conn.peer
233
- conn.peer.direct_recv_queue = recv_queue
234
- conn.peer.direct_recv_transform = transform
235
- return nil
236
- end
237
-
238
- if transform
239
- Reactor.spawn_pump(annotation: "recv pump") do
240
- loop do
241
- msg = conn.receive_message
242
- msg = transform.call(msg).freeze
243
- recv_queue.enqueue(msg)
244
- end
245
- rescue Async::Stop
246
- rescue ProtocolError, *CONNECTION_LOST
247
- connection_lost(conn)
248
- rescue => error
249
- signal_fatal_error(error)
250
- end
251
- else
252
- Reactor.spawn_pump(annotation: "recv pump") do
253
- loop do
254
- recv_queue.enqueue(conn.receive_message)
255
- end
256
- rescue Async::Stop
257
- rescue ProtocolError, *CONNECTION_LOST
258
- connection_lost(conn)
259
- rescue => error
260
- signal_fatal_error(error)
261
- end
262
- end
263
- end
264
-
265
-
266
- # Called when a connection is lost.
267
- #
268
- # @param connection [Connection]
269
- # @return [void]
270
- #
271
- def connection_lost(connection)
272
- endpoint = @connection_endpoints.delete(connection)
273
- @connections.delete(connection)
274
- @routing.connection_removed(connection)
275
- connection.close
276
-
277
- # Signal the connection task to exit.
278
- done = @connection_promises.delete(connection)
279
- done&.resolve(true)
280
-
281
- # Resolve all_peers_gone once: had peers, now have none.
282
- if @peer_connected.resolved? && @connections.empty?
283
- @all_peers_gone.resolve(true)
284
- end
285
-
286
- # Auto-reconnect if this was a connected (not bound) endpoint
287
- if endpoint && @connected_endpoints.include?(endpoint) && !@closed && !@closing && @reconnect_enabled
288
- schedule_reconnect(endpoint)
289
- end
290
- end
291
-
292
-
293
- # Closes all connections and listeners.
294
- #
295
- # @return [void]
296
- #
297
- def close
298
- return if @closed || @closing
299
- @closing = true
300
-
301
- # Stop accepting new connections — but only if we already have
302
- # peers to drain to. With zero connections the listeners must
303
- # stay open so late-arriving peers can still receive queued
304
- # messages during the linger period.
305
- unless @connections.empty?
306
- @listeners.each(&:stop)
307
- @listeners.clear
308
- end
309
-
310
- # Linger: wait for send queues to drain before closing.
311
- # linger=0 → close immediately, linger=nil → wait forever.
312
- # @closed is set AFTER draining so reconnect tasks keep
313
- # running during the linger period.
314
- linger = @options.linger
315
- if linger.nil? || linger > 0
316
- drain_timeout = linger # nil = wait forever, >0 = seconds
317
- drain_send_queues(drain_timeout)
318
- end
319
-
320
- @closed = true
321
-
322
- # Stop any remaining listeners.
323
- @listeners.each(&:stop)
324
- @listeners.clear
325
-
326
- # Close connections — causes pump tasks to get EOFError/IOError
327
- @connections.each(&:close)
328
- @connections.clear
329
- # Stop any remaining pump tasks
330
- @routing.stop rescue nil
331
- @tasks.each { |t| t.stop rescue nil }
332
- @tasks.clear
333
- end
334
-
335
-
336
- # Spawns a transient pump task with error propagation.
337
- #
338
- # Unexpected exceptions are caught and forwarded to
339
- # {#signal_fatal_error} so blocked callers (send/recv)
340
- # see the real error instead of deadlocking.
341
- #
342
- # @param annotation [String] task annotation for debugging
343
- # @yield the pump loop body
344
- # @return [Async::Task]
345
- #
346
- def spawn_pump_task(annotation:, &block)
347
- @parent_task.async(transient: true, annotation: annotation) do
348
- yield
349
- rescue Async::Stop, ProtocolError, *CONNECTION_LOST
350
- # normal shutdown / expected disconnect
351
- rescue => error
352
- signal_fatal_error(error)
353
- end
354
- end
355
-
356
-
357
- # Wraps an unexpected pump error as {OMQ::SocketDeadError} and
358
- # unblocks any callers waiting on the recv queue.
359
- #
360
- # Must be called from inside a rescue block so that +error+ is
361
- # +$!+ and Ruby sets it as +#cause+ on the new exception.
362
- #
363
- # @param error [Exception]
364
- #
365
- def signal_fatal_error(error)
366
- return if @closing || @closed
367
- @fatal_error = begin
368
- raise OMQ::SocketDeadError, "internal error killed #{@socket_type} socket"
369
- rescue => wrapped
370
- wrapped
371
- end
372
- @routing.recv_queue.enqueue(nil) rescue nil
373
- @peer_connected.resolve(nil) rescue nil
374
- end
375
-
376
-
377
- private
378
-
379
-
380
- # Saves the current Async task so connection subtrees can be
381
- # spawned as siblings of the caller's task.
382
- #
383
- def capture_parent_task
384
- @parent_task ||= Async::Task.current? ? Async::Task.current : nil
385
- end
386
-
387
-
388
- # Spawns an isolated connection task as a sibling of accept/reconnect
389
- # tasks. All per-connection children (heartbeat, recv pump, reaper)
390
- # live inside this task. When the connection dies, the entire subtree
391
- # is cleaned up by Async.
392
- #
393
- def spawn_connection(io, as_server:, endpoint: nil)
394
- task = @parent_task&.async(transient: true, annotation: "conn #{endpoint}") do
395
- done = Async::Promise.new
396
- setup_connection(io, as_server: as_server, endpoint: endpoint, done: done)
397
- done.wait
398
- rescue ProtocolError, *CONNECTION_LOST
399
- # handshake failed or connection lost — subtree cleaned up
400
- end
401
- @tasks << task if task
402
- end
403
-
404
-
405
- # Waits for the send queue to drain.
406
- #
407
- # @param timeout [Numeric, nil] max seconds to wait (nil = forever)
408
- #
409
- def drain_send_queues(timeout)
410
- return unless @routing.respond_to?(:send_queue)
411
- deadline = timeout ? Async::Clock.now + timeout : nil
412
-
413
- until @routing.send_queue.empty? &&
414
- (!@routing.respond_to?(:send_pump_idle?) || @routing.send_pump_idle?)
415
- if deadline
416
- remaining = deadline - Async::Clock.now
417
- break if remaining <= 0
418
- end
419
- sleep 0.001
420
- end
421
- end
422
-
423
-
424
- # Performs the ZMTP handshake, starts heartbeating, and registers
425
- # the new connection with the routing strategy.
426
- #
427
- # @param io [#read, #write, #close] underlying transport stream
428
- # @param as_server [Boolean] whether we are the ZMTP server side
429
- # @param endpoint [String, nil] endpoint for reconnection tracking
430
- # @param done [Async::Promise, nil] resolved when the connection is lost
431
- #
432
- def setup_connection(io, as_server:, endpoint: nil, done: nil)
433
- conn = Connection.new(
434
- io,
435
- socket_type: @socket_type.to_s,
436
- identity: @options.identity,
437
- as_server: as_server,
438
- mechanism: @options.mechanism&.dup,
439
- max_message_size: @options.max_message_size,
440
- )
441
- conn.handshake!
442
- start_heartbeat(conn)
443
- @connections << conn
444
- @connection_endpoints[conn] = endpoint if endpoint
445
- @connection_promises[conn] = done if done
446
- @routing.connection_added(conn)
447
- @peer_connected.resolve(conn)
448
- rescue ProtocolError, *CONNECTION_LOST
449
- conn&.close
450
- raise
451
- end
452
-
453
-
454
- # Spawns a heartbeat task for the connection.
455
- # The connection only tracks timestamps — the engine drives the loop.
456
- #
457
- # @param conn [Connection]
458
- # @return [void]
459
- #
460
- def start_heartbeat(conn)
461
- interval = @options.heartbeat_interval
462
- return unless interval
463
-
464
- ttl = @options.heartbeat_ttl || interval
465
- timeout = @options.heartbeat_timeout || interval
466
- conn.touch_heartbeat
467
-
468
- @tasks << Reactor.spawn_pump(annotation: "heartbeat") do
469
- loop do
470
- sleep interval
471
- conn.send_command(Codec::Command.ping(ttl: ttl, context: "".b))
472
- if conn.heartbeat_expired?(timeout)
473
- conn.close
474
- break
475
- end
476
- end
477
- rescue *CONNECTION_LOST
478
- # connection closed
479
- end
480
- end
481
-
482
-
483
- # Spawns a background task that reconnects to the given endpoint
484
- # with exponential back-off based on the reconnect_interval option.
485
- #
486
- # @param endpoint [String] endpoint to reconnect to
487
- # @param delay [Numeric, nil] initial delay in seconds (defaults to reconnect_interval)
488
- #
489
- def schedule_reconnect(endpoint, delay: nil)
490
- ri = @options.reconnect_interval
491
- if ri.is_a?(Range)
492
- delay ||= ri.begin
493
- max_delay = ri.end
494
- else
495
- delay ||= ri
496
- max_delay = nil
497
- end
498
-
499
- @tasks << Reactor.spawn_pump(annotation: "reconnect #{endpoint}") do
500
- loop do
501
- break if @closed
502
- sleep delay if delay > 0
503
- break if @closed
504
- begin
505
- transport = transport_for(endpoint)
506
- transport.connect(endpoint, self)
507
- break # connected successfully
508
- rescue *CONNECTION_LOST, *CONNECTION_FAILED, ProtocolError
509
- delay = [delay * 2, max_delay].min if max_delay
510
- # After first attempt with delay: 0, use the configured interval
511
- delay = ri.is_a?(Range) ? ri.begin : ri if delay == 0
512
- end
513
- end
514
- rescue Async::Stop
515
- # normal shutdown
516
- rescue => error
517
- signal_fatal_error(error)
518
- end
519
- end
520
-
521
-
522
- # Eagerly validates TCP hostnames so resolution errors fail
523
- # on connect, not silently in the background reconnect loop.
524
- # Reconnects still re-resolve (DNS may change), and transient
525
- # resolution failures during reconnect are retried with backoff.
526
- #
527
- def validate_endpoint!(endpoint)
528
- return unless endpoint.start_with?("tcp://")
529
- host = URI.parse(endpoint.sub("tcp://", "http://")).hostname
530
- Addrinfo.getaddrinfo(host, nil, nil, :STREAM) if host
531
- end
532
-
533
-
534
- def transport_for(endpoint)
535
- case endpoint
536
- when /\Atcp:\/\// then Transport::TCP
537
- when /\Aipc:\/\// then Transport::IPC
538
- when /\Ainproc:\/\// then Transport::Inproc
539
- else raise ArgumentError, "unsupported transport: #{endpoint}"
540
- end
541
- end
542
-
543
-
544
- def extract_tcp_port(endpoint)
545
- return nil unless endpoint&.start_with?("tcp://")
546
- port = endpoint.split(":").last.to_i
547
- port.positive? ? port : nil
548
- end
549
- end
550
- end
551
- end
@@ -1,48 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module OMQ
4
- module ZMTP
5
- # Pure Ruby socket options.
6
- #
7
- # All timeouts are in seconds (Numeric) or nil (no timeout).
8
- # HWM values are integers.
9
- #
10
- class Options
11
- DEFAULT_HWM = 1000
12
-
13
- # @param linger [Integer] linger period in seconds (default 0)
14
- #
15
- def initialize(linger: 0)
16
- @send_hwm = DEFAULT_HWM
17
- @recv_hwm = DEFAULT_HWM
18
- @linger = linger
19
- @identity = "".b
20
- @router_mandatory = false
21
- @read_timeout = nil # seconds, nil = no timeout
22
- @write_timeout = nil
23
- @reconnect_interval = 0.1 # seconds, or Range for backoff (e.g. 0.1..5.0)
24
- @heartbeat_interval = nil # seconds, nil = disabled
25
- @heartbeat_ttl = nil # seconds, nil = use heartbeat_interval
26
- @heartbeat_timeout = nil # seconds, nil = use heartbeat_interval
27
- @max_message_size = nil # bytes, nil = unlimited
28
- @conflate = false
29
- @mechanism = Mechanism::Null.new
30
- end
31
-
32
- attr_accessor :send_hwm, :recv_hwm,
33
- :linger, :identity,
34
- :router_mandatory, :conflate,
35
- :read_timeout, :write_timeout,
36
- :reconnect_interval,
37
- :heartbeat_interval, :heartbeat_ttl, :heartbeat_timeout,
38
- :max_message_size,
39
- :mechanism
40
-
41
- alias_method :router_mandatory?, :router_mandatory
42
- alias_method :recv_timeout, :read_timeout
43
- alias_method :recv_timeout=, :read_timeout=
44
- alias_method :send_timeout, :write_timeout
45
- alias_method :send_timeout=, :write_timeout=
46
- end
47
- end
48
- end