google-cloud-pubsub 1.0.2 → 2.19.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. checksums.yaml +4 -4
  2. data/AUTHENTICATION.md +16 -54
  3. data/CHANGELOG.md +464 -0
  4. data/CONTRIBUTING.md +328 -116
  5. data/EMULATOR.md +1 -1
  6. data/LOGGING.md +94 -2
  7. data/OVERVIEW.md +121 -68
  8. data/TROUBLESHOOTING.md +2 -8
  9. data/lib/google/cloud/pubsub/acknowledge_result.rb +79 -0
  10. data/lib/google/cloud/pubsub/async_publisher/batch.rb +319 -0
  11. data/lib/google/cloud/pubsub/async_publisher.rb +231 -156
  12. data/lib/google/cloud/pubsub/batch_publisher.rb +60 -30
  13. data/lib/google/cloud/pubsub/convert.rb +33 -7
  14. data/lib/google/cloud/pubsub/credentials.rb +2 -2
  15. data/lib/google/cloud/pubsub/errors.rb +93 -0
  16. data/lib/google/cloud/pubsub/flow_controller.rb +137 -0
  17. data/lib/google/cloud/pubsub/message.rb +45 -4
  18. data/lib/google/cloud/pubsub/policy.rb +3 -2
  19. data/lib/google/cloud/pubsub/project.rb +316 -49
  20. data/lib/google/cloud/pubsub/publish_result.rb +6 -1
  21. data/lib/google/cloud/pubsub/received_message.rb +171 -10
  22. data/lib/google/cloud/pubsub/retry_policy.rb +88 -0
  23. data/lib/google/cloud/pubsub/schema/list.rb +180 -0
  24. data/lib/google/cloud/pubsub/schema.rb +310 -0
  25. data/lib/google/cloud/pubsub/service.rb +285 -269
  26. data/lib/google/cloud/pubsub/snapshot/list.rb +4 -6
  27. data/lib/google/cloud/pubsub/snapshot.rb +5 -2
  28. data/lib/google/cloud/pubsub/subscriber/inventory.rb +69 -32
  29. data/lib/google/cloud/pubsub/subscriber/sequencer.rb +115 -0
  30. data/lib/google/cloud/pubsub/subscriber/stream.rb +108 -49
  31. data/lib/google/cloud/pubsub/subscriber/timed_unary_buffer.rb +191 -30
  32. data/lib/google/cloud/pubsub/subscriber.rb +155 -45
  33. data/lib/google/cloud/pubsub/subscription/list.rb +4 -6
  34. data/lib/google/cloud/pubsub/subscription/push_config.rb +55 -31
  35. data/lib/google/cloud/pubsub/subscription.rb +561 -77
  36. data/lib/google/cloud/pubsub/topic/list.rb +4 -6
  37. data/lib/google/cloud/pubsub/topic.rb +372 -52
  38. data/lib/google/cloud/pubsub/version.rb +1 -1
  39. data/lib/google/cloud/pubsub.rb +35 -46
  40. data/lib/google-cloud-pubsub.rb +21 -27
  41. metadata +26 -189
  42. data/lib/google/cloud/pubsub/v1/credentials.rb +0 -41
  43. data/lib/google/cloud/pubsub/v1/doc/google/iam/v1/iam_policy.rb +0 -21
  44. data/lib/google/cloud/pubsub/v1/doc/google/iam/v1/options.rb +0 -21
  45. data/lib/google/cloud/pubsub/v1/doc/google/iam/v1/policy.rb +0 -21
  46. data/lib/google/cloud/pubsub/v1/doc/google/protobuf/duration.rb +0 -91
  47. data/lib/google/cloud/pubsub/v1/doc/google/protobuf/empty.rb +0 -29
  48. data/lib/google/cloud/pubsub/v1/doc/google/protobuf/field_mask.rb +0 -222
  49. data/lib/google/cloud/pubsub/v1/doc/google/protobuf/timestamp.rb +0 -113
  50. data/lib/google/cloud/pubsub/v1/doc/google/pubsub/v1/pubsub.rb +0 -744
  51. data/lib/google/cloud/pubsub/v1/doc/google/type/expr.rb +0 -19
  52. data/lib/google/cloud/pubsub/v1/publisher_client.rb +0 -786
  53. data/lib/google/cloud/pubsub/v1/publisher_client_config.json +0 -105
  54. data/lib/google/cloud/pubsub/v1/subscriber_client.rb +0 -1385
  55. data/lib/google/cloud/pubsub/v1/subscriber_client_config.json +0 -138
  56. data/lib/google/cloud/pubsub/v1.rb +0 -17
  57. data/lib/google/pubsub/v1/pubsub_pb.rb +0 -249
  58. data/lib/google/pubsub/v1/pubsub_services_pb.rb +0 -211
