google-cloud-pubsub 0.31.1 → 0.32.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 (28) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +1 -0
  3. data/README.md +8 -8
  4. data/lib/google/cloud/pubsub/async_publisher.rb +4 -0
  5. data/lib/google/cloud/pubsub/credentials.rb +2 -14
  6. data/lib/google/cloud/pubsub/subscriber.rb +85 -0
  7. data/lib/google/cloud/pubsub/subscriber/async_stream_pusher.rb +24 -19
  8. data/lib/google/cloud/pubsub/subscriber/async_unary_pusher.rb +45 -26
  9. data/lib/google/cloud/pubsub/subscriber/inventory.rb +136 -0
  10. data/lib/google/cloud/pubsub/subscriber/stream.rb +80 -138
  11. data/lib/google/cloud/pubsub/subscription.rb +2 -2
  12. data/lib/google/cloud/pubsub/topic.rb +4 -4
  13. data/lib/google/cloud/pubsub/v1/credentials.rb +38 -0
  14. data/lib/google/cloud/pubsub/v1/doc/google/iam/v1/iam_policy.rb +62 -0
  15. data/lib/google/cloud/pubsub/v1/doc/google/iam/v1/policy.rb +127 -0
  16. data/lib/google/cloud/pubsub/v1/doc/google/protobuf/duration.rb +1 -1
  17. data/lib/google/cloud/pubsub/v1/doc/google/protobuf/empty.rb +28 -0
  18. data/lib/google/cloud/pubsub/v1/doc/google/protobuf/field_mask.rb +1 -1
  19. data/lib/google/cloud/pubsub/v1/doc/google/protobuf/timestamp.rb +1 -1
  20. data/lib/google/cloud/pubsub/v1/doc/google/pubsub/v1/pubsub.rb +113 -31
  21. data/lib/google/cloud/pubsub/v1/publisher_client.rb +180 -109
  22. data/lib/google/cloud/pubsub/v1/subscriber_client.rb +322 -193
  23. data/lib/google/cloud/pubsub/v1/subscriber_client_config.json +1 -1
  24. data/lib/google/cloud/pubsub/version.rb +1 -1
  25. data/lib/google/pubsub/v1/pubsub_pb.rb +21 -0
  26. data/lib/google/pubsub/v1/pubsub_services_pb.rb +87 -73
  27. metadata +23 -5
  28. data/lib/google/cloud/pubsub/v1/doc/overview.rb +0 -75
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3817e592071ccc0badbc1c511c848341de65d0e5fb4b9feba8c528074cd71724
4
- data.tar.gz: 9e789ffcc98b3672040ff67f5e198f6f214cde83a205d6893982ca87b5e99e2b
3
+ metadata.gz: 47a9873e0c2c11d5625c00418517bf241b344dd45edf5efabace63b042e26137
4
+ data.tar.gz: 6cd96cfb644807699b787fa4502905923ca522f2ae5efefe937d8b08b04eeeeb
5
5
  SHA512:
6
- metadata.gz: 52e115e0f6bf8968976a853064e30088374ea1836410785e3042fe00324f065e085d54e7b1502a34cd8fde8dc4395b901a95293856846f8b5b6f0af421c11586
7
- data.tar.gz: 2cf2bf6b75e926fcb0fda9ba22cae4a49a75b0fc7d408faf61ecc90fbd5a424e496369780eadee1b3fa90a448b37052933d70ee6b790dbed81ecbaed18380bae
6
+ metadata.gz: 1bd5df7f2e73e44bcd51c60f00baf3097567c9703899b6bffe5d0384a59b30f9fb33ed48b396674d221872c39831697168f5a541604ec48a0e08d08ea20d7f4a
7
+ data.tar.gz: 1cba0d5355c7565ab5c6b3787c72d55a27b31ff5555a5e9995641f8d88e291c88476c473621d090f1618cff7023604ac5616fa5106b34cca0d49f507a6b63111
data/.yardopts CHANGED
@@ -2,6 +2,7 @@
2
2
  --title=Google Cloud Pub/Sub
3
3
  --exclude lib/google/pubsub/v1
4
4
  --markup markdown
5
+ --markup-provider redcarpet
5
6
 
6
7
  ./lib/**/*.rb
