omq 0.10.0 → 0.12.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 (49) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +157 -0
  3. data/README.md +31 -4
  4. data/lib/omq/drop_queue.rb +54 -0
  5. data/lib/omq/engine.rb +103 -61
  6. data/lib/omq/monitor_event.rb +16 -0
  7. data/lib/omq/options.rb +6 -2
  8. data/lib/omq/pair.rb +2 -2
  9. data/lib/omq/pub_sub.rb +13 -12
  10. data/lib/omq/push_pull.rb +4 -4
  11. data/lib/omq/queue_interface.rb +73 -0
  12. data/lib/omq/readable.rb +2 -0
  13. data/lib/omq/req_rep.rb +4 -4
  14. data/lib/omq/router_dealer.rb +4 -4
  15. data/lib/omq/routing/dealer.rb +1 -1
  16. data/lib/omq/routing/fan_out.rb +26 -5
  17. data/lib/omq/routing/pair.rb +2 -2
  18. data/lib/omq/routing/pull.rb +1 -1
  19. data/lib/omq/routing/push.rb +2 -0
  20. data/lib/omq/routing/rep.rb +2 -2
  21. data/lib/omq/routing/req.rb +8 -3
  22. data/lib/omq/routing/round_robin.rb +4 -12
  23. data/lib/omq/routing/router.rb +2 -2
  24. data/lib/omq/routing/sub.rb +1 -2
  25. data/lib/omq/routing/xpub.rb +1 -1
  26. data/lib/omq/routing/xsub.rb +2 -2
  27. data/lib/omq/routing.rb +41 -11
  28. data/lib/omq/socket.rb +49 -2
  29. data/lib/omq/transport/inproc.rb +25 -7
  30. data/lib/omq/transport/ipc.rb +16 -4
  31. data/lib/omq/transport/tcp.rb +31 -8
  32. data/lib/omq/version.rb +1 -1
  33. data/lib/omq/writable.rb +1 -0
  34. data/lib/omq.rb +6 -16
  35. metadata +4 -15
  36. data/lib/omq/channel.rb +0 -14
  37. data/lib/omq/client_server.rb +0 -37
  38. data/lib/omq/peer.rb +0 -26
  39. data/lib/omq/radio_dish.rb +0 -74
  40. data/lib/omq/routing/channel.rb +0 -83
  41. data/lib/omq/routing/client.rb +0 -56
  42. data/lib/omq/routing/dish.rb +0 -78
  43. data/lib/omq/routing/gather.rb +0 -46
  44. data/lib/omq/routing/peer.rb +0 -101
  45. data/lib/omq/routing/radio.rb +0 -140
  46. data/lib/omq/routing/scatter.rb +0 -82
  47. data/lib/omq/routing/server.rb +0 -101
  48. data/lib/omq/scatter_gather.rb +0 -23
  49. data/lib/omq/single_frame.rb +0 -18
data/lib/omq/pub_sub.rb CHANGED
@@ -4,8 +4,8 @@ module OMQ
4
4
  class PUB < Socket
5
5
  include Writable
6
6
 
7
- def initialize(endpoints = nil, linger: 0, conflate: false)
8
- _init_engine(:PUB, linger: linger, conflate: conflate)
7
+ def initialize(endpoints = nil, linger: 0, on_mute: :drop_newest, conflate: false, backend: nil)
8
+ _init_engine(:PUB, linger: linger, on_mute: on_mute, conflate: conflate, backend: backend)
9
9
  _attach(endpoints, default: :bind)
10
10
  end
11
11
  end
@@ -21,13 +21,14 @@ module OMQ
21
21
 
22
22
  # @param endpoints [String, nil]
23
23
  # @param linger [Integer]
24
- # @param prefix [String, nil] subscription prefix; +nil+ (default)
24
+ # @param subscribe [String, nil] subscription prefix; +nil+ (default)
25
25
  # means no subscription — call {#subscribe} explicitly.
26
+ # @param on_mute [Symbol] :block (default), :drop_newest, or :drop_oldest
26
27
  #