@@ -125,17 +125,15 @@ module Google
125
125
  # puts snapshot.name
126
126
  # end
127
127
  #
128
- def all request_limit: nil
128
+ def all request_limit: nil, &block
129
129
  request_limit = request_limit.to_i if request_limit
130
- unless block_given?
131
- return enum_for :all, request_limit: request_limit
132
- end
130
+ return enum_for :all, request_limit: request_limit unless block_given?
133
131
  results = self
134
132
  loop do
135
- results.each { |r| yield r }
133
+ results.each(&block)
136
134
  if request_limit
137
135
  request_limit -= 1
138
- break if request_limit < 0
136
+ break if request_limit.negative?
139
137
  end
140
138
  break unless results.next?
141
139
  results = results.next
@@ -15,6 +15,7 @@
15
15
 
16
16
  require "google/cloud/errors"
17
17
  require "google/cloud/pubsub/snapshot/list"
18
+ require "google/cloud/pubsub/v1"
18
19
 
19
20
  module Google
20
21
  module Cloud
@@ -58,8 +59,10 @@ module Google
58
59
  end
59
60
 
60
61
  ##
61
- # The name of the snapshot. Format is
62
- # `projects/{project}/snapshots/{snap}`.
62
+ # The name of the snapshot.
63
+ #
64
+ # @return [String] A fully-qualified snapshot name in the form
65
+ # `projects/{project_id}/snapshots/{snapshot_id}`.
63
66
  def name
64
67
  @grpc.name
65
68
  end
@@ -22,30 +22,49 @@ module Google
22
22
  ##
23
23
  # @private
24
24
  class Inventory
25
+ InventoryItem = Struct.new :bytesize, :pulled_at do
26
+ def self.from rec_msg
27
+ new rec_msg.to_proto.bytesize, Time.now
28
+ end
29
+ end
30
+
25
31
  include MonitorMixin
26
32
 
27
- attr_reader :stream, :limit
33
+ attr_reader :stream
34
+ attr_reader :limit
35
+ attr_reader :bytesize
36
+ attr_reader :extension
37
+ attr_reader :max_duration_per_lease_extension
38
+ attr_accessor :min_duration_per_lease_extension
39
+ attr_reader :use_legacy_flow_control
28
40
 
29
- def initialize stream, limit
41
+ def initialize stream, limit:, bytesize:, extension:, max_duration_per_lease_extension:,
42
+ min_duration_per_lease_extension:, use_legacy_flow_control:
43
+ super()
30
44
  @stream = stream
31
45
  @limit = limit
32
- @_ack_ids = []
46
+ @bytesize = bytesize
47
+ @extension = extension
48
+ @max_duration_per_lease_extension = max_duration_per_lease_extension
49
+ @min_duration_per_lease_extension = min_duration_per_lease_extension
50
+ @use_legacy_flow_control = use_legacy_flow_control
51
+ @inventory = {}
33
52
  @wait_cond = new_cond
34
-
35
- super()
36
53
  end
37
54
 
38
55
  def ack_ids
