surfliner-metadata_consumer 0.1.0.pre.alpha.5 → 0.1.0.pre.alpha.7
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/CHANGES.md +21 -0
- data/Gemfile.lock +23 -23
- data/README.md +14 -7
- data/lib/surfliner/metadata_consumer/version.rb +1 -1
- data/lib/surfliner/mq/connection.rb +35 -27
- data/lib/surfliner/mq/connection_config.rb +31 -9
- data/lib/surfliner/mq/queue_config.rb +1 -13
- data/lib/surfliner/mq/router.rb +78 -0
- data/lib/surfliner/mq/topic.rb +25 -6
- metadata +6 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c496d7587eb02bc3020be31fb8a9052e67e52aa08b97c3dbc81bb26d44f29fc3
|
4
|
+
data.tar.gz: 57d58bc7f428b049efc1e1f4db521970a8f43e389b966c20fb99156313c6c085
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ebb4d7a8b61f8b03d4376454a526921d19e18eb94cca49029e8e75736ddee0ec61372a6bde72fafda21162eaa4f0ef655dc759f90150c309c8e20ec5404b450b
|
7
|
+
data.tar.gz: 43a75ff273881bf59ea4c20ae84e299b853b8c6779bc03e48e6fd42d6846a9268236aa034234ce8db6275ce600b255cac5eff23d722540496bc531347a589e7a
|
data/CHANGES.md
CHANGED
@@ -1,3 +1,24 @@
|
|
1
|
+
# 0.1.0.pre.alpha.7 (2025-04-28)
|
2
|
+
|
3
|
+
- Fix issue where `RABBITMQ_AWAIT_ON_CLOSE` environment variable could
|
4
|
+
not be omitted.
|
5
|
+
- Remove `:routing_key` from `Mq::QueueConfig`, to allow using multiple
|
6
|
+
routing keys with the same queue.
|
7
|
+
- Add `Mq::Router` for routing messages from a single queue to multiple
|
8
|
+
handlers by routing key.
|
9
|
+
|
10
|
+
# 0.1.0.pre.alpha.6 (2025-04-25)
|
11
|
+
|
12
|
+
- Update to Bunny 2.24
|
13
|
+
- Allow `Mq::Connection#connect` to take a block, in which case the method
|
14
|
+
yields the open connection, closing the connection and channel afterwards.
|
15
|
+
- Add an option `await_response_on_close` to `Mq::ConnectionConfig`, defaulting
|
16
|
+
to true, to indicate whether the RabbitMQ client should wait for a response
|
17
|
+
when closing a session, along with a corresponding environment variable
|
18
|
+
`RABBITMQ_AWAIT_ON_CLOSE`
|
19
|
+
- Allows passing additional options to `Mq:ConnectionConfig#initialize` and
|
20
|
+
`#from_env`, which will then be passed to `Bunny::Session#initialize`
|
21
|
+
|
1
22
|
# 0.1.0.pre.alpha.5 (2025-03-25)
|
2
23
|
|
3
24
|
In order to support using one connection (and one underlying `Bunny::Session`)
|
data/Gemfile.lock
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
surfliner-metadata_consumer (0.1.0.pre.alpha.
|
5
|
-
bunny (~> 2.
|
4
|
+
surfliner-metadata_consumer (0.1.0.pre.alpha.7)
|
5
|
+
bunny (~> 2.24)
|
6
6
|
opentelemetry-exporter-otlp (~> 0.26.3)
|
7
7
|
opentelemetry-instrumentation-all (~> 0.60.0)
|
8
8
|
opentelemetry-sdk (~> 1.4.1)
|
@@ -37,26 +37,26 @@ GEM
|
|
37
37
|
diff-lcs (1.6.1)
|
38
38
|
docile (1.4.1)
|
39
39
|
dotenv (2.8.1)
|
40
|
-
faraday (2.
|
40
|
+
faraday (2.13.1)
|
41
41
|
faraday-net_http (>= 2.0, < 3.5)
|
42
42
|
json
|
43
43
|
logger
|
44
44
|
faraday-net_http (3.4.0)
|
45
45
|
net-http (>= 0.5.0)
|
46
46
|
github-markup (5.0.1)
|
47
|
-
google-protobuf (3.25.
|
47
|
+
google-protobuf (3.25.7)
|
48
48
|
googleapis-common-protos-types (1.19.0)
|
49
49
|
google-protobuf (>= 3.18, < 5.a)
|
50
50
|
hashdiff (1.1.2)
|
51
51
|
io-console (0.8.0)
|
52
|
-
irb (1.15.
|
52
|
+
irb (1.15.2)
|
53
53
|
pp (>= 0.6.0)
|
54
54
|
rdoc (>= 4.0.0)
|
55
55
|
reline (>= 0.4.2)
|
56
|
-
json (2.
|
56
|
+
json (2.11.3)
|
57
57
|
language_server-protocol (3.17.0.4)
|
58
58
|
lint_roller (1.1.0)
|
59
|
-
logger (1.
|
59
|
+
logger (1.7.0)
|
60
60
|
net-http (0.6.0)
|
61
61
|
uri
|
62
62
|
opentelemetry-api (1.5.0)
|
@@ -255,8 +255,8 @@ GEM
|
|
255
255
|
opentelemetry-semantic_conventions
|
256
256
|
opentelemetry-semantic_conventions (1.11.0)
|
257
257
|
opentelemetry-api (~> 1.0)
|
258
|
-
parallel (1.
|
259
|
-
parser (3.3.
|
258
|
+
parallel (1.27.0)
|
259
|
+
parser (3.3.8.0)
|
260
260
|
ast (~> 2.4.1)
|
261
261
|
racc
|
262
262
|
pp (0.6.2)
|
@@ -271,11 +271,11 @@ GEM
|
|
271
271
|
rainbow (3.1.1)
|
272
272
|
rake (13.2.1)
|
273
273
|
rbtree (0.4.6)
|
274
|
-
rdoc (6.13.
|
274
|
+
rdoc (6.13.1)
|
275
275
|
psych (>= 4.0.0)
|
276
276
|
redcarpet (3.6.1)
|
277
277
|
regexp_parser (2.10.0)
|
278
|
-
reline (0.6.
|
278
|
+
reline (0.6.1)
|
279
279
|
io-console (~> 0.5)
|
280
280
|
rexml (3.4.1)
|
281
281
|
rsolr (2.6.0)
|
@@ -294,7 +294,7 @@ GEM
|
|
294
294
|
diff-lcs (>= 1.2.0, < 2.0)
|
295
295
|
rspec-support (~> 3.13.0)
|
296
296
|
rspec-support (3.13.2)
|
297
|
-
rubocop (1.
|
297
|
+
rubocop (1.75.4)
|
298
298
|
json (~> 2.3)
|
299
299
|
language_server-protocol (~> 3.17.0.2)
|
300
300
|
lint_roller (~> 1.1.0)
|
@@ -302,15 +302,15 @@ GEM
|
|
302
302
|
parser (>= 3.3.0.2)
|
303
303
|
rainbow (>= 2.2.2, < 4.0)
|
304
304
|
regexp_parser (>= 2.9.3, < 3.0)
|
305
|
-
rubocop-ast (>= 1.
|
305
|
+
rubocop-ast (>= 1.44.0, < 2.0)
|
306
306
|
ruby-progressbar (~> 1.7)
|
307
307
|
unicode-display_width (>= 2.4.0, < 4.0)
|
308
|
-
rubocop-ast (1.
|
308
|
+
rubocop-ast (1.44.1)
|
309
309
|
parser (>= 3.3.7.2)
|
310
310
|
prism (~> 1.4)
|
311
|
-
rubocop-performance (1.
|
311
|
+
rubocop-performance (1.25.0)
|
312
312
|
lint_roller (~> 1.1)
|
313
|
-
rubocop (>= 1.
|
313
|
+
rubocop (>= 1.75.0, < 2.0)
|
314
314
|
rubocop-ast (>= 1.38.0, < 2.0)
|
315
315
|
ruby-progressbar (1.13.0)
|
316
316
|
set (1.1.1)
|
@@ -326,19 +326,19 @@ GEM
|
|
326
326
|
sorted_set (1.0.3)
|
327
327
|
rbtree
|
328
328
|
set (~> 1.0)
|
329
|
-
standard (1.
|
329
|
+
standard (1.49.0)
|
330
330
|
language_server-protocol (~> 3.17.0.2)
|
331
331
|
lint_roller (~> 1.0)
|
332
|
-
rubocop (~> 1.
|
332
|
+
rubocop (~> 1.75.2)
|
333
333
|
standard-custom (~> 1.0.0)
|
334
|
-
standard-performance (~> 1.
|
334
|
+
standard-performance (~> 1.8)
|
335
335
|
standard-custom (1.0.2)
|
336
336
|
lint_roller (~> 1.0)
|
337
337
|
rubocop (~> 1.50)
|
338
|
-
standard-performance (1.
|
338
|
+
standard-performance (1.8.0)
|
339
339
|
lint_roller (~> 1.1)
|
340
|
-
rubocop-performance (~> 1.
|
341
|
-
stringio (3.1.
|
340
|
+
rubocop-performance (~> 1.25.0)
|
341
|
+
stringio (3.1.7)
|
342
342
|
unicode-display_width (3.1.4)
|
343
343
|
unicode-emoji (~> 4.0, >= 4.0.4)
|
344
344
|
unicode-emoji (4.0.4)
|
@@ -370,4 +370,4 @@ DEPENDENCIES
|
|
370
370
|
yard (~> 0.9.37)
|
371
371
|
|
372
372
|
BUNDLED WITH
|
373
|
-
2.6.
|
373
|
+
2.6.7
|
data/README.md
CHANGED
@@ -44,12 +44,13 @@ explicitly with a `ConnectionConfig` object, or implicitly, reading a default
|
|
44
44
|
configuration with `ConnectionConfig#from_env`. `ConnectionConfig#from_env` expects
|
45
45
|
the following environment variables:
|
46
46
|
|
47
|
-
| Variable | Sample value | Description
|
48
|
-
|
49
|
-
| `RABBITMQ_HOST` | `rabbitmq` | Hostname of RabbitMQ server
|
50
|
-
| `RABBITMQ_NODE_PORT_NUMBER` | `5672` | Port name of RabbitMQ server
|
51
|
-
| `RABBITMQ_USERNAME` | `user` | RabbitMQ username
|
52
|
-
| `RABBITMQ_PASSWORD` | `bitnami` | RabbitMQ password
|
47
|
+
| Variable | Sample value | Description |
|
48
|
+
|-----------------------------|--------------|-----------------------------------------------------------|
|
49
|
+
| `RABBITMQ_HOST` | `rabbitmq` | Hostname of RabbitMQ server |
|
50
|
+
| `RABBITMQ_NODE_PORT_NUMBER` | `5672` | Port name of RabbitMQ server |
|
51
|
+
| `RABBITMQ_USERNAME` | `user` | RabbitMQ username |
|
52
|
+
| `RABBITMQ_PASSWORD` | `bitnami` | RabbitMQ password |
|
53
|
+
| `RABBITMQ_AWAIT_ON_CLOSE` | `false` | Whether to wait on response when closing (default = true) |
|
53
54
|
|
54
55
|
The `Mq::Connection#with_topic` and `Mq::Connection#topic_from` can either take an
|
55
56
|
explicit `TopicConfig` object, or implicitly read a default configuration with
|
@@ -70,6 +71,13 @@ connection.with_topic(topic_config) do |topic|
|
|
70
71
|
end
|
71
72
|
```
|
72
73
|
|
74
|
+
`TopicConfig#publish` can either accept an explicit routing key, or read a default value
|
75
|
+
from the environment:
|
76
|
+
|
77
|
+
| Variable | Sample value | Description |
|
78
|
+
|---------------------------------|-------------------------------|----------------------|
|
79
|
+
| `RABBITMQ_PLATFORM_ROUTING_KEY` | `surfliner.metadata.daylight` | RabbitMQ routing key |
|
80
|
+
|
73
81
|
Similarly, `Mq::Topic#bind_queue` can either take an explicit `QueueConfig`, or implicitly
|
74
82
|
read a default with `QueConfig#from_env`. `QueueConfig#from_env` expects the following environment
|
75
83
|
variables:
|
@@ -77,7 +85,6 @@ variables:
|
|
77
85
|
| Variable | Sample value | Description |
|
78
86
|
|---------------------------------|-------------------------------|----------------------|
|
79
87
|
| `RABBITMQ_QUEUE` | `surfliner.metadata` | RabbitMQ queue name |
|
80
|
-
| `RABBITMQ_PLATFORM_ROUTING_KEY` | `surfliner.metadata.daylight` | RabbitMQ routing key |
|
81
88
|
|
82
89
|
And `QueueConfig#from_env` similarly accepts keyword options, which are forwarded to
|
83
90
|
[`Bunny::Channel#queue`](https://api.rubybunny.info/Bunny/Channel.html#queue-instance_method):
|
@@ -28,26 +28,22 @@ module Surfliner
|
|
28
28
|
@config = not_nil!(config)
|
29
29
|
end
|
30
30
|
|
31
|
-
# Opens a
|
32
|
-
#
|
31
|
+
# Opens this connection and creates a channel. If a block is
|
32
|
+
# given, yields the open connection, closing the connection and
|
33
|
+
# channel afterwards; otherwise, returns the open connection.
|
34
|
+
# @return [self] an open connection
|
35
|
+
# @yieldparam connection [self] an open connection
|
33
36
|
# @raise [IOError] if already connected
|
34
37
|
def connect
|
35
|
-
|
38
|
+
init_connection
|
36
39
|
|
37
|
-
|
38
|
-
@session = Bunny.new(config.session_url, logger: logger)
|
39
|
-
connect_on(session)
|
40
|
-
@channel = session.create_channel
|
40
|
+
return self unless block_given?
|
41
41
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
rescue Bunny::PossibleAuthenticationFailureError => err
|
48
|
-
# TODO: realistically, this only happens in session.start, where we're eating it
|
49
|
-
logger.error("Failed to authenticate to #{config.redacted_url}")
|
50
|
-
raise err
|
42
|
+
begin
|
43
|
+
yield self
|
44
|
+
ensure
|
45
|
+
close
|
46
|
+
end
|
51
47
|
end
|
52
48
|
|
53
49
|
# Opens a session, yields a client for the specified topic, and closes the
|
@@ -55,11 +51,9 @@ module Surfliner
|
|
55
51
|
# @param config [TopicConfig] topic configuration
|
56
52
|
# @yield [MqTopic] A client for the topic
|
57
53
|
def with_topic(config = TopicConfig.from_env)
|
58
|
-
connect
|
59
|
-
|
60
|
-
|
61
|
-
ensure
|
62
|
-
close
|
54
|
+
connect do |cxn|
|
55
|
+
yield cxn.topic_from(config)
|
56
|
+
end
|
63
57
|
end
|
64
58
|
|
65
59
|
# Returns a client for the specified topic. Note that this does _not_ open
|
@@ -76,13 +70,9 @@ module Surfliner
|
|
76
70
|
|
77
71
|
# Closes the session.
|
78
72
|
def close
|
79
|
-
return unless channel
|
80
|
-
return if channel.closed?
|
81
|
-
logger.info("closing channel")
|
82
|
-
channel.close
|
83
|
-
ensure
|
84
73
|
logger.info("closing session")
|
85
|
-
|
74
|
+
# Note: This will also close any open channels
|
75
|
+
session&.close(config.await_response_on_close)
|
86
76
|
end
|
87
77
|
|
88
78
|
# @return [true, false] True if the session is open, false otherwise
|
@@ -107,6 +97,24 @@ module Surfliner
|
|
107
97
|
|
108
98
|
private
|
109
99
|
|
100
|
+
def init_connection
|
101
|
+
raise IOError, "RabbitMQ session #{session} already open." if open?
|
102
|
+
|
103
|
+
logger.info("Rabbitmq message broker session url: #{config.redacted_url}")
|
104
|
+
opts = {logger:}.merge(config.opts)
|
105
|
+
@session = Bunny.new(config.session_url, **opts)
|
106
|
+
connect_on(session)
|
107
|
+
@channel = session.create_channel
|
108
|
+
rescue Bunny::TCPConnectionFailed => err
|
109
|
+
# TODO: realistically, this only happens in session.start, where we're eating it
|
110
|
+
logger.error("Connection to #{config.redacted_url} failed")
|
111
|
+
raise err
|
112
|
+
rescue Bunny::PossibleAuthenticationFailureError => err
|
113
|
+
# TODO: realistically, this only happens in session.start, where we're eating it
|
114
|
+
logger.error("Failed to authenticate to #{config.redacted_url}")
|
115
|
+
raise err
|
116
|
+
end
|
117
|
+
|
110
118
|
def connect_on(session, timeout = 120)
|
111
119
|
timer = 0
|
112
120
|
logger.info "Trying to open queue session with timeout=#{timeout}"
|
@@ -2,6 +2,9 @@ module Surfliner
|
|
2
2
|
module Mq
|
3
3
|
# An object encapsulating RabbitMQ configuration.
|
4
4
|
class ConnectionConfig
|
5
|
+
# @return [Array<String>] values of RABBITMQ_AWAIT_ON_CLOSE interpreted as false
|
6
|
+
FALSE_VALUES = ([""] + %w[0 off Off OFF f false False FALSE F n no No NO N]).freeze
|
7
|
+
|
5
8
|
# @return [String] The RabbitMQ hostname
|
6
9
|
attr_reader :host
|
7
10
|
|
@@ -14,36 +17,49 @@ module Surfliner
|
|
14
17
|
# @return [String] The RabbitMQ passsword
|
15
18
|
attr_reader :password
|
16
19
|
|
20
|
+
# @return [Boolean] whether the RabbitMQ client should wait for a response when closing the connection
|
21
|
+
attr_reader :await_response_on_close
|
22
|
+
|
23
|
+
# @return [Hash] Additional RabbitMQ connection options (see Bunny::Session#initialize)
|
24
|
+
attr_reader :opts
|
25
|
+
|
17
26
|
# Initializes a new `MqConfig` object.
|
18
27
|
# @param host [String] RabbitMQ hostname
|
19
28
|
# @param port [String] RabbitMQ AMQP port
|
20
29
|
# @param username [String] RabbitMQ username
|
21
30
|
# @param password [String] RabbitMQ passsword
|
22
|
-
|
31
|
+
# @param await_response_on_close [Boolean] whether the RabbitMQ client should wait for a response when closing the connection
|
32
|
+
# @param opts [Hash] additional RabbitMQ conection options (see Bunny::Session#initialize)
|
33
|
+
def initialize(host:, port:, username:, password:, await_response_on_close: true, **opts)
|
23
34
|
@host = host
|
24
35
|
@port = port
|
25
36
|
@username = username
|
26
37
|
@password = password
|
38
|
+
@await_response_on_close = parse_boolean(await_response_on_close)
|
39
|
+
@opts = opts
|
27
40
|
end
|
28
41
|
|
29
42
|
class << self
|
30
43
|
# Reads RabbitMQ configuration from environment variables and
|
31
44
|
# returns it as a new `ConnectionConfig` object.
|
32
45
|
#
|
33
|
-
# | Variable | Sample value | Description
|
34
|
-
#
|
35
|
-
# | `RABBITMQ_HOST` | `rabbitmq` | Hostname of RabbitMQ server
|
36
|
-
# | `RABBITMQ_NODE_PORT_NUMBER` | `5672` | Port name of RabbitMQ server
|
37
|
-
# | `RABBITMQ_USERNAME` | `user` | RabbitMQ username
|
38
|
-
# | `RABBITMQ_PASSWORD` | `bitnami` | RabbitMQ password
|
46
|
+
# | Variable | Sample value | Description |
|
47
|
+
# |-----------------------------|--------------|-----------------------------------------------------------|
|
48
|
+
# | `RABBITMQ_HOST` | `rabbitmq` | Hostname of RabbitMQ server |
|
49
|
+
# | `RABBITMQ_NODE_PORT_NUMBER` | `5672` | Port name of RabbitMQ server |
|
50
|
+
# | `RABBITMQ_USERNAME` | `user` | RabbitMQ username |
|
51
|
+
# | `RABBITMQ_PASSWORD` | `bitnami` | RabbitMQ password |
|
52
|
+
# | `RABBITMQ_AWAIT_ON_CLOSE` | `false` | Whether to wait on response when closing (default = true) |
|
39
53
|
#
|
40
54
|
# @return [ConnectionConfig] The configuration.
|
41
|
-
def from_env
|
55
|
+
def from_env(**opts)
|
42
56
|
ConnectionConfig.new(
|
43
57
|
host: ENV.fetch("RABBITMQ_HOST"),
|
44
58
|
port: ENV.fetch("RABBITMQ_NODE_PORT_NUMBER"),
|
45
59
|
username: ENV.fetch("RABBITMQ_USERNAME"),
|
46
|
-
password: ENV.fetch("RABBITMQ_PASSWORD")
|
60
|
+
password: ENV.fetch("RABBITMQ_PASSWORD"),
|
61
|
+
await_response_on_close: ENV["RABBITMQ_AWAIT_ON_CLOSE"] || true,
|
62
|
+
**opts
|
47
63
|
)
|
48
64
|
end
|
49
65
|
end
|
@@ -57,6 +73,12 @@ module Surfliner
|
|
57
73
|
def redacted_url
|
58
74
|
@redacted_url ||= session_url.sub(password, "REDACTED")
|
59
75
|
end
|
76
|
+
|
77
|
+
private
|
78
|
+
|
79
|
+
def parse_boolean(v)
|
80
|
+
!FALSE_VALUES.include?(v.to_s)
|
81
|
+
end
|
60
82
|
end
|
61
83
|
end
|
62
84
|
end
|
@@ -5,18 +5,13 @@ module Surfliner
|
|
5
5
|
# @return [String] The queue to listen to
|
6
6
|
attr_reader :name
|
7
7
|
|
8
|
-
# @return [String] platform routing key to listen to
|
9
|
-
attr_reader :routing_key
|
10
|
-
|
11
8
|
# @return [Hash] RabbitMQ queue options. (See Bunny::Channel#queue)
|
12
9
|
attr_reader :options
|
13
10
|
|
14
11
|
# @param name [String] queue exchange to listen to
|
15
|
-
# @param routing_key [String] platform routing key to listen to
|
16
12
|
# @param options [Hash] RabbitMQ queue options. (See Bunny::Channel#queue)
|
17
|
-
def initialize(name:,
|
13
|
+
def initialize(name:, options: {})
|
18
14
|
@name = name
|
19
|
-
@routing_key = routing_key
|
20
15
|
@options = options
|
21
16
|
end
|
22
17
|
|
@@ -27,22 +22,15 @@ module Surfliner
|
|
27
22
|
# | Variable | Sample value | Description |
|
28
23
|
# |---------------------------------|-------------------------------|----------------------|
|
29
24
|
# | `RABBITMQ_QUEUE` | `surfliner.metadata` | RabbitMQ queue name |
|
30
|
-
# | `RABBITMQ_PLATFORM_ROUTING_KEY` | `surfliner.metadata.daylight` | RabbitMQ routing key |
|
31
25
|
#
|
32
26
|
# @param options [Hash] RabbitMQ queue options. (See Bunny::Channel#queue)
|
33
27
|
# @return [QueueConfig] The configuration.
|
34
28
|
def from_env(**options)
|
35
29
|
QueueConfig.new(
|
36
30
|
name: ENV.fetch("RABBITMQ_QUEUE"),
|
37
|
-
routing_key: default_routing_key,
|
38
31
|
options:
|
39
32
|
)
|
40
33
|
end
|
41
|
-
|
42
|
-
# @return [String] The default routing key from `ENV["RABBITMQ_PLATFORM_ROUTING_KEY"]`
|
43
|
-
def default_routing_key
|
44
|
-
ENV.fetch("RABBITMQ_PLATFORM_ROUTING_KEY")
|
45
|
-
end
|
46
34
|
end
|
47
35
|
end
|
48
36
|
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
module Surfliner
|
2
|
+
module Mq
|
3
|
+
# Queue subscriber that routes messages by routing key to multiple handlers
|
4
|
+
class Router
|
5
|
+
# @return [Topic] the topic
|
6
|
+
attr_reader :topic
|
7
|
+
|
8
|
+
# @return [Bunny::Queue] the queue
|
9
|
+
attr_reader :queue
|
10
|
+
|
11
|
+
# @return [Bunny::Consumer] the consumer (used to unsubscribe)
|
12
|
+
attr_reader :consumer
|
13
|
+
|
14
|
+
# Initializes a new Router and subscribes it to the specified queue
|
15
|
+
# on the specified topic.
|
16
|
+
#
|
17
|
+
# @param topic [Mq::Topic] The topic to listen on
|
18
|
+
# @param queue_config [Mq::QueueConfig] The queue to subscribe to
|
19
|
+
def initialize(topic, queue_config: QueueConfig.from_env)
|
20
|
+
@topic = topic
|
21
|
+
@queue = topic.queue(queue_config)
|
22
|
+
@consumer = queue.subscribe(&method(:on_delivery))
|
23
|
+
end
|
24
|
+
|
25
|
+
# Adds a handler for the specified routing key.
|
26
|
+
# Handlers will receive only messages with the specified routing key.
|
27
|
+
# Multiple handler blocks can be added to the same queue and/or routing key.
|
28
|
+
#
|
29
|
+
# @param routing_key [String] the routing key to filter messages by
|
30
|
+
# @yieldparam payload_json [String] each payload
|
31
|
+
def add_handler(routing_key, &block)
|
32
|
+
raise "Can't add handlers after router shutdown" if consumer.nil?
|
33
|
+
|
34
|
+
log("adding handler #{block}")
|
35
|
+
handlers = handlers_for(routing_key)
|
36
|
+
queue.bind(topic.exchange, routing_key:) if handlers.empty?
|
37
|
+
handlers << block
|
38
|
+
end
|
39
|
+
|
40
|
+
# Removes all handlers and unsubscribes from the queue.
|
41
|
+
def shutdown
|
42
|
+
log("shutting down")
|
43
|
+
handlers_by_routing_key.clear
|
44
|
+
consumer.cancel
|
45
|
+
@consumer = nil
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
# Receives messages and routes them to the appropriate handler
|
51
|
+
#
|
52
|
+
# @param delivery_info [Bunny::DeliveryInfo] the delivery info
|
53
|
+
# @param _metadata [Bunny::MessageProperties] the message properties
|
54
|
+
# @param payload_json [String] the payload as a string
|
55
|
+
def on_delivery(delivery_info, _metadata, payload_json)
|
56
|
+
routing_key = delivery_info.routing_key
|
57
|
+
handlers_for(routing_key).each do |handler|
|
58
|
+
log("delivering #{payload_json} to handler #{handler} for #{routing_key}")
|
59
|
+
handler.call(payload_json)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
# @return [Hash{String => Array<Proc>}] all handlers by routing key
|
64
|
+
def handlers_by_routing_key
|
65
|
+
@handlers_by_routing_key ||= {}
|
66
|
+
end
|
67
|
+
|
68
|
+
# @return [Array<Proc>] the handlers for the specified key, if any
|
69
|
+
def handlers_for(routing_key)
|
70
|
+
(handlers_by_routing_key[routing_key] ||= [])
|
71
|
+
end
|
72
|
+
|
73
|
+
def log(msg)
|
74
|
+
topic.logger.debug("#{self.class}(#{topic.name}, #{queue.name}): #{msg}")
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
data/lib/surfliner/mq/topic.rb
CHANGED
@@ -38,17 +38,36 @@ module Surfliner
|
|
38
38
|
# @param payload [String] the payload to publish
|
39
39
|
# @param routing_key [String] platform routing key to publish to
|
40
40
|
# @return [Bunny::Exchange] see #exchange
|
41
|
-
def publish(payload, routing_key:
|
41
|
+
def publish(payload, routing_key: default_routing_key)
|
42
42
|
logger.info "Publishing to #{routing_key} with payload: #{payload}"
|
43
43
|
exchange.publish(payload, routing_key:)
|
44
44
|
end
|
45
45
|
|
46
|
+
# Creates or looks up the specified queue and binds it to receive
|
47
|
+
# messages with the specified routing key.
|
46
48
|
# @param config [QueueConfig] queue configuration
|
47
|
-
# @
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
49
|
+
# @param routing_key [String] platform routing key to bind on
|
50
|
+
# @return [Bunny::Queue] the queue
|
51
|
+
def bind_queue(config = QueueConfig.from_env, routing_key: default_routing_key)
|
52
|
+
queue(config).tap { |q| q.bind(exchange, routing_key:) }
|
53
|
+
end
|
54
|
+
|
55
|
+
# Creates or looks up the specified queue.
|
56
|
+
# @param config [QueueConfig] queue configuration
|
57
|
+
# @return [Bunny::Queue] the queue
|
58
|
+
def queue(config)
|
59
|
+
channel.queue(config.name, config.options)
|
60
|
+
end
|
61
|
+
|
62
|
+
# Returns the default (environment-variable-based) routing key
|
63
|
+
#
|
64
|
+
# | Variable | Sample value | Description |
|
65
|
+
# |---------------------------------|-------------------------------|----------------------|
|
66
|
+
# | `RABBITMQ_PLATFORM_ROUTING_KEY` | `surfliner.metadata.daylight` | RabbitMQ routing key |
|
67
|
+
#
|
68
|
+
# @return [String, nil] the configured default routing key
|
69
|
+
def default_routing_key
|
70
|
+
ENV.fetch("RABBITMQ_PLATFORM_ROUTING_KEY")
|
52
71
|
end
|
53
72
|
|
54
73
|
class << self
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: surfliner-metadata_consumer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.0.pre.alpha.
|
4
|
+
version: 0.1.0.pre.alpha.7
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Project Surfliner
|
8
8
|
bindir: bin
|
9
9
|
cert_chain: []
|
10
|
-
date:
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
11
11
|
dependencies:
|
12
12
|
- !ruby/object:Gem::Dependency
|
13
13
|
name: bunny
|
@@ -15,14 +15,14 @@ dependencies:
|
|
15
15
|
requirements:
|
16
16
|
- - "~>"
|
17
17
|
- !ruby/object:Gem::Version
|
18
|
-
version: '2.
|
18
|
+
version: '2.24'
|
19
19
|
type: :runtime
|
20
20
|
prerelease: false
|
21
21
|
version_requirements: !ruby/object:Gem::Requirement
|
22
22
|
requirements:
|
23
23
|
- - "~>"
|
24
24
|
- !ruby/object:Gem::Version
|
25
|
-
version: '2.
|
25
|
+
version: '2.24'
|
26
26
|
- !ruby/object:Gem::Dependency
|
27
27
|
name: opentelemetry-exporter-otlp
|
28
28
|
requirement: !ruby/object:Gem::Requirement
|
@@ -295,6 +295,7 @@ files:
|
|
295
295
|
- lib/surfliner/mq/connection.rb
|
296
296
|
- lib/surfliner/mq/connection_config.rb
|
297
297
|
- lib/surfliner/mq/queue_config.rb
|
298
|
+
- lib/surfliner/mq/router.rb
|
298
299
|
- lib/surfliner/mq/topic.rb
|
299
300
|
- lib/surfliner/mq/topic_config.rb
|
300
301
|
- lib/surfliner/util.rb
|
@@ -320,7 +321,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
320
321
|
- !ruby/object:Gem::Version
|
321
322
|
version: '0'
|
322
323
|
requirements: []
|
323
|
-
rubygems_version: 3.6.
|
324
|
+
rubygems_version: 3.6.7
|
324
325
|
specification_version: 4
|
325
326
|
summary: Surfliner metadata consumer
|
326
327
|
test_files: []
|