google-cloud-pubsub 2.22.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 +27 -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 +50 -45
  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 +18 -26
  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
@@ -1,4 +1,4 @@
1
- # Copyright 2017 Google LLC
1
+ # Copyright 2015 Google LLC
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -13,417 +13,580 @@
13
13
  # limitations under the License.
14
14
 
15
15
 
16
- require "google/cloud/pubsub/service"
17
- require "google/cloud/pubsub/subscriber/stream"
18
- require "google/cloud/pubsub/subscriber/timed_unary_buffer"
19
- require "monitor"
16
+ require "google/cloud/pubsub/convert"
17
+ require "google/cloud/errors"
18
+ require "google/cloud/pubsub/received_message"
19
+ require "google/cloud/pubsub/message_listener"
20
+ require "google/cloud/pubsub/v1"
20
21
 
21
22
  module Google
22
23
  module Cloud
23
24
  module PubSub
24
25
  ##
25
- # Subscriber object used to stream and process messages from a
26
- # Subscription. See {Google::Cloud::PubSub::Subscription#listen}
26
+ # # Subscriber
27
+
28
+ # A {Subscriber} is the primary interface for data plane operations,
29
+ # enabling you to receive messages from a subscription, either by streaming
30
+ # with a {MessageListener} or by pulling them directly.
27
31
  #
28
32
  # @example
29
33
  # require "google/cloud/pubsub"
30
34
  #
31
35
  # pubsub = Google::Cloud::PubSub.new
32
36
  #
33
- # sub = pubsub.subscription "my-topic-sub"
34
- #
35
- # subscriber = sub.listen do |received_message|
37
+ # subscriber = pubsub.subscriber "my-topic-sub"
38
+ # listener = subscriber.listen do |received_message|
36
39
  # # process message
37
40
  # received_message.acknowledge!
38
41
  # end
39
42
  #
40
- # # Start background threads that will call the block passed to listen.
41
- # subscriber.start
42
- #
43
- # # Shut down the subscriber when ready to stop receiving messages.
44
- # subscriber.stop!
43
+ # # Handle exceptions from listener
44
+ # listener.on_error do |exception|
45
+ # puts "Exception: #{exception.class} #{exception.message}"
46
+ # end
45
47
  #
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 2.
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.
48
+ # # Gracefully shut down the subscriber
49
+ # at_exit do
50
+ # listener.stop!
51
+ # end
63
52
  #
53
+ # # Start background threads that will call the block passed to listen.
54
+ # listener.start
55
+ # sleep
64
56
  class Subscriber
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
57
  ##
76
- # @private Implementation attributes.
77
- attr_reader :stream_pool, :thread_pool, :buffer, :service
58
+ # @private The Service object.
59
+ attr_accessor :service
78
60
 
79
61
  ##
80
- # @private Implementation attributes.
81
- attr_accessor :exactly_once_delivery_enabled
62
+ # @private The gRPC Google::Cloud::PubSub::V1::Subscription object.
63
+ attr_accessor :grpc
82
64
 
83
65
  ##
84
66
  # @private Create an empty {Subscriber} 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 || 2
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
67
+ def initialize
68
+ @service = nil
69
+ @grpc = nil
70
+ @resource_name = nil
71
+ @exists = nil
110
72
  end
111
73
 
112
74
  ##
113
- # Starts the subscriber pulling from the subscription and processing the
114
- # received messages.
75
+ # The underlying Subscription resource.
115
76
  #
116
- # @return [Subscriber] returns self so calls can be chained.
77
+ # Provides access to the `Google::Cloud::PubSub::V1::Subscription`
78
+ # resource managed by this subscriber.
117
79
  #
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
80
+ # Makes an API call to retrieve the actual subscription when called
81
+ # on a reference object. See {#reference?}.
82
+ #
83
+ # @return [Google::Cloud::PubSub::V1::Subscription]
84
+ #
85
+ def subscription_resource
86
+ ensure_grpc!
87
+ @grpc
132
88
  end
133
89
 
134
90
  ##