39
- @_ack_ids
56
+ @inventory.keys
40
57
  end
41
58
 
42
- def add *ack_ids
43
- ack_ids.flatten!
44
- ack_ids.compact!
45
- return if ack_ids.empty?
59
+ def add *rec_msgs
60
+ rec_msgs.flatten!
61
+ rec_msgs.compact!
62
+ return if rec_msgs.empty?
46
63
 
47
64
  synchronize do
48
- @_ack_ids += ack_ids
65
+ rec_msgs.each do |rec_msg|
66
+ @inventory[rec_msg.ack_id] = InventoryItem.from rec_msg
67
+ end
49
68
  @wait_cond.broadcast
50
69
  end
51
70
  end
@@ -56,20 +75,34 @@ module Google
56
75
  return if ack_ids.empty?
57
76
 
58
77
  synchronize do
59
- @_ack_ids -= ack_ids
78
+ @inventory.delete_if { |ack_id, _| ack_ids.include? ack_id }
79
+ @wait_cond.broadcast
80
+ end
81
+ end
82
+
83
+ def remove_expired!
84
+ synchronize do
85
+ extension_time = Time.new - extension
86
+ @inventory.delete_if { |_ack_id, item| item.pulled_at < extension_time }
60
87
  @wait_cond.broadcast
61
88
  end
62
89
  end
63
90
 
64
91
  def count
65
92
  synchronize do
66
- @_ack_ids.count
93
+ @inventory.count
94
+ end
95
+ end
96
+
97
+ def total_bytesize
98
+ synchronize do
99
+ @inventory.values.sum(&:bytesize)
67
100
  end
68
101
  end
69
102
 
70
103
  def empty?
71
104
  synchronize do
72
- @_ack_ids.empty?
105
+ @inventory.empty?
73
106
  end
74
107
  end
75
108
 
@@ -93,7 +126,9 @@ module Google
93
126
  end
94
127
 
95
128
  def full?
96
- count >= limit
129
+ synchronize do
130
+ @inventory.count >= limit || @inventory.values.sum(&:bytesize) >= bytesize
131
+ end
97
132
  end
98
133
 
99
134
  protected
@@ -101,26 +136,24 @@ module Google
101
136
  def background_run
102
137
  delay_target = nil
103
138
 
104
- synchronize do
105
- until @stopped
106
- if @_ack_ids.empty?
107
- delay_target = nil
108
-
109
- @wait_cond.wait # wait until broadcast
110
- next
111
- end
139
+ until stopped?
140
+ if empty?
141
+ delay_target = nil
112
142
 
113
- delay_target ||= calc_target
114
- delay_gap = delay_target - Time.now
143
+ synchronize { @wait_cond.wait } # wait until broadcast
144
+ next
145
+ end
115
146
 
116
- unless delay_gap.positive?
117
- delay_target = calc_target
118
- stream.renew_lease!
119
- next
120
- end
147
+ delay_target ||= calc_target
148
+ delay_gap = delay_target - Time.now
121
149
 
122
- @wait_cond.wait delay_gap
150
+ unless delay_gap.positive?
151
+ delay_target = calc_target
152
+ stream.renew_lease!
153
+ next
123
154
  end
155
+
156
+ synchronize { @wait_cond.wait delay_gap }
124
157
  end
125
158
  end
126
159
 
@@ -129,7 +162,11 @@ module Google
129
162
  end
130
163
 
131
164
  def calc_delay
132
- (stream.subscriber.deadline - 3) * rand(0.8..0.9)
165
+ delay = (stream.subscriber.deadline - 3) * rand(0.8..0.9)
166
+ delay = [delay, max_duration_per_lease_extension].min if max_duration_per_lease_extension.positive?
167
+ delay = [delay, min_duration_per_lease_extension].max if min_duration_per_lease_extension.positive? &&
168
+ stream.exactly_once_delivery_enabled
169
+ delay
133
170
  end