27
- def initialize(endpoints = nil, linger: 0, prefix: nil)
28
- _init_engine(:SUB, linger: linger)
28
+ def initialize(endpoints = nil, linger: 0, subscribe: nil, on_mute: :block, backend: nil)
29
+ _init_engine(:SUB, linger: linger, on_mute: on_mute, backend: backend)
29
30
  _attach(endpoints, default: :connect)
30
- subscribe(prefix) unless prefix.nil?
31
+ self.subscribe(subscribe) unless subscribe.nil?
31
32
  end
32
33
 
33
34
  # Subscribes to a topic prefix.
@@ -53,8 +54,8 @@ module OMQ
53
54
  include Readable
54
55
  include Writable
55
56
 
56
- def initialize(endpoints = nil, linger: 0)
57
- _init_engine(:XPUB, linger: linger)
57
+ def initialize(endpoints = nil, linger: 0, on_mute: :drop_newest, backend: nil)
58
+ _init_engine(:XPUB, linger: linger, on_mute: on_mute, backend: backend)
58
59
  _attach(endpoints, default: :bind)
59
60
  end
60
61
  end
@@ -65,13 +66,13 @@ module OMQ
65
66
 
66
67
  # @param endpoints [String, nil]
67
68
  # @param linger [Integer]
68
- # @param prefix [String, nil] subscription prefix; +nil+ (default)
69
+ # @param subscribe [String, nil] subscription prefix; +nil+ (default)
69
70
  # means no subscription — send a subscribe frame explicitly.
70
71
  #
71
- def initialize(endpoints = nil, linger: 0, prefix: nil)
72
- _init_engine(:XSUB, linger: linger)
72
+ def initialize(endpoints = nil, linger: 0, subscribe: nil, on_mute: :block, backend: nil)
73
+ _init_engine(:XSUB, linger: linger, on_mute: on_mute, backend: backend)
73
74
  _attach(endpoints, default: :connect)
74
- send("\x01#{prefix}".b) unless prefix.nil?
75
+ send("\x01#{subscribe}".b) unless subscribe.nil?
75
76
  end
76
77
  end
77
78
  end
data/lib/omq/push_pull.rb CHANGED
@@ -4,8 +4,8 @@ module OMQ
4
4
  class PUSH < Socket
5
5
  include Writable
6
6
 
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)
7
+ def initialize(endpoints = nil, linger: 0, send_hwm: nil, send_timeout: nil, backend: nil)
8
+ _init_engine(:PUSH, linger: linger, send_hwm: send_hwm, send_timeout: send_timeout, backend: backend)
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 Readable
15
15
 
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)
16
+ def initialize(endpoints = nil, linger: 0, recv_hwm: nil, recv_timeout: nil, backend: nil)
17
+ _init_engine(:PULL, linger: linger, recv_hwm: recv_hwm, recv_timeout: recv_timeout, backend: backend)
18
18
  _attach(endpoints, default: :bind)
19
19
  end
