omq 0.22.0 → 0.23.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 (51) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +139 -0
  3. data/README.md +17 -21
  4. data/lib/omq/channel.rb +35 -0
  5. data/lib/omq/client_server.rb +72 -0
  6. data/lib/omq/constants.rb +68 -0
  7. data/lib/omq/engine/connection_lifecycle.rb +11 -3
  8. data/lib/omq/engine/heartbeat.rb +3 -4
  9. data/lib/omq/engine/maintenance.rb +4 -5
  10. data/lib/omq/engine/reconnect.rb +12 -11
  11. data/lib/omq/engine/recv_pump.rb +3 -4
  12. data/lib/omq/engine/socket_lifecycle.rb +26 -9
  13. data/lib/omq/engine.rb +196 -85
  14. data/lib/omq/peer.rb +49 -0
  15. data/lib/omq/pub_sub.rb +2 -2
  16. data/lib/omq/radio_dish.rb +122 -0
  17. data/lib/omq/reactor.rb +14 -5
  18. data/lib/omq/routing/channel.rb +110 -0
  19. data/lib/omq/routing/client.rb +70 -0
  20. data/lib/omq/routing/conn_send_pump.rb +12 -10
  21. data/lib/omq/routing/dealer.rb +1 -13
  22. data/lib/omq/routing/dish.rb +94 -0
  23. data/lib/omq/routing/fan_out.rb +14 -13
  24. data/lib/omq/routing/gather.rb +60 -0
  25. data/lib/omq/routing/pair.rb +7 -24
  26. data/lib/omq/routing/peer.rb +95 -0
  27. data/lib/omq/routing/pub.rb +0 -11
  28. data/lib/omq/routing/pull.rb +1 -13
  29. data/lib/omq/routing/push.rb +1 -10
  30. data/lib/omq/routing/radio.rb +187 -0
  31. data/lib/omq/routing/rep.rb +10 -20
  32. data/lib/omq/routing/req.rb +5 -17
  33. data/lib/omq/routing/round_robin.rb +17 -18
  34. data/lib/omq/routing/router.rb +3 -17
  35. data/lib/omq/routing/scatter.rb +77 -0
  36. data/lib/omq/routing/server.rb +90 -0
  37. data/lib/omq/routing/sub.rb +1 -13
  38. data/lib/omq/routing/xpub.rb +0 -11
  39. data/lib/omq/routing/xsub.rb +6 -23
  40. data/lib/omq/routing.rb +5 -2
  41. data/lib/omq/scatter_gather.rb +56 -0
  42. data/lib/omq/socket.rb +8 -23
  43. data/lib/omq/transport/inproc/direct_pipe.rb +17 -15
  44. data/lib/omq/transport/inproc.rb +11 -3
  45. data/lib/omq/transport/ipc.rb +41 -13
  46. data/lib/omq/transport/tcp.rb +59 -23
  47. data/lib/omq/transport/udp.rb +281 -0
  48. data/lib/omq/version.rb +1 -1
  49. data/lib/omq.rb +9 -64
  50. metadata +16 -2
  51. data/lib/omq/monitor_event.rb +0 -16
@@ -22,8 +22,6 @@ module OMQ
22
22
  @connection = nil
23
23
  @recv_queue = Routing.build_queue(engine.options.recv_hwm, :block)
24
24
  @send_queue = Routing.build_queue(engine.options.send_hwm, :block)
25
- @send_pump = nil
26
- @tasks = []
27
25
  end
28
26
 
29
27
 
@@ -52,8 +50,7 @@ module OMQ
52
50
  raise "PAIR allows only one peer" if @connection
53
51
  @connection = connection
54
52
 
55
- task = @engine.start_recv_pump(connection, @recv_queue)
56
- @tasks << task if task
53
+ @engine.start_recv_pump(connection, @recv_queue)
57
54
 
58
55
  unless connection.is_a?(Transport::Inproc::DirectPipe)
