google-cloud-pubsub 1.9.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|