134
171
  end
135
172
  end
@@ -0,0 +1,115 @@
1
+ # Copyright 2019 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 "monitor"
17
+
18
+ module Google
19
+ module Cloud
20
+ module PubSub
21
+ class Subscriber
22
+ ##
23
+ # @private The sequencer's job is simple, keep track of all the
24
+ # streams's recieved message and deliver the messages with an
25
+ # ordering_key in the order they were recieved. The sequencer ensures
26
+ # only one callback can be performed at a time per ordering_key.
27
+ class Sequencer
28
+ include MonitorMixin
29
+
30
+ ##
31
+ # @private Create an empty Subscriber::Sequencer object.
32
+ def initialize &block
33
+ raise ArgumentError if block.nil?
34
+
35
+ super() # to init MonitorMixin
36
+
37
+ @seq_hash = Hash.new { |hash, key| hash[key] = [] }
38
+ @process_callback = block
39
+ end
40
+
41
+ ##
42
+ # @private Add a ReceivedMessage to the sequencer.
43
+ def add message
44
+ # Messages without ordering_key are not managed by the sequencer
45
+ if message.ordering_key.empty?
46
+ @process_callback.call message
47
+ return
48
+ end
49
+
50
+ perform_callback = synchronize do
51
+ # The purpose of this block is to add the message to the
52
+ # sequencer, and to return whether the message should be processed
53
+ # immediately, or whether it will be processed later by #next. We
54
+ # want to ensure that these operations happen atomically.
55
+
56
+ @seq_hash[message.ordering_key].push message
57
+ @seq_hash[message.ordering_key].count == 1
58
+ end
59
+
60
+ @process_callback.call message if perform_callback
61
+ end
62
+
63
+ ##
64
+ # @private Indicate a ReceivedMessage was processed, and the next in
65
+ # the queue can now be processed.
66
+ def next message
67
+ # Messages without ordering_key are not managed by the sequencer
68
+ return if message.ordering_key.empty?
69
+
70
+ next_message = synchronize do
71
+ # The purpose of this block is to remove the message that was
72
+ # processed from the sequencer, and to return the next message to
73
+ # be processed. We want to ensure that these operations happen
74
+ # atomically.
75
+
76
+ # The message should be at index 0, so this should be a very quick
77
+ # operation.
78
+ if @seq_hash[message.ordering_key].first != message
79
+ # Raising this error will stop the other messages with this
80
+ # ordering key from being processed by the callback (delivered).
81
+ raise OrderedMessageDeliveryError, message
82
+ end
83
+
84
+ # Remove the message
85
+ @seq_hash[message.ordering_key].shift
86
+
87
+ # Retrieve the next message to be processed, or nil if empty
88
+ next_msg = @seq_hash[message.ordering_key].first
89
+
90
+ # Remove the ordering_key from hash when empty
91
+ @seq_hash.delete message.ordering_key if next_msg.nil?
92
+
93
+ # Return the next message to be processed, or nil if empty
94
+ next_msg
95
+ end
96
+
97
+ @process_callback.call next_message unless next_message.nil?
98
+ end
99
+
100
+ # @private
101
+ def to_s
102
+ "#{@seq_hash.count}/#{@seq_hash.values.sum(&:count)}"
103
+ end
104
+
105
+ # @private
106
+ def inspect
107
+ "#<#{self.class.name} (#{self})>"
108
+ end
109
+ end
110
+ end
111
+ end
112
+
113
+ Pubsub = PubSub unless const_defined? :Pubsub
114
+ end
115
+ end
@@ -13,6 +13,7 @@
13
13
  # limitations under the License.
14
14
 
15
15
 
16
+ require "google/cloud/pubsub/subscriber/sequencer"
16
17
  require "google/cloud/pubsub/subscriber/enumerator_queue"
17
18
  require "google/cloud/pubsub/subscriber/inventory"
18
19
  require "google/cloud/pubsub/service"
