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