135
- # Immediately stops the subscriber. 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 [Subscriber] 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
91
+ # The name of the subscription.
92
+ #
93
+ # @return [String] A fully-qualified subscription name in the form
94
+ # `projects/{project_id}/subscriptions/{subscription_id}`.
95
+ #
96
+ def name
97
+ return @resource_name if reference?
98
+ @grpc.name
151
99
  end
152
100
 
153
101
  ##
154
- # Blocks until the subscriber 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 subscriber. To stop the subscriber, first call
159
- # {#stop} and then call {#wait!} to block until the subscriber is
160
- # stopped.
102
+ # This value is the maximum number of seconds after a subscriber
103
+ # receives a message before the subscriber should acknowledge the
104
+ # message.
161
105
  #
162
- # @param [Number, nil] timeout The number of seconds to block until the
163
- # subscriber is fully stopped. Default will block indefinitely.
106
+ # Makes an API call to retrieve the deadline value when called on a
107
+ # reference object. See {#reference?}.
164
108
  #
165
- # @return [Subscriber] 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
109
+ # @return [Integer]
110
+ def deadline
111
+ ensure_grpc!
112
+ @grpc.ack_deadline_seconds
171
113
  end
172
114
 
173
115
  ##
174
- # Stop this subscriber and block until the subscriber is fully stopped
175
- # and all received messages have been processed or released, or until
176
- # `timeout` seconds have passed.
116
+ # Whether message ordering has been enabled. When enabled, messages
117
+ # published with the same `ordering_key` will be delivered in the order
118
+ # they were published. When disabled, messages may be delivered in any
119
+ # order.
177
120
  #
178
- # The same as calling {#stop} and {#wait!}.
121
+ # @note At the time of this release, ordering keys are not yet publicly
122
+ # enabled and requires special project enablements.
179
123
  #
180
- # @param [Number, nil] timeout The number of seconds to block until the
181
- # subscriber is fully stopped. Default will block indefinitely.
124
+ # See {Publisher#publish_async}, {#listen}, and {Message#ordering_key}.
182
125
  #
183
- # @return [Subscriber] returns self so calls can be chained.
126
+ # Makes an API call to retrieve the enable_message_ordering value when called on a
127
+ # reference object. See {#reference?}.
184
128
  #
185
- def stop! timeout = nil
186
- stop
187
- wait! timeout
129
+ # @return [Boolean]
130
+ #
131
+ def message_ordering?
132
+ ensure_grpc!
133
+ @grpc.enable_message_ordering
188
134
  end
189
135
 
190
136
  ##
191
- # Whether the subscriber has been started.
137
+ # Determines whether the subscription exists in the Pub/Sub service.
192
138
  #
193
- # @return [boolean] `true` when started, `false` otherwise.
139
+ # Makes an API call to determine whether the subscription resource
140
+ # exists when called on a reference object. See {#reference?}.
194
141
  #
195
- def started?
196
- synchronize { @started }
197
- end
198
-
199
- ##
200
- # Whether the subscriber has been stopped.
142
+ # @return [Boolean]
201
143
  #
202
- # @return [boolean] `true` when stopped, `false` otherwise.
144
+ # @example
145
+ # require "google/cloud/pubsub"
146
+ #
147
+ # pubsub = Google::Cloud::PubSub.new
203
148
  #
204
- def stopped?
205
- synchronize { @stopped }
149
+ # subscriber = pubsub.subscriber "my-topic-sub"
150
+ # subscriber.exists? #=> true
151
+ #
152
+ def exists?
153
+ # Always true if the object is not set as reference
154
+ return true unless reference?
155
+ # If we have a value, return it
156
+ return @exists unless @exists.nil?
157
+ ensure_grpc!
158
+ @exists = true
159
+ rescue Google::Cloud::NotFoundError
160
+ @exists = false
206
161
  end
207
162
 
208
163
  ##
209
- # Register to be notified of errors when raised.
164
+ # Pulls messages from the server, blocking until messages are available
165
+ # when called with the `immediate: false` option, which is recommended
166
+ # to avoid adverse impacts on the performance of pull operations.
210
167
  #
211
- # If an unhandled error has occurred the subscriber will attempt to
212
- # recover from the error and resume listening.
168
+ # Raises an API error with status `UNAVAILABLE` if there are too many
169
+ # concurrent pull requests pending for the given subscription.
213
170
  #