20
20
  end
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OMQ
4
+ # Async::Queue-compatible read interface.
5
+ #
6
+ # Automatically included by {Readable}. Provides #dequeue, #pop,
7
+ # #wait, and #each so sockets can be used where an Async::Queue
8
+ # is expected.
9
+ #
10
+ module QueueReadable
11
+ # Dequeues the next message.
12
+ #
13
+ # @param timeout [Numeric, nil] timeout in seconds (overrides
14
+ # the socket's +read_timeout+ for this call)
15
+ # @return [Array<String>] message parts
16
+ # @raise [IO::TimeoutError] if timeout exceeded
17
+ #
18
+ def dequeue(timeout: @options.read_timeout)
19
+ msg = @recv_mutex.synchronize { @recv_buffer.shift }
20
+ return msg if msg
21
+
22
+ batch = Reactor.run { with_timeout(timeout) { @engine.dequeue_recv_batch(Readable::RECV_BATCH_SIZE) } }
23
+ msg = batch.shift
24
+ @recv_mutex.synchronize { @recv_buffer.concat(batch) } unless batch.empty?
25
+ msg
26
+ end
27
+
28
+ alias_method :pop, :dequeue
29
+
30
+ # Waits for the next message indefinitely (ignores read_timeout).
31
+ #
32
+ # @return [Array<String>] message parts
33
+ #
34
+ def wait
35
+ dequeue(timeout: nil)
36
+ end
37
+
38
+ # Yields each received message until the socket is closed or
39
+ # a receive timeout expires.
40
+ #
41
+ # @yield [Array<String>] message parts
42
+ # @return [void]
43
+ #
44
+ def each
45
+ while (msg = receive)
46
+ yield msg
47
+ end
48
+ rescue IO::TimeoutError
49
+ nil
50
+ end
51
+ end
52
+
53
+
54
+ # Async::Queue-compatible write interface.
55
+ #
56
+ # Automatically included by {Writable}. Provides #enqueue, #push,
57
+ # and #signal so sockets can be used where an Async::Queue is
58
+ # expected.
59
+ #
60
+ module QueueWritable
61
+ # Enqueues one or more messages for sending.
62
+ #
63
+ # @param messages [String, Array<String>]
64
+ # @return [self]
65
+ #
66
+ def enqueue(*messages)
67
+ messages.each { |msg| send(msg) }
68
+ self
69
+ end
70
+
71
+ alias_method :push, :enqueue
72
+ end
73
+ end
data/lib/omq/readable.rb CHANGED
@@ -6,6 +6,8 @@ module OMQ
6
6
  # Pure Ruby Readable mixin. Dequeues messages from the engine's recv queue.
7
7
  #
8
8
  module Readable
9
+ include QueueReadable
10
+
9
11
  # Maximum messages to prefetch from the recv queue per drain.
10
12
  RECV_BATCH_SIZE = 64
11
13
 
data/lib/omq/req_rep.rb CHANGED
@@ -5,8 +5,8 @@ module OMQ
5
5
  include Readable
6
6
  include Writable
7
7
 
8
- def initialize(endpoints = nil, linger: 0)
9
- _init_engine(:REQ, linger: linger)
8
+ def initialize(endpoints = nil, linger: 0, backend: nil)
9
+ _init_engine(:REQ, linger: linger, backend: backend)
10
10
  _attach(endpoints, default: :connect)
11
11
  end
12
12
  end
@@ -15,8 +15,8 @@ module OMQ
15
15
  include Readable
16
16
  include Writable
17
17
 
18
- def initialize(endpoints = nil, linger: 0)
19
- _init_engine(:REP, linger: linger)
18
+ def initialize(endpoints = nil, linger: 0, backend: nil)
19
+ _init_engine(:REP, linger: linger, backend: backend)
20
20
  _attach(endpoints, default: :bind)
21
21
  end
22
22
  end
@@ -5,8 +5,8 @@ module OMQ
5
5
  include Readable
6
6
  include Writable
7
7
 
8
- def initialize(endpoints = nil, linger: 0)
9
- _init_engine(:DEALER, linger: linger)
8
+ def initialize(endpoints = nil, linger: 0, backend: nil)
9
+ _init_engine(:DEALER, linger: linger, backend: backend)
10
10
  _attach(endpoints, default: :connect)
11
11
  end
12
12
  end
@@ -17,8 +17,8 @@ module OMQ
17
17
  include Readable
18
18
  include Writable
19
19
 
20
- def initialize(endpoints = nil, linger: 0)
21
- _init_engine(:ROUTER, linger: linger)
20
+ def initialize(endpoints = nil, linger: 0, backend: nil)
21
+ _init_engine(:ROUTER, linger: linger, backend: backend)
22
22
  _attach(endpoints, default: :bind)
23
23
  end
24
24
 
@@ -13,7 +13,7 @@ module OMQ
13
13
  #
14
14
  def initialize(engine)
15
15
  @engine = engine
16
- @recv_queue = Async::LimitedQueue.new(engine.options.recv_hwm)
16
+ @recv_queue = Routing.build_queue(engine.options.recv_hwm, :block)
17
17
  @tasks = []
18
18
  init_round_robin(engine)
19
19
  end
@@ -13,12 +13,16 @@ module OMQ
13
13
  module FanOut
14
14
  attr_reader :subscriber_joined
15
15
 
16
+ # @return [Boolean] true when the send pump is idle (not sending a batch)
17
+ def send_pump_idle? = @send_pump_idle
18
+
16
19
  private