59
56
  start_send_pump(connection)
@@ -64,11 +61,7 @@ module OMQ
64
61
  # @param connection [Connection]
65
62
  #
66
63
  def connection_removed(connection)
67
- if @connection == connection
68
- @connection = nil
69
- @send_pump&.stop
70
- @send_pump = nil
71
- end
64
+ @connection = nil if @connection == connection
72
65
  end
73
66
 
74
67
 
@@ -84,16 +77,6 @@ module OMQ
84
77
  end
85
78
 
86
79
 
87
- # Stops all background tasks.
88
- #
89
- # @return [void]
90
- #
91
- def stop
92
- @tasks.each(&:stop)
93
- @tasks.clear
94
- end
95
-
96
-
97
80
  # @return [Boolean] true when the shared send queue is empty
98
81
  #
99
82
  def send_queues_drained?
@@ -105,10 +88,11 @@ module OMQ
105
88
 
106
89
 
107
90
  def start_send_pump(conn)
108
- @send_pump = @engine.spawn_conn_pump_task(conn, annotation: "send pump") do
91
+ @engine.spawn_conn_pump_task(conn, annotation: "send pump") do
92
+ batch = []
93
+
109
94
  loop do
110
- batch = [@send_queue.dequeue]
111
- Routing.drain_send_queue(@send_queue, batch)
95
+ Routing.dequeue_batch(@send_queue, batch)
112
96
 
113
97
  if batch.size == 1
114
98
  conn.write_message(batch.first)
@@ -120,10 +104,9 @@ module OMQ
120
104
  batch.each do |parts|
121
105
  @engine.emit_verbose_msg_sent(conn, parts)
122
106
  end
107
+ batch.clear
123
108
  end
124
109
  end
125
-
126
- @tasks << @send_pump
127
110
  end
128
111
 
129
112
  end
@@ -0,0 +1,95 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "securerandom"
4
+
5
+ module OMQ
6
+ module Routing
7
+ # PEER socket routing: bidirectional multi-peer with auto-generated
8
+ # 4-byte routing IDs.
9
+ #
10
+ # Prepends routing ID on receive. Strips routing ID on send and
11
+ # routes to the identified connection.
12
+ #
13
+ class Peer
14
+ # @return [Async::LimitedQueue]
15
+ #
16
+ attr_reader :recv_queue
17
+
18
+
19
+ # @return [Hash{String => Connection}] routing_id → connection
20
+ #
21
+ attr_reader :connections_by_routing_id
22
+
23
+
24
+ # @param engine [Engine]
25
+ #
26
+ def initialize(engine)
27
+ @engine = engine
28
+ @recv_queue = Routing.build_queue(engine.options.recv_hwm, :block)
29
+ @connections_by_routing_id = {}
30
+ @routing_id_by_connection = {}
31
+ @conn_queues = {}
32
+ end
33
+
34
+
35
+ # Dequeues the next received message. Blocks until one is available.
36
+ #
37
+ # @return [Array<String>, nil]
38
+ #
39
+ def dequeue_recv
40
+ @recv_queue.dequeue
41
+ end
42
+
43
+
44
+ # Wakes a blocked {#dequeue_recv} with a nil sentinel.
45
+ #
46
+ # @return [void]
47
+ #
48
+ def unblock_recv
49
+ @recv_queue.enqueue(nil)
50
+ end
51
+
52
+
53
+ # @param connection [Connection]
54
+ #
55
+ def connection_added(connection)
56
+ routing_id = SecureRandom.bytes(4)
57
+ @connections_by_routing_id[routing_id] = connection
58
+ @routing_id_by_connection[connection] = routing_id
59
+
60
+ @engine.start_recv_pump(connection, @recv_queue) { |msg| [routing_id, *msg] }
61
+
62
+ q = Routing.build_queue(@engine.options.send_hwm, :block)
63
+ @conn_queues[connection] = q
64
+ ConnSendPump.start(@engine, connection, q)
65
+ end
66
+
67
+
68
+ # @param connection [Connection]
69
+ #
70
+ def connection_removed(connection)
71
+ routing_id = @routing_id_by_connection.delete(connection)
72
+ @connections_by_routing_id.delete(routing_id) if routing_id
73
+ @conn_queues.delete(connection)
74
+ end
75
+
76
+
77
+ # @param parts [Array<String>]
78
+ #
79
+ def enqueue(parts)
80
+ routing_id = parts.first
81
+ conn = @connections_by_routing_id[routing_id]
82
+ return unless conn
83
+ @conn_queues[conn]&.enqueue(parts[1..])
84
+ end
85
+
86
+
87
+ # True when all per-connection send queues are empty.
88
+ #
89
+ def send_queues_drained?
90
+ @conn_queues.values.all?(&:empty?)
91
+ end
92
+
93
+ end
94
+ end
95
+ end
@@ -15,7 +15,6 @@ module OMQ
15
15
  #