214
- # Multiple error handlers can be added.
171
+ # See also {#listen} for the preferred way to process messages as they
172
+ # become available.
215
173
  #
216
- # @yield [callback] The block to be called when an error is raised.
217
- # @yieldparam [Exception] error The error raised.
174
+ # @param [Boolean] immediate Whether to return immediately or block until
175
+ # messages are available.
218
176
  #
219
- # @example
177
+ # **Warning:** The default value of this field is `true`. However, sending
178
+ # `true` is discouraged because it adversely impacts the performance of
179
+ # pull operations. We recommend that users always explicitly set this field
180
+ # to `false`.
181
+ #
182
+ # If this field set to `true`, the system will respond immediately
183
+ # even if it there are no messages available to return in the pull
184
+ # response. Otherwise, the system may wait (for a bounded amount of time)
185
+ # until at least one message is available, rather than returning no messages.
186
+ #
187
+ # See also {#listen} for the preferred way to process messages as they
188
+ # become available.
189
+ # @param [Integer] max The maximum number of messages to return for this
190
+ # request. The Pub/Sub system may return fewer than the number
191
+ # specified. The default value is `100`, the maximum value is `1000`.
192
+ #
193
+ # @return [Array<Google::Cloud::PubSub::ReceivedMessage>]
194
+ #
195
+ # @example The `immediate: false` option is now recommended to avoid adverse impacts on pull operations:
220
196
  # require "google/cloud/pubsub"
221
197
  #
222
198
  # pubsub = Google::Cloud::PubSub.new
223
199
  #
224
- # sub = pubsub.subscription "my-topic-sub"
225
- #
226
- # subscriber = sub.listen do |received_message|
227
- # # process message
200
+ # subscriber = pubsub.subscriber "my-topic-sub"
201
+ # received_messages = subscriber.pull immediate: false
202
+ # received_messages.each do |received_message|
228
203
  # received_message.acknowledge!
229
204
  # end
230
205
  #
231
- # # Register to be notified when unhandled errors occur.
232
- # subscriber.on_error do |error|
233
- # # log error
234
- # puts error
235
- # end
206
+ # @example A maximum number of messages returned can also be specified:
207
+ # require "google/cloud/pubsub"
236
208
  #
237
- # # Start listening for messages and errors.
238
- # subscriber.start
209
+ # pubsub = Google::Cloud::PubSub.new
239
210
  #
240
- # # Shut down the subscriber when ready to stop receiving messages.
241
- # subscriber.stop!
211
+ # subscriber = pubsub.subscriber "my-topic-sub"
212
+ # received_messages = subscriber.pull immediate: false, max: 10
213
+ # received_messages.each do |received_message|
214
+ # received_message.acknowledge!
215
+ # end
242
216
  #
243
- def on_error &block
244
- synchronize do
245
- @error_callbacks << block
217
+ def pull immediate: true, max: 100
218
+ ensure_service!
219
+ options = { immediate: immediate, max: max }
220
+ list_grpc = service.pull name, options
221
+ Array(list_grpc.received_messages).map do |msg_grpc|
222
+ ReceivedMessage.from_grpc msg_grpc, self
246
223
  end
224
+ rescue Google::Cloud::DeadlineExceededError
225
+ []
247
226
  end
248
227
 
249
228
  ##
250
- # The most recent unhandled error to occur while listening to messages
251
- # on the subscriber.
229
+ # Pulls from the server while waiting for messages to become available.
230
+ # This is the same as:
231
+ #
232
+ # subscriber.pull immediate: false
233
+ #
234
+ # See also {#listen} for the preferred way to process messages as they
235
+ # become available.
236
+ #
237
+ # @param [Integer] max The maximum number of messages to return for this
238
+ # request. The Pub/Sub system may return fewer than the number
239
+ # specified. The default value is `100`, the maximum value is `1000`.
240
+ #
241
+ # @return [Array<Google::Cloud::PubSub::ReceivedMessage>]
242
+ #
243
+ # @example
244
+ # require "google/cloud/pubsub"
252
245
  #
