omq 0.13.0 → 0.14.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +35 -0
- data/README.md +27 -0
- data/lib/omq/drop_queue.rb +3 -0
- data/lib/omq/engine/connection_setup.rb +38 -15
- data/lib/omq/engine/heartbeat.rb +1 -1
- data/lib/omq/engine/maintenance.rb +35 -0
- data/lib/omq/engine/reconnect.rb +38 -12
- data/lib/omq/engine/recv_pump.rb +89 -46
- data/lib/omq/engine.rb +42 -0
- data/lib/omq/options.rb +44 -0
- data/lib/omq/pair.rb +6 -0
- data/lib/omq/pub_sub.rb +25 -0
- data/lib/omq/push_pull.rb +17 -0
- data/lib/omq/queue_interface.rb +1 -0
- data/lib/omq/readable.rb +2 -0
- data/lib/omq/req_rep.rb +13 -0
- data/lib/omq/router_dealer.rb +12 -0
- data/lib/omq/routing/conn_send_pump.rb +1 -1
- data/lib/omq/routing/dealer.rb +7 -0
- data/lib/omq/routing/fair_queue.rb +28 -0
- data/lib/omq/routing/fan_out.rb +19 -7
- data/lib/omq/routing/pair.rb +9 -1
- data/lib/omq/routing/pub.rb +8 -0
- data/lib/omq/routing/pull.rb +7 -0
- data/lib/omq/routing/rep.rb +11 -1
- data/lib/omq/routing/req.rb +7 -0
- data/lib/omq/routing/round_robin.rb +4 -3
- data/lib/omq/routing/router.rb +10 -1
- data/lib/omq/routing/sub.rb +10 -0
- data/lib/omq/routing/xpub.rb +8 -0
- data/lib/omq/routing/xsub.rb +13 -3
- data/lib/omq/routing.rb +26 -11
- data/lib/omq/socket.rb +23 -5
- data/lib/omq/transport/inproc/direct_pipe.rb +12 -1
- data/lib/omq/transport/inproc.rb +5 -0
- data/lib/omq/transport/ipc.rb +7 -1
- data/lib/omq/transport/tcp.rb +12 -7
- data/lib/omq/version.rb +1 -1
- data/lib/omq/writable.rb +2 -0
- data/lib/omq.rb +4 -1
- metadata +6 -5
data/lib/omq/options.rb
CHANGED
|
@@ -9,6 +9,7 @@ module OMQ
|
|
|
9
9
|
class Options
|
|
10
10
|
DEFAULT_HWM = 1000
|
|
11
11
|
|
|
12
|
+
|
|
12
13
|
# @param linger [Integer] linger period in seconds (default 0)
|
|
13
14
|
#
|
|
14
15
|
def initialize(linger: 0)
|
|
@@ -30,6 +31,40 @@ module OMQ
|
|
|
30
31
|
@qos = 0 # 0 = fire-and-forget, 1 = at-least-once (see omq-qos gem)
|
|
31
32
|
end
|
|
32
33
|
|
|
34
|
+
|
|
35
|
+
# @!attribute send_hwm
|
|
36
|
+
# @return [Integer] send high water mark (default 1000, 0 = unbounded)
|
|
37
|
+
# @!attribute recv_hwm
|
|
38
|
+
# @return [Integer] receive high water mark (default 1000, 0 = unbounded)
|
|
39
|
+
# @!attribute linger
|
|
40
|
+
# @return [Integer, nil] linger period in seconds (nil = wait forever, 0 = immediate)
|
|
41
|
+
# @!attribute identity
|
|
42
|
+
# @return [String] socket identity for ROUTER addressing (default "")
|
|
43
|
+
# @!attribute router_mandatory
|
|
44
|
+
# @return [Boolean] raise on unroutable messages (default false)
|
|
45
|
+
# @!attribute conflate
|
|
46
|
+
# @return [Boolean] keep only the latest message per topic (default false)
|
|
47
|
+
# @!attribute read_timeout
|
|
48
|
+
# @return [Numeric, nil] read timeout in seconds (nil = no timeout)
|
|
49
|
+
# @!attribute write_timeout
|
|
50
|
+
# @return [Numeric, nil] write timeout in seconds (nil = no timeout)
|
|
51
|
+
# @!attribute reconnect_interval
|
|
52
|
+
# @return [Numeric, Range] reconnect interval in seconds, or Range for exponential backoff
|
|
53
|
+
# @!attribute heartbeat_interval
|
|
54
|
+
# @return [Numeric, nil] PING interval in seconds (nil = disabled)
|
|
55
|
+
# @!attribute heartbeat_ttl
|
|
56
|
+
# @return [Numeric, nil] TTL advertised in PING (nil = use heartbeat_interval)
|
|
57
|
+
# @!attribute heartbeat_timeout
|
|
58
|
+
# @return [Numeric, nil] time without traffic before closing (nil = use heartbeat_interval)
|
|
59
|
+
# @!attribute max_message_size
|
|
60
|
+
# @return [Integer, nil] maximum message size in bytes
|
|
61
|
+
# @!attribute on_mute
|
|
62
|
+
# @return [Symbol] mute strategy (:block, :drop_newest, :drop_oldest)
|
|
63
|
+
# @!attribute mechanism
|
|
64
|
+
# @return [Protocol::ZMTP::Mechanism::Null, Protocol::ZMTP::Mechanism::Curve] security mechanism
|
|
65
|
+
# @!attribute qos
|
|
66
|
+
# @return [Integer] quality of service level (0 = fire-and-forget)
|
|
67
|
+
#
|
|
33
68
|
attr_accessor :send_hwm, :recv_hwm,
|
|
34
69
|
:linger, :identity,
|
|
35
70
|
:router_mandatory, :conflate,
|
|
@@ -41,10 +76,19 @@ module OMQ
|
|
|
41
76
|
:mechanism,
|
|
42
77
|
:qos
|
|
43
78
|
|
|
79
|
+
# @return [Boolean] true if router_mandatory is set
|
|
44
80
|
alias_method :router_mandatory?, :router_mandatory
|
|
81
|
+
|
|
82
|
+
# @return [Numeric, nil] alias for #read_timeout
|
|
45
83
|
alias_method :recv_timeout, :read_timeout
|
|
84
|
+
|
|
85
|
+
# @param val [Numeric, nil] alias for #read_timeout=
|
|
46
86
|
alias_method :recv_timeout=, :read_timeout=
|
|
87
|
+
|
|
88
|
+
# @return [Numeric, nil] alias for #write_timeout
|
|
47
89
|
alias_method :send_timeout, :write_timeout
|
|
90
|
+
|
|
91
|
+
# @param val [Numeric, nil] alias for #write_timeout=
|
|
48
92
|
alias_method :send_timeout=, :write_timeout=
|
|
49
93
|
end
|
|
50
94
|
end
|
data/lib/omq/pair.rb
CHANGED
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module OMQ
|
|
4
|
+
# PAIR socket — exclusive 1-to-1 bidirectional communication.
|
|
5
|
+
#
|
|
4
6
|
class PAIR < Socket
|
|
5
7
|
include Readable
|
|
6
8
|
include Writable
|
|
7
9
|
|
|
10
|
+
# @param endpoints [String, nil] endpoint to bind/connect
|
|
11
|
+
# @param linger [Integer] linger period in seconds
|
|
12
|
+
# @param backend [Symbol, nil] :ruby (default) or :ffi
|
|
13
|
+
#
|
|
8
14
|
def initialize(endpoints = nil, linger: 0, backend: nil)
|
|
9
15
|
_init_engine(:PAIR, linger: linger, backend: backend)
|
|
10
16
|
_attach(endpoints, default: :connect)
|
data/lib/omq/pub_sub.rb
CHANGED
|
@@ -1,15 +1,24 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module OMQ
|
|
4
|
+
# PUB socket — publish messages to all matching subscribers.
|
|
5
|
+
#
|
|
4
6
|
class PUB < Socket
|
|
5
7
|
include Writable
|
|
6
8
|
|
|
9
|
+
# @param endpoints [String, nil] endpoint to bind/connect
|
|
10
|
+
# @param linger [Integer] linger period in seconds
|
|
11
|
+
# @param on_mute [Symbol] mute strategy for slow subscribers
|
|
12
|
+
# @param conflate [Boolean] keep only latest message per topic
|
|
13
|
+
# @param backend [Symbol, nil] :ruby (default) or :ffi
|
|
14
|
+
#
|
|
7
15
|
def initialize(endpoints = nil, linger: 0, on_mute: :drop_newest, conflate: false, backend: nil)
|
|
8
16
|
_init_engine(:PUB, linger: linger, on_mute: on_mute, conflate: conflate, backend: backend)
|
|
9
17
|
_attach(endpoints, default: :bind)
|
|
10
18
|
end
|
|
11
19
|
end
|
|
12
20
|
|
|
21
|
+
|
|
13
22
|
# SUB socket.
|
|
14
23
|
#
|
|
15
24
|
class SUB < Socket
|
|
@@ -19,6 +28,7 @@ module OMQ
|
|
|
19
28
|
#
|
|
20
29
|
EVERYTHING = ''
|
|
21
30
|
|
|
31
|
+
|
|
22
32
|
# @param endpoints [String, nil]
|
|
23
33
|
# @param linger [Integer]
|
|
24
34
|
# @param subscribe [String, nil] subscription prefix; +nil+ (default)
|
|
@@ -31,6 +41,7 @@ module OMQ
|
|
|
31
41
|
self.subscribe(subscribe) unless subscribe.nil?
|
|
32
42
|
end
|
|
33
43
|
|
|
44
|
+
|
|
34
45
|
# Subscribes to a topic prefix.
|
|
35
46
|
#
|
|
36
47
|
# @param prefix [String]
|
|
@@ -40,6 +51,7 @@ module OMQ
|
|
|
40
51
|
@engine.routing.subscribe(prefix)
|
|
41
52
|
end
|
|
42
53
|
|
|
54
|
+
|
|
43
55
|
# Unsubscribes from a topic prefix.
|
|
44
56
|
#
|
|
45
57
|
# @param prefix [String]
|
|
@@ -50,16 +62,27 @@ module OMQ
|
|
|
50
62
|
end
|
|
51
63
|
end
|
|
52
64
|
|
|
65
|
+
|
|
66
|
+
# XPUB socket — like PUB but exposes subscription events to the application.
|
|
67
|
+
#
|
|
53
68
|
class XPUB < Socket
|
|
54
69
|
include Readable
|
|
55
70
|
include Writable
|
|
56
71
|
|
|
72
|
+
# @param endpoints [String, nil] endpoint to bind/connect
|
|
73
|
+
# @param linger [Integer] linger period in seconds
|
|
74
|
+
# @param on_mute [Symbol] mute strategy for slow subscribers
|
|
75
|
+
# @param backend [Symbol, nil] :ruby (default) or :ffi
|
|
76
|
+
#
|
|
57
77
|
def initialize(endpoints = nil, linger: 0, on_mute: :drop_newest, backend: nil)
|
|
58
78
|
_init_engine(:XPUB, linger: linger, on_mute: on_mute, backend: backend)
|
|
59
79
|
_attach(endpoints, default: :bind)
|
|
60
80
|
end
|
|
61
81
|
end
|
|
62
82
|
|
|
83
|
+
|
|
84
|
+
# XSUB socket — like SUB but subscriptions are sent as data frames.
|
|
85
|
+
#
|
|
63
86
|
class XSUB < Socket
|
|
64
87
|
include Readable
|
|
65
88
|
include Writable
|
|
@@ -68,6 +91,8 @@ module OMQ
|
|
|
68
91
|
# @param linger [Integer]
|
|
69
92
|
# @param subscribe [String, nil] subscription prefix; +nil+ (default)
|
|
70
93
|
# means no subscription — send a subscribe frame explicitly.
|
|
94
|
+
# @param on_mute [Symbol] mute strategy (:block, :drop_newest, :drop_oldest)
|
|
95
|
+
# @param backend [Symbol, nil] :ruby (default) or :ffi
|
|
71
96
|
#
|
|
72
97
|
def initialize(endpoints = nil, linger: 0, subscribe: nil, on_mute: :block, backend: nil)
|
|
73
98
|
_init_engine(:XSUB, linger: linger, on_mute: on_mute, backend: backend)
|
data/lib/omq/push_pull.rb
CHANGED
|
@@ -1,18 +1,35 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module OMQ
|
|
4
|
+
# PUSH socket — push messages to connected PULL peers via round-robin.
|
|
5
|
+
#
|
|
4
6
|
class PUSH < Socket
|
|
5
7
|
include Writable
|
|
6
8
|
|
|
9
|
+
# @param endpoints [String, nil] endpoint to bind/connect
|
|
10
|
+
# @param linger [Integer] linger period in seconds
|
|
11
|
+
# @param send_hwm [Integer, nil] send high water mark (nil uses default)
|
|
12
|
+
# @param send_timeout [Numeric, nil] send timeout in seconds
|
|
13
|
+
# @param backend [Symbol, nil] :ruby (default) or :ffi
|
|
14
|
+
#
|
|
7
15
|
def initialize(endpoints = nil, linger: 0, send_hwm: nil, send_timeout: nil, backend: nil)
|
|
8
16
|
_init_engine(:PUSH, linger: linger, send_hwm: send_hwm, send_timeout: send_timeout, backend: backend)
|
|
9
17
|
_attach(endpoints, default: :connect)
|
|
10
18
|
end
|
|
11
19
|
end
|
|
12
20
|
|
|
21
|
+
|
|
22
|
+
# PULL socket — receive messages from PUSH peers via fair-queue.
|
|
23
|
+
#
|
|
13
24
|
class PULL < Socket
|
|
14
25
|
include Readable
|
|
15
26
|
|
|
27
|
+
# @param endpoints [String, nil] endpoint to bind/connect
|
|
28
|
+
# @param linger [Integer] linger period in seconds
|
|
29
|
+
# @param recv_hwm [Integer, nil] receive high water mark (nil uses default)
|
|
30
|
+
# @param recv_timeout [Numeric, nil] receive timeout in seconds
|
|
31
|
+
# @param backend [Symbol, nil] :ruby (default) or :ffi
|
|
32
|
+
#
|
|
16
33
|
def initialize(endpoints = nil, linger: 0, recv_hwm: nil, recv_timeout: nil, backend: nil)
|
|
17
34
|
_init_engine(:PULL, linger: linger, recv_hwm: recv_hwm, recv_timeout: recv_timeout, backend: backend)
|
|
18
35
|
_attach(endpoints, default: :bind)
|
data/lib/omq/queue_interface.rb
CHANGED
data/lib/omq/readable.rb
CHANGED
|
@@ -11,6 +11,7 @@ module OMQ
|
|
|
11
11
|
# Maximum messages to prefetch from the recv queue per drain.
|
|
12
12
|
RECV_BATCH_SIZE = 64
|
|
13
13
|
|
|
14
|
+
|
|
14
15
|
# Receives the next message. Returns from a local prefetch
|
|
15
16
|
# buffer when available, otherwise drains up to
|
|
16
17
|
# {RECV_BATCH_SIZE} messages from the recv queue in one
|
|
@@ -23,6 +24,7 @@ module OMQ
|
|
|
23
24
|
@recv_mutex.synchronize { @recv_buffer.shift } || fill_recv_buffer
|
|
24
25
|
end
|
|
25
26
|
|
|
27
|
+
|
|
26
28
|
# Waits until the socket is readable.
|
|
27
29
|
#
|
|
28
30
|
# @param timeout [Numeric, nil] timeout in seconds
|
data/lib/omq/req_rep.rb
CHANGED
|
@@ -1,20 +1,33 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module OMQ
|
|
4
|
+
# REQ socket — send a request, then receive one reply (strict alternation).
|
|
5
|
+
#
|
|
4
6
|
class REQ < Socket
|
|
5
7
|
include Readable
|
|
6
8
|
include Writable
|
|
7
9
|
|
|
10
|
+
# @param endpoints [String, nil] endpoint to bind/connect
|
|
11
|
+
# @param linger [Integer] linger period in seconds
|
|
12
|
+
# @param backend [Symbol, nil] :ruby (default) or :ffi
|
|
13
|
+
#
|
|
8
14
|
def initialize(endpoints = nil, linger: 0, backend: nil)
|
|
9
15
|
_init_engine(:REQ, linger: linger, backend: backend)
|
|
10
16
|
_attach(endpoints, default: :connect)
|
|
11
17
|
end
|
|
12
18
|
end
|
|
13
19
|
|
|
20
|
+
|
|
21
|
+
# REP socket — receive a request, then send one reply (strict alternation).
|
|
22
|
+
#
|
|
14
23
|
class REP < Socket
|
|
15
24
|
include Readable
|
|
16
25
|
include Writable
|
|
17
26
|
|
|
27
|
+
# @param endpoints [String, nil] endpoint to bind/connect
|
|
28
|
+
# @param linger [Integer] linger period in seconds
|
|
29
|
+
# @param backend [Symbol, nil] :ruby (default) or :ffi
|
|
30
|
+
#
|
|
18
31
|
def initialize(endpoints = nil, linger: 0, backend: nil)
|
|
19
32
|
_init_engine(:REP, linger: linger, backend: backend)
|
|
20
33
|
_attach(endpoints, default: :bind)
|
data/lib/omq/router_dealer.rb
CHANGED
|
@@ -1,27 +1,39 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module OMQ
|
|
4
|
+
# DEALER socket — asynchronous round-robin send, fair-queue receive.
|
|
5
|
+
#
|
|
4
6
|
class DEALER < Socket
|
|
5
7
|
include Readable
|
|
6
8
|
include Writable
|
|
7
9
|
|
|
10
|
+
# @param endpoints [String, nil] endpoint to bind/connect
|
|
11
|
+
# @param linger [Integer] linger period in seconds
|
|
12
|
+
# @param backend [Symbol, nil] :ruby (default) or :ffi
|
|
13
|
+
#
|
|
8
14
|
def initialize(endpoints = nil, linger: 0, backend: nil)
|
|
9
15
|
_init_engine(:DEALER, linger: linger, backend: backend)
|
|
10
16
|
_attach(endpoints, default: :connect)
|
|
11
17
|
end
|
|
12
18
|
end
|
|
13
19
|
|
|
20
|
+
|
|
14
21
|
# ROUTER socket.
|
|
15
22
|
#
|
|
16
23
|
class ROUTER < Socket
|
|
17
24
|
include Readable
|
|
18
25
|
include Writable
|
|
19
26
|
|
|
27
|
+
# @param endpoints [String, nil] endpoint to bind/connect
|
|
28
|
+
# @param linger [Integer] linger period in seconds
|
|
29
|
+
# @param backend [Symbol, nil] :ruby (default) or :ffi
|
|
30
|
+
#
|
|
20
31
|
def initialize(endpoints = nil, linger: 0, backend: nil)
|
|
21
32
|
_init_engine(:ROUTER, linger: linger, backend: backend)
|
|
22
33
|
_attach(endpoints, default: :bind)
|
|
23
34
|
end
|
|
24
35
|
|
|
36
|
+
|
|
25
37
|
# Sends a message to a specific peer by identity.
|
|
26
38
|
#
|
|
27
39
|
# @param receiver [String] peer identity
|
data/lib/omq/routing/dealer.rb
CHANGED
|
@@ -19,6 +19,7 @@ module OMQ
|
|
|
19
19
|
init_round_robin(engine)
|
|
20
20
|
end
|
|
21
21
|
|
|
22
|
+
|
|
22
23
|
# @return [FairQueue]
|
|
23
24
|
#
|
|
24
25
|
attr_reader :recv_queue
|
|
@@ -31,6 +32,7 @@ module OMQ
|
|
|
31
32
|
add_round_robin_send_connection(connection)
|
|
32
33
|
end
|
|
33
34
|
|
|
35
|
+
|
|
34
36
|
# @param connection [Connection]
|
|
35
37
|
#
|
|
36
38
|
def connection_removed(connection)
|
|
@@ -39,12 +41,17 @@ module OMQ
|
|
|
39
41
|
remove_round_robin_send_connection(connection)
|
|
40
42
|
end
|
|
41
43
|
|
|
44
|
+
|
|
42
45
|
# @param parts [Array<String>]
|
|
43
46
|
#
|
|
44
47
|
def enqueue(parts)
|
|
45
48
|
enqueue_round_robin(parts)
|
|
46
49
|
end
|
|
47
50
|
|
|
51
|
+
|
|
52
|
+
# Stops all background tasks.
|
|
53
|
+
#
|
|
54
|
+
# @return [void]
|
|
48
55
|
#
|
|
49
56
|
def stop
|
|
50
57
|
@tasks.each(&:stop)
|
|
@@ -12,6 +12,8 @@ module OMQ
|
|
|
12
12
|
# SignalingQueue wrapper, which also wakes a blocked #dequeue.
|
|
13
13
|
#
|
|
14
14
|
class FairQueue
|
|
15
|
+
# Creates an empty fair queue with no per-connection queues.
|
|
16
|
+
#
|
|
15
17
|
def initialize
|
|
16
18
|
@queues = [] # ordered list of per-connection inner queues
|
|
17
19
|
@mapping = {} # connection => inner queue
|
|
@@ -21,6 +23,7 @@ module OMQ
|
|
|
21
23
|
@closed = false
|
|
22
24
|
end
|
|
23
25
|
|
|
26
|
+
|
|
24
27
|
# Registers a per-connection queue. Called when a connection is added.
|
|
25
28
|
#
|
|
26
29
|
# @param conn [Connection]
|
|
@@ -31,6 +34,7 @@ module OMQ
|
|
|
31
34
|
@queues << q
|
|
32
35
|
end
|
|
33
36
|
|
|
37
|
+
|
|
34
38
|
# Removes the per-connection queue for a disconnected peer.
|
|
35
39
|
#
|
|
36
40
|
# If the queue is empty it is removed immediately. If it still has
|
|
@@ -47,6 +51,7 @@ module OMQ
|
|
|
47
51
|
# Non-empty orphaned queues stay in @queues until drained
|
|
48
52
|
end
|
|
49
53
|
|
|
54
|
+
|
|
50
55
|
# Wakes a blocked #dequeue. Called by SignalingQueue after each enqueue.
|
|
51
56
|
#
|
|
52
57
|
def signal
|
|
@@ -54,6 +59,7 @@ module OMQ
|
|
|
54
59
|
@condition.signal
|
|
55
60
|
end
|
|
56
61
|
|
|
62
|
+
|
|
57
63
|
# Returns the next message from any per-connection queue, in fair
|
|
58
64
|
# round-robin order. Blocks until a message is available.
|
|
59
65
|
#
|
|
@@ -81,6 +87,7 @@ module OMQ
|
|
|
81
87
|
end
|
|
82
88
|
end
|
|
83
89
|
|
|
90
|
+
|
|
84
91
|
# Injects a nil sentinel to unblock a waiting #dequeue.
|
|
85
92
|
# Called by Engine on close or fatal error.
|
|
86
93
|
#
|
|
@@ -89,6 +96,7 @@ module OMQ
|
|
|
89
96
|
@condition.signal
|
|
90
97
|
end
|
|
91
98
|
|
|
99
|
+
|
|
92
100
|
# @return [Boolean]
|
|
93
101
|
#
|
|
94
102
|
def empty?
|
|
@@ -126,18 +134,38 @@ module OMQ
|
|
|
126
134
|
# signals the FairQueue to wake a blocked #dequeue.
|
|
127
135
|
#
|
|
128
136
|
class SignalingQueue
|
|
137
|
+
# @param inner [Async::LimitedQueue] the per-connection bounded queue
|
|
138
|
+
# @param fair_queue [FairQueue] the parent fair queue to signal on enqueue
|
|
139
|
+
#
|
|
129
140
|
def initialize(inner, fair_queue)
|
|
130
141
|
@inner = inner
|
|
131
142
|
@fair = fair_queue
|
|
132
143
|
end
|
|
133
144
|
|
|
145
|
+
|
|
146
|
+
# Enqueues a message and signals the fair queue.
|
|
147
|
+
#
|
|
148
|
+
# @param msg [Array<String>]
|
|
149
|
+
# @return [void]
|
|
150
|
+
#
|
|
134
151
|
def enqueue(msg)
|
|
135
152
|
@inner.enqueue(msg)
|
|
136
153
|
@fair.signal
|
|
137
154
|
end
|
|
138
155
|
|
|
156
|
+
|
|
157
|
+
# @param timeout [Numeric, nil] dequeue timeout
|
|
158
|
+
# @return [Array<String>, nil]
|
|
159
|
+
#
|
|
139
160
|
def dequeue(timeout: nil) = @inner.dequeue(timeout: timeout)
|
|
161
|
+
|
|
162
|
+
# @return [Boolean]
|
|
163
|
+
#
|
|
140
164
|
def empty? = @inner.empty?
|
|
165
|
+
|
|
166
|
+
# @param item [Object, nil]
|
|
167
|
+
# @return [void]
|
|
168
|
+
#
|
|
141
169
|
def push(item) = @inner.push(item)
|
|
142
170
|
end
|
|
143
171
|
end
|
data/lib/omq/routing/fan_out.rb
CHANGED
|
@@ -17,10 +17,11 @@ module OMQ
|
|
|
17
17
|
# their #initialize.
|
|
18
18
|
#
|
|
19
19
|
module FanOut
|
|
20
|
+
# @return [Async::Promise] resolves when the first subscriber joins
|
|
21
|
+
#
|
|
20
22
|
attr_reader :subscriber_joined
|
|
21
23
|
|
|
22
|
-
#
|
|
23
|
-
# Used by Engine#drain_send_queues during linger.
|
|
24
|
+
# @return [Boolean] true when all per-connection send queues are empty
|
|
24
25
|
#
|
|
25
26
|
def send_queues_drained?
|
|
26
27
|
@conn_queues.values.all?(&:empty?)
|
|
@@ -31,6 +32,7 @@ module OMQ
|
|
|
31
32
|
def init_fan_out(engine)
|
|
32
33
|
@connections = Set.new
|
|
33
34
|
@subscriptions = {} # connection => Set of prefixes
|
|
35
|
+
@subscribe_all = Set.new # connections subscribed to "" (match-all fast path)
|
|
34
36
|
@conn_queues = {} # connection => per-connection send queue
|
|
35
37
|
@conn_send_tasks = {} # connection => send pump task
|
|
36
38
|
@conflate = engine.options.conflate
|
|
@@ -42,11 +44,13 @@ module OMQ
|
|
|
42
44
|
# @return [Boolean] whether the connection is subscribed to the topic
|
|
43
45
|
#
|
|
44
46
|
def subscribed?(conn, topic)
|
|
47
|
+
return true if @subscribe_all.include?(conn)
|
|
45
48
|
subs = @subscriptions[conn]
|
|
46
49
|
return false unless subs
|
|
47
50
|
subs.any? { |prefix| topic.start_with?(prefix) }
|
|
48
51
|
end
|
|
49
52
|
|
|
53
|
+
|
|
50
54
|
# Called when a subscription command is received from a peer.
|
|
51
55
|
# Override in subclasses to expose subscriptions to the
|
|
52
56
|
# application (e.g. XPUB enqueues to recv_queue).
|
|
@@ -56,9 +60,11 @@ module OMQ
|
|
|
56
60
|
#
|
|
57
61
|
def on_subscribe(conn, prefix)
|
|
58
62
|
@subscriptions[conn] << prefix.b.freeze
|
|
63
|
+
@subscribe_all.add(conn) if prefix.empty?
|
|
59
64
|
@subscriber_joined.resolve(conn) unless @subscriber_joined.resolved?
|
|
60
65
|
end
|
|
61
66
|
|
|
67
|
+
|
|
62
68
|
# Called when a cancel command is received from a peer.
|
|
63
69
|
# Override in subclasses (e.g. XPUB enqueues to recv_queue).
|
|
64
70
|
#
|
|
@@ -67,6 +73,7 @@ module OMQ
|
|
|
67
73
|
#
|
|
68
74
|
def on_cancel(conn, prefix)
|
|
69
75
|
@subscriptions[conn]&.delete(prefix)
|
|
76
|
+
@subscribe_all.delete(conn) if prefix.empty?
|
|
70
77
|
end
|
|
71
78
|
|
|
72
79
|
|
|
@@ -88,6 +95,7 @@ module OMQ
|
|
|
88
95
|
# @param conn [Connection]
|
|
89
96
|
#
|
|
90
97
|
def remove_fan_out_send_connection(conn)
|
|
98
|
+
@subscribe_all.delete(conn)
|
|
91
99
|
@conn_queues.delete(conn)
|
|
92
100
|
@conn_send_tasks.delete(conn)&.stop
|
|
93
101
|
end
|
|
@@ -108,7 +116,7 @@ module OMQ
|
|
|
108
116
|
#
|
|
109
117
|
def fan_out_enqueue(parts)
|
|
110
118
|
@connections.each do |conn|
|
|
111
|
-
@conn_queues[conn]
|
|
119
|
+
@conn_queues[conn].enqueue(parts)
|
|
112
120
|
end
|
|
113
121
|
end
|
|
114
122
|
|
|
@@ -120,8 +128,10 @@ module OMQ
|
|
|
120
128
|
next unless frame.command?
|
|
121
129
|
cmd = Protocol::ZMTP::Codec::Command.from_body(frame.body)
|
|
122
130
|
case cmd.name
|
|
123
|
-
when "SUBSCRIBE"
|
|
124
|
-
|
|
131
|
+
when "SUBSCRIBE"
|
|
132
|
+
on_subscribe(conn, cmd.data)
|
|
133
|
+
when "CANCEL"
|
|
134
|
+
on_cancel(conn, cmd.data)
|
|
125
135
|
end
|
|
126
136
|
end
|
|
127
137
|
rescue *CONNECTION_LOST
|
|
@@ -131,7 +141,7 @@ module OMQ
|
|
|
131
141
|
|
|
132
142
|
|
|
133
143
|
# Starts a dedicated send pump for one subscriber connection.
|
|
134
|
-
# Uses write_wire (pre-encoded bytes) for non-
|
|
144
|
+
# Uses write_wire (pre-encoded bytes) for non-encrypted TCP connections
|
|
135
145
|
# to avoid re-encoding the same message N times during fan-out.
|
|
136
146
|
# In conflate mode, drains the batch and keeps only the latest
|
|
137
147
|
# message per topic before writing.
|
|
@@ -140,12 +150,13 @@ module OMQ
|
|
|
140
150
|
# @param q [Async::LimitedQueue, DropQueue]
|
|
141
151
|
#
|
|
142
152
|
def start_conn_send_pump(conn, q)
|
|
143
|
-
use_wire = conn.respond_to?(:write_wire) && !
|
|
153
|
+
use_wire = conn.respond_to?(:write_wire) && !conn.encrypted?
|
|
144
154
|
task = @conflate ? start_conn_send_pump_conflate(conn, q) : start_conn_send_pump_normal(conn, q, use_wire)
|
|
145
155
|
@conn_send_tasks[conn] = task
|
|
146
156
|
@tasks << task
|
|
147
157
|
end
|
|
148
158
|
|
|
159
|
+
|
|
149
160
|
def start_conn_send_pump_normal(conn, q, use_wire)
|
|
150
161
|
@engine.spawn_pump_task(annotation: "send pump") do
|
|
151
162
|
loop do
|
|
@@ -159,6 +170,7 @@ module OMQ
|
|
|
159
170
|
end
|
|
160
171
|
end
|
|
161
172
|
|
|
173
|
+
|
|
162
174
|
def write_matching_batch(conn, batch, use_wire)
|
|
163
175
|
sent = false
|
|
164
176
|
batch.each do |parts|
|
data/lib/omq/routing/pair.rb
CHANGED
|
@@ -23,6 +23,7 @@ module OMQ
|
|
|
23
23
|
@tasks = []
|
|
24
24
|
end
|
|
25
25
|
|
|
26
|
+
|
|
26
27
|
# @return [FairQueue]
|
|
27
28
|
#
|
|
28
29
|
attr_reader :recv_queue
|
|
@@ -45,6 +46,7 @@ module OMQ
|
|
|
45
46
|
end
|
|
46
47
|
end
|
|
47
48
|
|
|
49
|
+
|
|
48
50
|
# @param connection [Connection]
|
|
49
51
|
#
|
|
50
52
|
def connection_removed(connection)
|
|
@@ -57,6 +59,7 @@ module OMQ
|
|
|
57
59
|
end
|
|
58
60
|
end
|
|
59
61
|
|
|
62
|
+
|
|
60
63
|
# @param parts [Array<String>]
|
|
61
64
|
#
|
|
62
65
|
def enqueue(parts)
|
|
@@ -70,13 +73,18 @@ module OMQ
|
|
|
70
73
|
end
|
|
71
74
|
end
|
|
72
75
|
|
|
76
|
+
|
|
77
|
+
# Stops all background tasks.
|
|
78
|
+
#
|
|
79
|
+
# @return [void]
|
|
73
80
|
#
|
|
74
81
|
def stop
|
|
75
82
|
@tasks.each(&:stop)
|
|
76
83
|
@tasks.clear
|
|
77
84
|
end
|
|
78
85
|
|
|
79
|
-
|
|
86
|
+
|
|
87
|
+
# @return [Boolean] true when the staging and send queues are empty
|
|
80
88
|
#
|
|
81
89
|
def send_queues_drained?
|
|
82
90
|
@staging_queue.empty? && (@send_queue.nil? || @send_queue.empty?)
|
data/lib/omq/routing/pub.rb
CHANGED
|
@@ -19,12 +19,14 @@ module OMQ
|
|
|
19
19
|
init_fan_out(engine)
|
|
20
20
|
end
|
|
21
21
|
|
|
22
|
+
|
|
22
23
|
# PUB is write-only.
|
|
23
24
|
#
|
|
24
25
|
def recv_queue
|
|
25
26
|
raise "PUB sockets cannot receive"
|
|
26
27
|
end
|
|
27
28
|
|
|
29
|
+
|
|
28
30
|
# @param connection [Connection]
|
|
29
31
|
#
|
|
30
32
|
def connection_added(connection)
|
|
@@ -34,6 +36,7 @@ module OMQ
|
|
|
34
36
|
add_fan_out_send_connection(connection)
|
|
35
37
|
end
|
|
36
38
|
|
|
39
|
+
|
|
37
40
|
# @param connection [Connection]
|
|
38
41
|
#
|
|
39
42
|
def connection_removed(connection)
|
|
@@ -42,12 +45,17 @@ module OMQ
|
|
|
42
45
|
remove_fan_out_send_connection(connection)
|
|
43
46
|
end
|
|
44
47
|
|
|
48
|
+
|
|
45
49
|
# @param parts [Array<String>]
|
|
46
50
|
#
|
|
47
51
|
def enqueue(parts)
|
|
48
52
|
fan_out_enqueue(parts)
|
|
49
53
|
end
|
|
50
54
|
|
|
55
|
+
|
|
56
|
+
# Stops all background tasks.
|
|
57
|
+
#
|
|
58
|
+
# @return [void]
|
|
51
59
|
#
|
|
52
60
|
def stop
|
|
53
61
|
@tasks.each(&:stop)
|
data/lib/omq/routing/pull.rb
CHANGED
|
@@ -14,6 +14,7 @@ module OMQ
|
|
|
14
14
|
@tasks = []
|
|
15
15
|
end
|
|
16
16
|
|
|
17
|
+
|
|
17
18
|
# @return [FairQueue]
|
|
18
19
|
#
|
|
19
20
|
attr_reader :recv_queue
|
|
@@ -24,6 +25,7 @@ module OMQ
|
|
|
24
25
|
add_fair_recv_connection(connection)
|
|
25
26
|
end
|
|
26
27
|
|
|
28
|
+
|
|
27
29
|
# @param connection [Connection]
|
|
28
30
|
#
|
|
29
31
|
def connection_removed(connection)
|
|
@@ -31,12 +33,17 @@ module OMQ
|
|
|
31
33
|
# recv pump stops on EOFError
|
|
32
34
|
end
|
|
33
35
|
|
|
36
|
+
|
|
34
37
|
# PULL is read-only.
|
|
35
38
|
#
|
|
36
39
|
def enqueue(_parts)
|
|
37
40
|
raise "PULL sockets cannot send"
|
|
38
41
|
end
|
|
39
42
|
|
|
43
|
+
|
|
44
|
+
# Stops all background tasks.
|
|
45
|
+
#
|
|
46
|
+
# @return [void]
|
|
40
47
|
#
|
|
41
48
|
def stop
|
|
42
49
|
@tasks.each(&:stop)
|