pgmq-ruby 0.4.0 → 0.5.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/.github/workflows/ci.yml +42 -22
- data/.github/workflows/push.yml +1 -1
- data/.rspec +1 -0
- data/.rubocop.yml +66 -0
- data/.yard-lint.yml +1 -3
- data/CHANGELOG.md +35 -0
- data/CLAUDE.md +310 -0
- data/Gemfile +5 -5
- data/Gemfile.lint +16 -0
- data/Gemfile.lint.lock +120 -0
- data/Gemfile.lock +20 -6
- data/README.md +213 -10
- data/Rakefile +71 -2
- data/docker-compose.yml +2 -2
- data/lib/pgmq/client/consumer.rb +80 -7
- data/lib/pgmq/client/maintenance.rb +4 -21
- data/lib/pgmq/client/message_lifecycle.rb +69 -44
- data/lib/pgmq/client/metrics.rb +2 -2
- data/lib/pgmq/client/multi_queue.rb +9 -9
- data/lib/pgmq/client/producer.rb +7 -7
- data/lib/pgmq/client/queue_management.rb +9 -9
- data/lib/pgmq/client/topics.rb +268 -0
- data/lib/pgmq/client.rb +13 -12
- data/lib/pgmq/connection.rb +11 -11
- data/lib/pgmq/message.rb +11 -9
- data/lib/pgmq/metrics.rb +7 -7
- data/lib/pgmq/queue_metadata.rb +7 -7
- data/lib/pgmq/version.rb +1 -1
- data/lib/pgmq.rb +3 -3
- data/package-lock.json +331 -0
- data/package.json +9 -0
- data/pgmq-ruby.gemspec +20 -20
- data/renovate.json +20 -1
- metadata +8 -2
- data/.coditsu/ci.yml +0 -3
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module PGMQ
|
|
4
|
+
class Client
|
|
5
|
+
# Topic routing operations (AMQP-like patterns)
|
|
6
|
+
#
|
|
7
|
+
# This module provides AMQP-style topic routing for PGMQ, allowing messages
|
|
8
|
+
# to be routed to multiple queues based on pattern matching.
|
|
9
|
+
#
|
|
10
|
+
# Topic patterns support wildcards:
|
|
11
|
+
# - `*` matches exactly one word between dots (e.g., `orders.*` matches `orders.new`)
|
|
12
|
+
# - `#` matches zero or more words (e.g., `orders.#` matches `orders.new.urgent`)
|
|
13
|
+
#
|
|
14
|
+
# @note Requires PGMQ v1.11.0+
|
|
15
|
+
module Topics
|
|
16
|
+
# Binds a topic pattern to a queue
|
|
17
|
+
#
|
|
18
|
+
# Messages sent with routing keys matching this pattern will be delivered
|
|
19
|
+
# to the specified queue.
|
|
20
|
+
#
|
|
21
|
+
# @param pattern [String] topic pattern with optional wildcards (* or #)
|
|
22
|
+
# @param queue_name [String] name of the queue to bind
|
|
23
|
+
# @return [void]
|
|
24
|
+
#
|
|
25
|
+
# @example Bind exact routing key
|
|
26
|
+
# client.bind_topic("orders.new", "new_orders")
|
|
27
|
+
#
|
|
28
|
+
# @example Bind with single-word wildcard
|
|
29
|
+
# client.bind_topic("orders.*", "all_order_events")
|
|
30
|
+
#
|
|
31
|
+
# @example Bind with multi-word wildcard
|
|
32
|
+
# client.bind_topic("orders.#", "order_audit_log")
|
|
33
|
+
def bind_topic(pattern, queue_name)
|
|
34
|
+
validate_queue_name!(queue_name)
|
|
35
|
+
|
|
36
|
+
with_connection do |conn|
|
|
37
|
+
conn.exec_params(
|
|
38
|
+
"SELECT pgmq.bind_topic($1::text, $2::text)",
|
|
39
|
+
[pattern, queue_name]
|
|
40
|
+
)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
nil
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Unbinds a topic pattern from a queue
|
|
47
|
+
#
|
|
48
|
+
# @param pattern [String] topic pattern to unbind
|
|
49
|
+
# @param queue_name [String] name of the queue to unbind from
|
|
50
|
+
# @return [Boolean] true if the binding was removed, false if it didn't exist
|
|
51
|
+
#
|
|
52
|
+
# @example
|
|
53
|
+
# client.unbind_topic("orders.new", "new_orders")
|
|
54
|
+
def unbind_topic(pattern, queue_name)
|
|
55
|
+
validate_queue_name!(queue_name)
|
|
56
|
+
|
|
57
|
+
result = with_connection do |conn|
|
|
58
|
+
conn.exec_params(
|
|
59
|
+
"SELECT pgmq.unbind_topic($1::text, $2::text)",
|
|
60
|
+
[pattern, queue_name]
|
|
61
|
+
)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
result[0]["unbind_topic"] == "t"
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Sends a message via topic routing
|
|
68
|
+
#
|
|
69
|
+
# The message will be delivered to all queues whose bound patterns match
|
|
70
|
+
# the routing key.
|
|
71
|
+
#
|
|
72
|
+
# @param routing_key [String] dot-separated routing key (e.g., "orders.new.priority")
|
|
73
|
+
# @param message [String] message as JSON string
|
|
74
|
+
# @param headers [String, nil] optional headers as JSON string
|
|
75
|
+
# @param delay [Integer] delay in seconds before message becomes visible
|
|
76
|
+
# @return [Integer] count of queues the message was delivered to
|
|
77
|
+
#
|
|
78
|
+
# @example Basic topic send
|
|
79
|
+
# count = client.produce_topic("orders.new", '{"order_id":123}')
|
|
80
|
+
#
|
|
81
|
+
# @example With headers and delay
|
|
82
|
+
# count = client.produce_topic("orders.new.priority",
|
|
83
|
+
# '{"order_id":123}',
|
|
84
|
+
# headers: '{"trace_id":"abc"}',
|
|
85
|
+
# delay: 30)
|
|
86
|
+
def produce_topic(routing_key, message, headers: nil, delay: 0)
|
|
87
|
+
result = with_connection do |conn|
|
|
88
|
+
if headers
|
|
89
|
+
conn.exec_params(
|
|
90
|
+
"SELECT pgmq.send_topic($1::text, $2::jsonb, $3::jsonb, $4::integer)",
|
|
91
|
+
[routing_key, message, headers, delay]
|
|
92
|
+
)
|
|
93
|
+
elsif delay > 0
|
|
94
|
+
conn.exec_params(
|
|
95
|
+
"SELECT pgmq.send_topic($1::text, $2::jsonb, $3::integer)",
|
|
96
|
+
[routing_key, message, delay]
|
|
97
|
+
)
|
|
98
|
+
else
|
|
99
|
+
conn.exec_params(
|
|
100
|
+
"SELECT pgmq.send_topic($1::text, $2::jsonb)",
|
|
101
|
+
[routing_key, message]
|
|
102
|
+
)
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
result[0]["send_topic"].to_i
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# Sends multiple messages via topic routing
|
|
110
|
+
#
|
|
111
|
+
# All messages will be delivered to all queues whose bound patterns match
|
|
112
|
+
# the routing key.
|
|
113
|
+
#
|
|
114
|
+
# @param routing_key [String] dot-separated routing key
|
|
115
|
+
# @param messages [Array<String>] array of message payloads as JSON strings
|
|
116
|
+
# @param headers [Array<String>, nil] optional array of headers as JSON strings
|
|
117
|
+
# @param delay [Integer] delay in seconds before messages become visible
|
|
118
|
+
# @return [Array<Hash>] array of hashes with :queue_name and :msg_id
|
|
119
|
+
#
|
|
120
|
+
# @example Batch topic send
|
|
121
|
+
# results = client.produce_batch_topic("orders.new", [
|
|
122
|
+
# '{"order_id":1}',
|
|
123
|
+
# '{"order_id":2}'
|
|
124
|
+
# ])
|
|
125
|
+
# # => [{ queue_name: "new_orders", msg_id: "1" }, ...]
|
|
126
|
+
def produce_batch_topic(routing_key, messages, headers: nil, delay: 0)
|
|
127
|
+
return [] if messages.empty?
|
|
128
|
+
|
|
129
|
+
if headers && headers.length != messages.length
|
|
130
|
+
raise ArgumentError,
|
|
131
|
+
"headers array length (#{headers.length}) must match messages array length (#{messages.length})"
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
result = with_connection do |conn|
|
|
135
|
+
encoder = PG::TextEncoder::Array.new
|
|
136
|
+
encoded_messages = encoder.encode(messages)
|
|
137
|
+
|
|
138
|
+
if headers
|
|
139
|
+
encoded_headers = encoder.encode(headers)
|
|
140
|
+
conn.exec_params(
|
|
141
|
+
"SELECT * FROM pgmq.send_batch_topic($1::text, $2::jsonb[], $3::jsonb[], $4::integer)",
|
|
142
|
+
[routing_key, encoded_messages, encoded_headers, delay]
|
|
143
|
+
)
|
|
144
|
+
elsif delay > 0
|
|
145
|
+
conn.exec_params(
|
|
146
|
+
"SELECT * FROM pgmq.send_batch_topic($1::text, $2::jsonb[], $3::integer)",
|
|
147
|
+
[routing_key, encoded_messages, delay]
|
|
148
|
+
)
|
|
149
|
+
else
|
|
150
|
+
conn.exec_params(
|
|
151
|
+
"SELECT * FROM pgmq.send_batch_topic($1::text, $2::jsonb[])",
|
|
152
|
+
[routing_key, encoded_messages]
|
|
153
|
+
)
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
result.map do |row|
|
|
158
|
+
{ queue_name: row["queue_name"], msg_id: row["msg_id"] }
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
# Lists all topic bindings
|
|
163
|
+
#
|
|
164
|
+
# @param queue_name [String, nil] optional queue name to filter by
|
|
165
|
+
# @return [Array<Hash>] array of binding hashes with pattern, queue_name, bound_at
|
|
166
|
+
#
|
|
167
|
+
# @example List all bindings
|
|
168
|
+
# bindings = client.list_topic_bindings
|
|
169
|
+
# # => [{ pattern: "orders.*", queue_name: "orders", bound_at: "..." }, ...]
|
|
170
|
+
#
|
|
171
|
+
# @example List bindings for specific queue
|
|
172
|
+
# bindings = client.list_topic_bindings(queue_name: "orders")
|
|
173
|
+
def list_topic_bindings(queue_name: nil)
|
|
174
|
+
result = with_connection do |conn|
|
|
175
|
+
if queue_name
|
|
176
|
+
validate_queue_name!(queue_name)
|
|
177
|
+
conn.exec_params(
|
|
178
|
+
"SELECT pattern, queue_name, bound_at FROM pgmq.list_topic_bindings($1::text)",
|
|
179
|
+
[queue_name]
|
|
180
|
+
)
|
|
181
|
+
else
|
|
182
|
+
conn.exec("SELECT pattern, queue_name, bound_at FROM pgmq.list_topic_bindings()")
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
result.map do |row|
|
|
187
|
+
{
|
|
188
|
+
pattern: row["pattern"],
|
|
189
|
+
queue_name: row["queue_name"],
|
|
190
|
+
bound_at: row["bound_at"]
|
|
191
|
+
}
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
# Tests which queues a routing key would match
|
|
196
|
+
#
|
|
197
|
+
# Useful for debugging topic routing configurations.
|
|
198
|
+
#
|
|
199
|
+
# @param routing_key [String] routing key to test
|
|
200
|
+
# @return [Array<Hash>] array of matched bindings with pattern and queue_name
|
|
201
|
+
#
|
|
202
|
+
# @example Test routing
|
|
203
|
+
# matches = client.test_routing("orders.new.priority")
|
|
204
|
+
# # => [{ pattern: "orders.*", queue_name: "new_orders" }, ...]
|
|
205
|
+
def test_routing(routing_key)
|
|
206
|
+
result = with_connection do |conn|
|
|
207
|
+
conn.exec_params(
|
|
208
|
+
"SELECT pattern, queue_name FROM pgmq.test_routing($1::text)",
|
|
209
|
+
[routing_key]
|
|
210
|
+
)
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
result.map do |row|
|
|
214
|
+
{ pattern: row["pattern"], queue_name: row["queue_name"] }
|
|
215
|
+
end
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
# Validates a routing key
|
|
219
|
+
#
|
|
220
|
+
# Routing keys are dot-separated words (no wildcards allowed).
|
|
221
|
+
# Returns false for invalid routing keys (PGMQ raises an error for invalid keys).
|
|
222
|
+
#
|
|
223
|
+
# @param routing_key [String] routing key to validate
|
|
224
|
+
# @return [Boolean] true if valid, false if invalid
|
|
225
|
+
#
|
|
226
|
+
# @example
|
|
227
|
+
# client.validate_routing_key("orders.new.priority") # => true
|
|
228
|
+
# client.validate_routing_key("orders.*") # => false (wildcards not allowed)
|
|
229
|
+
def validate_routing_key(routing_key)
|
|
230
|
+
result = with_connection do |conn|
|
|
231
|
+
conn.exec_params(
|
|
232
|
+
"SELECT pgmq.validate_routing_key($1::text)",
|
|
233
|
+
[routing_key]
|
|
234
|
+
)
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
result[0]["validate_routing_key"] == "t"
|
|
238
|
+
rescue PGMQ::Errors::ConnectionError => e
|
|
239
|
+
# PGMQ raises an error for invalid routing keys
|
|
240
|
+
return false if e.message.include?("invalid characters")
|
|
241
|
+
|
|
242
|
+
raise
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
# Validates a topic pattern
|
|
246
|
+
#
|
|
247
|
+
# Topic patterns can include wildcards: * (single word) or # (zero or more words).
|
|
248
|
+
#
|
|
249
|
+
# @param pattern [String] topic pattern to validate
|
|
250
|
+
# @return [Boolean] true if valid
|
|
251
|
+
#
|
|
252
|
+
# @example
|
|
253
|
+
# client.validate_topic_pattern("orders.*") # => true
|
|
254
|
+
# client.validate_topic_pattern("orders.#") # => true
|
|
255
|
+
# client.validate_topic_pattern("orders.new") # => true
|
|
256
|
+
def validate_topic_pattern(pattern)
|
|
257
|
+
result = with_connection do |conn|
|
|
258
|
+
conn.exec_params(
|
|
259
|
+
"SELECT pgmq.validate_topic_pattern($1::text)",
|
|
260
|
+
[pattern]
|
|
261
|
+
)
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
result[0]["validate_topic_pattern"] == "t"
|
|
265
|
+
end
|
|
266
|
+
end
|
|
267
|
+
end
|
|
268
|
+
end
|
data/lib/pgmq/client.rb
CHANGED
|
@@ -31,8 +31,9 @@ module PGMQ
|
|
|
31
31
|
include Consumer # Single-queue reading operations
|
|
32
32
|
include MultiQueue # Multi-queue operations
|
|
33
33
|
include MessageLifecycle # Message state transitions (pop, delete, archive)
|
|
34
|
-
include Maintenance # Queue maintenance (purge,
|
|
34
|
+
include Maintenance # Queue maintenance (purge, notifications)
|
|
35
35
|
include Metrics # Monitoring and metrics
|
|
36
|
+
include Topics # Topic routing (AMQP-like patterns, PGMQ v1.11.0+)
|
|
36
37
|
|
|
37
38
|
# Default visibility timeout in seconds
|
|
38
39
|
DEFAULT_VT = 30
|
|
@@ -65,15 +66,15 @@ module PGMQ
|
|
|
65
66
|
auto_reconnect: true
|
|
66
67
|
)
|
|
67
68
|
@connection = if conn_params.is_a?(Connection)
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
69
|
+
conn_params
|
|
70
|
+
else
|
|
71
|
+
Connection.new(
|
|
72
|
+
conn_params,
|
|
73
|
+
pool_size: pool_size,
|
|
74
|
+
pool_timeout: pool_timeout,
|
|
75
|
+
auto_reconnect: auto_reconnect
|
|
76
|
+
)
|
|
77
|
+
end
|
|
77
78
|
end
|
|
78
79
|
|
|
79
80
|
# Closes all connections in the pool
|
|
@@ -109,7 +110,7 @@ module PGMQ
|
|
|
109
110
|
if queue_name.nil? || queue_name.to_s.strip.empty?
|
|
110
111
|
raise(
|
|
111
112
|
Errors::InvalidQueueNameError,
|
|
112
|
-
|
|
113
|
+
"Queue name cannot be empty"
|
|
113
114
|
)
|
|
114
115
|
end
|
|
115
116
|
|
|
@@ -131,7 +132,7 @@ module PGMQ
|
|
|
131
132
|
raise(
|
|
132
133
|
Errors::InvalidQueueNameError,
|
|
133
134
|
"Invalid queue name '#{queue_name}': must start with a letter or underscore " \
|
|
134
|
-
|
|
135
|
+
"and contain only letters, digits, and underscores"
|
|
135
136
|
)
|
|
136
137
|
end
|
|
137
138
|
end
|
data/lib/pgmq/connection.rb
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require
|
|
4
|
-
require
|
|
3
|
+
require "pg"
|
|
4
|
+
require "connection_pool"
|
|
5
5
|
|
|
6
6
|
module PGMQ
|
|
7
7
|
# Manages database connections for PGMQ
|
|
@@ -45,7 +45,7 @@ module PGMQ
|
|
|
45
45
|
if conn_params.nil?
|
|
46
46
|
raise(
|
|
47
47
|
PGMQ::Errors::ConfigurationError,
|
|
48
|
-
|
|
48
|
+
"Connection parameters are required"
|
|
49
49
|
)
|
|
50
50
|
end
|
|
51
51
|
|
|
@@ -113,12 +113,12 @@ module PGMQ
|
|
|
113
113
|
def connection_lost_error?(error)
|
|
114
114
|
# Common connection lost errors
|
|
115
115
|
lost_connection_messages = [
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
116
|
+
"server closed the connection",
|
|
117
|
+
"connection not open",
|
|
118
|
+
"no connection to the server",
|
|
119
|
+
"terminating connection",
|
|
120
|
+
"connection to server was lost",
|
|
121
|
+
"could not receive data from server"
|
|
122
122
|
]
|
|
123
123
|
|
|
124
124
|
message = error.message.downcase
|
|
@@ -145,7 +145,7 @@ module PGMQ
|
|
|
145
145
|
return parse_connection_string(params) if params.is_a?(String)
|
|
146
146
|
return params if params.is_a?(Hash) && !params.empty?
|
|
147
147
|
|
|
148
|
-
raise PGMQ::Errors::ConfigurationError,
|
|
148
|
+
raise PGMQ::Errors::ConfigurationError, "Invalid connection parameters format"
|
|
149
149
|
end
|
|
150
150
|
|
|
151
151
|
# Parses a PostgreSQL connection string
|
|
@@ -173,7 +173,7 @@ module PGMQ
|
|
|
173
173
|
ConnectionPool.new(size: @pool_size, timeout: @pool_timeout) do
|
|
174
174
|
create_connection(params)
|
|
175
175
|
end
|
|
176
|
-
rescue
|
|
176
|
+
rescue => e
|
|
177
177
|
raise PGMQ::Errors::ConnectionError, "Failed to create connection pool: #{e.message}"
|
|
178
178
|
end
|
|
179
179
|
|
data/lib/pgmq/message.rb
CHANGED
|
@@ -11,12 +11,13 @@ module PGMQ
|
|
|
11
11
|
# puts msg.msg_id # => "123" (String from PG)
|
|
12
12
|
# puts msg.read_ct # => "1" (String from PG)
|
|
13
13
|
# puts msg.enqueued_at # => "2025-01-15 10:30:00+00" (String from PG)
|
|
14
|
+
# puts msg.last_read_at # => "2025-01-15 10:31:00+00" (String from PG, nil if never read)
|
|
14
15
|
# puts msg.vt # => "2025-01-15 10:30:30+00" (String from PG)
|
|
15
16
|
# puts msg.message # => "{\"order_id\":456}" (Raw JSONB string)
|
|
16
17
|
# puts msg.headers # => "{\"trace_id\":\"abc123\"}" (Raw JSONB string, optional)
|
|
17
18
|
# puts msg.queue_name # => "my_queue" (only present for multi-queue operations)
|
|
18
19
|
class Message < Data.define(
|
|
19
|
-
:msg_id, :read_ct, :enqueued_at, :vt, :message, :headers, :queue_name
|
|
20
|
+
:msg_id, :read_ct, :enqueued_at, :last_read_at, :vt, :message, :headers, :queue_name
|
|
20
21
|
)
|
|
21
22
|
class << self
|
|
22
23
|
# Creates a new Message from a database row
|
|
@@ -27,19 +28,20 @@ module PGMQ
|
|
|
27
28
|
# No parsing, no deserialization, no transformation
|
|
28
29
|
# The pg gem returns JSONB as String by default
|
|
29
30
|
super(
|
|
30
|
-
msg_id: row[
|
|
31
|
-
read_ct: row[
|
|
32
|
-
enqueued_at: row[
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
31
|
+
msg_id: row["msg_id"],
|
|
32
|
+
read_ct: row["read_ct"],
|
|
33
|
+
enqueued_at: row["enqueued_at"],
|
|
34
|
+
last_read_at: row["last_read_at"], # nil if message has never been read
|
|
35
|
+
vt: row["vt"],
|
|
36
|
+
message: row["message"],
|
|
37
|
+
headers: row["headers"], # JSONB column for metadata (optional)
|
|
38
|
+
queue_name: row["queue_name"] # nil for single-queue operations
|
|
37
39
|
)
|
|
38
40
|
end
|
|
39
41
|
end
|
|
40
42
|
|
|
41
43
|
# Alias for msg_id (common in messaging systems)
|
|
42
44
|
# @return [String]
|
|
43
|
-
|
|
45
|
+
alias_method :id, :msg_id
|
|
44
46
|
end
|
|
45
47
|
end
|
data/lib/pgmq/metrics.rb
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require
|
|
3
|
+
require "time"
|
|
4
4
|
|
|
5
5
|
module PGMQ
|
|
6
6
|
# Represents metrics for a PGMQ queue
|
|
@@ -24,12 +24,12 @@ module PGMQ
|
|
|
24
24
|
def new(row, **)
|
|
25
25
|
# Return raw values as-is from PostgreSQL
|
|
26
26
|
super(
|
|
27
|
-
queue_name: row[
|
|
28
|
-
queue_length: row[
|
|
29
|
-
newest_msg_age_sec: row[
|
|
30
|
-
oldest_msg_age_sec: row[
|
|
31
|
-
total_messages: row[
|
|
32
|
-
scrape_time: row[
|
|
27
|
+
queue_name: row["queue_name"],
|
|
28
|
+
queue_length: row["queue_length"],
|
|
29
|
+
newest_msg_age_sec: row["newest_msg_age_sec"],
|
|
30
|
+
oldest_msg_age_sec: row["oldest_msg_age_sec"],
|
|
31
|
+
total_messages: row["total_messages"],
|
|
32
|
+
scrape_time: row["scrape_time"]
|
|
33
33
|
)
|
|
34
34
|
end
|
|
35
35
|
end
|
data/lib/pgmq/queue_metadata.rb
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require
|
|
3
|
+
require "time"
|
|
4
4
|
|
|
5
5
|
module PGMQ
|
|
6
6
|
# Represents metadata about a PGMQ queue
|
|
@@ -18,20 +18,20 @@ module PGMQ
|
|
|
18
18
|
def new(row, **)
|
|
19
19
|
# Return raw values as-is from PostgreSQL
|
|
20
20
|
super(
|
|
21
|
-
queue_name: row[
|
|
22
|
-
created_at: row[
|
|
23
|
-
is_partitioned: row[
|
|
24
|
-
is_unlogged: row[
|
|
21
|
+
queue_name: row["queue_name"],
|
|
22
|
+
created_at: row["created_at"],
|
|
23
|
+
is_partitioned: row["is_partitioned"],
|
|
24
|
+
is_unlogged: row["is_unlogged"]
|
|
25
25
|
)
|
|
26
26
|
end
|
|
27
27
|
end
|
|
28
28
|
|
|
29
29
|
# Alias for is_partitioned
|
|
30
30
|
# @return [String] 't' or 'f' from PostgreSQL
|
|
31
|
-
|
|
31
|
+
alias_method :partitioned?, :is_partitioned
|
|
32
32
|
|
|
33
33
|
# Alias for is_unlogged
|
|
34
34
|
# @return [String] 't' or 'f' from PostgreSQL
|
|
35
|
-
|
|
35
|
+
alias_method :unlogged?, :is_unlogged
|
|
36
36
|
end
|
|
37
37
|
end
|
data/lib/pgmq/version.rb
CHANGED
data/lib/pgmq.rb
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require
|
|
4
|
-
require
|
|
3
|
+
require "zeitwerk"
|
|
4
|
+
require "time"
|
|
5
5
|
|
|
6
6
|
loader = Zeitwerk::Loader.for_gem
|
|
7
7
|
loader.inflector.inflect(
|
|
8
|
-
|
|
8
|
+
"pgmq" => "PGMQ"
|
|
9
9
|
)
|
|
10
10
|
loader.setup
|
|
11
11
|
loader.eager_load
|