7
8
  -
data/README.md CHANGED
@@ -74,14 +74,14 @@ end
74
74
 
75
75
  ## Supported Ruby Versions
76
76
 
77
- This library is supported on Ruby 2.0+.
78
-
79
- However, Ruby 2.3 or later is strongly recommended, as earlier releases have
80
- reached or are nearing end-of-life. After June 1, 2018, Google will provide
81
- official support only for Ruby versions that are considered current and
82
- supported by Ruby Core (that is, Ruby versions that are either in normal
83
- maintenance or in security maintenance).
84
- See https://www.ruby-lang.org/en/downloads/branches/ for further details.
77
+ This library is supported on Ruby 2.3+.
78
+
79
+ Google provides official support for Ruby versions that are actively supported
80
+ by Ruby Core—that is, Ruby versions that are either in normal maintenance or
81
+ in security maintenance, and not end of life. Currently, this means Ruby 2.3
82
+ and later. Older versions of Ruby _may_ still work, but are unsupported and not
83
+ recommended. See https://www.ruby-lang.org/en/downloads/branches/ for details
84
+ about the Ruby support schedule.
85
85
 
86
86
  ## Versioning
87
87
 
@@ -228,6 +228,8 @@ module Google
228
228
  end
229
229
 
230
230
  def publish_batch_async topic_name, batch
231
+ return unless @publish_thread_pool.running?
232
+
231
233
  Concurrent::Future.new(executor: @publish_thread_pool) do
232
234
  begin
233
235
  grpc = @service.publish topic_name, batch.messages
@@ -250,6 +252,8 @@ module Google
250
252
  end
251
253
 
252
254
  def execute_callback_async callback, publish_result
255
+ return unless @callback_thread_pool.running?
256
+
253
257
  Concurrent::Future.new(executor: @callback_thread_pool) do
254
258
  callback.call publish_result
255
259
  end.execute
@@ -14,6 +14,7 @@
14
14
 
15
15
 
16
16
  require "googleauth"
17
+ require "google/cloud/pubsub/v1/credentials.rb"
17
18
 
18
19
  module Google
19
20
  module Cloud
@@ -37,20 +38,7 @@ module Google
37
38
  #
38
39
  # pubsub.project_id #=> "my-project"
39
40
  #
40
- class Credentials < Google::Auth::Credentials
41
- SCOPE = ["https://www.googleapis.com/auth/pubsub"].freeze
42
- PATH_ENV_VARS = %w[PUBSUB_CREDENTIALS
43
- PUBSUB_KEYFILE
44
- GOOGLE_CLOUD_CREDENTIALS
45
- GOOGLE_CLOUD_KEYFILE
46
- GCLOUD_KEYFILE].freeze
47
- JSON_ENV_VARS = %w[PUBSUB_CREDENTIALS_JSON
48
- PUBSUB_KEYFILE_JSON
49
- GOOGLE_CLOUD_CREDENTIALS_JSON
50
- GOOGLE_CLOUD_KEYFILE_JSON
51
- GCLOUD_KEYFILE_JSON].freeze
52
- DEFAULT_PATHS = \
53
- ["~/.config/gcloud/application_default_credentials.json"].freeze
41
+ class Credentials < Google::Cloud::Pubsub::V1::Credentials
54
42
  end
55
43
  end
56
44
  end
@@ -74,6 +74,7 @@ module Google
74
74
  def initialize subscription_name, callback, deadline: nil, streams: nil,
75
75
  inventory: nil, threads: {}, service: nil
76
76
  @callback = callback
77
+ @error_callbacks = []
77
78
  @subscription_name = subscription_name
78
79
  @deadline = deadline || 60
79
80
  @streams = streams || 4
@@ -169,6 +170,90 @@ module Google
169
170
  synchronize { @stopped }
170
171
  end
171
172
 
