google-cloud-pubsub 2.23.0 → 3.0.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 (34) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +18 -0
  3. data/OVERVIEW.md +188 -144
  4. data/lib/google/cloud/pubsub/admin_clients.rb +116 -0
  5. data/lib/google/cloud/pubsub/async_publisher.rb +9 -9
  6. data/lib/google/cloud/pubsub/batch_publisher.rb +4 -4
  7. data/lib/google/cloud/pubsub/errors.rb +3 -3
  8. data/lib/google/cloud/pubsub/message.rb +8 -8
  9. data/lib/google/cloud/pubsub/{subscriber → message_listener}/enumerator_queue.rb +1 -1
  10. data/lib/google/cloud/pubsub/{subscriber → message_listener}/inventory.rb +2 -4
  11. data/lib/google/cloud/pubsub/{subscriber → message_listener}/sequencer.rb +1 -1
  12. data/lib/google/cloud/pubsub/{subscriber → message_listener}/stream.rb +10 -10
  13. data/lib/google/cloud/pubsub/{subscriber → message_listener}/timed_unary_buffer.rb +1 -1
  14. data/lib/google/cloud/pubsub/message_listener.rb +413 -0
  15. data/lib/google/cloud/pubsub/project.rb +98 -531
  16. data/lib/google/cloud/pubsub/publisher.rb +373 -0
  17. data/lib/google/cloud/pubsub/received_message.rb +44 -39
  18. data/lib/google/cloud/pubsub/service.rb +24 -386
  19. data/lib/google/cloud/pubsub/subscriber.rb +442 -279
  20. data/lib/google/cloud/pubsub/version.rb +1 -1
  21. data/lib/google/cloud/pubsub.rb +8 -15
  22. data/lib/google-cloud-pubsub.rb +5 -4
  23. metadata +9 -17
  24. data/lib/google/cloud/pubsub/policy.rb +0 -188
  25. data/lib/google/cloud/pubsub/retry_policy.rb +0 -88
  26. data/lib/google/cloud/pubsub/schema/list.rb +0 -180
  27. data/lib/google/cloud/pubsub/schema.rb +0 -378
  28. data/lib/google/cloud/pubsub/snapshot/list.rb +0 -178
  29. data/lib/google/cloud/pubsub/snapshot.rb +0 -205
  30. data/lib/google/cloud/pubsub/subscription/list.rb +0 -205
  31. data/lib/google/cloud/pubsub/subscription/push_config.rb +0 -268
  32. data/lib/google/cloud/pubsub/subscription.rb +0 -1467
  33. data/lib/google/cloud/pubsub/topic/list.rb +0 -171
  34. data/lib/google/cloud/pubsub/topic.rb +0 -1100
@@ -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