google-cloud-pubsub 3.4.0 → 3.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/CHANGELOG.md +15 -0
- data/lib/google/cloud/pubsub/async_publisher.rb +1 -1
- data/lib/google/cloud/pubsub/batch_publisher.rb +1 -1
- data/lib/google/cloud/pubsub/internal_logger.rb +1 -1
- data/lib/google/cloud/pubsub/message_listener/inventory.rb +27 -2
- data/lib/google/cloud/pubsub/message_listener/stream.rb +64 -24
- data/lib/google/cloud/pubsub/message_listener/timed_unary_buffer.rb +6 -2
- data/lib/google/cloud/pubsub/message_listener.rb +29 -7
- data/lib/google/cloud/pubsub/service.rb +8 -4
- data/lib/google/cloud/pubsub/subscriber.rb +51 -2
- data/lib/google/cloud/pubsub/version.rb +1 -1
- data/lib/google/cloud/pubsub.rb +0 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 5694017436fe99de18ba1d45e73ecdb0941b0183e115c3d02cad10085dcaae2c
|
|
4
|
+
data.tar.gz: b4a7a35193d6b9e75614474dc6510d1c978bbaa49fc2973123da33ea38b33116
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f49b732799a033333beef767ac6667d603d066ff5bc5f7207b7c05ab127a4996e95f472a9377826ac2fc5898f6b31f710d2408473f611b1114815968edd91f95
|
|
7
|
+
data.tar.gz: 3cb7429db18f8d79c4ff6d1b0a1e488ebf090268fcb9e7ad35f19f4dced3e77c1616c89a8b6d070a9e9e4fc9fd57a3db0cdff2fb44cfc4f8432e57fe6c8719d2
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
# Release History
|
|
2
2
|
|
|
3
|
+
### 3.5.0 (2026-09-14)
|
|
4
|
+
|
|
5
|
+
#### Features
|
|
6
|
+
|
|
7
|
+
* Support subscriber shutdown options ([#36480](https://github.com/googleapis/google-cloud-ruby/issues/36480))
|
|
8
|
+
#### Documentation
|
|
9
|
+
|
|
10
|
+
* Add subscriber shutdown documentation and examples ([#36482](https://github.com/googleapis/google-cloud-ruby/issues/36482))
|
|
11
|
+
|
|
12
|
+
### 3.4.1 (2026-08-24)
|
|
13
|
+
|
|
14
|
+
#### Bug Fixes
|
|
15
|
+
|
|
16
|
+
* Propagate logger to v1 admin and schema clients ([#32440](https://github.com/googleapis/google-cloud-ruby/issues/32440)) ([#36298](https://github.com/googleapis/google-cloud-ruby/issues/36298))
|
|
17
|
+
|
|
3
18
|
### 3.4.0 (2026-07-29)
|
|
4
19
|
|
|
5
20
|
#### Features
|
|
@@ -379,7 +379,7 @@ module Google
|
|
|
379
379
|
grpc = @service.publish topic_name,
|
|
380
380
|
items.map(&:msg),
|
|
381
381
|
compress: compress && batch.total_message_bytes >= compression_bytes_threshold
|
|
382
|
-
service.
|
|
382
|
+
service.internal_logger.log_batch "publish-batch", reason, "publish", items.count, items.sum(&:bytesize)
|
|
383
383
|
items.zip Array(grpc.message_ids) do |item, id|
|
|
384
384
|
@flow_controller.release item.bytesize
|
|
385
385
|
next unless item.callback
|
|
@@ -122,7 +122,7 @@ module Google
|
|
|
122
122
|
grpc = service.publish topic_name,
|
|
123
123
|
messages,
|
|
124
124
|
compress: compress && total_message_bytes >= compression_bytes_threshold
|
|
125
|
-
service.
|
|
125
|
+
service.internal_logger.log_batch "publish-batch", reason, "publish", messages.count, @total_message_bytes
|
|
126
126
|
to_gcloud_messages Array(grpc.message_ids)
|
|
127
127
|
end
|
|
128
128
|
end
|
|
@@ -31,7 +31,7 @@ module Google
|
|
|
31
31
|
def log level, subtag, &message_block
|
|
32
32
|
return unless VALID_LOG_LEVELS.include?(level) && block_given?
|
|
33
33
|
# Only log if the logger is explicitly tagged for 'pubsub'.
|
|
34
|
-
return unless @logger && @logger.progname == LOG_NAME
|
|
34
|
+
return unless @logger.respond_to?(:progname) && @logger.progname == LOG_NAME
|
|
35
35
|
|
|
36
36
|
@logger.public_send(level, "#{LOG_NAME}:#{subtag}", &message_block)
|
|
37
37
|
end
|
|
@@ -50,7 +50,7 @@ module Google
|
|
|
50
50
|
end
|
|
51
51
|
|
|
52
52
|
def ack_ids
|
|
53
|
-
@inventory.keys
|
|
53
|
+
synchronize { @inventory.keys }
|
|
54
54
|
end
|
|
55
55
|
|
|
56
56
|
def add *rec_msgs
|
|
@@ -86,7 +86,7 @@ module Google
|
|
|
86
86
|
extension_time = Time.new - extension
|
|
87
87
|
expired, keep = @inventory.partition { |_ack_id, item| item.pulled_at < extension_time }
|
|
88
88
|
@inventory = keep.to_h
|
|
89
|
-
stream.subscriber.service.
|
|
89
|
+
stream.subscriber.service.internal_logger.log_expiry expired
|
|
90
90
|
@wait_cond.broadcast
|
|
91
91
|
end
|
|
92
92
|
end
|
|
@@ -109,6 +109,31 @@ module Google
|
|
|
109
109
|
end
|
|
110
110
|
end
|
|
111
111
|
|
|
112
|
+
##
|
|
113
|
+
# @private
|
|
114
|
+
# Blocks until the inventory is empty or until timeout expires.
|
|
115
|
+
#
|
|
116
|
+
# @param [Numeric, nil] timeout Maximum time in seconds to wait, or nil to wait indefinitely.
|
|
117
|
+
# @return [Boolean] true if inventory became empty, false if timed out.
|
|
118
|
+
def wait_until_empty timeout = nil
|
|
119
|
+
synchronize do
|
|
120
|
+
return true if @inventory.empty?
|
|
121
|
+
|
|
122
|
+
if timeout
|
|
123
|
+
target_time = Process.clock_gettime(Process::CLOCK_MONOTONIC) + timeout
|
|
124
|
+
while !@inventory.empty? && !@stopped
|
|
125
|
+
remaining = target_time - Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
126
|
+
break if remaining <= 0
|
|
127
|
+
|
|
128
|
+
@wait_cond.wait remaining
|
|
129
|
+
end
|
|
130
|
+
else
|
|
131
|
+
@wait_cond.wait_while { !@inventory.empty? && !@stopped }
|
|
132
|
+
end
|
|
133
|
+
@inventory.empty?
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
|
|
112
137
|
def start
|
|
113
138
|
@background_thread ||= Thread.new { background_run }
|
|
114
139
|
|
|
@@ -111,11 +111,16 @@ module Google
|
|
|
111
111
|
self
|
|
112
112
|
end
|
|
113
113
|
|
|
114
|
+
##
|
|
115
|
+
# @private
|
|
116
|
+
# Stops pulling messages from the subscription.
|
|
117
|
+
#
|
|
118
|
+
# @return [Stream] self for chaining.
|
|
114
119
|
def stop
|
|
115
120
|
synchronize do
|
|
116
121
|
break if @stopped
|
|
117
122
|
|
|
118
|
-
subscriber.service.
|
|
123
|
+
subscriber.service.internal_logger.log :info, "subscriber-streams" do
|
|
119
124
|
"stopping stream for subscription #{@subscriber.subscription_name}"
|
|
120
125
|
end
|
|
121
126
|
# Close the stream by pushing the sentinel value.
|
|
@@ -129,22 +134,45 @@ module Google
|
|
|
129
134
|
|
|
130
135
|
@keepalive_monitor.stop
|
|
131
136
|
|
|
132
|
-
#
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
# Once all the callbacks are stopped, we can stop the inventory.
|
|
138
|
-
@inventory.stop
|
|
137
|
+
# When :nack_immediately, release all queued messages immediately and shut down callback pool.
|
|
138
|
+
if nack_immediately?
|
|
139
|
+
nack_unprocessed_messages!
|
|
140
|
+
@callback_thread_pool.shutdown
|
|
141
|
+
end
|
|
139
142
|
end
|
|
140
143
|
|
|
141
144
|
self
|
|
142
145
|
end
|
|
143
146
|
|
|
147
|
+
##
|
|
148
|
+
# @private
|
|
149
|
+
# Nacks all messages currently held in inventory.
|
|
150
|
+
def nack_unprocessed_messages!
|
|
151
|
+
synchronize do
|
|
152
|
+
ack_ids = @inventory.ack_ids
|
|
153
|
+
return if ack_ids.empty?
|
|
154
|
+
|
|
155
|
+
@subscriber.buffer.modify_ack_deadline 0, ack_ids
|
|
156
|
+
@subscriber.buffer.flush!
|
|
157
|
+
@inventory.remove ack_ids
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
|
|
144
161
|
def stopped?
|
|
145
162
|
synchronize { @stopped }
|
|
146
163
|
end
|
|
147
164
|
|
|
165
|
+
##
|
|
166
|
+
# @private
|
|
167
|
+
# Returns whether the subscriber is stopped and configured to nack unprocessed messages immediately.
|
|
168
|
+
#
|
|
169
|
+
# @return [Boolean]
|
|
170
|
+
def nack_immediately?
|
|
171
|
+
return false unless @stopped
|
|
172
|
+
|
|
173
|
+
@subscriber.shutdown_behavior == :nack_immediately
|
|
174
|
+
end
|
|
175
|
+
|
|
148
176
|
def stream_open?
|
|
149
177
|
synchronize { @stream_open }
|
|
150
178
|
end
|
|
@@ -157,14 +185,26 @@ module Google
|
|
|
157
185
|
!stopped?
|
|
158
186
|
end
|
|
159
187
|
|
|
188
|
+
##
|
|
189
|
+
# @private
|
|
190
|
+
# Blocks until all received messages are processed, or until timeout expires.
|
|
191
|
+
#
|
|
192
|
+
# @param [Numeric, nil] timeout The maximum seconds to wait, or nil to wait indefinitely.
|
|
193
|
+
# @return [Stream] self for chaining.
|
|
160
194
|
def wait! timeout = nil
|
|
161
|
-
|
|
162
|
-
@
|
|
195
|
+
deadline = Process.clock_gettime(Process::CLOCK_MONOTONIC) + timeout if timeout
|
|
196
|
+
emptied = @inventory.wait_until_empty timeout
|
|
197
|
+
nack_unprocessed_messages! unless emptied
|
|
163
198
|
|
|
199
|
+
@callback_thread_pool.shutdown
|
|
200
|
+
pool_timeout = [deadline - Process.clock_gettime(Process::CLOCK_MONOTONIC), 0].max if deadline
|
|
201
|
+
@callback_thread_pool.wait_for_termination pool_timeout
|
|
202
|
+
|
|
203
|
+
# Once all callbacks are finished and inventory is clear, stop the inventory.
|
|
204
|
+
@inventory.stop
|
|
164
205
|
self
|
|
165
206
|
end
|
|
166
207
|
|
|
167
|
-
|
|
168
208
|
def request_queue_active?
|
|
169
209
|
!@request_queue.nil?
|
|
170
210
|
end
|
|
@@ -185,13 +225,13 @@ module Google
|
|
|
185
225
|
end
|
|
186
226
|
|
|
187
227
|
def log_info msg
|
|
188
|
-
subscriber.service.
|
|
228
|
+
subscriber.service.internal_logger.log :info, "subscriber-streams" do
|
|
189
229
|
msg
|
|
190
230
|
end
|
|
191
231
|
end
|
|
192
232
|
|
|
193
233
|
def log_error msg
|
|
194
|
-
subscriber.service.
|
|
234
|
+
subscriber.service.internal_logger.log :error, "subscriber-streams" do
|
|
195
235
|
msg
|
|
196
236
|
end
|
|
197
237
|
end
|
|
@@ -289,7 +329,7 @@ module Google
|
|
|
289
329
|
synchronize do
|
|
290
330
|
# Don't allow a stream to restart if already stopped
|
|
291
331
|
if @stopped
|
|
292
|
-
subscriber.service.
|
|
332
|
+
subscriber.service.internal_logger.log :debug, "subscriber-streams" do
|
|
293
333
|
"not filling stream for subscription #{@subscriber.subscription_name} because stream is already" \
|
|
294
334
|
" stopped"
|
|
295
335
|
end
|
|
@@ -319,7 +359,7 @@ module Google
|
|
|
319
359
|
@stream_open = false
|
|
320
360
|
end
|
|
321
361
|
enum = @subscriber.service.streaming_pull @request_queue.each, options
|
|
322
|
-
subscriber.service.
|
|
362
|
+
subscriber.service.internal_logger.log :info, "subscriber-streams" do
|
|
323
363
|
"rpc: streamingPull, subscription: #{@subscriber.subscription_name}, stream opened"
|
|
324
364
|
end
|
|
325
365
|
|
|
@@ -396,7 +436,7 @@ module Google
|
|
|
396
436
|
GRPC::ResourceExhausted, GRPC::Unauthenticated,
|
|
397
437
|
GRPC::Unavailable => e
|
|
398
438
|
status_code = e.respond_to?(:code) ? e.code : e.class.name
|
|
399
|
-
subscriber.service.
|
|
439
|
+
subscriber.service.internal_logger.log :error, "subscriber-streams" do
|
|
400
440
|
"Subscriber stream for subscription #{@subscriber.subscription_name} has ended with status " \
|
|
401
441
|
"#{status_code}; will be retried."
|
|
402
442
|
end
|
|
@@ -404,13 +444,13 @@ module Google
|
|
|
404
444
|
backoff_and_wait!
|
|
405
445
|
retry
|
|
406
446
|
rescue RestartStream
|
|
407
|
-
subscriber.service.
|
|
447
|
+
subscriber.service.internal_logger.log :info, "subscriber-streams" do
|
|
408
448
|
"Subscriber stream for subscription #{@subscriber.subscription_name} has ended; will be retried."
|
|
409
449
|
end
|
|
410
450
|
backoff_and_wait!
|
|
411
451
|
retry
|
|
412
452
|
rescue StandardError => e
|
|
413
|
-
subscriber.service.
|
|
453
|
+
subscriber.service.internal_logger.log :error, "subscriber-streams" do
|
|
414
454
|
"error on stream for subscription #{@subscriber.subscription_name}: #{e.inspect}"
|
|
415
455
|
end
|
|
416
456
|
@subscriber.error! e
|
|
@@ -463,19 +503,19 @@ module Google
|
|
|
463
503
|
end
|
|
464
504
|
|
|
465
505
|
def perform_callback_sync rec_msg
|
|
466
|
-
subscriber.service.
|
|
506
|
+
subscriber.service.internal_logger.log :info, "callback-delivery" do
|
|
467
507
|
"message (ID #{rec_msg.message_id}, ackID #{rec_msg.ack_id}) delivery to user callbacks"
|
|
468
508
|
end
|
|
469
|
-
@subscriber.callback.call rec_msg unless
|
|
509
|
+
@subscriber.callback.call rec_msg unless nack_immediately?
|
|
470
510
|
rescue StandardError => e
|
|
471
|
-
subscriber.service.
|
|
511
|
+
subscriber.service.internal_logger.log :info, "callback-exceptions" do
|
|
472
512
|
"message (ID #{rec_msg.message_id}, ackID #{rec_msg.ack_id}) caused a user callback exception: " \
|
|
473
513
|
"#{e.inspect}"
|
|
474
514
|
end
|
|
475
515
|
@subscriber.error! e
|
|
476
516
|
ensure
|
|
477
517
|
release rec_msg
|
|
478
|
-
if @sequencer &&
|
|
518
|
+
if @sequencer && !nack_immediately?
|
|
479
519
|
begin
|
|
480
520
|
@sequencer.next rec_msg
|
|
481
521
|
rescue OrderedMessageDeliveryError => e
|
|
@@ -498,7 +538,7 @@ module Google
|
|
|
498
538
|
return unless pause_streaming?
|
|
499
539
|
|
|
500
540
|
@paused = true
|
|
501
|
-
subscriber.service.
|
|
541
|
+
subscriber.service.internal_logger.log :info, "subscriber-flow-control" do
|
|
502
542
|
"subscriber for #{@subscriber.subscription_name} is client-side flow control blocked"
|
|
503
543
|
end
|
|
504
544
|
end
|
|
@@ -519,7 +559,7 @@ module Google
|
|
|
519
559
|
# leaving last_pong_at stale. Updating timestamp guarantees the monitor thread will not trigger an immediate
|
|
520
560
|
# false-positive restart while the reader thread wakes up to drain buffered frames.
|
|
521
561
|
@keepalive_monitor.record_pong!
|
|
522
|
-
subscriber.service.
|
|
562
|
+
subscriber.service.internal_logger.log :info, "subscriber-flow-control" do
|
|
523
563
|
"subscriber for #{@subscriber.subscription_name} is unblocking client-side flow control"
|
|
524
564
|
end
|
|
525
565
|
# Signal to the background thread that we are unpaused
|
|
@@ -320,7 +320,9 @@ module Google
|
|
|
320
320
|
reason
|
|
321
321
|
end
|
|
322
322
|
requests[:acknowledge].each do |req|
|
|
323
|
-
@subscriber.service.
|
|
323
|
+
@subscriber.service.internal_logger.log_batch(
|
|
324
|
+
"ack-batch", new_reason, "ack", req.ack_ids.length, req.to_proto.bytesize
|
|
325
|
+
)
|
|
324
326
|
end
|
|
325
327
|
end
|
|
326
328
|
requests[:modify_ack_deadline] =
|
|
@@ -329,7 +331,9 @@ module Google
|
|
|
329
331
|
type = mod_deadline.zero? ? "nack" : "modack"
|
|
330
332
|
new_reason = mod_ack_reqs.length > 1 ? "#{reason} and partitioned for exceeding max bytes" : reason
|
|
331
333
|
mod_ack_reqs.each do |req|
|
|
332
|
-
@subscriber.service.
|
|
334
|
+
@subscriber.service.internal_logger.log_batch(
|
|
335
|
+
"ack-batch", new_reason, type, req.ack_ids.length, req.to_proto.bytesize
|
|
336
|
+
)
|
|
333
337
|
end
|
|
334
338
|
mod_ack_reqs
|
|
335
339
|
end.flatten
|
|
@@ -60,6 +60,12 @@ module Google
|
|
|
60
60
|
# acknowledgement ({ReceivedMessage#ack!}) and delay messages
|
|
61
61
|
# ({ReceivedMessage#nack!}, {ReceivedMessage#modify_ack_deadline!}).
|
|
62
62
|
# Default is 4.
|
|
63
|
+
# @attr_reader [Symbol] shutdown_behavior The strategy used to handle
|
|
64
|
+
# unprocessed messages when stopping the subscriber (`:wait_for_processing`
|
|
65
|
+
# or `:nack_immediately`). Default is `:wait_for_processing`.
|
|
66
|
+
# @attr_reader [Numeric, nil] shutdown_timeout The maximum number of
|
|
67
|
+
# seconds to wait during shutdown before forcing remaining messages
|
|
68
|
+
# to be nacked. Default is `nil`.
|
|
63
69
|
#
|
|
64
70
|
class MessageListener
|
|
65
71
|
include MonitorMixin
|
|
@@ -71,6 +77,8 @@ module Google
|
|
|
71
77
|
attr_reader :message_ordering
|
|
72
78
|
attr_reader :callback_threads
|
|
73
79
|
attr_reader :push_threads
|
|
80
|
+
attr_reader :shutdown_behavior
|
|
81
|
+
attr_reader :shutdown_timeout
|
|
74
82
|
|
|
75
83
|
##
|
|
76
84
|
# @private Implementation attributes.
|
|
@@ -83,7 +91,7 @@ module Google
|
|
|
83
91
|
##
|
|
84
92
|
# @private Create an empty {MessageListener} object.
|
|
85
93
|
def initialize subscription_name, callback, deadline: nil, message_ordering: nil, streams: nil, inventory: nil,
|
|
86
|
-
threads: {}, service: nil
|
|
94
|
+
threads: {}, shutdown_behavior: :wait_for_processing, shutdown_timeout: nil, service: nil
|
|
87
95
|
super() # to init MonitorMixin
|
|
88
96
|
|
|
89
97
|
@callback = callback
|
|
@@ -91,6 +99,16 @@ module Google
|
|
|
91
99
|
@subscription_name = subscription_name
|
|
92
100
|
@deadline = deadline || 60
|
|
93
101
|
@streams = streams || 1
|
|
102
|
+
@shutdown_behavior = shutdown_behavior || :wait_for_processing
|
|
103
|
+
@shutdown_timeout = shutdown_timeout
|
|
104
|
+
|
|
105
|
+
unless [:wait_for_processing, :nack_immediately].include? @shutdown_behavior
|
|
106
|
+
raise ArgumentError, "Invalid shutdown_behavior: #{@shutdown_behavior.inspect}"
|
|
107
|
+
end
|
|
108
|
+
if @shutdown_timeout && (!@shutdown_timeout.is_a?(Numeric) || @shutdown_timeout.negative?)
|
|
109
|
+
raise ArgumentError, "Invalid shutdown_timeout: #{@shutdown_timeout.inspect}"
|
|
110
|
+
end
|
|
111
|
+
|
|
94
112
|
coerce_inventory inventory
|
|
95
113
|
@message_ordering = message_ordering
|
|
96
114
|
@callback_threads = Integer(threads[:callback] || 8)
|
|
@@ -145,7 +163,7 @@ module Google
|
|
|
145
163
|
@started = false
|
|
146
164
|
@stopped = true
|
|
147
165
|
@stream_pool.map(&:stop)
|
|
148
|
-
wait_stop_buffer_thread!
|
|
166
|
+
wait_stop_buffer_thread! @shutdown_timeout
|
|
149
167
|
self
|
|
150
168
|
end
|
|
151
169
|
end
|
|
@@ -160,12 +178,14 @@ module Google
|
|
|
160
178
|
# stopped.
|
|
161
179
|
#
|
|
162
180
|
# @param [Number, nil] timeout The number of seconds to block until the
|
|
163
|
-
# subscriber is fully stopped. Default will block indefinitely
|
|
181
|
+
# subscriber is fully stopped. Default will block indefinitely or use
|
|
182
|
+
# configured `shutdown_timeout`.
|
|
164
183
|
#
|
|
165
184
|
# @return [MessageListener] returns self so calls can be chained.
|
|
166
185
|
#
|
|
167
186
|
def wait! timeout = nil
|
|
168
|
-
|
|
187
|
+
timeout ||= @shutdown_timeout
|
|
188
|
+
wait_stop_buffer_thread! timeout
|
|
169
189
|
@wait_stop_buffer_thread.join timeout
|
|
170
190
|
self
|
|
171
191
|
end
|
|
@@ -178,11 +198,13 @@ module Google
|
|
|
178
198
|
# The same as calling {#stop} and {#wait!}.
|
|
179
199
|
#
|
|
180
200
|
# @param [Number, nil] timeout The number of seconds to block until the
|
|
181
|
-
# listener is fully stopped. Default will block indefinitely
|
|
201
|
+
# listener is fully stopped. Default will block indefinitely or use
|
|
202
|
+
# configured `shutdown_timeout`.
|
|
182
203
|
#
|
|
183
204
|
# @return [MessageListener] returns self so calls can be chained.
|
|
184
205
|
#
|
|
185
206
|
def stop! timeout = nil
|
|
207
|
+
timeout ||= @shutdown_timeout
|
|
186
208
|
stop
|
|
187
209
|
wait! timeout
|
|
188
210
|
end
|
|
@@ -365,10 +387,10 @@ module Google
|
|
|
365
387
|
|
|
366
388
|
##
|
|
367
389
|
# Starts a new thread to call wait! (blocking) on each Stream and then stop the TimedUnaryBuffer.
|
|
368
|
-
def wait_stop_buffer_thread!
|
|
390
|
+
def wait_stop_buffer_thread! timeout = nil
|
|
369
391
|
synchronize do
|
|
370
392
|
@wait_stop_buffer_thread ||= Thread.new do
|
|
371
|
-
@stream_pool.
|
|
393
|
+
@stream_pool.each { |s| s.wait! timeout }
|
|
372
394
|
# Shutdown the buffer TimerTask (and flush the buffer) after the streams are all stopped.
|
|
373
395
|
@buffer.stop
|
|
374
396
|
end
|
|
@@ -43,7 +43,7 @@ module Google
|
|
|
43
43
|
|
|
44
44
|
##
|
|
45
45
|
# @private The InternalLogger object.
|
|
46
|
-
attr_reader :
|
|
46
|
+
attr_reader :internal_logger
|
|
47
47
|
|
|
48
48
|
##
|
|
49
49
|
# Creates a new Service instance.
|
|
@@ -55,6 +55,7 @@ module Google
|
|
|
55
55
|
@client_id = SecureRandom.uuid.freeze
|
|
56
56
|
@universe_domain = universe_domain || ENV["GOOGLE_CLOUD_UNIVERSE_DOMAIN"] || "googleapis.com"
|
|
57
57
|
@logger = logger
|
|
58
|
+
@internal_logger = InternalLogger.new logger
|
|
58
59
|
end
|
|
59
60
|
|
|
60
61
|
def subscription_admin
|
|
@@ -67,6 +68,7 @@ module Google
|
|
|
67
68
|
config.lib_name = "gccl"
|
|
68
69
|
config.lib_version = Google::Cloud::PubSub::VERSION
|
|
69
70
|
config.metadata = { "google-cloud-resource-prefix": "projects/#{@project}" }
|
|
71
|
+
config.logger = @logger if @logger
|
|
70
72
|
end
|
|
71
73
|
end
|
|
72
74
|
attr_accessor :mocked_subscription_admin
|
|
@@ -81,6 +83,7 @@ module Google
|
|
|
81
83
|
config.lib_name = "gccl"
|
|
82
84
|
config.lib_version = Google::Cloud::PubSub::VERSION
|
|
83
85
|
config.metadata = { "google-cloud-resource-prefix": "projects/#{@project}" }
|
|
86
|
+
config.logger = @logger if @logger
|
|
84
87
|
end
|
|
85
88
|
end
|
|
86
89
|
attr_accessor :mocked_topic_admin
|
|
@@ -94,6 +97,7 @@ module Google
|
|
|
94
97
|
config.lib_name = "gccl"
|
|
95
98
|
config.lib_version = Google::Cloud::PubSub::VERSION
|
|
96
99
|
config.metadata = { "google-cloud-resource-prefix": "projects/#{@project}" }
|
|
100
|
+
config.logger = @logger if @logger
|
|
97
101
|
end
|
|
98
102
|
iam
|
|
99
103
|
end
|
|
@@ -110,6 +114,7 @@ module Google
|
|
|
110
114
|
config.lib_name = "gccl"
|
|
111
115
|
config.lib_version = Google::Cloud::PubSub::VERSION
|
|
112
116
|
config.metadata = { "google-cloud-resource-prefix": "projects/#{@project}" }
|
|
117
|
+
config.logger = @logger if @logger
|
|
113
118
|
end
|
|
114
119
|
end
|
|
115
120
|
attr_accessor :mocked_schemas
|
|
@@ -147,7 +152,7 @@ module Google
|
|
|
147
152
|
##
|
|
148
153
|
# Acknowledges receipt of a message.
|
|
149
154
|
def acknowledge subscription, *ack_ids
|
|
150
|
-
|
|
155
|
+
internal_logger.log_ack_nack ack_ids, "ack"
|
|
151
156
|
subscription_admin.acknowledge_internal subscription: subscription_path(subscription),
|
|
152
157
|
ack_ids: ack_ids
|
|
153
158
|
end
|
|
@@ -156,7 +161,7 @@ module Google
|
|
|
156
161
|
# Modifies the ack deadline for a specific message.
|
|
157
162
|
def modify_ack_deadline subscription, ids, deadline
|
|
158
163
|
if deadline.zero?
|
|
159
|
-
|
|
164
|
+
internal_logger.log_ack_nack Array(ids), "nack"
|
|
160
165
|
end
|
|
161
166
|
subscription_admin.modify_ack_deadline_internal subscription: subscription_path(subscription),
|
|
162
167
|
ack_ids: Array(ids),
|
|
@@ -203,7 +208,6 @@ module Google
|
|
|
203
208
|
rpc.timeout = timeout if rpc.respond_to? :timeout=
|
|
204
209
|
end
|
|
205
210
|
end
|
|
206
|
-
|
|
207
211
|
end
|
|
208
212
|
end
|
|
209
213
|
Pubsub = PubSub unless const_defined? :Pubsub
|
|
@@ -324,6 +324,16 @@ module Google
|
|
|
324
324
|
# acknowledgement ({ReceivedMessage#ack!}) and modify ack deadline
|
|
325
325
|
# messages ({ReceivedMessage#nack!},
|
|
326
326
|
# {ReceivedMessage#modify_ack_deadline!}). Default is 4.
|
|
327
|
+
# @param [Symbol] shutdown_behavior The strategy used to handle
|
|
328
|
+
# unprocessed messages when stopping the subscriber.
|
|
329
|
+
# Supported values:
|
|
330
|
+
# * `:wait_for_processing` (default) - Waits for in-flight callbacks
|
|
331
|
+
# to complete and acknowledge or nack messages.
|
|
332
|
+
# * `:nack_immediately` - Immediately nacks all unprocessed messages
|
|
333
|
+
# back to Pub/Sub for rapid redelivery to other subscribers.
|
|
334
|
+
# @param [Numeric, nil] shutdown_timeout The maximum number of seconds
|
|
335
|
+
# to wait during shutdown before forcing remaining in-flight messages
|
|
336
|
+
# to be nacked. Default is `nil` (blocks indefinitely until callbacks finish).
|
|
327
337
|
#
|
|
328
338
|
# @yield [received_message] a block for processing new messages
|
|
329
339
|
# @yieldparam [ReceivedMessage] received_message the newly received
|
|
@@ -408,13 +418,52 @@ module Google
|
|
|
408
418
|
# # Shut down the subscriber when ready to stop receiving messages.
|
|
409
419
|
# listener.stop!
|
|
410
420
|
#
|
|
411
|
-
|
|
421
|
+
# @example Immediately release unprocessed messages on shutdown:
|
|
422
|
+
# require "google/cloud/pubsub"
|
|
423
|
+
#
|
|
424
|
+
# pubsub = Google::Cloud::PubSub.new
|
|
425
|
+
#
|
|
426
|
+
# subscriber = pubsub.subscriber "my-topic-sub"
|
|
427
|
+
#
|
|
428
|
+
# listener = subscriber.listen shutdown_behavior: :nack_immediately do |received_message|
|
|
429
|
+
# # process message
|
|
430
|
+
# puts "Data: #{received_message.message.data}"
|
|
431
|
+
# received_message.acknowledge!
|
|
432
|
+
# end
|
|
433
|
+
#
|
|
434
|
+
# listener.start
|
|
435
|
+
#
|
|
436
|
+
# # Shut down immediately, releasing (nacking) any unprocessed messages
|
|
437
|
+
# listener.stop!
|
|
438
|
+
#
|
|
439
|
+
# @example Gracefully shut down with a custom timeout:
|
|
440
|
+
# require "google/cloud/pubsub"
|
|
441
|
+
#
|
|
442
|
+
# pubsub = Google::Cloud::PubSub.new
|
|
443
|
+
#
|
|
444
|
+
# subscriber = pubsub.subscriber "my-topic-sub"
|
|
445
|
+
#
|
|
446
|
+
# listener = subscriber.listen shutdown_behavior: :wait_for_processing, shutdown_timeout: 30 do |received_message|
|
|
447
|
+
# # process message
|
|
448
|
+
# puts "Data: #{received_message.message.data}"
|
|
449
|
+
# received_message.acknowledge!
|
|
450
|
+
# end
|
|
451
|
+
#
|
|
452
|
+
# listener.start
|
|
453
|
+
#
|
|
454
|
+
# # Wait up to 30 seconds for in-flight callbacks before forcing remaining to nack
|
|
455
|
+
# listener.stop!
|
|
456
|
+
#
|
|
457
|
+
def listen deadline: nil, message_ordering: nil, streams: nil, inventory: nil, threads: {},
|
|
458
|
+
shutdown_behavior: :wait_for_processing, shutdown_timeout: nil, &block
|
|
412
459
|
ensure_service!
|
|
413
460
|
deadline ||= self.deadline
|
|
414
461
|
message_ordering = message_ordering? if message_ordering.nil?
|
|
415
462
|
|
|
416
463
|
MessageListener.new name, block, deadline: deadline, streams: streams, inventory: inventory,
|
|
417
|
-
message_ordering: message_ordering, threads: threads,
|
|
464
|
+
message_ordering: message_ordering, threads: threads,
|
|
465
|
+
shutdown_behavior: shutdown_behavior, shutdown_timeout: shutdown_timeout,
|
|
466
|
+
service: service
|
|
418
467
|
end
|
|
419
468
|
|
|
420
469
|
##
|
data/lib/google/cloud/pubsub.rb
CHANGED
|
@@ -122,7 +122,6 @@ module Google
|
|
|
122
122
|
project_id = project_id.to_s # Always cast to a string
|
|
123
123
|
raise ArgumentError, "project_id is missing" if project_id.empty?
|
|
124
124
|
|
|
125
|
-
logger = Google::Cloud::PubSub::InternalLogger.new logger
|
|
126
125
|
service = PubSub::Service.new project_id, credentials,
|
|
127
126
|
host: endpoint,
|
|
128
127
|
timeout: timeout,
|