173
+ ##
174
+ # Register to be notified of errors when raised.
175
+ #
176
+ # If an unhandled error has occurred the subscriber will attempt to
177
+ # recover from the error and resume listening.
178
+ #
179
+ # Multiple error handlers can be added.
180
+ #
181
+ # @yield [callback] The block to be called when an error is raised.
182
+ # @yieldparam [Exception] error The error raised.
183
+ #
184
+ # @example
185
+ # require "google/cloud/pubsub"
186
+ #
187
+ # pubsub = Google::Cloud::Pubsub.new
188
+ #
189
+ # sub = pubsub.subscription "my-topic-sub"
190
+ #
191
+ # subscriber = sub.listen do |received_message|
192
+ # # process message
193
+ # received_message.acknowledge!
194
+ # end
195
+ #
196
+ # # Register to be notified when unhandled errors occur.
197
+ # subscriber.on_error do |error|
198
+ # # log error
199
+ # puts error
200
+ # end
201
+ #
202
+ # # Start listening for messages and errors.
203
+ # subscriber.start
204
+ #
205
+ # # Shut down the subscriber when ready to stop receiving messages.
206
+ # subscriber.stop.wait!
207
+ #
208
+ def on_error &block
209
+ synchronize do
210
+ @error_callbacks << block
211
+ end
212
+ end
213
+
214
+ ##
215
+ # The most recent unhandled error to occur while listening to messages
216
+ # on the subscriber.
217
+ #
218
+ # If an unhandled error has occurred the subscriber will attempt to
219
+ # recover from the error and resume listening.
220
+ #
221
+ # @return [Exception, nil] error The most recent error raised.
222
+ #
223
+ # @example
224
+ # require "google/cloud/pubsub"
225
+ #
226
+ # pubsub = Google::Cloud::Pubsub.new
227
+ #
228
+ # sub = pubsub.subscription "my-topic-sub"
229
+ #
230
+ # subscriber = sub.listen do |received_message|
231
+ # # process message
232
+ # received_message.acknowledge!
233
+ # end
234
+ #
235
+ # # Start listening for messages and errors.
236
+ # subscriber.start
237
+ #
238
+ # # If an error was raised, it can be retrieved here:
239
+ # subscriber.last_error #=> nil
240
+ #
241
+ # # Shut down the subscriber when ready to stop receiving messages.
242
+ # subscriber.stop.wait!
243
+ #
244
+ def last_error
245
+ synchronize { @last_error }
246
+ end
247
+
248
+ # @private returns error object from the stream thread.
249
+ def error! error
250
+ error_callbacks = synchronize do
251
+ @last_error = error
252
+ @error_callbacks
253
+ end
254
+ error_callbacks.each { |error_callback| error_callback.call error }
255
+ end
256
+
172
257
  ##
173
258
  # @private
174
259
  def to_s
@@ -59,9 +59,6 @@ module Google
59
59
  end
60
60
  end
61
61
 
62
- @batch_created_at ||= Time.now
63
- @background_thread ||= Thread.new { run_background }
64
-
65
62
  push_batch_request! if @batch.ready?
66
63
  end
67
64
 
@@ -88,9 +85,6 @@ module Google
88
85
  end
89
86
  end
90
87
 
91
- @batch_created_at ||= Time.now
92
- @background_thread ||= Thread.new { run_background }
93
-
94
88
  push_batch_request! if @batch.ready?
95
89
  end
96
90
 
@@ -100,15 +94,25 @@ module Google
100
94
  nil
101
95
  end
102
96
 
97
+ def start
98
+ synchronize do
99
+ @stopped = false
100
+
101
+ @background_thread ||= Thread.new { run_background }
102
+ end
103
+
104
+ self
105
+ end
106
+
103
107
  def stop
104
108
  synchronize do
105
- @stopped = true
109
+ push_batch_request!
106
110
 
107
- # Stop any background activity, clean up happens in wait!
108
- @background_thread.kill if @background_thread
111
+ @stopped = true
112
+ @cond.broadcast
109
113
  end
110
114
 
111
- push_batch_request!
115
+ self
112
116
  end
113
117
 
114
118
  def started?
@@ -125,19 +129,20 @@ module Google
125
129
  synchronize do
126
130
  until @stopped
127
131
  if @batch.nil?
128
- @cond.wait
132
+ @cond.wait # wait until broadcast
129
133
  next
130
134
  end
131
135
 
132
- time_since_batch_creation = Time.now - @batch_created_at
133
- if time_since_batch_creation > @interval
136
+ time_from_batch_creation = Time.now - @batch.created_at
137
+ time_until_next_push = @interval - time_from_batch_creation
138
+
139
+ if time_until_next_push <= 0
134
140
  # interval met, publish the batch...