17
20
 
18
21
  def init_fan_out(engine)
19
22
  @connections = []
20
23
  @subscriptions = {} # connection => Set of prefixes
21
- @send_queue = Async::LimitedQueue.new(engine.options.send_hwm)
24
+ @send_queue = Routing.build_queue(engine.options.send_hwm, :block)
25
+ @on_mute = engine.options.on_mute
22
26
  @send_pump_started = false
23
27
  @send_pump_idle = true
24
28
  @conflate = engine.options.conflate
@@ -57,8 +61,14 @@ module OMQ
57
61
  @subscriptions[conn]&.delete(prefix)
58
62
  end
59
63
 
60
- # @return [Boolean] true when the send pump is idle (not sending a batch)
61
- def send_pump_idle? = @send_pump_idle
64
+ # Returns true if the connection is muted (recv queue full) and
65
+ # the on_mute strategy says to drop rather than block.
66
+ #
67
+ def muted?(conn)
68
+ return false if @on_mute == :block
69
+ q = conn.direct_recv_queue if conn.respond_to?(:direct_recv_queue)
70
+ q&.respond_to?(:limited?) && q.limited?
71
+ end
62
72
 
63
73
 
64
74
  def start_send_pump
@@ -83,6 +93,7 @@ module OMQ
83
93
  end
84
94
  end
85
95
  @latest.each do |conn, parts|
96
+ next if muted?(conn)
86
97
  begin
87
98
  conn.write_message(parts)
88
99
  @written << conn
@@ -91,11 +102,21 @@ module OMQ
91
102
  end
92
103
  else
93
104
  batch.each do |parts|
94
- topic = parts.first || EMPTY_BINARY
105
+ topic = parts.first || EMPTY_BINARY
106
+ wire_bytes = nil
107
+
95
108
  @connections.each do |conn|
96
109
  next unless subscribed?(conn, topic)
110
+ next if muted?(conn)
97
111
  begin
98
- conn.write_message(parts)
112
+ if conn.respond_to?(:curve?) && conn.curve?
113
+ conn.write_message(parts)
114
+ elsif conn.respond_to?(:write_wire)
115
+ wire_bytes ||= Protocol::ZMTP::Codec::Frame.encode_message(parts)
116
+ conn.write_wire(wire_bytes)
117
+ else
118
+ conn.write_message(parts)
119
+ end
99
120
  @written << conn
100
121
  rescue *CONNECTION_LOST
101
122
  end
@@ -14,8 +14,8 @@ module OMQ
14
14
  def initialize(engine)
15
15
  @engine = engine
16
16
  @connection = nil
17
- @recv_queue = Async::LimitedQueue.new(engine.options.recv_hwm)
18
- @send_queue = Async::LimitedQueue.new(engine.options.send_hwm)
17
+ @recv_queue = Routing.build_queue(engine.options.recv_hwm, :block)
18
+ @send_queue = Routing.build_queue(engine.options.send_hwm, :block)
19
19
  @tasks = []
20
20
  @send_pump_idle = true
21
21
  end
@@ -9,7 +9,7 @@ module OMQ
9
9
  #
10
10
  def initialize(engine)
11
11
  @engine = engine
12
- @recv_queue = Async::LimitedQueue.new(engine.options.recv_hwm)
12
+ @recv_queue = Routing.build_queue(engine.options.recv_hwm, :block)
13
13
  @tasks = []
14
14
  end
15
15
 
@@ -76,6 +76,8 @@ module OMQ
76
76
  @engine.connection_lost(conn)
77
77
  end
78
78
  end
79
+
80
+
79
81
  end
80
82
  end
81
83
  end
@@ -15,8 +15,8 @@ module OMQ
15
15
 
16
16
  def initialize(engine)
17
17
  @engine = engine
18
- @recv_queue = Async::LimitedQueue.new(engine.options.recv_hwm)
19
- @send_queue = Async::LimitedQueue.new(engine.options.send_hwm)
18
+ @recv_queue = Routing.build_queue(engine.options.recv_hwm, :block)
19
+ @send_queue = Routing.build_queue(engine.options.send_hwm, :block)
20
20
  @pending_replies = []