253
- # If an unhandled error has occurred the subscriber will attempt to
254
- # recover from the error and resume listening.
246
+ # pubsub = Google::Cloud::PubSub.new
255
247
  #
256
- # @return [Exception, nil] error The most recent error raised.
248
+ # subscriber = pubsub.subscriber "my-topic-sub"
249
+ # received_messages = subscriber.wait_for_messages
250
+ # received_messages.each do |received_message|
251
+ # received_message.acknowledge!
252
+ # end
253
+ #
254
+ def wait_for_messages max: 100
255
+ pull immediate: false, max: max
256
+ end
257
+
258
+ ##
259
+ # Create a {MessageListener} object that receives and processes messages
260
+ # using the code provided in the callback. Messages passed to the
261
+ # callback should acknowledge ({ReceivedMessage#acknowledge!}) or reject
262
+ # ({ReceivedMessage#reject!}) the message. If no action is taken, the
263
+ # message will be removed from the subscriber and made available for
264
+ # redelivery after the callback is completed.
265
+ #
266
+ # Google Cloud Pub/Sub ordering keys provide the ability to ensure
267
+ # related messages are sent to subscribers in the order in which they
268
+ # were published. Messages can be tagged with an ordering key, a string
269
+ # that identifies related messages for which publish order should be
270
+ # respected. The service guarantees that, for a given ordering key and
271
+ # publisher, messages are sent to subscribers in the order in which they
272
+ # were published. Ordering does not require sacrificing high throughput
273
+ # or scalability, as the service automatically distributes messages for
274
+ # different ordering keys across subscribers.
275
+ #
276
+ # To use ordering keys, the subscription must be created with message
277
+ # ordering enabled before calling {#listen}. When enabled, the subscriber
278
+ # will deliver messages with the same `ordering_key` in the order they were
279
+ # published.
280
+ #
281
+ # @note At the time of this release, ordering keys are not yet publicly
282
+ # enabled and requires special project enablements.
283
+ #
284
+ # @param [Numeric] deadline The default number of seconds the stream
285
+ # will hold received messages before modifying the message's ack
286
+ # deadline. The minimum is 10, the maximum is 600. Default is
287
+ # {#deadline}. Optional.
288
+ #
289
+ # When using a reference object an API call will be made to retrieve
290
+ # the default deadline value for the subscription when this argument
291
+ # is not provided. See {#reference?}.
292
+ # @param [Boolean] message_ordering Whether message ordering has been
293
+ # enabled. The value provided must match the value set on the Pub/Sub
294
+ # service. See {#message_ordering?}. Optional.
295
+ #
296
+ # When using a reference object an API call will be made to retrieve
297
+ # the default message_ordering value for the subscription when this
298
+ # argument is not provided. See {#reference?}.
299
+ # @param [Integer] streams The number of concurrent streams to open to
300
+ # pull messages from the subscription. Default is 1. Optional.
301
+ # @param [Hash, Integer] inventory The settings to control how received messages are to be handled by the
302
+ # subscriber. When provided as an Integer instead of a Hash only `max_outstanding_messages` will be set.
303
+ # Optional.
304
+ #
305
+ # Hash keys and values may include the following:
306
+ #
307
+ # * `:max_outstanding_messages` [Integer] The number of received messages to be collected by subscriber.
308
+ # Default is 1,000. (Note: replaces `:limit`, which is deprecated.)
309
+ # * `:max_outstanding_bytes` [Integer] The total byte size of received messages to be collected by
310
+ # subscriber. Default is 100,000,000 (100MB). (Note: replaces `:bytesize`, which is deprecated.)
311
+ # * `:max_total_lease_duration` [Integer] The number of seconds that received messages can be held awaiting
312
+ # processing. Default is 3,600 (1 hour). (Note: replaces `:extension`, which is deprecated.)
313
+ # * `:max_duration_per_lease_extension` [Integer] The maximum amount of time in seconds for a single lease
314
+ # extension attempt. Bounds the delay before a message redelivery if the subscriber fails to extend the
315
+ # deadline. Default is 0 (disabled).
316
+ # @param [Hash] threads The number of threads to create to handle
317
+ # concurrent calls by each stream opened by the subscriber. Optional.
318
+ #
319
+ # Hash keys and values may include the following:
320
+ #
321
+ # * `:callback` (Integer) The number of threads used to handle the
322
+ # received messages. Default is 8.
323
+ # * `:push` (Integer) The number of threads to handle
324
+ # acknowledgement ({ReceivedMessage#ack!}) and modify ack deadline
325
+ # messages ({ReceivedMessage#nack!},
326
+ # {ReceivedMessage#modify_ack_deadline!}). Default is 4.
327
+ #
328
+ # @yield [received_message] a block for processing new messages
329
+ # @yieldparam [ReceivedMessage] received_message the newly received
330
+ # message
331
+ #
332
+ # @return [MessageListener]
257
333
  #
