google-cloud-pubsub 1.9.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.
- checksums.yaml +7 -0
- data/.yardopts +18 -0
- data/AUTHENTICATION.md +177 -0
- data/CHANGELOG.md +538 -0
- data/CODE_OF_CONDUCT.md +40 -0
- data/CONTRIBUTING.md +188 -0
- data/EMULATOR.md +37 -0
- data/LICENSE +201 -0
- data/LOGGING.md +32 -0
- data/OVERVIEW.md +557 -0
- data/TROUBLESHOOTING.md +31 -0
- data/lib/google-cloud-pubsub.rb +139 -0
- data/lib/google/cloud/pubsub.rb +173 -0
- data/lib/google/cloud/pubsub/async_publisher.rb +399 -0
- data/lib/google/cloud/pubsub/async_publisher/batch.rb +309 -0
- data/lib/google/cloud/pubsub/batch_publisher.rb +99 -0
- data/lib/google/cloud/pubsub/convert.rb +91 -0
- data/lib/google/cloud/pubsub/credentials.rb +47 -0
- data/lib/google/cloud/pubsub/errors.rb +85 -0
- data/lib/google/cloud/pubsub/message.rb +158 -0
- data/lib/google/cloud/pubsub/policy.rb +187 -0
- data/lib/google/cloud/pubsub/project.rb +393 -0
- data/lib/google/cloud/pubsub/publish_result.rb +103 -0
- data/lib/google/cloud/pubsub/received_message.rb +297 -0
- data/lib/google/cloud/pubsub/retry_policy.rb +90 -0
- data/lib/google/cloud/pubsub/service.rb +514 -0
- data/lib/google/cloud/pubsub/snapshot.rb +202 -0
- data/lib/google/cloud/pubsub/snapshot/list.rb +178 -0
- data/lib/google/cloud/pubsub/subscriber.rb +399 -0
- data/lib/google/cloud/pubsub/subscriber/enumerator_queue.rb +54 -0
- data/lib/google/cloud/pubsub/subscriber/inventory.rb +166 -0
- data/lib/google/cloud/pubsub/subscriber/sequencer.rb +115 -0
- data/lib/google/cloud/pubsub/subscriber/stream.rb +401 -0
- data/lib/google/cloud/pubsub/subscriber/timed_unary_buffer.rb +231 -0
- data/lib/google/cloud/pubsub/subscription.rb +1279 -0
- data/lib/google/cloud/pubsub/subscription/list.rb +205 -0
- data/lib/google/cloud/pubsub/subscription/push_config.rb +244 -0
- data/lib/google/cloud/pubsub/topic.rb +934 -0
- data/lib/google/cloud/pubsub/topic/list.rb +171 -0
- data/lib/google/cloud/pubsub/v1.rb +17 -0
- data/lib/google/cloud/pubsub/v1/credentials.rb +41 -0
- data/lib/google/cloud/pubsub/v1/doc/google/iam/v1/iam_policy.rb +21 -0
- data/lib/google/cloud/pubsub/v1/doc/google/iam/v1/options.rb +21 -0
- data/lib/google/cloud/pubsub/v1/doc/google/iam/v1/policy.rb +21 -0
- data/lib/google/cloud/pubsub/v1/doc/google/protobuf/duration.rb +91 -0
- data/lib/google/cloud/pubsub/v1/doc/google/protobuf/empty.rb +29 -0
- data/lib/google/cloud/pubsub/v1/doc/google/protobuf/field_mask.rb +222 -0
- data/lib/google/cloud/pubsub/v1/doc/google/protobuf/timestamp.rb +113 -0
- data/lib/google/cloud/pubsub/v1/doc/google/pubsub/v1/pubsub.rb +833 -0
- data/lib/google/cloud/pubsub/v1/doc/google/type/expr.rb +19 -0
- data/lib/google/cloud/pubsub/v1/publisher_client.rb +928 -0
- data/lib/google/cloud/pubsub/v1/publisher_client_config.json +120 -0
- data/lib/google/cloud/pubsub/v1/subscriber_client.rb +1466 -0
- data/lib/google/cloud/pubsub/v1/subscriber_client_config.json +153 -0
- data/lib/google/cloud/pubsub/version.rb +24 -0
- data/lib/google/pubsub/v1/pubsub_pb.rb +269 -0
- data/lib/google/pubsub/v1/pubsub_services_pb.rb +215 -0
- metadata +337 -0
@@ -0,0 +1,202 @@
|
|
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/errors"
|
17
|
+
require "google/cloud/pubsub/snapshot/list"
|
18
|
+
|
19
|
+
module Google
|
20
|
+
module Cloud
|
21
|
+
module PubSub
|
22
|
+
##
|
23
|
+
# # Snapshot
|
24
|
+
#
|
25
|
+
# A named resource created from a subscription to retain a stream of
|
26
|
+
# messages from a topic. A snapshot is guaranteed to retain:
|
27
|
+
#
|
28
|
+
# * The existing backlog on the subscription. More precisely, this is
|
29
|
+
# defined as the messages in the subscription's backlog that are
|
30
|
+
# unacknowledged upon the successful completion of the
|
31
|
+
# `create_snapshot` operation; as well as:
|
32
|
+
# * Any messages published to the subscription's topic following the
|
33
|
+
# successful completion of the `create_snapshot` operation.
|
34
|
+
#
|
35
|
+
# @example
|
36
|
+
# require "google/cloud/pubsub"
|
37
|
+
#
|
38
|
+
# pubsub = Google::Cloud::PubSub.new
|
39
|
+
# sub = pubsub.subscription "my-sub"
|
40
|
+
#
|
41
|
+
# snapshot = sub.create_snapshot "my-snapshot"
|
42
|
+
# snapshot.name #=> "projects/my-project/snapshots/my-snapshot"
|
43
|
+
#
|
44
|
+
class Snapshot
|
45
|
+
##
|
46
|
+
# @private The Service object.
|
47
|
+
attr_accessor :service
|
48
|
+
|
49
|
+
##
|
50
|
+
# @private The gRPC Google::Cloud::PubSub::V1::Snapshot object.
|
51
|
+
attr_accessor :grpc
|
52
|
+
|
53
|
+
##
|
54
|
+
# @private Create an empty {Snapshot} object.
|
55
|
+
def initialize
|
56
|
+
@service = nil
|
57
|
+
@grpc = Google::Cloud::PubSub::V1::Snapshot.new
|
58
|
+
end
|
59
|
+
|
60
|
+
##
|
61
|
+
# The name of the snapshot. Format is
|
62
|
+
# `projects/{project}/snapshots/{snap}`.
|
63
|
+
def name
|
64
|
+
@grpc.name
|
65
|
+
end
|
66
|
+
|
67
|
+
##
|
68
|
+
# The {Topic} from which this snapshot is retaining messages.
|
69
|
+
#
|
70
|
+
# @return [Topic]
|
71
|
+
#
|
72
|
+
# @example
|
73
|
+
# require "google/cloud/pubsub"
|
74
|
+
#
|
75
|
+
# pubsub = Google::Cloud::PubSub.new
|
76
|
+
# sub = pubsub.subscription "my-sub"
|
77
|
+
#
|
78
|
+
# snapshot = sub.create_snapshot "my-snapshot"
|
79
|
+
# snapshot.topic.name #=> "projects/my-project/topics/my-topic"
|
80
|
+
#
|
81
|
+
def topic
|
82
|
+
Topic.from_name @grpc.topic, service
|
83
|
+
end
|
84
|
+
|
85
|
+
##
|
86
|
+
# The snapshot is guaranteed to exist up until this time.
|
87
|
+
# A newly-created snapshot expires no later than 7 days from the time of
|
88
|
+
# its creation. Its exact lifetime is determined at creation by the
|
89
|
+
# existing backlog in the source subscription. Specifically, the
|
90
|
+
# lifetime of the snapshot is 7 days - (age of oldest unacked message in
|
91
|
+
# the subscription). For example, consider a subscription whose oldest
|
92
|
+
# unacked message is 3 days old. If a snapshot is created from this
|
93
|
+
# subscription, the snapshot -- which will always capture this 3-day-old
|
94
|
+
# backlog as long as the snapshot exists -- will expire in 4 days.
|
95
|
+
#
|
96
|
+
# @return [Time] The time until which the snapshot is guaranteed to
|
97
|
+
# exist.
|
98
|
+
#
|
99
|
+
# @example
|
100
|
+
# require "google/cloud/pubsub"
|
101
|
+
#
|
102
|
+
# pubsub = Google::Cloud::PubSub.new
|
103
|
+
# sub = pubsub.subscription "my-sub"
|
104
|
+
#
|
105
|
+
# snapshot = sub.create_snapshot "my-snapshot"
|
106
|
+
# snapshot.topic.name #=> "projects/my-project/topics/my-topic"
|
107
|
+
# snapshot.expiration_time
|
108
|
+
#
|
109
|
+
def expiration_time
|
110
|
+
self.class.timestamp_from_grpc @grpc.expire_time
|
111
|
+
end
|
112
|
+
|
113
|
+
##
|
114
|
+
# A hash of user-provided labels associated with this snapshot.
|
115
|
+
# Labels can be used to organize and group snapshots.See [Creating and
|
116
|
+
# Managing Labels](https://cloud.google.com/pubsub/docs/labels).
|
117
|
+
#
|
118
|
+
# The returned hash is frozen and changes are not allowed. Use
|
119
|
+
# {#labels=} to update the labels for this snapshot.
|
120
|
+
#
|
121
|
+
# @return [Hash] The frozen labels hash.
|
122
|
+
#
|
123
|
+
def labels
|
124
|
+
@grpc.labels.to_h.freeze
|
125
|
+
end
|
126
|
+
|
127
|
+
##
|
128
|
+
# Sets the hash of user-provided labels associated with this
|
129
|
+
# snapshot. Labels can be used to organize and group snapshots.
|
130
|
+
# Label keys and values can be no longer than 63 characters, can only
|
131
|
+
# contain lowercase letters, numeric characters, underscores and dashes.
|
132
|
+
# International characters are allowed. Label values are optional. Label
|
133
|
+
# keys must start with a letter and each label in the list must have a
|
134
|
+
# different key. See [Creating and Managing
|
135
|
+
# Labels](https://cloud.google.com/pubsub/docs/labels).
|
136
|
+
#
|
137
|
+
# @param [Hash] new_labels The new labels hash.
|
138
|
+
#
|
139
|
+
def labels= new_labels
|
140
|
+
raise ArgumentError, "Value must be a Hash" if new_labels.nil?
|
141
|
+
labels_map = Google::Protobuf::Map.new :string, :string
|
142
|
+
Hash(new_labels).each { |k, v| labels_map[String(k)] = String(v) }
|
143
|
+
update_grpc = @grpc.dup
|
144
|
+
update_grpc.labels = labels_map
|
145
|
+
@grpc = service.update_snapshot update_grpc, :labels
|
146
|
+
end
|
147
|
+
|
148
|
+
##
|
149
|
+
# Removes an existing snapshot. All messages retained in the snapshot
|
150
|
+
# are immediately dropped. After a snapshot is deleted, a new one may be
|
151
|
+
# created with the same name, but the new one has no association with
|
152
|
+
# the old snapshot or its subscription, unless the same subscription is
|
153
|
+
# specified.
|
154
|
+
#
|
155
|
+
# @return [Boolean] Returns `true` if the snapshot was deleted.
|
156
|
+
#
|
157
|
+
# @example
|
158
|
+
# require "google/cloud/pubsub"
|
159
|
+
#
|
160
|
+
# pubsub = Google::Cloud::PubSub.new
|
161
|
+
#
|
162
|
+
# pubsub.snapshots.each do |snapshot|
|
163
|
+
# snapshot.delete
|
164
|
+
# end
|
165
|
+
#
|
166
|
+
def delete
|
167
|
+
ensure_service!
|
168
|
+
service.delete_snapshot name
|
169
|
+
true
|
170
|
+
end
|
171
|
+
|
172
|
+
##
|
173
|
+
# @private New Snapshot from a Google::Cloud::PubSub::V1::Snapshot
|
174
|
+
# object.
|
175
|
+
def self.from_grpc grpc, service
|
176
|
+
new.tap do |f|
|
177
|
+
f.grpc = grpc
|
178
|
+
f.service = service
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
##
|
183
|
+
# @private Get a Time object from a Google::Protobuf::Timestamp object.
|
184
|
+
def self.timestamp_from_grpc grpc_timestamp
|
185
|
+
return nil if grpc_timestamp.nil?
|
186
|
+
Time.at grpc_timestamp.seconds, Rational(grpc_timestamp.nanos, 1000)
|
187
|
+
end
|
188
|
+
|
189
|
+
protected
|
190
|
+
|
191
|
+
##
|
192
|
+
# @private Raise an error unless an active connection to the service is
|
193
|
+
# available.
|
194
|
+
def ensure_service!
|
195
|
+
raise "Must have active connection to service" unless service
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
Pubsub = PubSub unless const_defined? :Pubsub
|
201
|
+
end
|
202
|
+
end
|
@@ -0,0 +1,178 @@
|
|
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 "delegate"
|
17
|
+
|
18
|
+
module Google
|
19
|
+
module Cloud
|
20
|
+
module PubSub
|
21
|
+
class Snapshot
|
22
|
+
##
|
23
|
+
# Snapshot::List is a special case Array with additional values.
|
24
|
+
class List < DelegateClass(::Array)
|
25
|
+
##
|
26
|
+
# If not empty, indicates that there are more snapshots
|
27
|
+
# that match the request and this value should be passed to
|
28
|
+
# the next {Google::Cloud::PubSub::Project#snapshots} to continue.
|
29
|
+
attr_accessor :token
|
30
|
+
|
31
|
+
##
|
32
|
+
# @private Create a new Snapshot::List with an array of values.
|
33
|
+
def initialize arr = []
|
34
|
+
@prefix = nil
|
35
|
+
@token = nil
|
36
|
+
@max = nil
|
37
|
+
super arr
|
38
|
+
end
|
39
|
+
|
40
|
+
##
|
41
|
+
# Whether there a next page of snapshots.
|
42
|
+
#
|
43
|
+
# @return [Boolean]
|
44
|
+
#
|
45
|
+
# @example
|
46
|
+
# require "google/cloud/pubsub"
|
47
|
+
#
|
48
|
+
# pubsub = Google::Cloud::PubSub.new
|
49
|
+
#
|
50
|
+
# snapshots = pubsub.snapshots
|
51
|
+
# if snapshots.next?
|
52
|
+
# next_snapshots = snapshots.next
|
53
|
+
# end
|
54
|
+
#
|
55
|
+
def next?
|
56
|
+
!token.nil?
|
57
|
+
end
|
58
|
+
|
59
|
+
##
|
60
|
+
# Retrieve the next page of snapshots.
|
61
|
+
#
|
62
|
+
# @return [Snapshot::List]
|
63
|
+
#
|
64
|
+
# @example
|
65
|
+
# require "google/cloud/pubsub"
|
66
|
+
#
|
67
|
+
# pubsub = Google::Cloud::PubSub.new
|
68
|
+
#
|
69
|
+
# snapshots = pubsub.snapshots
|
70
|
+
# if snapshots.next?
|
71
|
+
# next_snapshots = snapshots.next
|
72
|
+
# end
|
73
|
+
#
|
74
|
+
def next
|
75
|
+
return nil unless next?
|
76
|
+
ensure_service!
|
77
|
+
next_snapshots
|
78
|
+
end
|
79
|
+
|
80
|
+
##
|
81
|
+
# Retrieves remaining results by repeatedly invoking {#next} until
|
82
|
+
# {#next?} returns `false`. Calls the given block once for each
|
83
|
+
# result, which is passed as the argument to the block.
|
84
|
+
#
|
85
|
+
# An Enumerator is returned if no block is given.
|
86
|
+
#
|
87
|
+
# This method will make repeated API calls until all remaining results
|
88
|
+
# are retrieved. (Unlike `#each`, for example, which merely iterates
|
89
|
+
# over the results returned by a single API call.) Use with caution.
|
90
|
+
#
|
91
|
+
# @param [Integer] request_limit The upper limit of API requests to
|
92
|
+
# make to load all snapshots. Default is no limit.
|
93
|
+
# @yield [snapshot] The block for accessing each snapshot.
|
94
|
+
# @yieldparam [Snapshot] snapshot The snapshot object.
|
95
|
+
#
|
96
|
+
# @return [Enumerator]
|
97
|
+
#
|
98
|
+
# @example Iterating each snapshot by passing a block:
|
99
|
+
# require "google/cloud/pubsub"
|
100
|
+
#
|
101
|
+
# pubsub = Google::Cloud::PubSub.new
|
102
|
+
#
|
103
|
+
# snapshots = pubsub.snapshots
|
104
|
+
# snapshots.all do |snapshot|
|
105
|
+
# puts snapshot.name
|
106
|
+
# end
|
107
|
+
#
|
108
|
+
# @example Using the enumerator by not passing a block:
|
109
|
+
# require "google/cloud/pubsub"
|
110
|
+
#
|
111
|
+
# pubsub = Google::Cloud::PubSub.new
|
112
|
+
#
|
113
|
+
# snapshots = pubsub.snapshots
|
114
|
+
# all_names = snapshots.all.map do |snapshot|
|
115
|
+
# snapshot.name
|
116
|
+
# end
|
117
|
+
#
|
118
|
+
# @example Limit the number of API calls made:
|
119
|
+
# require "google/cloud/pubsub"
|
120
|
+
#
|
121
|
+
# pubsub = Google::Cloud::PubSub.new
|
122
|
+
#
|
123
|
+
# snapshots = pubsub.snapshots
|
124
|
+
# snapshots.all(request_limit: 10) do |snapshot|
|
125
|
+
# puts snapshot.name
|
126
|
+
# end
|
127
|
+
#
|
128
|
+
def all request_limit: nil
|
129
|
+
request_limit = request_limit.to_i if request_limit
|
130
|
+
return enum_for :all, request_limit: request_limit unless block_given?
|
131
|
+
results = self
|
132
|
+
loop do
|
133
|
+
results.each { |r| yield r }
|
134
|
+
if request_limit
|
135
|
+
request_limit -= 1
|
136
|
+
break if request_limit.negative?
|
137
|
+
end
|
138
|
+
break unless results.next?
|
139
|
+
results = results.next
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
##
|
144
|
+
# @private New Snapshots::List from a
|
145
|
+
# Google::Cloud::PubSub::V1::ListSnapshotsRequest object.
|
146
|
+
def self.from_grpc grpc_list, service, max = nil
|
147
|
+
subs = new(Array(grpc_list.snapshots).map do |grpc|
|
148
|
+
Snapshot.from_grpc grpc, service
|
149
|
+
end)
|
150
|
+
token = grpc_list.next_page_token
|
151
|
+
token = nil if token == "".freeze
|
152
|
+
subs.instance_variable_set :@token, token
|
153
|
+
subs.instance_variable_set :@service, service
|
154
|
+
subs.instance_variable_set :@max, max
|
155
|
+
subs
|
156
|
+
end
|
157
|
+
|
158
|
+
protected
|
159
|
+
|
160
|
+
##
|
161
|
+
# @private Raise an error unless an active connection to the service
|
162
|
+
# is available.
|
163
|
+
def ensure_service!
|
164
|
+
raise "Must have active connection to service" unless @service
|
165
|
+
end
|
166
|
+
|
167
|
+
def next_snapshots
|
168
|
+
options = { prefix: @prefix, token: @token, max: @max }
|
169
|
+
grpc = @service.list_snapshots options
|
170
|
+
self.class.from_grpc grpc, @service, @max
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
Pubsub = PubSub unless const_defined? :Pubsub
|
177
|
+
end
|
178
|
+
end
|
@@ -0,0 +1,399 @@
|
|
1
|
+
# Copyright 2017 Google LLC
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# https://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
|
16
|
+
require "google/cloud/pubsub/service"
|
17
|
+
require "google/cloud/pubsub/subscriber/stream"
|
18
|
+
require "google/cloud/pubsub/subscriber/timed_unary_buffer"
|
19
|
+
require "monitor"
|
20
|
+
|
21
|
+
module Google
|
22
|
+
module Cloud
|
23
|
+
module PubSub
|
24
|
+
##
|
25
|
+
# Subscriber object used to stream and process messages from a
|
26
|
+
# Subscription. See {Google::Cloud::PubSub::Subscription#listen}
|
27
|
+
#
|
28
|
+
# @example
|
29
|
+
# require "google/cloud/pubsub"
|
30
|
+
#
|
31
|
+
# pubsub = Google::Cloud::PubSub.new
|
32
|
+
#
|
33
|
+
# sub = pubsub.subscription "my-topic-sub"
|
34
|
+
#
|
35
|
+
# subscriber = sub.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
|
+
# subscriber.start
|
42
|
+
#
|
43
|
+
# # Shut down the subscriber when ready to stop receiving messages.
|
44
|
+
# subscriber.stop.wait!
|
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 4.
|
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 Subscriber
|
65
|
+
include MonitorMixin
|
66
|
+
|
67
|
+
attr_reader :subscription_name, :callback, :deadline, :streams, :message_ordering, :callback_threads,
|
68
|
+
:push_threads
|
69
|
+
|
70
|
+
##
|
71
|
+
# @private Implementation attributes.
|
72
|
+
attr_reader :stream_pool, :thread_pool, :buffer, :service
|
73
|
+
|
74
|
+
##
|
75
|
+
# @private Create an empty {Subscriber} object.
|
76
|
+
def initialize subscription_name, callback, deadline: nil, message_ordering: nil, streams: nil, inventory: nil,
|
77
|
+
threads: {}, service: nil
|
78
|
+
super() # to init MonitorMixin
|
79
|
+
|
80
|
+
@callback = callback
|
81
|
+
@error_callbacks = []
|
82
|
+
@subscription_name = subscription_name
|
83
|
+
@deadline = deadline || 60
|
84
|
+
@streams = streams || 2
|
85
|
+
coerce_inventory inventory
|
86
|
+
@message_ordering = message_ordering
|
87
|
+
@callback_threads = Integer(threads[:callback] || 8)
|
88
|
+
@push_threads = Integer(threads[:push] || 4)
|
89
|
+
|
90
|
+
@service = service
|
91
|
+
|
92
|
+
@started = @stopped = nil
|
93
|
+
|
94
|
+
stream_pool = Array.new @streams do
|
95
|
+
Thread.new { Stream.new self }
|
96
|
+
end
|
97
|
+
@stream_pool = stream_pool.map(&:value)
|
98
|
+
|
99
|
+
@buffer = TimedUnaryBuffer.new self
|
100
|
+
end
|
101
|
+
|
102
|
+
##
|
103
|
+
# Starts the subscriber pulling from the subscription and processing the
|
104
|
+
# received messages.
|
105
|
+
#
|
106
|
+
# @return [Subscriber] returns self so calls can be chained.
|
107
|
+
#
|
108
|
+
def start
|
109
|
+
start_pool = synchronize do
|
110
|
+
@started = true
|
111
|
+
@stopped = false
|
112
|
+
|
113
|
+
# Start the buffer before the streams are all started
|
114
|
+
@buffer.start
|
115
|
+
@stream_pool.map do |stream|
|
116
|
+
Thread.new { stream.start }
|
117
|
+
end
|
118
|
+
end
|
119
|
+
start_pool.map(&:join)
|
120
|
+
|
121
|
+
self
|
122
|
+
end
|
123
|
+
|
124
|
+
##
|
125
|
+
# Immediately stops the subscriber. No new messages will be pulled from
|
126
|
+
# the subscription. All actions taken on received messages that have not
|
127
|
+
# yet been sent to the API will be sent to the API. All received but
|
128
|
+
# unprocessed messages will be released back to the API and redelivered.
|
129
|
+
# Use {#wait!} to block until the subscriber is fully stopped and all
|
130
|
+
# received messages have been processed or released.
|
131
|
+
#
|
132
|
+
# @return [Subscriber] returns self so calls can be chained.
|
133
|
+
#
|
134
|
+
def stop
|
135
|
+
stop_pool = synchronize do
|
136
|
+
@started = false
|
137
|
+
@stopped = true
|
138
|
+
|
139
|
+
@stream_pool.map do |stream|
|
140
|
+
Thread.new { stream.stop }
|
141
|
+
end
|
142
|
+
end
|
143
|
+
stop_pool.map(&:join)
|
144
|
+
# Stop the buffer after the streams are all stopped
|
145
|
+
synchronize { @buffer.stop }
|
146
|
+
|
147
|
+
self
|
148
|
+
end
|
149
|
+
|
150
|
+
##
|
151
|
+
# Blocks until the subscriber is fully stopped and all received messages
|
152
|
+
# have been processed or released, or until `timeout` seconds have
|
153
|
+
# passed.
|
154
|
+
#
|
155
|
+
# Does not stop the subscriber. To stop the subscriber, first call
|
156
|
+
# {#stop} and then call {#wait!} to block until the subscriber is
|
157
|
+
# stopped.
|
158
|
+
#
|
159
|
+
# @param [Number, nil] timeout The number of seconds to block until the
|
160
|
+
# subscriber is fully stopped. Default will block indefinitely.
|
161
|
+
#
|
162
|
+
# @return [Subscriber] returns self so calls can be chained.
|
163
|
+
#
|
164
|
+
def wait! timeout = nil
|
165
|
+
wait_pool = synchronize do
|
166
|
+
@stream_pool.map do |stream|
|
167
|
+
Thread.new { stream.wait! timeout }
|
168
|
+
end
|
169
|
+
end
|
170
|
+
wait_pool.map(&:join)
|
171
|
+
|
172
|
+
self
|
173
|
+
end
|
174
|
+
|
175
|
+
##
|
176
|
+
# Stop this subscriber and block until the subscriber is fully stopped
|
177
|
+
# and all received messages have been processed or released, or until
|
178
|
+
# `timeout` seconds have passed.
|
179
|
+
#
|
180
|
+
# The same as calling {#stop} and {#wait!}.
|
181
|
+
#
|
182
|
+
# @param [Number, nil] timeout The number of seconds to block until the
|
183
|
+
# subscriber is fully stopped. Default will block indefinitely.
|
184
|
+
#
|
185
|
+
# @return [Subscriber] returns self so calls can be chained.
|
186
|
+
#
|
187
|
+
def stop! timeout = nil
|
188
|
+
stop
|
189
|
+
wait! timeout
|
190
|
+
end
|
191
|
+
|
192
|
+
##
|
193
|
+
# Whether the subscriber has been started.
|
194
|
+
#
|
195
|
+
# @return [boolean] `true` when started, `false` otherwise.
|
196
|
+
#
|
197
|
+
def started?
|
198
|
+
synchronize { @started }
|
199
|
+
end
|
200
|
+
|
201
|
+
##
|
202
|
+
# Whether the subscriber has been stopped.
|
203
|
+
#
|
204
|
+
# @return [boolean] `true` when stopped, `false` otherwise.
|
205
|
+
#
|
206
|
+
def stopped?
|
207
|
+
synchronize { @stopped }
|
208
|
+
end
|
209
|
+
|
210
|
+
##
|
211
|
+
# Register to be notified of errors when raised.
|
212
|
+
#
|
213
|
+
# If an unhandled error has occurred the subscriber will attempt to
|
214
|
+
# recover from the error and resume listening.
|
215
|
+
#
|
216
|
+
# Multiple error handlers can be added.
|
217
|
+
#
|
218
|
+
# @yield [callback] The block to be called when an error is raised.
|
219
|
+
# @yieldparam [Exception] error The error raised.
|
220
|
+
#
|
221
|
+
# @example
|
222
|
+
# require "google/cloud/pubsub"
|
223
|
+
#
|
224
|
+
# pubsub = Google::Cloud::PubSub.new
|
225
|
+
#
|
226
|
+
# sub = pubsub.subscription "my-topic-sub"
|
227
|
+
#
|
228
|
+
# subscriber = sub.listen do |received_message|
|
229
|
+
# # process message
|
230
|
+
# received_message.acknowledge!
|
231
|
+
# end
|
232
|
+
#
|
233
|
+
# # Register to be notified when unhandled errors occur.
|
234
|
+
# subscriber.on_error do |error|
|
235
|
+
# # log error
|
236
|
+
# puts error
|
237
|
+
# end
|
238
|
+
#
|
239
|
+
# # Start listening for messages and errors.
|
240
|
+
# subscriber.start
|
241
|
+
#
|
242
|
+
# # Shut down the subscriber when ready to stop receiving messages.
|
243
|
+
# subscriber.stop.wait!
|
244
|
+
#
|
245
|
+
def on_error &block
|
246
|
+
synchronize do
|
247
|
+
@error_callbacks << block
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
##
|
252
|
+
# The most recent unhandled error to occur while listening to messages
|
253
|
+
# on the subscriber.
|
254
|
+
#
|
255
|
+
# If an unhandled error has occurred the subscriber will attempt to
|
256
|
+
# recover from the error and resume listening.
|
257
|
+
#
|
258
|
+
# @return [Exception, nil] error The most recent error raised.
|
259
|
+
#
|
260
|
+
# @example
|
261
|
+
# require "google/cloud/pubsub"
|
262
|
+
#
|
263
|
+
# pubsub = Google::Cloud::PubSub.new
|
264
|
+
#
|
265
|
+
# sub = pubsub.subscription "my-topic-sub"
|
266
|
+
#
|
267
|
+
# subscriber = sub.listen do |received_message|
|
268
|
+
# # process message
|
269
|
+
# received_message.acknowledge!
|
270
|
+
# end
|
271
|
+
#
|
272
|
+
# # Start listening for messages and errors.
|
273
|
+
# subscriber.start
|
274
|
+
#
|
275
|
+
# # If an error was raised, it can be retrieved here:
|
276
|
+
# subscriber.last_error #=> nil
|
277
|
+
#
|
278
|
+
# # Shut down the subscriber when ready to stop receiving messages.
|
279
|
+
# subscriber.stop.wait!
|
280
|
+
#
|
281
|
+
def last_error
|
282
|
+
synchronize { @last_error }
|
283
|
+
end
|
284
|
+
|
285
|
+
##
|
286
|
+
# The number of received messages to be collected by subscriber. Default is 1,000.
|
287
|
+
#
|
288
|
+
# @return [Integer] The maximum number of messages.
|
289
|
+
#
|
290
|
+
def max_outstanding_messages
|
291
|
+
@inventory[:max_outstanding_messages]
|
292
|
+
end
|
293
|
+
# @deprecated Use {#max_outstanding_messages}.
|
294
|
+
alias inventory_limit max_outstanding_messages
|
295
|
+
# @deprecated Use {#max_outstanding_messages}.
|
296
|
+
alias inventory max_outstanding_messages
|
297
|
+
|
298
|
+
##
|
299
|
+
# The total byte size of received messages to be collected by subscriber. Default is 100,000,000 (100MB).
|
300
|
+
#
|
301
|
+
# @return [Integer] The maximum number of bytes.
|
302
|
+
#
|
303
|
+
def max_outstanding_bytes
|
304
|
+
@inventory[:max_outstanding_bytes]
|
305
|
+
end
|
306
|
+
# @deprecated Use {#max_outstanding_bytes}.
|
307
|
+
alias inventory_bytesize max_outstanding_bytes
|
308
|
+
|
309
|
+
##
|
310
|
+
# The number of seconds that received messages can be held awaiting processing. Default is 3,600 (1 hour).
|
311
|
+
#
|
312
|
+
# @return [Integer] The maximum number of seconds.
|
313
|
+
#
|
314
|
+
def max_total_lease_duration
|
315
|
+
@inventory[:max_total_lease_duration]
|
316
|
+
end
|
317
|
+
# @deprecated Use {#max_total_lease_duration}.
|
318
|
+
alias inventory_extension max_total_lease_duration
|
319
|
+
|
320
|
+
##
|
321
|
+
# The maximum amount of time in seconds for a single lease extension attempt. Bounds the delay before a message
|
322
|
+
# redelivery if the subscriber fails to extend the deadline. Default is 0 (disabled).
|
323
|
+
#
|
324
|
+
# @return [Integer] The maximum number of seconds.
|
325
|
+
#
|
326
|
+
def max_duration_per_lease_extension
|
327
|
+
@inventory[:max_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
|
+
}
|
339
|
+
end
|
340
|
+
|
341
|
+
# @private returns error object from the stream thread.
|
342
|
+
def error! error
|
343
|
+
error_callbacks = synchronize do
|
344
|
+
@last_error = error
|
345
|
+
@error_callbacks
|
346
|
+
end
|
347
|
+
error_callbacks = default_error_callbacks if error_callbacks.empty?
|
348
|
+
error_callbacks.each { |error_callback| error_callback.call error }
|
349
|
+
end
|
350
|
+
|
351
|
+
##
|
352
|
+
# @private
|
353
|
+
def to_s
|
354
|
+
"(subscription: #{subscription_name}, streams: [#{stream_pool.map(&:to_s).join(', ')}])"
|
355
|
+
end
|
356
|
+
|
357
|
+
##
|
358
|
+
# @private
|
359
|
+
def inspect
|
360
|
+
"#<#{self.class.name} #{self}>"
|
361
|
+
end
|
362
|
+
|
363
|
+
protected
|
364
|
+
|
365
|
+
def coerce_inventory inventory
|
366
|
+
@inventory = inventory
|
367
|
+
if @inventory.is_a? Hash
|
368
|
+
@inventory = @inventory.dup
|
369
|
+
# Support deprecated field names
|
370
|
+
@inventory[:max_outstanding_messages] ||= @inventory.delete :limit
|
371
|
+
@inventory[:max_outstanding_bytes] ||= @inventory.delete :bytesize
|
372
|
+
@inventory[:max_total_lease_duration] ||= @inventory.delete :extension
|
373
|
+
else
|
374
|
+
@inventory = { max_outstanding_messages: @inventory }
|
375
|
+
end
|
376
|
+
@inventory[:max_outstanding_messages] = Integer(@inventory[:max_outstanding_messages] || 1000)
|
377
|
+
@inventory[:max_outstanding_bytes] = Integer(@inventory[:max_outstanding_bytes] || 100_000_000)
|
378
|
+
@inventory[:max_total_lease_duration] = Integer(@inventory[:max_total_lease_duration] || 3600)
|
379
|
+
@inventory[:max_duration_per_lease_extension] = Integer(@inventory[:max_duration_per_lease_extension] || 0)
|
380
|
+
end
|
381
|
+
|
382
|
+
def default_error_callbacks
|
383
|
+
# This is memoized to reduce calls to the configuration.
|
384
|
+
@default_error_callbacks ||= begin
|
385
|
+
error_callback = Google::Cloud::PubSub.configure.on_error
|
386
|
+
error_callback ||= Google::Cloud.configure.on_error
|
387
|
+
if error_callback
|
388
|
+
[error_callback]
|
389
|
+
else
|
390
|
+
[]
|
391
|
+
end
|
392
|
+
end
|
393
|
+
end
|
394
|
+
end
|
395
|
+
end
|
396
|
+
|
397
|
+
Pubsub = PubSub unless const_defined? :Pubsub
|
398
|
+
end
|
399
|
+
end
|