21
21
  @tasks = []
22
22
  @send_pump_started = false
@@ -12,9 +12,10 @@ module OMQ
12
12
  # @param engine [Engine]
13
13
  #
14
14
  def initialize(engine)
15
- @engine = engine
16
- @recv_queue = Async::LimitedQueue.new(engine.options.recv_hwm)
17
- @tasks = []
15
+ @engine = engine
16
+ @recv_queue = Routing.build_queue(engine.options.recv_hwm, :block)
17
+ @tasks = []
18
+ @state = :ready # :ready or :waiting_reply
18
19
  init_round_robin(engine)
19
20
  end
20
21
 
@@ -22,6 +23,7 @@ module OMQ
22
23
  #
23
24
  attr_reader :recv_queue, :send_queue
24
25
 
26
+
25
27
  # @param connection [Connection]
26
28
  #
27
29
  def connection_added(connection)
@@ -29,6 +31,7 @@ module OMQ
29
31
  signal_connection_available
30
32
  update_direct_pipe
31
33
  task = @engine.start_recv_pump(connection, @recv_queue) do |msg|
34
+ @state = :ready
32
35
  msg.first&.empty? ? msg[1..] : msg
33
36
  end
34
37
  @tasks << task if task
@@ -45,6 +48,8 @@ module OMQ
45
48
  # @param parts [Array<String>]
46
49
  #
47
50
  def enqueue(parts)
51
+ raise SocketError, "REQ socket expects send/recv/send/recv order" unless @state == :ready
52
+ @state = :waiting_reply
48
53
  enqueue_round_robin(parts)
49
54
  end
50
55
 
@@ -12,6 +12,9 @@ module OMQ
12
12
  # their #initialize.
13
13
  #
14
14
  module RoundRobin
15
+ # @return [Boolean] true when the send pump is idle (not sending a batch)
16
+ def send_pump_idle? = @send_pump_idle
17
+
15
18
  private
16
19
 
17
20
 
@@ -23,7 +26,7 @@ module OMQ
23
26
  @connections = []
24
27
  @cycle = @connections.cycle
25
28
  @connection_available = Async::Promise.new
26
- @send_queue = Async::LimitedQueue.new(engine.options.send_hwm)
29
+ @send_queue = Routing.build_queue(engine.options.send_hwm, :block)
27
30
  @send_pump_started = false
28
31
  @send_pump_idle = true
29
32
  @direct_pipe = nil
@@ -90,13 +93,6 @@ module OMQ
90
93
  def transform_send(parts) = parts
91
94
 
92
95
 
93
- # Starts the background send pump that dequeues messages
94
- # and dispatches them round-robin across connections.
95
- #
96
- # @return [Boolean] true when the send pump is idle (not sending a batch)
97
- def send_pump_idle? = @send_pump_idle
98
-
99
-
100
96
  def start_send_pump
101
97
  @send_pump_started = true
102
98
  @tasks << @engine.spawn_pump_task(annotation: "send pump") do
@@ -144,14 +140,11 @@ module OMQ
144
140
  @written << conn
145
141
  rescue *CONNECTION_LOST
146
142
  @engine.connection_lost(conn)
147
- # Flush what we've written so far
148
143
  @written.each do |c|
149
144
  c.flush
150
145
  rescue *CONNECTION_LOST
151
- # will be cleaned up
152
146
  end
153
147
  @written.clear
154
- # Fall back to send_with_retry for this and remaining
155
148
  send_with_retry(parts)
156
149
  batch[(i + 1)..].each { |p| send_with_retry(p) }
157
150
  return
@@ -160,7 +153,6 @@ module OMQ
160
153
  @written.each do |conn|
161
154
  conn.flush
162
155
  rescue *CONNECTION_LOST
163
- # will be cleaned up
164
156
  end
165
157
  end
166
158
  end
@@ -15,8 +15,8 @@ module OMQ
15
15
  #
16
16
  def initialize(engine)
17
17
  @engine = engine