@@ -34,33 +35,46 @@ module Google
34
35
  attr_reader :callback_thread_pool
35
36
 
36
37
  ##
37
- # Subscriber attributes.
38
+ # @private Subscriber attributes.
38
39
  attr_reader :subscriber
39
40
 
41
+ ##
42
+ # @private Inventory.
43
+ attr_reader :inventory
44
+
45
+ ##
46
+ # @private Sequencer.
47
+ attr_reader :sequencer
48
+
49
+ ##
50
+ # @private exactly_once_delivery_enabled.
51
+ attr_reader :exactly_once_delivery_enabled
52
+
40
53
  ##
41
54
  # @private Create an empty Subscriber::Stream object.
42
55
  def initialize subscriber
56
+ super() # to init MonitorMixin
57
+
43
58
  @subscriber = subscriber
44
59
 
45
60
  @request_queue = nil
46
61
  @stopped = nil
47
62
  @paused = nil
48
63
  @pause_cond = new_cond
64
+ @exactly_once_delivery_enabled = false
65
+
66
+ @inventory = Inventory.new self, **@subscriber.stream_inventory
67
+
68
+ @sequencer = Sequencer.new(&method(:perform_callback_async)) if subscriber.message_ordering
49
69
 
50
- @inventory = Inventory.new self, @subscriber.stream_inventory
51
- @callback_thread_pool = Concurrent::ThreadPoolExecutor.new \
52
- max_threads: @subscriber.callback_threads
70
+ @callback_thread_pool = Concurrent::ThreadPoolExecutor.new max_threads: @subscriber.callback_threads
53
71
 
54
72
  @stream_keepalive_task = Concurrent::TimerTask.new(
55
73
  execution_interval: 30
56
74
  ) do
57
75
  # push empty request every 30 seconds to keep stream alive
58
- unless inventory.empty?
59
- push Google::Cloud::PubSub::V1::StreamingPullRequest.new
60
- end
76
+ push Google::Cloud::PubSub::V1::StreamingPullRequest.new unless inventory.empty?
61
77
  end.execute
62
-
63
- super() # to init MonitorMixin
64
78
  end
65
79
 
66
80
  def start
@@ -81,7 +95,7 @@ module Google
81
95
 
82
96
  # Close the stream by pushing the sentinel value.
83
97
  # The unary pusher does not use the stream, so it can close here.
84
- @request_queue.push self unless @request_queue.nil?
98
+ @request_queue&.push self
85
99
 
86
100
  # Signal to the background thread that we are stopped.
87
101
  @stopped = true
@@ -107,6 +121,10 @@ module Google
107
121
  synchronize { @paused }
108
122
  end
109
123
 
124
+ def running?
125
+ !stopped?
126
+ end
127
+
110
128
  def wait! timeout = nil
111
129
  # Wait for all queued callbacks to be processed.
112
130
  @callback_thread_pool.wait_for_termination timeout
@@ -116,13 +134,13 @@ module Google
116
134
 
117
135
  ##
118
136
  # @private
119
- def acknowledge *messages
137
+ def acknowledge *messages, &callback
120
138
  ack_ids = coerce_ack_ids messages
121
139
  return true if ack_ids.empty?
122
140
 
123
141
  synchronize do
124
142
  @inventory.remove ack_ids
125
- @subscriber.buffer.acknowledge ack_ids
143
+ @subscriber.buffer.acknowledge ack_ids, callback
126
144
  end
127
145
 
128
146
  true
@@ -130,13 +148,13 @@ module Google
130
148
 
131
149
  ##
132
150
  # @private
133
- def modify_ack_deadline deadline, *messages
151
+ def modify_ack_deadline deadline, *messages, &callback
134
152
  mod_ack_ids = coerce_ack_ids messages
135
153
  return true if mod_ack_ids.empty?
136
154
 
137
155
  synchronize do
138
156
  @inventory.remove mod_ack_ids