258
334
  # @example
259
335
  # require "google/cloud/pubsub"
260
336
  #
261
337
  # pubsub = Google::Cloud::PubSub.new
262
338
  #
263
- # sub = pubsub.subscription "my-topic-sub"
339
+ # subscriber = pubsub.subscriber "my-topic-sub"
264
340
  #
265
- # subscriber = sub.listen do |received_message|
341
+ # listener = subscriber.listen do |received_message|
266
342
  # # process message
343
+ # puts "Data: #{received_message.message.data}, published at #{received_message.message.published_at}"
267
344
  # received_message.acknowledge!
268
345
  # end
269
346
  #
270
- # # Start listening for messages and errors.
271
- # subscriber.start
347
+ # # Start background threads that will call block passed to listen.
348
+ # listener.start
349
+ #
350
+ # # Shut down the subscriber when ready to stop receiving messages.
351
+ # listener.stop!
352
+ #
353
+ # @example Configuring to increase concurrent callbacks:
354
+ # require "google/cloud/pubsub"
355
+ #
356
+ # pubsub = Google::Cloud::PubSub.new
272
357
  #
273
- # # If an error was raised, it can be retrieved here:
274
- # subscriber.last_error #=> nil
358
+ # subscriber = pubsub.subscriber "my-topic-sub"
359
+ #
360
+ # listener = subscriber.listen threads: { callback: 16 } do |rec_message|
361
+ # # store the message somewhere before acknowledging
362
+ # store_in_backend rec_message.data # takes a few seconds
363
+ # rec_message.acknowledge!
364
+ # end
365
+ #
366
+ # # Start background threads that will call block passed to listen.
367
+ # listener.start
275
368
  #
276
369
  # # Shut down the subscriber when ready to stop receiving messages.
277
- # subscriber.stop!
370
+ # listener.stop!
278
371
  #
279
- def last_error
280
- synchronize { @last_error }
281
- end
282
-
283
- ##
284
- # The number of received messages to be collected by subscriber. Default is 1,000.
372
+ # @example Ordered messages are supported using ordering_key:
373
+ # require "google/cloud/pubsub"
374
+ #
375
+ # pubsub = Google::Cloud::PubSub.new
376
+ #
377
+ # subscriber = pubsub.subscriber "my-ordered-topic-sub"
378
+ # subscriber.message_ordering? #=> true
379
+ #
380
+ # listener = subscriber.listen do |received_message|
381
+ # # messsages with the same ordering_key are received
382
+ # # in the order in which they were published.
383
+ # received_message.acknowledge!
384
+ # end
385
+ #
386
+ # # Start background threads that will call block passed to listen.
387
+ # listener.start
388
+ #
389
+ # # Shut down the subscriber when ready to stop receiving messages.
390
+ # listener.stop!
391
+ #
392
+ # @example Set the maximum amount of time before redelivery if the subscriber fails to extend the deadline:
393
+ # require "google/cloud/pubsub"
394
+ #
395
+ # pubsub = Google::Cloud::PubSub.new
396
+ #
397
+ # subscriber = pubsub.subscriber "my-topic-sub"
398
+ #
399
+ # listener = subscriber.listen inventory: { max_duration_per_lease_extension: 20 } do |received_message|
400
+ # # Process message very slowly with possibility of failure.
401
+ # process rec_message.data # takes minutes
402
+ # rec_message.acknowledge!
403
+ # end
404
+ #
405
+ # # Start background threads that will call block passed to listen.
406
+ # listener.start
285
407
  #