18
- @recv_queue = Async::LimitedQueue.new(engine.options.recv_hwm)
19
- @send_queue = Async::LimitedQueue.new(engine.options.send_hwm)
18
+ @recv_queue = Routing.build_queue(engine.options.recv_hwm, :block)
19
+ @send_queue = Routing.build_queue(engine.options.send_hwm, :block)
20
20
  @connections_by_identity = {}
21
21
  @identity_by_connection = {}
22
22
  @tasks = []
@@ -13,7 +13,7 @@ module OMQ
13
13
  def initialize(engine)
14
14
  @engine = engine
15
15
  @connections = []
16
- @recv_queue = Async::LimitedQueue.new(engine.options.recv_hwm)
16
+ @recv_queue = Routing.build_queue(engine.options.recv_hwm, engine.options.on_mute)
17
17
  @subscriptions = Set.new
18
18
  @tasks = []
19
19
  end
@@ -26,7 +26,6 @@ module OMQ
26
26
  #
27
27
  def connection_added(connection)
28
28
  @connections << connection
29
- # Send existing subscriptions to new peer
30
29
  @subscriptions.each do |prefix|
31
30
  connection.send_command(Protocol::ZMTP::Codec::Command.subscribe(prefix))
32
31
  end
@@ -15,7 +15,7 @@ module OMQ
15
15
  #
16
16
  def initialize(engine)
17
17
  @engine = engine
18
- @recv_queue = Async::LimitedQueue.new(engine.options.recv_hwm)
18
+ @recv_queue = Routing.build_queue(engine.options.recv_hwm, :block)
19
19
  @tasks = []
20
20
  init_fan_out(engine)
21
21
  end
@@ -14,8 +14,8 @@ module OMQ
14
14
  def initialize(engine)
15
15
  @engine = engine
16
16
  @connections = []
17
- @recv_queue = Async::LimitedQueue.new(engine.options.recv_hwm)
18
- @send_queue = Async::LimitedQueue.new(engine.options.send_hwm)
17
+ @recv_queue = Routing.build_queue(engine.options.recv_hwm, engine.options.on_mute)
18
+ @send_queue = Routing.build_queue(engine.options.send_hwm, :block)
19
19
  @tasks = []
20
20
  @send_pump_started = false
21
21
  @send_pump_idle = true
data/lib/omq/routing.rb CHANGED
@@ -3,6 +3,7 @@
3
3
  require "async"
4
4
  require "async/queue"
5
5
  require "async/limited_queue"
6
+ require_relative "drop_queue"
6
7
 
7
8
  module OMQ
8
9
  # Routing strategies for each ZMQ socket type.
@@ -14,6 +15,42 @@ module OMQ
14
15
  # Shared frozen empty binary string to avoid repeated allocations.
15
16
  EMPTY_BINARY = "".b.freeze
16
17
 
18
+ # Plugin registry for socket types not built into omq.
19
+ # Populated by sister gems via +Routing.register+.
20
+ #
21
+ @registry = {}
22
+
23
+ class << self
24
+ # Registers a routing strategy class for a socket type.
25
+ # Called by omq-draft (and other plugins) at require time.
26
+ #
27
+ # @param socket_type [Symbol] e.g. :RADIO, :CLIENT
28
+ # @param strategy_class [Class]
29
+ #
30
+ def register(socket_type, strategy_class)
31
+ @registry[socket_type] = strategy_class
32
+ end
33
+ end
34
+
35
+ # Builds a send or recv queue based on the mute strategy.
36
+ #
37
+ # @param hwm [Integer] high water mark
38
+ # @param on_mute [Symbol] :block, :drop_newest, or :drop_oldest
39
+ # @return [Async::LimitedQueue, DropQueue]
40
+ #
41
+ def self.build_queue(hwm, on_mute)
42
+ return Async::Queue.new if hwm.nil? || hwm == 0
43
+
44
+ case on_mute
45
+ when :block
46
+ Async::LimitedQueue.new(hwm)
47
+ when :drop_newest, :drop_oldest
48
+ DropQueue.new(hwm, strategy: on_mute)
49
+ else
50
+ raise ArgumentError, "unknown on_mute strategy: #{on_mute.inspect}"
51
+ end
52
+ end
53
+
17
54
  # Drains all available messages from +queue+ into +batch+ without
