google-cloud-pubsub 2.15.5 → 3.3.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/AUTHENTICATION.md +12 -1
- data/CHANGELOG.md +129 -0
- data/OVERVIEW.md +189 -145
- data/lib/google/cloud/pubsub/admin_clients.rb +116 -0
- data/lib/google/cloud/pubsub/async_publisher.rb +20 -19
- data/lib/google/cloud/pubsub/batch_publisher.rb +7 -5
- data/lib/google/cloud/pubsub/convert.rb +1 -1
- data/lib/google/cloud/pubsub/errors.rb +3 -3
- data/lib/google/cloud/pubsub/flow_controller.rb +0 -2
- data/lib/google/cloud/pubsub/internal_logger.rb +76 -0
- data/lib/google/cloud/pubsub/message.rb +10 -11
- data/lib/google/cloud/pubsub/{subscriber → message_listener}/enumerator_queue.rb +1 -1
- data/lib/google/cloud/pubsub/{subscriber → message_listener}/inventory.rb +13 -10
- data/lib/google/cloud/pubsub/{subscriber → message_listener}/sequencer.rb +1 -1
- data/lib/google/cloud/pubsub/{subscriber → message_listener}/stream.rb +58 -17
- data/lib/google/cloud/pubsub/{subscriber → message_listener}/timed_unary_buffer.rb +29 -9
- data/lib/google/cloud/pubsub/message_listener.rb +413 -0
- data/lib/google/cloud/pubsub/project.rb +108 -523
- data/lib/google/cloud/pubsub/publisher.rb +424 -0
- data/lib/google/cloud/pubsub/received_message.rb +50 -45
- data/lib/google/cloud/pubsub/service.rb +48 -349
- data/lib/google/cloud/pubsub/subscriber.rb +442 -279
- data/lib/google/cloud/pubsub/version.rb +1 -1
- data/lib/google/cloud/pubsub.rb +51 -30
- data/lib/google-cloud-pubsub.rb +30 -10
- metadata +19 -183
- data/lib/google/cloud/pubsub/policy.rb +0 -188
- data/lib/google/cloud/pubsub/retry_policy.rb +0 -88
- data/lib/google/cloud/pubsub/schema/list.rb +0 -180
- data/lib/google/cloud/pubsub/schema.rb +0 -310
- data/lib/google/cloud/pubsub/snapshot/list.rb +0 -178
- data/lib/google/cloud/pubsub/snapshot.rb +0 -205
- data/lib/google/cloud/pubsub/subscription/list.rb +0 -205
- data/lib/google/cloud/pubsub/subscription/push_config.rb +0 -268
- data/lib/google/cloud/pubsub/subscription.rb +0 -1467
- data/lib/google/cloud/pubsub/topic/list.rb +0 -171
- data/lib/google/cloud/pubsub/topic.rb +0 -1100
|
@@ -12,10 +12,9 @@
|
|
|
12
12
|
# See the License for the specific language governing permissions and
|
|
13
13
|
# limitations under the License.
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
require "google/cloud/pubsub/
|
|
17
|
-
require "google/cloud/pubsub/
|
|
18
|
-
require "google/cloud/pubsub/subscriber/inventory"
|
|
15
|
+
require "google/cloud/pubsub/message_listener/sequencer"
|
|
16
|
+
require "google/cloud/pubsub/message_listener/enumerator_queue"
|
|
17
|
+
require "google/cloud/pubsub/message_listener/inventory"
|
|
19
18
|
require "google/cloud/pubsub/service"
|
|
20
19
|
require "google/cloud/errors"
|
|
21
20
|
require "monitor"
|
|
@@ -24,7 +23,7 @@ require "concurrent"
|
|
|
24
23
|
module Google
|
|
25
24
|
module Cloud
|
|
26
25
|
module PubSub
|
|
27
|
-
class
|
|
26
|
+
class MessageListener
|
|
28
27
|
##
|
|
29
28
|
# @private
|
|
30
29
|
class Stream
|
|
@@ -73,7 +72,12 @@ module Google
|
|
|
73
72
|
execution_interval: 30
|
|
74
73
|
) do
|
|
75
74
|
# push empty request every 30 seconds to keep stream alive
|
|
76
|
-
|
|
75
|
+
unless inventory.empty?
|
|
76
|
+
subscriber.service.logger.log :info, "subscriber-streams" do
|
|
77
|
+
"sending keepAlive to stream for subscription #{@subscriber.subscription_name}"
|
|
78
|
+
end
|
|
79
|
+
push Google::Cloud::PubSub::V1::StreamingPullRequest.new
|
|
80
|
+
end
|
|
77
81
|
end.execute
|
|
78
82
|
end
|
|
79
83
|
|
|
@@ -93,6 +97,9 @@ module Google
|
|
|
93
97
|
synchronize do
|
|
94
98
|
break if @stopped
|
|
95
99
|
|
|
100
|
+
subscriber.service.logger.log :info, "subscriber-streams" do
|
|
101
|
+
"stopping stream for subscription #{@subscriber.subscription_name}"
|
|
102
|
+
end
|
|
96
103
|
# Close the stream by pushing the sentinel value.
|
|
97
104
|
# The unary pusher does not use the stream, so it can close here.
|
|
98
105
|
@request_queue&.push self
|
|
@@ -215,7 +222,13 @@ module Google
|
|
|
215
222
|
def background_run
|
|
216
223
|
synchronize do
|
|
217
224
|
# Don't allow a stream to restart if already stopped
|
|
218
|
-
|
|
225
|
+
if @stopped
|
|
226
|
+
subscriber.service.logger.log :debug, "subscriber-streams" do
|
|
227
|
+
"not filling stream for subscription #{@subscriber.subscription_name} because stream is already" \
|
|
228
|
+
" stopped"
|
|
229
|
+
end
|
|
230
|
+
return
|
|
231
|
+
end
|
|
219
232
|
|
|
220
233
|
@stopped = false
|
|
221
234
|
@paused = false
|
|
@@ -233,6 +246,9 @@ module Google
|
|
|
233
246
|
# Call the StreamingPull API to get the response enumerator
|
|
234
247
|
options = { :"metadata" => { :"x-goog-request-params" => @subscriber.subscription_name } }
|
|
235
248
|
enum = @subscriber.service.streaming_pull @request_queue.each, options
|
|
249
|
+
subscriber.service.logger.log :info, "subscriber-streams" do
|
|
250
|
+
"rpc: streamingPull, subscription: #{@subscriber.subscription_name}, stream opened"
|
|
251
|
+
end
|
|
236
252
|
|
|
237
253
|
loop do
|
|
238
254
|
synchronize do
|
|
@@ -246,7 +262,7 @@ module Google
|
|
|
246
262
|
break if synchronize { @stopped }
|
|
247
263
|
|
|
248
264
|
begin
|
|
249
|
-
# Cannot
|
|
265
|
+
# Cannot synchronize the enumerator, causes deadlock
|
|
250
266
|
response = enum.next
|
|
251
267
|
new_exactly_once_delivery_enabled = response&.subscription_properties&.exactly_once_delivery_enabled
|
|
252
268
|
received_messages = response.received_messages
|
|
@@ -287,13 +303,23 @@ module Google
|
|
|
287
303
|
stop
|
|
288
304
|
rescue GRPC::Cancelled, GRPC::DeadlineExceeded, GRPC::Internal,
|
|
289
305
|
GRPC::ResourceExhausted, GRPC::Unauthenticated,
|
|
290
|
-
GRPC::Unavailable
|
|
306
|
+
GRPC::Unavailable => e
|
|
307
|
+
status_code = e.respond_to?(:code) ? e.code : e.class.name
|
|
308
|
+
subscriber.service.logger.log :error, "subscriber-streams" do
|
|
309
|
+
"Subscriber stream for subscription #{@subscriber.subscription_name} has ended with status " \
|
|
310
|
+
"#{status_code}; will be retried."
|
|
311
|
+
end
|
|
291
312
|
# Restart the stream with an incremental back for a retriable error.
|
|
292
|
-
|
|
293
313
|
retry
|
|
294
314
|
rescue RestartStream
|
|
315
|
+
subscriber.service.logger.log :info, "subscriber-streams" do
|
|
316
|
+
"Subscriber stream for subscription #{@subscriber.subscription_name} has ended; will be retried."
|
|
317
|
+
end
|
|
295
318
|
retry
|
|
296
319
|
rescue StandardError => e
|
|
320
|
+
subscriber.service.logger.log :error, "subscriber-streams" do
|
|
321
|
+
"error on stream for subscription #{@subscriber.subscription_name}: #{e.inspect}"
|
|
322
|
+
end
|
|
297
323
|
@subscriber.error! e
|
|
298
324
|
|
|
299
325
|
retry
|
|
@@ -336,13 +362,22 @@ module Google
|
|
|
336
362
|
return unless callback_thread_pool.running?
|
|
337
363
|
|
|
338
364
|
Concurrent::Promises.future_on(
|
|
339
|
-
callback_thread_pool,
|
|
365
|
+
callback_thread_pool,
|
|
366
|
+
rec_msg,
|
|
367
|
+
&method(:perform_callback_sync)
|
|
340
368
|
)
|
|
341
369
|
end
|
|
342
370
|
|
|
343
371
|
def perform_callback_sync rec_msg
|
|
372
|
+
subscriber.service.logger.log :info, "callback-delivery" do
|
|
373
|
+
"message (ID #{rec_msg.message_id}, ackID #{rec_msg.ack_id}) delivery to user callbacks"
|
|
374
|
+
end
|
|
344
375
|
@subscriber.callback.call rec_msg unless stopped?
|
|
345
376
|
rescue StandardError => e
|
|
377
|
+
subscriber.service.logger.log :info, "callback-exceptions" do
|
|
378
|
+
"message (ID #{rec_msg.message_id}, ackID #{rec_msg.ack_id}) caused a user callback exception: " \
|
|
379
|
+
"#{e.inspect}"
|
|
380
|
+
end
|
|
346
381
|
@subscriber.error! e
|
|
347
382
|
ensure
|
|
348
383
|
release rec_msg
|
|
@@ -369,11 +404,14 @@ module Google
|
|
|
369
404
|
return unless pause_streaming?
|
|
370
405
|
|
|
371
406
|
@paused = true
|
|
407
|
+
subscriber.service.logger.log :info, "subscriber-flow-control" do
|
|
408
|
+
"subscriber for #{@subscriber.subscription_name} is client-side flow control blocked"
|
|
409
|
+
end
|
|
372
410
|
end
|
|
373
411
|
|
|
374
412
|
def pause_streaming?
|
|
375
|
-
return if @stopped
|
|
376
|
-
return if @paused
|
|
413
|
+
return false if @stopped
|
|
414
|
+
return false if @paused
|
|
377
415
|
|
|
378
416
|
@inventory.full?
|
|
379
417
|
end
|
|
@@ -382,13 +420,16 @@ module Google
|
|
|
382
420
|
return unless unpause_streaming?
|
|
383
421
|
|
|
384
422
|
@paused = nil
|
|
423
|
+
subscriber.service.logger.log :info, "subscriber-flow-control" do
|
|
424
|
+
"subscriber for #{@subscriber.subscription_name} is unblocking client-side flow control"
|
|
425
|
+
end
|
|
385
426
|
# signal to the background thread that we are unpaused
|
|
386
427
|
@pause_cond.broadcast
|
|
387
428
|
end
|
|
388
429
|
|
|
389
430
|
def unpause_streaming?
|
|
390
|
-
return if @stopped
|
|
391
|
-
return if @paused.nil?
|
|
431
|
+
return false if @stopped
|
|
432
|
+
return false if @paused.nil?
|
|
392
433
|
|
|
393
434
|
@inventory.count < @inventory.limit * 0.8
|
|
394
435
|
end
|
|
@@ -400,8 +441,8 @@ module Google
|
|
|
400
441
|
req.modify_deadline_ack_ids += @inventory.ack_ids
|
|
401
442
|
req.modify_deadline_seconds += @inventory.ack_ids.map { @subscriber.deadline }
|
|
402
443
|
req.client_id = @subscriber.service.client_id
|
|
403
|
-
req.max_outstanding_messages = @inventory.
|
|
404
|
-
req.max_outstanding_bytes = @inventory.
|
|
444
|
+
req.max_outstanding_messages = @inventory.limit
|
|
445
|
+
req.max_outstanding_bytes = @inventory.bytesize
|
|
405
446
|
end
|
|
406
447
|
end
|
|
407
448
|
|
|
@@ -22,7 +22,7 @@ require "retriable"
|
|
|
22
22
|
module Google
|
|
23
23
|
module Cloud
|
|
24
24
|
module PubSub
|
|
25
|
-
class
|
|
25
|
+
class MessageListener
|
|
26
26
|
##
|
|
27
27
|
# @private
|
|
28
28
|
class TimedUnaryBuffer
|
|
@@ -64,7 +64,7 @@ module Google
|
|
|
64
64
|
@retry_thread_pool = Concurrent::ThreadPoolExecutor.new max_threads: @subscriber.callback_threads
|
|
65
65
|
@callback_thread_pool = Concurrent::ThreadPoolExecutor.new max_threads: @subscriber.callback_threads
|
|
66
66
|
@task = Concurrent::TimerTask.new execution_interval: interval do
|
|
67
|
-
flush!
|
|
67
|
+
flush! reason: "interval timeout"
|
|
68
68
|
end
|
|
69
69
|
end
|
|
70
70
|
|
|
@@ -108,9 +108,9 @@ module Google
|
|
|
108
108
|
true
|
|
109
109
|
end
|
|
110
110
|
|
|
111
|
-
def flush!
|
|
111
|
+
def flush! reason: "manual flush"
|
|
112
112
|
# Grab requests from the buffer and release synchronize ASAP
|
|
113
|
-
requests = flush_requests!
|
|
113
|
+
requests = flush_requests! reason
|
|
114
114
|
return if requests.empty?
|
|
115
115
|
|
|
116
116
|
# Perform the RCP calls concurrently
|
|
@@ -167,7 +167,7 @@ module Google
|
|
|
167
167
|
@task.shutdown
|
|
168
168
|
@retry_thread_pool.shutdown
|
|
169
169
|
@callback_thread_pool.shutdown
|
|
170
|
-
flush!
|
|
170
|
+
flush! reason: "shutdown"
|
|
171
171
|
self
|
|
172
172
|
end
|
|
173
173
|
|
|
@@ -296,7 +296,9 @@ module Google
|
|
|
296
296
|
end
|
|
297
297
|
end
|
|
298
298
|
|
|
299
|
-
|
|
299
|
+
# rubocop:disable Metrics/AbcSize
|
|
300
|
+
|
|
301
|
+
def flush_requests! reason
|
|
300
302
|
prev_reg =
|
|
301
303
|
synchronize do
|
|
302
304
|
return {} if @register.empty?
|
|
@@ -308,16 +310,34 @@ module Google
|
|
|
308
310
|
groups = prev_reg.each_pair.group_by { |_ack_id, delay| delay }
|
|
309
311
|
req_hash = groups.transform_values { |v| v.map(&:first) }
|
|
310
312
|
|
|
311
|
-
requests = { acknowledge: [] }
|
|
313
|
+
requests = { acknowledge: [], modify_ack_deadline: [] }
|
|
312
314
|
ack_ids = Array(req_hash.delete(:ack)) # ack has no deadline set
|
|
313
|
-
|
|
315
|
+
if ack_ids.any?
|
|
316
|
+
requests[:acknowledge] = create_acknowledge_requests ack_ids
|
|
317
|
+
new_reason = if requests[:acknowledge].length > 1
|
|
318
|
+
"#{reason} and partitioned for exceeding max bytes"
|
|
319
|
+
else
|
|
320
|
+
reason
|
|
321
|
+
end
|
|
322
|
+
requests[:acknowledge].each do |req|
|
|
323
|
+
@subscriber.service.logger.log_batch "ack-batch", new_reason, "ack", req.ack_ids.length, req.to_proto.bytesize
|
|
324
|
+
end
|
|
325
|
+
end
|
|
314
326
|
requests[:modify_ack_deadline] =
|
|
315
327
|
req_hash.map do |mod_deadline, mod_ack_ids|
|
|
316
|
-
create_modify_ack_deadline_requests mod_deadline, mod_ack_ids
|
|
328
|
+
mod_ack_reqs = create_modify_ack_deadline_requests mod_deadline, mod_ack_ids
|
|
329
|
+
type = mod_deadline.zero? ? "nack" : "modack"
|
|
330
|
+
new_reason = mod_ack_reqs.length > 1 ? "#{reason} and partitioned for exceeding max bytes" : reason
|
|
331
|
+
mod_ack_reqs.each do |req|
|
|
332
|
+
@subscriber.service.logger.log_batch "ack-batch", new_reason, type, req.ack_ids.length, req.to_proto.bytesize
|
|
333
|
+
end
|
|
334
|
+
mod_ack_reqs
|
|
317
335
|
end.flatten
|
|
318
336
|
requests
|
|
319
337
|
end
|
|
320
338
|
|
|
339
|
+
# rubocop:enable Metrics/AbcSize
|
|
340
|
+
|
|
321
341
|
def create_acknowledge_requests ack_ids
|
|
322
342
|
req = Google::Cloud::PubSub::V1::AcknowledgeRequest.new(
|
|
323
343
|
subscription: subscription_name,
|
|
@@ -0,0 +1,413 @@
|
|
|
1
|
+
# Copyright 2017 Google LLC
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# https://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
require "google/cloud/pubsub/service"
|
|
17
|
+
require "google/cloud/pubsub/message_listener/stream"
|
|
18
|
+
require "google/cloud/pubsub/message_listener/timed_unary_buffer"
|
|
19
|
+
require "monitor"
|
|
20
|
+
|
|
21
|
+
module Google
|
|
22
|
+
module Cloud
|
|
23
|
+
module PubSub
|
|
24
|
+
##
|
|
25
|
+
# MessageListener object used to stream and process messages from a
|
|
26
|
+
# Subscriber. See {Google::Cloud::PubSub::Subscriber#listen}
|
|
27
|
+
#
|
|
28
|
+
# @example
|
|
29
|
+
# require "google/cloud/pubsub"
|
|
30
|
+
#
|
|
31
|
+
# pubsub = Google::Cloud::PubSub.new
|
|
32
|
+
#
|
|
33
|
+
# subscriber = pubsub.subscriber "my-topic-sub"
|
|
34
|
+
#
|
|
35
|
+
# listener = subscriber.listen do |received_message|
|
|
36
|
+
# # process message
|
|
37
|
+
# received_message.acknowledge!
|
|
38
|
+
# end
|
|
39
|
+
#
|
|
40
|
+
# # Start background threads that will call the block passed to listen.
|
|
41
|
+
# listener.start
|
|
42
|
+
#
|
|
43
|
+
# # Shut down the subscriber when ready to stop receiving messages.
|
|
44
|
+
# listener.stop!
|
|
45
|
+
#
|
|
46
|
+
# @attr_reader [String] subscription_name The name of the subscription the
|
|
47
|
+
# messages are pulled from.
|
|
48
|
+
# @attr_reader [Proc] callback The procedure that will handle the messages
|
|
49
|
+
# received from the subscription.
|
|
50
|
+
# @attr_reader [Numeric] deadline The default number of seconds the stream
|
|
51
|
+
# will hold received messages before modifying the message's ack
|
|
52
|
+
# deadline. The minimum is 10, the maximum is 600. Default is 60.
|
|
53
|
+
# @attr_reader [Boolean] message_ordering Whether message ordering has
|
|
54
|
+
# been enabled.
|
|
55
|
+
# @attr_reader [Integer] streams The number of concurrent streams to open
|
|
56
|
+
# to pull messages from the subscription. Default is 1.
|
|
57
|
+
# @attr_reader [Integer] callback_threads The number of threads used to
|
|
58
|
+
# handle the received messages. Default is 8.
|
|
59
|
+
# @attr_reader [Integer] push_threads The number of threads to handle
|
|
60
|
+
# acknowledgement ({ReceivedMessage#ack!}) and delay messages
|
|
61
|
+
# ({ReceivedMessage#nack!}, {ReceivedMessage#modify_ack_deadline!}).
|
|
62
|
+
# Default is 4.
|
|
63
|
+
#
|
|
64
|
+
class MessageListener
|
|
65
|
+
include MonitorMixin
|
|
66
|
+
|
|
67
|
+
attr_reader :subscription_name
|
|
68
|
+
attr_reader :callback
|
|
69
|
+
attr_reader :deadline
|
|
70
|
+
attr_reader :streams
|
|
71
|
+
attr_reader :message_ordering
|
|
72
|
+
attr_reader :callback_threads
|
|
73
|
+
attr_reader :push_threads
|
|
74
|
+
|
|
75
|
+
##
|
|
76
|
+
# @private Implementation attributes.
|
|
77
|
+
attr_reader :stream_pool, :thread_pool, :buffer, :service
|
|
78
|
+
|
|
79
|
+
##
|
|
80
|
+
# @private Implementation attributes.
|
|
81
|
+
attr_accessor :exactly_once_delivery_enabled
|
|
82
|
+
|
|
83
|
+
##
|
|
84
|
+
# @private Create an empty {MessageListener} object.
|
|
85
|
+
def initialize subscription_name, callback, deadline: nil, message_ordering: nil, streams: nil, inventory: nil,
|
|
86
|
+
threads: {}, service: nil
|
|
87
|
+
super() # to init MonitorMixin
|
|
88
|
+
|
|
89
|
+
@callback = callback
|
|
90
|
+
@error_callbacks = []
|
|
91
|
+
@subscription_name = subscription_name
|
|
92
|
+
@deadline = deadline || 60
|
|
93
|
+
@streams = streams || 1
|
|
94
|
+
coerce_inventory inventory
|
|
95
|
+
@message_ordering = message_ordering
|
|
96
|
+
@callback_threads = Integer(threads[:callback] || 8)
|
|
97
|
+
@push_threads = Integer(threads[:push] || 4)
|
|
98
|
+
@exactly_once_delivery_enabled = nil
|
|
99
|
+
|
|
100
|
+
@service = service
|
|
101
|
+
|
|
102
|
+
@started = @stopped = nil
|
|
103
|
+
|
|
104
|
+
stream_pool = Array.new @streams do
|
|
105
|
+
Thread.new { Stream.new self }
|
|
106
|
+
end
|
|
107
|
+
@stream_pool = stream_pool.map(&:value)
|
|
108
|
+
|
|
109
|
+
@buffer = TimedUnaryBuffer.new self
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
##
|
|
113
|
+
# Starts the listener pulling from the subscription and processing the
|
|
114
|
+
# received messages.
|
|
115
|
+
#
|
|
116
|
+
# @return [MessageListener] returns self so calls can be chained.
|
|
117
|
+
#
|
|
118
|
+
def start
|
|
119
|
+
start_pool = synchronize do
|
|
120
|
+
@started = true
|
|
121
|
+
@stopped = false
|
|
122
|
+
|
|
123
|
+
# Start the buffer before the streams are all started
|
|
124
|
+
@buffer.start
|
|
125
|
+
@stream_pool.map do |stream|
|
|
126
|
+
Thread.new { stream.start }
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
start_pool.map(&:join)
|
|
130
|
+
|
|
131
|
+
self
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
##
|
|
135
|
+
# Immediately stops the listener. No new messages will be pulled from
|
|
136
|
+
# the subscription. Use {#wait!} to block until all received messages have
|
|
137
|
+
# been processed or released: All actions taken on received messages that
|
|
138
|
+
# have not yet been sent to the API will be sent to the API. All received
|
|
139
|
+
# but unprocessed messages will be released back to the API and redelivered.
|
|
140
|
+
#
|
|
141
|
+
# @return [MessageListener] returns self so calls can be chained.
|
|
142
|
+
#
|
|
143
|
+
def stop
|
|
144
|
+
synchronize do
|
|
145
|
+
@started = false
|
|
146
|
+
@stopped = true
|
|
147
|
+
@stream_pool.map(&:stop)
|
|
148
|
+
wait_stop_buffer_thread!
|
|
149
|
+
self
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
##
|
|
154
|
+
# Blocks until the listener is fully stopped and all received messages
|
|
155
|
+
# have been processed or released, or until `timeout` seconds have
|
|
156
|
+
# passed.
|
|
157
|
+
#
|
|
158
|
+
# Does not stop the listener. To stop the listener, first call
|
|
159
|
+
# {#stop} and then call {#wait!} to block until the listener is
|
|
160
|
+
# stopped.
|
|
161
|
+
#
|
|
162
|
+
# @param [Number, nil] timeout The number of seconds to block until the
|
|
163
|
+
# subscriber is fully stopped. Default will block indefinitely.
|
|
164
|
+
#
|
|
165
|
+
# @return [MessageListener] returns self so calls can be chained.
|
|
166
|
+
#
|
|
167
|
+
def wait! timeout = nil
|
|
168
|
+
wait_stop_buffer_thread!
|
|
169
|
+
@wait_stop_buffer_thread.join timeout
|
|
170
|
+
self
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
##
|
|
174
|
+
# Stop this listener and block until the listener is fully stopped
|
|
175
|
+
# and all received messages have been processed or released, or until
|
|
176
|
+
# `timeout` seconds have passed.
|
|
177
|
+
#
|
|
178
|
+
# The same as calling {#stop} and {#wait!}.
|
|
179
|
+
#
|
|
180
|
+
# @param [Number, nil] timeout The number of seconds to block until the
|
|
181
|
+
# listener is fully stopped. Default will block indefinitely.
|
|
182
|
+
#
|
|
183
|
+
# @return [MessageListener] returns self so calls can be chained.
|
|
184
|
+
#
|
|
185
|
+
def stop! timeout = nil
|
|
186
|
+
stop
|
|
187
|
+
wait! timeout
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
##
|
|
191
|
+
# Whether the listener has been started.
|
|
192
|
+
#
|
|
193
|
+
# @return [boolean] `true` when started, `false` otherwise.
|
|
194
|
+
#
|
|
195
|
+
def started?
|
|
196
|
+
synchronize { @started }
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
##
|
|
200
|
+
# Whether the listener has been stopped.
|
|
201
|
+
#
|
|
202
|
+
# @return [boolean] `true` when stopped, `false` otherwise.
|
|
203
|
+
#
|
|
204
|
+
def stopped?
|
|
205
|
+
synchronize { @stopped }
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
##
|
|
209
|
+
# Register to be notified of errors when raised.
|
|
210
|
+
#
|
|
211
|
+
# If an unhandled error has occurred the listener will attempt to
|
|
212
|
+
# recover from the error and resume listening.
|
|
213
|
+
#
|
|
214
|
+
# Multiple error handlers can be added.
|
|
215
|
+
#
|
|
216
|
+
# @yield [callback] The block to be called when an error is raised.
|
|
217
|
+
# @yieldparam [Exception] error The error raised.
|
|
218
|
+
#
|
|
219
|
+
# @example
|
|
220
|
+
# require "google/cloud/pubsub"
|
|
221
|
+
#
|
|
222
|
+
# pubsub = Google::Cloud::PubSub.new
|
|
223
|
+
#
|
|
224
|
+
# subscriber = pubsub.subscriber "my-topic-sub"
|
|
225
|
+
#
|
|
226
|
+
# listener = subscriber.listen do |received_message|
|
|
227
|
+
# # process message
|
|
228
|
+
# received_message.acknowledge!
|
|
229
|
+
# end
|
|
230
|
+
#
|
|
231
|
+
# # Register to be notified when unhandled errors occur.
|
|
232
|
+
# listener.on_error do |error|
|
|
233
|
+
# # log error
|
|
234
|
+
# puts error
|
|
235
|
+
# end
|
|
236
|
+
#
|
|
237
|
+
# # Start listening for messages and errors.
|
|
238
|
+
# listener.start
|
|
239
|
+
#
|
|
240
|
+
# # Shut down the subscriber when ready to stop receiving messages.
|
|
241
|
+
# listener.stop!
|
|
242
|
+
#
|
|
243
|
+
def on_error &block
|
|
244
|
+
synchronize do
|
|
245
|
+
@error_callbacks << block
|
|
246
|
+
end
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
##
|
|
250
|
+
# The most recent unhandled error to occur while listening to messages
|
|
251
|
+
# on the listener.
|
|
252
|
+
#
|
|
253
|
+
# If an unhandled error has occurred the listener will attempt to
|
|
254
|
+
# recover from the error and resume listening.
|
|
255
|
+
#
|
|
256
|
+
# @return [Exception, nil] error The most recent error raised.
|
|
257
|
+
#
|
|
258
|
+
# @example
|
|
259
|
+
# require "google/cloud/pubsub"
|
|
260
|
+
#
|
|
261
|
+
# pubsub = Google::Cloud::PubSub.new
|
|
262
|
+
#
|
|
263
|
+
# subscriber = pubsub.subscriber "my-topic-sub"
|
|
264
|
+
#
|
|
265
|
+
# listener = subscriber.listen do |received_message|
|
|
266
|
+
# # process message
|
|
267
|
+
# received_message.acknowledge!
|
|
268
|
+
# end
|
|
269
|
+
#
|
|
270
|
+
# # Start listening for messages and errors.
|
|
271
|
+
# listener.start
|
|
272
|
+
#
|
|
273
|
+
# # If an error was raised, it can be retrieved here:
|
|
274
|
+
# listener.last_error #=> nil
|
|
275
|
+
#
|
|
276
|
+
# # Shut down the subscriber when ready to stop receiving messages.
|
|
277
|
+
# listener.stop!
|
|
278
|
+
#
|
|
279
|
+
def last_error
|
|
280
|
+
synchronize { @last_error }
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
##
|
|
284
|
+
# The number of received messages to be collected by listener. Default is 1,000.
|
|
285
|
+
#
|
|
286
|
+
# @return [Integer] The maximum number of messages.
|
|
287
|
+
#
|
|
288
|
+
def max_outstanding_messages
|
|
289
|
+
@inventory[:max_outstanding_messages]
|
|
290
|
+
end
|
|
291
|
+
|
|
292
|
+
##
|
|
293
|
+
# The total byte size of received messages to be collected by listener. Default is 100,000,000 (100MB).
|
|
294
|
+
#
|
|
295
|
+
# @return [Integer] The maximum number of bytes.
|
|
296
|
+
#
|
|
297
|
+
def max_outstanding_bytes
|
|
298
|
+
@inventory[:max_outstanding_bytes]
|
|
299
|
+
end
|
|
300
|
+
|
|
301
|
+
##
|
|
302
|
+
# The number of seconds that received messages can be held awaiting processing. Default is 3,600 (1 hour).
|
|
303
|
+
#
|
|
304
|
+
# @return [Integer] The maximum number of seconds.
|
|
305
|
+
#
|
|
306
|
+
def max_total_lease_duration
|
|
307
|
+
@inventory[:max_total_lease_duration]
|
|
308
|
+
end
|
|
309
|
+
|
|
310
|
+
##
|
|
311
|
+
# The maximum amount of time in seconds for a single lease extension attempt. Bounds the delay before a message
|
|
312
|
+
# redelivery if the listener fails to extend the deadline. Default is 0 (disabled).
|
|
313
|
+
#
|
|
314
|
+
# @return [Integer] The maximum number of seconds.
|
|
315
|
+
#
|
|
316
|
+
def max_duration_per_lease_extension
|
|
317
|
+
@inventory[:max_duration_per_lease_extension]
|
|
318
|
+
end
|
|
319
|
+
|
|
320
|
+
##
|
|
321
|
+
# The minimum amount of time in seconds for a single lease extension attempt. Bounds the delay before a message
|
|
322
|
+
# redelivery if the listener fails to extend the deadline. Default is 0 (disabled).
|
|
323
|
+
#
|
|
324
|
+
# @return [Integer] The minimum number of seconds.
|
|
325
|
+
#
|
|
326
|
+
def min_duration_per_lease_extension
|
|
327
|
+
@inventory[:min_duration_per_lease_extension]
|
|
328
|
+
end
|
|
329
|
+
|
|
330
|
+
##
|
|
331
|
+
# @private
|
|
332
|
+
def stream_inventory
|
|
333
|
+
{
|
|
334
|
+
limit: @inventory[:max_outstanding_messages].fdiv(@streams).ceil,
|
|
335
|
+
bytesize: @inventory[:max_outstanding_bytes].fdiv(@streams).ceil,
|
|
336
|
+
extension: @inventory[:max_total_lease_duration],
|
|
337
|
+
max_duration_per_lease_extension: @inventory[:max_duration_per_lease_extension],
|
|
338
|
+
min_duration_per_lease_extension: @inventory[:min_duration_per_lease_extension]
|
|
339
|
+
}
|
|
340
|
+
end
|
|
341
|
+
|
|
342
|
+
# @private returns error object from the stream thread.
|
|
343
|
+
def error! error
|
|
344
|
+
error_callbacks = synchronize do
|
|
345
|
+
@last_error = error
|
|
346
|
+
@error_callbacks
|
|
347
|
+
end
|
|
348
|
+
error_callbacks = default_error_callbacks if error_callbacks.empty?
|
|
349
|
+
error_callbacks.each { |error_callback| error_callback.call error }
|
|
350
|
+
end
|
|
351
|
+
|
|
352
|
+
##
|
|
353
|
+
# @private
|
|
354
|
+
def to_s
|
|
355
|
+
"(subscription: #{subscription_name}, streams: [#{stream_pool.map(&:to_s).join(', ')}])"
|
|
356
|
+
end
|
|
357
|
+
|
|
358
|
+
##
|
|
359
|
+
# @private
|
|
360
|
+
def inspect
|
|
361
|
+
"#<#{self.class.name} #{self}>"
|
|
362
|
+
end
|
|
363
|
+
|
|
364
|
+
protected
|
|
365
|
+
|
|
366
|
+
##
|
|
367
|
+
# Starts a new thread to call wait! (blocking) on each Stream and then stop the TimedUnaryBuffer.
|
|
368
|
+
def wait_stop_buffer_thread!
|
|
369
|
+
synchronize do
|
|
370
|
+
@wait_stop_buffer_thread ||= Thread.new do
|
|
371
|
+
@stream_pool.map(&:wait!)
|
|
372
|
+
# Shutdown the buffer TimerTask (and flush the buffer) after the streams are all stopped.
|
|
373
|
+
@buffer.stop
|
|
374
|
+
end
|
|
375
|
+
end
|
|
376
|
+
end
|
|
377
|
+
|
|
378
|
+
def coerce_inventory inventory
|
|
379
|
+
@inventory = inventory
|
|
380
|
+
if @inventory.is_a? Hash
|
|
381
|
+
@inventory = @inventory.dup
|
|
382
|
+
# Support deprecated field names
|
|
383
|
+
@inventory[:max_outstanding_messages] ||= @inventory.delete :limit
|
|
384
|
+
@inventory[:max_outstanding_bytes] ||= @inventory.delete :bytesize
|
|
385
|
+
@inventory[:max_total_lease_duration] ||= @inventory.delete :extension
|
|
386
|
+
else
|
|
387
|
+
@inventory = { max_outstanding_messages: @inventory }
|
|
388
|
+
end
|
|
389
|
+
@inventory[:max_outstanding_messages] = Integer(@inventory[:max_outstanding_messages] || 1000)
|
|
390
|
+
@inventory[:max_outstanding_bytes] = Integer(@inventory[:max_outstanding_bytes] || 100_000_000)
|
|
391
|
+
@inventory[:max_total_lease_duration] = Integer(@inventory[:max_total_lease_duration] || 3600)
|
|
392
|
+
@inventory[:max_duration_per_lease_extension] = Integer(@inventory[:max_duration_per_lease_extension] || 0)
|
|
393
|
+
@inventory[:min_duration_per_lease_extension] = Integer(@inventory[:min_duration_per_lease_extension] || 0)
|
|
394
|
+
end
|
|
395
|
+
|
|
396
|
+
def default_error_callbacks
|
|
397
|
+
# This is memoized to reduce calls to the configuration.
|
|
398
|
+
@default_error_callbacks ||= begin
|
|
399
|
+
error_callback = Google::Cloud::PubSub.configure.on_error
|
|
400
|
+
error_callback ||= Google::Cloud.configure.on_error
|
|
401
|
+
if error_callback
|
|
402
|
+
[error_callback]
|
|
403
|
+
else
|
|
404
|
+
[]
|
|
405
|
+
end
|
|
406
|
+
end
|
|
407
|
+
end
|
|
408
|
+
end
|
|
409
|
+
end
|
|
410
|
+
|
|
411
|
+
Pubsub = PubSub unless const_defined? :Pubsub
|
|
412
|
+
end
|
|
413
|
+
end
|