286
- # @return [Integer] The maximum number of messages.
408
+ # # Shut down the subscriber when ready to stop receiving messages.
409
+ # listener.stop!
287
410
  #
288
- def max_outstanding_messages
289
- @inventory[:max_outstanding_messages]
411
+ def listen deadline: nil, message_ordering: nil, streams: nil, inventory: nil, threads: {}, &block
412
+ ensure_service!
413
+ deadline ||= self.deadline
414
+ message_ordering = message_ordering? if message_ordering.nil?
415
+
416
+ MessageListener.new name, block, deadline: deadline, streams: streams, inventory: inventory,
417
+ message_ordering: message_ordering, threads: threads, service: service
290
418
  end
291
- # @deprecated Use {#max_outstanding_messages}.
292
- alias inventory_limit max_outstanding_messages
293
- # @deprecated Use {#max_outstanding_messages}.
294
- alias inventory max_outstanding_messages
295
419
 
296
420
  ##
297
- # The total byte size of received messages to be collected by subscriber. Default is 100,000,000 (100MB).
421
+ # Acknowledges receipt of a message. After an ack,
422
+ # the Pub/Sub system can remove the message from the subscription.
423
+ # Acknowledging a message whose ack deadline has expired may succeed,
424
+ # although the message may have been sent again.
425
+ # Acknowledging a message more than once will not result in an error.
426
+ # This is only used for messages received via pull.
427
+ #
428
+ # See also {ReceivedMessage#acknowledge!}.
429
+ #
430
+ # @param [ReceivedMessage, String] messages One or more
431
+ # {ReceivedMessage} objects or ack_id values.
432
+ #
433
+ # @example
434
+ # require "google/cloud/pubsub"
298
435
  #
299
- # @return [Integer] The maximum number of bytes.
436
+ # pubsub = Google::Cloud::PubSub.new
437
+ #
438
+ # subscriber = pubsub.subscriber "my-topic-sub"
439
+ # received_messages = sub.pull immediate: false
440
+ # subscriber.acknowledge received_messages
300
441
  #
301
- def max_outstanding_bytes
302
- @inventory[:max_outstanding_bytes]
442
+ def acknowledge *messages
443
+ ack_ids = coerce_ack_ids messages
444
+ return true if ack_ids.empty?
445
+ ensure_service!
446
+ service.acknowledge name, *ack_ids
447
+ true
303
448
  end
304
- # @deprecated Use {#max_outstanding_bytes}.
305
- alias inventory_bytesize max_outstanding_bytes
449
+ alias ack acknowledge
306
450
 
307
451
  ##
308
- # Whether to enforce flow control at the client side only or to enforce it at both the client and
309
- # the server. For more details about flow control see https://cloud.google.com/pubsub/docs/pull#config.
452
+ # Modifies the acknowledge deadline for messages.
453
+ #
454
+ # This indicates that more time is needed to process the messages, or to
455
+ # make the messages available for redelivery if the processing was
456
+ # interrupted.
457
+ #
458
+ # See also {ReceivedMessage#modify_ack_deadline!}.
459
+ #
460
+ # @param [Integer] new_deadline The new ack deadline in seconds from the
461
+ # time this request is sent to the Pub/Sub system. Must be >= 0. For
462
+ # example, if the value is `10`, the new ack deadline will expire 10
463
+ # seconds after the call is made. Specifying `0` may immediately make
464
+ # the message available for another pull request.
465
+ # @param [ReceivedMessage, String] messages One or more
466
+ # {ReceivedMessage} objects or ack_id values.
310
467
  #
311
- # @return [Boolean] `true` when only client side flow control is enforced, `false` when both client and
312
- # server side flow control are enforced.
468
+ # @example
469
+ # require "google/cloud/pubsub"
470
+ #
471
+ # pubsub = Google::Cloud::PubSub.new
313
472
  #