18
55
  # blocking. Call after the initial blocking dequeue.
19
56
  #
@@ -49,17 +86,10 @@ module OMQ
49
86
  when :SUB then Sub
50
87
  when :XPUB then XPub
51
88
  when :XSUB then XSub
52
- when :PUSH then Push
53
- when :PULL then Pull
54
- when :CLIENT then Client
55
- when :SERVER then Server
56
- when :RADIO then Radio
57
- when :DISH then Dish
58
- when :SCATTER then Scatter
59
- when :GATHER then Gather
60
- when :PEER then Peer
61
- when :CHANNEL then Channel
62
- else raise ArgumentError, "unknown socket type: #{socket_type}"
89
+ when :PUSH then Push
90
+ when :PULL then Pull
91
+ else
92
+ @registry[socket_type] or raise ArgumentError, "unknown socket type: #{socket_type.inspect}"
63
93
  end
64
94
  end
65
95
  end
data/lib/omq/socket.rb CHANGED
@@ -36,6 +36,7 @@ module OMQ
36
36
  :heartbeat_ttl, :heartbeat_ttl=,
37
37
  :heartbeat_timeout, :heartbeat_timeout=,
38
38
  :max_message_size, :max_message_size=,
39
+ :on_mute, :on_mute=,
39
40
  :mechanism, :mechanism=
40
41
 
41
42
 
@@ -140,6 +141,46 @@ module OMQ
140
141
  end
141
142
 
142
143
 
144
+ # Yields lifecycle events for this socket.
145
+ #
146
+ # Spawns a background fiber that reads from an internal event queue.
147
+ # The block receives {MonitorEvent} instances until the socket is
148
+ # closed or the returned task is stopped.
149
+ #
150
+ # @yield [event] called for each lifecycle event
151
+ # @yieldparam event [MonitorEvent]
152
+ # @return [Async::Task] the monitor task (call +#stop+ to end early)
153
+ #
154
+ # @example
155
+ # task = socket.monitor do |event|
156
+ # case event
157
+ # in type: :connected, endpoint:
158
+ # puts "peer up: #{endpoint}"
159
+ # in type: :disconnected, endpoint:
160
+ # puts "peer down: #{endpoint}"
161
+ # end
162
+ # end
163
+ # # later:
164
+ # task.stop
165
+ #
166
+ def monitor(&block)
167
+ ensure_parent_task
168
+ queue = Async::Queue.new
169
+ @engine.monitor_queue = queue
170
+ Reactor.run do
171
+ @engine.parent_task.async(transient: true, annotation: "monitor") do
172
+ while (event = queue.dequeue)
173
+ block.call(event)
174
+ end
175
+ rescue Async::Stop
176
+ ensure
177
+ @engine.monitor_queue = nil
178
+ block.call(MonitorEvent.new(type: :monitor_stopped))
179
+ end
180
+ end
181
+ end
182
+
183
+
143
184
  # Disable auto-reconnect for connected endpoints.
144
185
  def reconnect_enabled=(val)
145
186
  @engine.reconnect_enabled = val
@@ -224,16 +265,22 @@ module OMQ
224
265
  # @param linger [Integer]
225
266
  #
226
267
  def _init_engine(socket_type, linger:, send_hwm: nil, recv_hwm: nil,
227
- send_timeout: nil, recv_timeout: nil, conflate: false)
268
+ send_timeout: nil, recv_timeout: nil, conflate: false,
269
+ on_mute: nil, backend: nil)
228
270
  @options = Options.new(linger: linger)
229
271
  @options.send_hwm = send_hwm if send_hwm
230
272
  @options.recv_hwm = recv_hwm if recv_hwm
231
273
  @options.send_timeout = send_timeout if send_timeout
232
274
  @options.recv_timeout = recv_timeout if recv_timeout
233
275
  @options.conflate = conflate
276
+ @options.on_mute = on_mute if on_mute
234
277
  @recv_buffer = []
235
278
  @recv_mutex = Mutex.new
236
- @engine = Engine.new(socket_type, @options)
279
+ @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}"
283
+ end
237
284
  end
238
285
  end
239
286
  end