139
- @subscriber.buffer.modify_ack_deadline deadline, mod_ack_ids
157
+ @subscriber.buffer.modify_ack_deadline deadline, mod_ack_ids, callback
140
158
  end
141
159
 
142
160
  true
@@ -162,18 +180,14 @@ module Google
162
180
  synchronize { @request_queue.push request }
163
181
  end
164
182
 
165
- def inventory
166
- synchronize { @inventory }
167
- end
168
-
169
183
  ##
170
184
  # @private
171
185
  def renew_lease!
172
186
  synchronize do
173
187
  return true if @inventory.empty?
174
188
 
175
- @subscriber.buffer.renew_lease @subscriber.deadline,
176
- @inventory.ack_ids
189
+ @inventory.remove_expired!
190
+ @subscriber.buffer.renew_lease @subscriber.deadline, @inventory.ack_ids
177
191
  unpause_streaming!
178
192
  end
179
193
 
@@ -182,8 +196,8 @@ module Google
182
196
 
183
197
  # @private
184
198
  def to_s
185
- "(inventory: #{@inventory.count}, " \
186
- "status: #{status}, thread: #{thread_status})"
199
+ seq_str = "sequenced: #{sequencer}, " if sequencer
200
+ "(inventory: #{@inventory.count}, #{seq_str}status: #{status}, thread: #{thread_status})"
187
201
  end
188
202
 
189
203
  # @private
@@ -217,7 +231,8 @@ module Google
217
231
  end
218
232
 
219
233
  # Call the StreamingPull API to get the response enumerator
220
- enum = @subscriber.service.streaming_pull @request_queue.each
234
+ options = { :"metadata" => { :"x-goog-request-params" => @subscriber.subscription_name } }
235
+ enum = @subscriber.service.streaming_pull @request_queue.each, options
221
236
 
222
237
  loop do
223
238
  synchronize do
@@ -231,28 +246,33 @@ module Google
231
246
  break if synchronize { @stopped }
232
247
 
233
248
  begin
234
- # Cannot syncronize the enumerator, causes deadlock
249
+ # Cannot synchronize the enumerator, causes deadlock
235
250
  response = enum.next
251
+ new_exactly_once_delivery_enabled = response&.subscription_properties&.exactly_once_delivery_enabled
252
+ received_messages = response.received_messages
236
253
 
237
- # Create a list of all the received ack_id values
238
- received_ack_ids = response.received_messages.map(&:ack_id)
239
-
254
+ # Use synchronize so changes happen atomically
240
255
  synchronize do
241
- # Create receipt of received messages reception
242
- @subscriber.buffer.modify_ack_deadline @subscriber.deadline,
243
- received_ack_ids
256
+ update_min_duration_per_lease_extension new_exactly_once_delivery_enabled
257
+ @exactly_once_delivery_enabled = new_exactly_once_delivery_enabled unless new_exactly_once_delivery_enabled.nil?
258
+ @subscriber.exactly_once_delivery_enabled = @exactly_once_delivery_enabled
244
259
 
245
- # Add received messages to inventory
246
- @inventory.add received_ack_ids
260
+ # Create receipt of received messages reception
261
+ if @exactly_once_delivery_enabled
262
+ create_receipt_modack_for_eos received_messages
263
+ else
264
+ @subscriber.buffer.modify_ack_deadline @subscriber.deadline, received_messages.map(&:ack_id)
265
+ # Add received messages to inventory
266
+ @inventory.add received_messages
267
+ end
247
268
  end
248
269
 
249
- response.received_messages.each do |rec_msg_grpc|
270
+ received_messages.each do |rec_msg_grpc|
250
271
  rec_msg = ReceivedMessage.from_grpc(rec_msg_grpc, self)
251
- synchronize do
252
- # Call user provided code for received message
253
- perform_callback_async rec_msg
254
- end
255
- end
272
+ # No need to synchronize the callback future
273
+ register_callback rec_msg
274
+ end if !@exactly_once_delivery_enabled # Exactly once delivery scenario is handled by callback
275
+
256
276
  synchronize { pause_streaming! }