16
16
  def initialize(engine)
17
17
  @engine = engine
18
- @tasks = []
19
18
  init_fan_out(engine)
20
19
  end
21
20
 
@@ -62,16 +61,6 @@ module OMQ
62
61
  fan_out_enqueue(parts)
63
62
  end
64
63
 
65
-
66
- # Stops all background tasks.
67
- #
68
- # @return [void]
69
- #
70
- def stop
71
- @tasks.each(&:stop)
72
- @tasks.clear
73
- end
74
-
75
64
  end
76
65
  end
77
66
  end
@@ -10,7 +10,6 @@ module OMQ
10
10
  def initialize(engine)
11
11
  @engine = engine
12
12
  @recv_queue = Routing.build_queue(engine.options.recv_hwm, :block)
13
- @tasks = []
14
13
  end
15
14
 
16
15
 
@@ -42,8 +41,7 @@ module OMQ
42
41
  # @param connection [Connection]
43
42
  #
44
43
  def connection_added(connection)
45
- task = @engine.start_recv_pump(connection, @recv_queue)
46
- @tasks << task if task
44
+ @engine.start_recv_pump(connection, @recv_queue)
47
45
  end
48
46
 
49
47
 
@@ -60,16 +58,6 @@ module OMQ
60
58
  raise "PULL sockets cannot send"
61
59
  end
62
60
 
63
-
64
- # Stops all background tasks.
65
- #
66
- # @return [void]
67
- #
68
- def stop
69
- @tasks.each(&:stop)
70
- @tasks.clear
71
- end
72
-
73
61
  end
74
62
  end
75
63
  end
@@ -12,7 +12,6 @@ module OMQ
12
12
  #
13
13
  def initialize(engine)
14
14
  @engine = engine
15
- @tasks = []
16
15
  init_round_robin(engine)
17
16
  end
18
17
 
@@ -57,14 +56,6 @@ module OMQ
57
56
  end
58
57
 
59
58
 
60
- # Stops all background tasks (send pumps, reapers).
61
- #
62
- def stop
63
- @tasks.each(&:stop)
64
- @tasks.clear
65
- end
66
-
67
-
68
59
  private
69
60
 
70
61
 
@@ -74,7 +65,7 @@ module OMQ
74
65
  #
75
66
  def start_reaper(conn)
76
67
  return if conn.is_a?(Transport::Inproc::DirectPipe)
77
- @tasks << @engine.spawn_conn_pump_task(conn, annotation: "reaper") do
68
+ @engine.spawn_conn_pump_task(conn, annotation: "reaper") do
78
69
  conn.receive_message # blocks until peer disconnects; then exits
79
70
  end
80
71
  end