135
141
  push_batch_request!
136
- @cond.wait
137
- else
138
- # still waiting for the interval to publish the batch...
139
- @cond.wait(@interval - time_since_batch_creation)
142
+ time_until_next_push = nil # wait until broadcast
140
143
  end
144
+
145
+ @cond.wait time_until_next_push
141
146
  end
142
147
  end
143
148
  end
@@ -148,16 +153,16 @@ module Google
148
153
  @stream.push @batch.request
149
154
 
150
155
  @batch = nil
151
- @batch_created_at = nil
152
156
  end
153
157
 
154
158
  class Batch
155
- attr_reader :max_bytes, :request
159
+ attr_reader :max_bytes, :request, :created_at
156
160
 
157
161
  def initialize max_bytes: 10000000
158
162
  @max_bytes = max_bytes
159
163
  @request = Google::Pubsub::V1::StreamingPullRequest.new
160
164
  @total_message_bytes = 0
165
+ @created_at = Time.now
161
166
  end
162
167
 
163
168
  def ack ack_id
@@ -59,9 +59,6 @@ module Google
59
59
  end
60
60
  end
61
61
 
62
- @batch_created_at ||= Time.now
63
- @background_thread ||= Thread.new { run_background }
64
-
65
62
  push_batch_request! if @batch.ready?
66
63
  end
67
64
 
@@ -88,9 +85,6 @@ module Google
88
85
  end
89
86
  end
90
87
 
91
- @batch_created_at ||= Time.now
92
- @background_thread ||= Thread.new { run_background }
93
-
94
88
  push_batch_request! if @batch.ready?
95
89
  end
96
90
 
@@ -100,15 +94,25 @@ module Google
100
94
  nil
101
95
  end
102
96
 
97
+ def start
98
+ synchronize do
99
+ @stopped = false
100
+
101
+ @background_thread ||= Thread.new { run_background }
102
+ end
103
+
104
+ self
105
+ end
106
+
103
107
  def stop
104
108
  synchronize do
105
- @stopped = true
109
+ push_batch_request!
106
110
 
107
- # Stop any background activity, clean up happens in wait!
108
- @background_thread.kill if @background_thread
111
+ @stopped = true
112
+ @cond.broadcast
109
113
  end
110
114
 
111
- push_batch_request!
115
+ self
112
116
  end
113
117
 
114
118
  def started?
@@ -125,19 +129,20 @@ module Google
125
129
  synchronize do
126
130
  until @stopped
127
131
  if @batch.nil?
128
- @cond.wait
132
+ @cond.wait # wait until broadcast
129
133
  next
130
134
  end
131
135
 
132
- time_since_batch_creation = Time.now - @batch_created_at
133
- if time_since_batch_creation > @interval
136
+ time_from_batch_creation = Time.now - @batch.created_at
137
+ time_until_next_push = @interval - time_from_batch_creation
138
+
139
+ if time_until_next_push <= 0
134
140
  # interval met, publish the batch...
135
141
  push_batch_request!
136
- @cond.wait
137
- else
138
- # still waiting for the interval to publish the batch...
139
- @cond.wait(@interval - time_since_batch_creation)
142
+ time_until_next_push = nil # wait until broadcast
140
143
  end
144
+
145
+ @cond.wait time_until_next_push
141
146
  end
142
147
  end
143
148
  end
@@ -145,34 +150,48 @@ module Google
145
150
  def push_batch_request!
146
151
  return unless @batch
147
152
 
153
+ sub_name = @stream.subscriber.subscription_name
148
154
  service = @stream.subscriber.service
149
- name = @stream.subscriber.subscription_name
150
155
 
151
156
  if @batch.ack?
152
157
  ack_ids = @batch.ack_ids
153
- Concurrent::Future.new(executor: @stream.push_thread_pool) do
154
- service.acknowledge name, *ack_ids
155
- end.execute
158
+ push_change_request do
159
+ service.acknowledge sub_name, *ack_ids
160
+ end
156
161
  end
157
162
  if @batch.delay?
158
163
  @batch.modify_deadline_hash.each do |delay_seconds, delay_ack_ids|