257
277
  rescue StopIteration
258
278
  break
@@ -267,9 +287,8 @@ module Google
267
287
  stop
268
288
  rescue GRPC::Cancelled, GRPC::DeadlineExceeded, GRPC::Internal,
269
289
  GRPC::ResourceExhausted, GRPC::Unauthenticated,
270
- GRPC::Unavailable, GRPC::Core::CallError
290
+ GRPC::Unavailable
271
291
  # Restart the stream with an incremental back for a retriable error.
272
- # Also when GRPC raises the internal CallError.
273
292
 
274
293
  retry
275
294
  rescue RestartStream
@@ -282,18 +301,56 @@ module Google
282
301
 
283
302
  # rubocop:enable all
284
303
 
304
+ def create_receipt_modack_for_eos received_messages
305
+ received_messages.each do |rec_msg_grpc|
306
+ callback = proc do |result|
307
+ if result.succeeded?
308
+ synchronize { @inventory.add rec_msg_grpc }
309
+ rec_msg = ReceivedMessage.from_grpc rec_msg_grpc, self
310
+ register_callback rec_msg
311
+ end
312
+ end
313
+ @subscriber.buffer.modify_ack_deadline @subscriber.deadline, [rec_msg_grpc.ack_id], callback
314
+ end
315
+ end
316
+
317
+ # Updates min_duration_per_lease_extension to 60 when exactly_once_delivery_enabled
318
+ # and reverts back to default 0 when disabled.
319
+ # Skips if exactly_once_enabled is not modified.
320
+ def update_min_duration_per_lease_extension new_exactly_once_delivery_enabled
321
+ return if new_exactly_once_delivery_enabled == @exactly_once_delivery_enabled
322
+ @inventory.min_duration_per_lease_extension = new_exactly_once_delivery_enabled ? 60 : 0
323
+ end
324
+
325
+ def register_callback rec_msg
326
+ if @sequencer
327
+ # Add the message to the sequencer to invoke the callback.
328
+ @sequencer.add rec_msg
329
+ else
330
+ # Call user provided code for received message
331
+ perform_callback_async rec_msg
332
+ end
333
+ end
334
+
285
335
  def perform_callback_async rec_msg
286
336
  return unless callback_thread_pool.running?
287
337
 
288
338
  Concurrent::Promises.future_on(
289
- callback_thread_pool, self, rec_msg
290
- ) do |stream, msg|
339
+ callback_thread_pool, rec_msg, &method(:perform_callback_sync)
340
+ )
341
+ end
342
+
343
+ def perform_callback_sync rec_msg
344
+ @subscriber.callback.call rec_msg unless stopped?
345
+ rescue StandardError => e
346
+ @subscriber.error! e
347
+ ensure
348
+ release rec_msg
349
+ if @sequencer && running?
291
350
  begin
292
- stream.subscriber.callback.call msg unless stream.stopped?
293
- rescue StandardError => callback_error
294
- stream.subscriber.error! callback_error
295
- ensure
296
- stream.release msg
351
+ @sequencer.next rec_msg
352
+ rescue OrderedMessageDeliveryError => e
353
+ @subscriber.error! e
297
354
  end
298
355
  end
299
356
  end
@@ -341,8 +398,10 @@ module Google
341
398
  req.subscription = @subscriber.subscription_name
342
399
  req.stream_ack_deadline_seconds = @subscriber.deadline
343
400
  req.modify_deadline_ack_ids += @inventory.ack_ids
344
- req.modify_deadline_seconds += \
345
- @inventory.ack_ids.map { @subscriber.deadline }
401
+ req.modify_deadline_seconds += @inventory.ack_ids.map { @subscriber.deadline }
402
+ 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
346
405
  end
347
406
  end
348
407