@@ -0,0 +1,187 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OMQ
4
+ module Routing
5
+ # RADIO socket routing: group-based fan-out to DISH peers.
6
+ #
7
+ # Like PUB/FanOut but with exact group matching and JOIN/LEAVE
8
+ # commands instead of SUBSCRIBE/CANCEL.
9
+ #
10
+ # Messages are sent as two frames on the wire:
11
+ # group (MORE=1) + body (MORE=0)
12
+ #
13
+ class Radio
14
+ # Sentinel used for UDP connections that have no group filter:
15
+ # any group is considered a match.
16
+ #
17
+ ANY_GROUPS = Object.new.tap { |o| o.define_singleton_method(:include?) { |_| true } }.freeze
18
+
19
+
20
+ # @return [Async::LimitedQueue]
21
+ #
22
+ attr_reader :send_queue
23
+
24
+
25
+ # @param engine [Engine]
26
+ #
27
+ def initialize(engine)
28
+ @engine = engine
29
+ @connections = []
30
+ @groups = {} # connection => Set of joined groups (or ANY_GROUPS for UDP)
31
+ @send_queue = Routing.build_queue(engine.options.send_hwm, :block)
32
+ @on_mute = engine.options.on_mute
33
+ @send_pump_started = false
34
+ @conflate = engine.options.conflate
35
+ @written = Set.new
36
+ @latest = {} if @conflate
37
+ end
38
+
39
+
40
+ # RADIO is write-only.
41
+ #
42
+ def recv_queue
43
+ raise "RADIO sockets cannot receive"
44
+ end
45
+
46
+
47
+ # No-op; RADIO has no recv queue to unblock.
48
+ #
49
+ def unblock_recv
50
+ end
51
+
52
+
53
+ # @param connection [Connection]
54
+ #
55
+ def connection_added(connection)
56
+ @connections << connection
57
+ if connection.respond_to?(:read_frame)
58
+ @groups[connection] = Set.new
59
+ start_group_listener(connection)
60
+ else
61
+ @groups[connection] = ANY_GROUPS # UDP: fan-out to all groups
62
+ end
63
+ start_send_pump unless @send_pump_started
64
+ end
65
+
66
+
67
+ # @param connection [Connection]
68
+ #
69
+ def connection_removed(connection)
70
+ @connections.delete(connection)
71
+ @groups.delete(connection)
72
+ end
73
+
74
+
75
+ # Enqueues a message for sending.
76
+ #
77
+ # @param parts [Array<String>] [group, body]
78
+ #
79
+ def enqueue(parts)
80
+ @send_queue.enqueue(parts)
81
+ end
82
+
83
+
84
+ # True when the send queue is empty.
85
+ #
86
+ def send_queues_drained?
87
+ @send_queue.empty?
88
+ end
89
+
90
+
91
+ private
92
+
93
+
94
+ def muted?(conn)
95
+ return false if @on_mute == :block
96
+ q = conn.direct_recv_queue if conn.respond_to?(:direct_recv_queue)
97
+ q&.respond_to?(:limited?) && q.limited?
98
+ end
99
+
100
+
101
+ def start_send_pump
102
+ @send_pump_started = true
103
+ @engine.spawn_pump_task(annotation: "send pump", parent: @engine.barrier) do
104
+ batch = []
105
+
106
+ loop do
107
+ @send_pump_idle = true
108
+ Routing.dequeue_batch(@send_queue, batch)
109
+ @send_pump_idle = false
110
+
111
+ @written.clear
112
+
113
+ if @conflate
114
+ # Keep only the last matching message per connection.
115
+ @latest.clear
116
+ batch.each do |parts|
117
+ group = parts[0]
118
+ body = parts[1] || EMPTY_BINARY
119
+ @connections.each do |conn|
120
+ next unless @groups[conn]&.include?(group)
121
+ @latest[conn] = [group, body]
122
+ end
123
+ end
124
+ @latest.each do |conn, msg|
125
+ next if muted?(conn)
126
+ begin
127
+ conn.write_message(msg)
128
+ @written << conn
129
+ rescue *CONNECTION_LOST
130
+ end
131
+ end
132
+ else
133
+ batch.each do |parts|
134
+ group = parts[0]
135
+ body = parts[1] || EMPTY_BINARY
136
+ msg = [group, body]
137
+ wire_bytes = nil
138
+
139
+ @connections.each do |conn|
140
+ next unless @groups[conn]&.include?(group)
141
+ next if muted?(conn)
142
+ begin
143
+ if conn.respond_to?(:curve?) && conn.curve?
144
+ conn.write_message(msg)
145
+ elsif conn.respond_to?(:write_wire)
146
+ wire_bytes ||= Protocol::ZMTP::Codec::Frame.encode_message(msg)
147
+ conn.write_wire(wire_bytes)
148
+ else
149
+ conn.write_message(msg)
150
+ end
151
+ @written << conn
152
+ rescue *CONNECTION_LOST
153
+ end
154
+ end
155
+ end
156
+ end
157
+
158
+ @written.each do |conn|
159
+ conn.flush
160
+ rescue *CONNECTION_LOST
161
+ end
162
+
163
+ batch.clear
164
+ end
165
+ end
166
+ end
167
+
168
+
169
+ def start_group_listener(conn)
170
+ @engine.spawn_conn_pump_task(conn, annotation: "group listener") do
171
+ loop do
172
+ frame = conn.read_frame
173
+ next unless frame.command?
174
+ cmd = Protocol::ZMTP::Codec::Command.from_body(frame.body)
175
+ case cmd.name
176
+ when "JOIN"
177
+ @groups[conn]&.add(cmd.data)
178
+ when "LEAVE"
179
+ @groups[conn]&.delete(cmd.data)
180
+ end
181
+ end
182
+ end
183
+ end
184
+
185
+ end
186
+ end
187
+ end
@@ -23,9 +23,7 @@ module OMQ
23
23
  @engine = engine