159
- Concurrent::Future.new(executor: @stream.push_thread_pool) do
160
- service.modify_ack_deadline name, delay_ack_ids, delay_seconds
161
- end.execute
164
+ push_change_request do
165
+ service.modify_ack_deadline sub_name,
166
+ delay_ack_ids,
167
+ delay_seconds
168
+ end
162
169
  end
163
170
  end
164
171
 
165
172
  @batch = nil
166
- @batch_created_at = nil
173
+ end
174
+
175
+ def push_change_request
176
+ return unless @stream.push_thread_pool.running?
177
+
178
+ Concurrent::Future.new(executor: @stream.push_thread_pool) do
179
+ begin
180
+ yield
181
+ rescue StandardError => push_error
182
+ @stream.subscriber.error! push_error
183
+ end
184
+ end.execute
167
185
  end
168
186
 
169
187
  class Batch
170
- attr_reader :max_bytes, :request
188
+ attr_reader :max_bytes, :request, :created_at
171
189
 
172
190
  def initialize max_bytes: 10000000
173
191
  @max_bytes = max_bytes
174
192
  @request = Google::Pubsub::V1::StreamingPullRequest.new
175
193
  @total_message_bytes = 0
194
+ @created_at = Time.now
176
195
  end
177
196
 
178
197
  def ack ack_id
@@ -0,0 +1,136 @@
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 "monitor"
17
+
18
+ module Google
19
+ module Cloud
20
+ module Pubsub
21
+ class Subscriber
22
+ ##
23
+ # @private
24
+ class Inventory
25
+ include MonitorMixin
26
+
27
+ attr_reader :stream, :limit
28
+
29
+ def initialize stream, limit
30
+ @stream = stream
31
+ @limit = limit
32
+ @_ack_ids = []
33
+ @wait_cond = new_cond
34
+
35
+ super()
36
+ end
37
+
38
+ def ack_ids
39
+ @_ack_ids
40
+ end
41
+
42
+ def add *ack_ids
43
+ ack_ids.flatten!.compact!
44
+ return if ack_ids.empty?
45
+
46
+ synchronize do
47
+ @_ack_ids += ack_ids
48
+ @wait_cond.broadcast
49
+ end
50
+ end
51
+
52
+ def remove *ack_ids
53
+ ack_ids.flatten!.compact!
54
+ return if ack_ids.empty?
55
+
56
+ synchronize do
57
+ @_ack_ids -= ack_ids
58
+ @wait_cond.broadcast
59
+ end
60
+ end
61
+
62
+ def count
63
+ synchronize do
64
+ @_ack_ids.count
65
+ end
66
+ end
67
+
68
+ def empty?
69
+ synchronize do
70
+ @_ack_ids.empty?
71
+ end
72
+ end
73
+
74
+ def start
75
+ @background_thread ||= Thread.new { background_run }
76
+
77
+ self
78
+ end
79
+
80
+ def stop
81
+ synchronize do
82
+ @stopped = true
83
+ @wait_cond.broadcast
84
+ end
85
+
86
+ self
87
+ end
88
+
89
+ def stopped?
90
+ synchronize { @stopped }
91
+ end
92
+
93
+ def full?
94
+ count >= limit
95
+ end
96
+
97
+ protected
98
+
99
+ def background_run
100
+ delay_target = nil
101
+
102
+ synchronize do
103
+ until @stopped
104
+ if @_ack_ids.empty?
105
+ delay_target = nil
106
+
107
+ @wait_cond.wait # wait until broadcast
108
+ next
109
+ end
110
+
111
+ delay_target ||= calc_target
112
+ delay_gap = delay_target - Time.now
113
+
114
+ unless delay_gap.positive?
115
+ delay_target = nil
116
+ delay_gap = nil # wait until broadcast
117
+ stream.delay_inventory!
118
+ end
119
+
120
+ @wait_cond.wait delay_gap
121
+ end
122
+ end
123
+ end
124
+
125
+ def calc_target
126
+ Time.now + calc_delay
127
+ end
128
+
129
+ def calc_delay
130
+ (stream.subscriber.deadline - 3) * rand(0.8..0.9)
131
+ end
132
+ end
133
+ end
134
+ end
135
+ end
136
+ end