314
- def use_legacy_flow_control?
315
- @inventory[:use_legacy_flow_control]
473
+ # subscriber = pubsub.subscriber "my-topic-sub"
474
+ # received_messages = subscriber.pull immediate: false
475
+ # subscriber.modify_ack_deadline 120, received_messages
476
+ #
477
+ def modify_ack_deadline new_deadline, *messages
478
+ ack_ids = coerce_ack_ids messages
479
+ ensure_service!
480
+ service.modify_ack_deadline name, ack_ids, new_deadline
481
+ true
316
482
  end
317
483
 
318
484
  ##
319
- # The number of seconds that received messages can be held awaiting processing. Default is 3,600 (1 hour).
485
+ # Determines whether the subscription object was created without
486
+ # retrieving the resource representation from the Pub/Sub service.
320
487
  #
321
- # @return [Integer] The maximum number of seconds.
488
+ # @return [Boolean] `true` when the subscription was created without a
489
+ # resource representation, `false` otherwise.
322
490
  #
323
- def max_total_lease_duration
324
- @inventory[:max_total_lease_duration]
491
+ # @example
492
+ # require "google/cloud/pubsub"
493
+ #
494
+ # pubsub = Google::Cloud::PubSub.new
495
+ #
496
+ # subscriber = pubsub.subscriber "my-topic-sub", skip_lookup: true
497
+ #
498
+ # subscriber.reference? #=> true
499
+ #
500
+ def reference?
501
+ @grpc.nil?
325
502
  end
326
- # @deprecated Use {#max_total_lease_duration}.
327
- alias inventory_extension max_total_lease_duration
328
503
 
329
504
  ##
330
- # The maximum amount of time in seconds for a single lease extension attempt. Bounds the delay before a message
331
- # redelivery if the subscriber fails to extend the deadline. Default is 0 (disabled).
505
+ # Determines whether the subscription object was created with a resource
506
+ # representation from the Pub/Sub service.
507
+ #
508
+ # @return [Boolean] `true` when the subscription was created with a
509
+ # resource representation, `false` otherwise.
332
510
  #
333
- # @return [Integer] The maximum number of seconds.
511
+ # @example
512
+ # require "google/cloud/pubsub"
513
+ #
514
+ # pubsub = Google::Cloud::PubSub.new
515
+ #
516
+ # subscriber = pubsub.subscriber "my-topic-sub"
334
517
  #
335
- def max_duration_per_lease_extension
336
- @inventory[:max_duration_per_lease_extension]
518
+ # subscriber.resource? #=> true
519
+ #
520
+ def resource?
521
+ !@grpc.nil?
337
522
  end
338
523
 
339
524
  ##
340
- # The minimum amount of time in seconds for a single lease extension attempt. Bounds the delay before a message
341
- # redelivery if the subscriber fails to extend the deadline. Default is 0 (disabled).
525
+ # Reloads the subscription with current data from the Pub/Sub service.
526
+ #
527
+ # @return [Google::Cloud::PubSub::Subscription] Returns the reloaded
528
+ # subscription
342
529
  #
343
- # @return [Integer] The minimum number of seconds.
530
+ # @example
531
+ # require "google/cloud/pubsub"
344
532
  #
345
- def min_duration_per_lease_extension
346
- @inventory[:min_duration_per_lease_extension]
533
+ # pubsub = Google::Cloud::PubSub.new
534
+ #
535
+ # sub = Google::Cloud::PubSub::Subscriber.from_name "my-topic-sub", pubsub.service
536
+ #
537
+ # sub.reload!
538
+ #
539
+ def reload!
540
+ ensure_service!
541
+ subscription_path = service.subscription_path name
542
+ @grpc = service.subscription_admin.get_subscription subscription: subscription_path
543
+ @resource_name = nil
544
+ self
347
545
  end
348
546
 
349
547
  ##
350
548
  # @private
351
- def stream_inventory
352
- {
353
- limit: @inventory[:max_outstanding_messages].fdiv(@streams).ceil,
354
- bytesize: @inventory[:max_outstanding_bytes].fdiv(@streams).ceil,
355
- extension: @inventory[:max_total_lease_duration],
356
- max_duration_per_lease_extension: @inventory[:max_duration_per_lease_extension],
357
- min_duration_per_lease_extension: @inventory[:min_duration_per_lease_extension],
358
- use_legacy_flow_control: @inventory[:use_legacy_flow_control]
359
- }
360
- end
361
-
362
- # @private returns error object from the stream thread.
363
- def error! error
364
- error_callbacks = synchronize do
365
- @last_error = error
366
- @error_callbacks
549
+ # New Subscriber from a Google::Cloud::PubSub::V1::Subscription
550
+ # object.
551
+ def self.from_grpc grpc, service
552
+ new.tap do |f|
553
+ f.grpc = grpc
554
+ f.service = service
367
555
  end