24
24
  @recv_queue = Routing.build_queue(engine.options.recv_hwm, :block)
25
25
  @pending_replies = []
26
- @conn_queues = {} # connection => per-connection send queue
27
- @conn_send_tasks = {} # connection => send pump task
28
- @tasks = []
26
+ @conn_queues = {}
29
27
  end
30
28
 
31
29
 
@@ -50,28 +48,26 @@ module OMQ
50
48
  # @param connection [Connection]
51
49
  #
52
50
  def connection_added(connection)
53
- task = @engine.start_recv_pump(connection, @recv_queue) do |msg|
51
+ @engine.start_recv_pump(connection, @recv_queue) do |msg|
54
52
  delimiter = msg.index { |p| p.empty? } || msg.size
55
53
  envelope = msg[0, delimiter]
56
54
  body = msg[(delimiter + 1)..] || []
57
55
 
58
- @pending_replies << { conn: connection, envelope: envelope }
56
+ @pending_replies << [connection, envelope]
59
57
  body
60
58
  end
61
- @tasks << task if task
62
59
 
63
60
  q = Routing.build_queue(@engine.options.send_hwm, :block)
64
61
  @conn_queues[connection] = q
65
- @conn_send_tasks[connection] = ConnSendPump.start(@engine, connection, q, @tasks)
62
+ ConnSendPump.start(@engine, connection, q)
66
63
  end
67
64
 
68
65
 
69
66
  # @param connection [Connection]
70
67
  #
71
68
  def connection_removed(connection)
72
- @pending_replies.reject! { |r| r[:conn] == connection }
69
+ @pending_replies.reject! { |r| r[0] == connection }
73
70
  @conn_queues.delete(connection)
74
- @conn_send_tasks.delete(connection)&.stop
75
71
  end
76
72
 
77
73
 
@@ -83,18 +79,12 @@ module OMQ
83
79
  def enqueue(parts)
84
80
  reply_info = @pending_replies.shift
85
81
  return unless reply_info
86
- conn = reply_info[:conn]
87
- @conn_queues[conn]&.enqueue([*reply_info[:envelope], EMPTY_FRAME, *parts])
88
- end
89
-
90
82
 
91
- # Stops all background tasks.
92
- #
93
- # @return [void]
94
- #
95
- def stop
96
- @tasks.each(&:stop)
97
- @tasks.clear
83
+ conn, envelope = reply_info
84
+ msg = envelope
85
+ msg << EMPTY_FRAME
86
+ msg.concat(parts)
87
+ @conn_queues[conn]&.enqueue(msg)
98
88
  end
99
89
 
100
90
 
@@ -21,10 +21,9 @@ module OMQ
21
21
  # @param engine [Engine]
22
22
  #
23
23
  def initialize(engine)
24
- @engine = engine
25
- @recv_queue = Routing.build_queue(engine.options.recv_hwm, :block)
26
- @tasks = []
27
- @state = :ready # :ready or :waiting_reply
24
+ @engine = engine
25
+ @recv_queue = Routing.build_queue(engine.options.recv_hwm, :block)
26
+ @state = :ready # :ready or :waiting_reply
28
27
  init_round_robin(engine)
29
28
  end
30
29
 
@@ -50,12 +49,11 @@ module OMQ
50
49
  # @param connection [Connection]
51
50
  #
52
51
  def connection_added(connection)
53
- task = @engine.start_recv_pump(connection, @recv_queue) do |msg|
52
+ @engine.start_recv_pump(connection, @recv_queue) do |msg|
54
53
  @state = :ready
55
54
  msg.first&.empty? ? msg[1..] : msg
56
55
  end
57
56
 
58
- @tasks << task if task
59
57
  add_round_robin_send_connection(connection)
60
58
  end
61
59
 
@@ -77,23 +75,13 @@ module OMQ
77
75
  end
78
76
 
79
77
 
80
- # Stops all background tasks.
81
- #
82
- # @return [void]
83
- #
84
- def stop
85
- @tasks.each(&:stop)
86
- @tasks.clear
87
- end
88
-
89
-
90
78
  private
91
79
 
92
80
 
93
81
  # REQ prepends empty delimiter frame on the wire.
94
82
  #
95
83
  def transform_send(parts)
96
- [EMPTY_BINARY, *parts]
84
+ parts.dup.unshift(EMPTY_BINARY)
97
85
  end
98
86
 
99
87
  end
@@ -12,7 +12,7 @@ module OMQ
12
12
  # which is strictly better than libzmq's strict per-pipe round-robin
13
13
  # for PUSH-style patterns.
14
14
  #
15
- # See DESIGN.md "Per-socket HWM (not per-connection)" for the
15
+ # See doc/DESIGN.md "Per-socket HWM (not per-connection)" for the
16
16
  # full reasoning.
17
17
  #
18
18
  # Including classes must call `init_round_robin(engine)` from
@@ -38,11 +38,10 @@ module OMQ
38
38
  # @param engine [Engine]
39
39
  #
40
40
  def init_round_robin(engine)
41
- @connections = []
42
- @send_queue = Routing.build_queue(engine.options.send_hwm, :block)
43
- @direct_pipe = nil
44
- @conn_send_tasks = {} # conn => send pump task
45
- @in_flight = 0 # messages dequeued but not yet written
41
+ @connections = []
42
+ @send_queue = Routing.build_queue(engine.options.send_hwm, :block)
43
+ @direct_pipe = nil
44
+ @in_flight = 0 # messages dequeued but not yet written
46
45
  end
47
46
 
48
47
 
@@ -58,16 +57,16 @@ module OMQ
58
57
  end
59
58
 
60
59
 
61
- # Removes the connection and stops its send pump. Any message
62
- # the pump had already dequeued but not yet written is dropped --
63
- # matching libzmq's behavior on `pipe_terminated`. PUSH has no
64
- # cross-peer ordering guarantee, so this is safe.
60
+ # Removes the connection. Any message the pump had already
61
+ # dequeued but not yet written is dropped — matching libzmq's
62
+ # behavior on `pipe_terminated`. PUSH has no cross-peer ordering
63
+ # guarantee, so this is safe. The pump itself is torn down by
64
+ # the per-connection lifecycle barrier.
65
65
  #