368
- error_callbacks = default_error_callbacks if error_callbacks.empty?
369
- error_callbacks.each { |error_callback| error_callback.call error }
370
556
  end
371
557
 
372
558
  ##
373
- # @private
374
- def to_s
375
- "(subscription: #{subscription_name}, streams: [#{stream_pool.map(&:to_s).join(', ')}])"
376
- end
377
-
378
- ##
379
- # @private
380
- def inspect
381
- "#<#{self.class.name} #{self}>"
559
+ # @private New reference {Subscriber} object without making an HTTP
560
+ # request.
561
+ def self.from_name name, service, options = {}
562
+ name = service.subscription_path name, options
563
+ from_grpc(nil, service).tap do |s|
564
+ s.instance_variable_set :@resource_name, name
565
+ end
382
566
  end
383
567
 
384
568
  protected
385
569
 
386
570
  ##
387
- # Starts a new thread to call wait! (blocking) on each Stream and then stop the TimedUnaryBuffer.
388
- def wait_stop_buffer_thread!
389
- synchronize do
390
- @wait_stop_buffer_thread ||= Thread.new do
391
- @stream_pool.map(&:wait!)
392
- # Shutdown the buffer TimerTask (and flush the buffer) after the streams are all stopped.
393
- @buffer.stop
394
- end
395
- end
571
+ # @private Raise an error unless an active connection to the service is
572
+ # available.
573
+ def ensure_service!
574
+ raise "Must have active connection to service" unless service
396
575
  end
397
576
 
398
- def coerce_inventory inventory
399
- @inventory = inventory
400
- if @inventory.is_a? Hash
401
- @inventory = @inventory.dup
402
- # Support deprecated field names
403
- @inventory[:max_outstanding_messages] ||= @inventory.delete :limit
404
- @inventory[:max_outstanding_bytes] ||= @inventory.delete :bytesize
405
- @inventory[:max_total_lease_duration] ||= @inventory.delete :extension
406
- else
407
- @inventory = { max_outstanding_messages: @inventory }
408
- end
409
- @inventory[:max_outstanding_messages] = Integer(@inventory[:max_outstanding_messages] || 1000)
410
- @inventory[:max_outstanding_bytes] = Integer(@inventory[:max_outstanding_bytes] || 100_000_000)
411
- @inventory[:max_total_lease_duration] = Integer(@inventory[:max_total_lease_duration] || 3600)
412
- @inventory[:max_duration_per_lease_extension] = Integer(@inventory[:max_duration_per_lease_extension] || 0)
413
- @inventory[:min_duration_per_lease_extension] = Integer(@inventory[:min_duration_per_lease_extension] || 0)
414
- @inventory[:use_legacy_flow_control] = @inventory[:use_legacy_flow_control] || false
577
+ ##
578
+ # Ensures a Google::Cloud::PubSub::V1::Subscription object exists.
579
+ def ensure_grpc!
580
+ ensure_service!
581
+ reload! if reference?
415
582
  end
416
583
 
417
- def default_error_callbacks
418
- # This is memoized to reduce calls to the configuration.
419
- @default_error_callbacks ||= begin
420
- error_callback = Google::Cloud::PubSub.configure.on_error
421
- error_callback ||= Google::Cloud.configure.on_error
422
- if error_callback
423
- [error_callback]
424
- else
425
- []
426
- end
584
+ ##
585
+ # Makes sure the values are the `ack_id`. If given several
586
+ # {ReceivedMessage} objects extract the `ack_id` values.
587
+ def coerce_ack_ids messages
588
+ Array(messages).flatten.map do |msg|
589
+ msg.respond_to?(:ack_id) ? msg.ack_id : msg.to_s
427
590
  end
428
591
  end
429
592
  end