66
66
  # @param conn [Connection]
67
67
  #
68
68
  def remove_round_robin_send_connection(conn)
69
69
  update_direct_pipe
70
- @conn_send_tasks.delete(conn)
71
70
  end
72
71
 
73
72
 
@@ -132,10 +131,11 @@ module OMQ
132
131
  # @param conn [Connection]
133
132
  #
134
133
  def start_conn_send_pump(conn)
135
- task = @engine.spawn_conn_pump_task(conn, annotation: "send pump") do
134
+ @engine.spawn_conn_pump_task(conn, annotation: "send pump") do
135
+ batch = []
136
+
136
137
  loop do
137
- batch = [@send_queue.dequeue]
138
- drain_send_queue_capped(batch)
138
+ dequeue_batch_capped(batch)
139
139
  @in_flight += batch.size
140
140
 
141
141
  begin
@@ -147,17 +147,16 @@ module OMQ
147
147
  batch.each do |parts|
148
148
  @engine.emit_verbose_msg_sent(conn, parts)
149
149
  end
150
+ batch.clear
150
151
 
151
152
  Async::Task.current.yield
152
153
  end
153
154
  end
154
-
155
- @conn_send_tasks[conn] = task
156
- @tasks << task
157
155
  end
158
156
 
159
157
 
160
- def drain_send_queue_capped(batch)
158
+ def dequeue_batch_capped(batch = [])
159
+ batch << @send_queue.dequeue
161
160
  bytes = batch_bytes(batch.first)
162
161
 
163
162
  while batch.size < BATCH_MSG_CAP && bytes < BATCH_BYTE_CAP
@@ -23,9 +23,7 @@ module OMQ
23
23
  @recv_queue = Routing.build_queue(engine.options.recv_hwm, :block)
24
24
  @connections_by_identity = {}
25
25
  @identity_by_connection = {}
26
- @conn_queues = {} # connection => per-connection send queue
27
- @conn_send_tasks = {} # connection => send pump task
28
- @tasks = []
26
+ @conn_queues = {}
29
27
  end
30
28
 
31
29
 
@@ -55,12 +53,11 @@ module OMQ
55
53
  @connections_by_identity[identity] = connection
56
54
  @identity_by_connection[connection] = identity
57
55
 
58
- task = @engine.start_recv_pump(connection, @recv_queue) { |msg| [identity, *msg] }
59
- @tasks << task if task
56
+ @engine.start_recv_pump(connection, @recv_queue) { |msg| [identity, *msg] }
60
57
 
61
58
  q = Routing.build_queue(@engine.options.send_hwm, :block)
62
59
  @conn_queues[connection] = q
63
- @conn_send_tasks[connection] = ConnSendPump.start(@engine, connection, q, @tasks)
60
+ ConnSendPump.start(@engine, connection, q)
64
61
  end
65
62
 
66
63
 
@@ -70,7 +67,6 @@ module OMQ
70
67
  identity = @identity_by_connection.delete(connection)
71
68
  @connections_by_identity.delete(identity) if identity
72
69
  @conn_queues.delete(connection)
73
- @conn_send_tasks.delete(connection)&.stop
74
70
  end
75
71
 
76
72
 
@@ -91,16 +87,6 @@ module OMQ
91
87
  end
92
88
 
93
89
 
94
- # Stops all background tasks.
95
- #
96
- # @return [void]
97
- #
98
- def stop
99
- @tasks.each(&:stop)
100
- @tasks.clear
101
- end
102
-
103
-
104
90
  # @return [Boolean] true when all per-connection send queues are empty
105
91
  #
106
92
  def send_queues_drained?