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
data/TROUBLESHOOTING.md
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# Troubleshooting
|
2
|
+
|
3
|
+
## Where can I get more help?
|
4
|
+
|
5
|
+
### Ask the Community
|
6
|
+
|
7
|
+
If you have a question about how to use a Google Cloud client library in your
|
8
|
+
project or are stuck in the Developer's console and don't know where to turn,
|
9
|
+
it's possible your questions have already been addressed by the community.
|
10
|
+
|
11
|
+
First, check out the appropriate tags on StackOverflow:
|
12
|
+
- [`google-cloud-platform+ruby+pubsub`][so-ruby]
|
13
|
+
|
14
|
+
Next, try searching through the issues on GitHub:
|
15
|
+
|
16
|
+
- [`api:pubsub` issues][gh-search-ruby]
|
17
|
+
|
18
|
+
Still nothing?
|
19
|
+
|
20
|
+
### Ask the Developers
|
21
|
+
|
22
|
+
If you're experiencing a bug with the code, or have an idea for how it can be
|
23
|
+
improved, *please* create a new issue on GitHub so we can talk about it.
|
24
|
+
|
25
|
+
- [New issue][gh-ruby]
|
26
|
+
|
27
|
+
[so-ruby]: http://stackoverflow.com/questions/tagged/google-cloud-platform+ruby+pubsub
|
28
|
+
|
29
|
+
[gh-search-ruby]: https://github.com/googleapis/google-cloud-ruby/issues?q=label%3A%22api%3A+pubsub%22
|
30
|
+
|
31
|
+
[gh-ruby]: https://github.com/googleapis/google-cloud-ruby/issues/new
|
@@ -0,0 +1,139 @@
|
|
1
|
+
# Copyright 2016 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
|
+
##
|
17
|
+
# This file is here to be autorequired by bundler, so that the
|
18
|
+
# Google::Cloud.pubsub and Google::Cloud#pubsub methods can be available, but
|
19
|
+
# the library and all dependencies won't be loaded until required and used.
|
20
|
+
|
21
|
+
|
22
|
+
gem "google-cloud-core"
|
23
|
+
require "google/cloud" unless defined? Google::Cloud.new
|
24
|
+
require "google/cloud/config"
|
25
|
+
require "googleauth"
|
26
|
+
|
27
|
+
module Google
|
28
|
+
module Cloud
|
29
|
+
##
|
30
|
+
# Creates a new object for connecting to the Pub/Sub service.
|
31
|
+
# Each call creates a new connection.
|
32
|
+
#
|
33
|
+
# For more information on connecting to Google Cloud see the
|
34
|
+
# {file:AUTHENTICATION.md Authentication Guide}.
|
35
|
+
#
|
36
|
+
# @param [String, Array<String>] scope The OAuth 2.0 scopes controlling the
|
37
|
+
# set of resources and operations that the connection can access. See
|
38
|
+
# [Using OAuth 2.0 to Access Google
|
39
|
+
# APIs](https://developers.google.com/identity/protocols/OAuth2).
|
40
|
+
#
|
41
|
+
# The default scope is:
|
42
|
+
#
|
43
|
+
# * `https://www.googleapis.com/auth/pubsub`
|
44
|
+
# @param [Integer] timeout Default timeout to use in requests. Optional.
|
45
|
+
# @param [Hash] client_config A hash of values to override the default
|
46
|
+
# behavior of the API client. Optional.
|
47
|
+
#
|
48
|
+
# @return [Google::Cloud::PubSub::Project]
|
49
|
+
#
|
50
|
+
# @example
|
51
|
+
# require "google/cloud"
|
52
|
+
#
|
53
|
+
# gcloud = Google::Cloud.new
|
54
|
+
# pubsub = gcloud.pubsub
|
55
|
+
# topic = pubsub.topic "my-topic"
|
56
|
+
# topic.publish "task completed"
|
57
|
+
#
|
58
|
+
# @example The default scope can be overridden with the `scope` option:
|
59
|
+
# require "google/cloud"
|
60
|
+
#
|
61
|
+
# gcloud = Google::Cloud.new
|
62
|
+
# platform_scope = "https://www.googleapis.com/auth/cloud-platform"
|
63
|
+
# pubsub = gcloud.pubsub scope: platform_scope
|
64
|
+
#
|
65
|
+
def pubsub scope: nil, timeout: nil, client_config: nil
|
66
|
+
timeout ||= @timeout
|
67
|
+
Google::Cloud.pubsub @project, @keyfile, scope: scope, timeout: timeout, client_config: client_config
|
68
|
+
end
|
69
|
+
|
70
|
+
##
|
71
|
+
# Creates a new object for connecting to the Pub/Sub service.
|
72
|
+
# Each call creates a new connection.
|
73
|
+
#
|
74
|
+
# For more information on connecting to Google Cloud see the
|
75
|
+
# {file:AUTHENTICATION.md Authentication Guide}.
|
76
|
+
#
|
77
|
+
# @param [String] project_id Project identifier for the Pub/Sub service you
|
78
|
+
# are connecting to. If not present, the default project for the
|
79
|
+
# credentials is used.
|
80
|
+
# @param [String, Hash, Google::Auth::Credentials] credentials The path to
|
81
|
+
# the keyfile as a String, the contents of the keyfile as a Hash, or a
|
82
|
+
# Google::Auth::Credentials object.
|
83
|
+
# (See {Google::Cloud::PubSub::Credentials})
|
84
|
+
# @param [String, Array<String>] scope The OAuth 2.0 scopes controlling the
|
85
|
+
# set of resources and operations that the connection can access. See
|
86
|
+
# [Using OAuth 2.0 to Access Google
|
87
|
+
# APIs](https://developers.google.com/identity/protocols/OAuth2).
|
88
|
+
#
|
89
|
+
# The default scope is:
|
90
|
+
#
|
91
|
+
# * `https://www.googleapis.com/auth/pubsub`
|
92
|
+
# @param [Integer] timeout Default timeout to use in requests. Optional.
|
93
|
+
# @param [Hash] client_config A hash of values to override the default
|
94
|
+
# behavior of the API client. Optional.
|
95
|
+
#
|
96
|
+
# @return [Google::Cloud::PubSub::Project]
|
97
|
+
#
|
98
|
+
# @example
|
99
|
+
# require "google/cloud"
|
100
|
+
#
|
101
|
+
# pubsub = Google::Cloud.pubsub
|
102
|
+
#
|
103
|
+
# topic = pubsub.topic "my-topic"
|
104
|
+
# topic.publish "task completed"
|
105
|
+
#
|
106
|
+
def self.pubsub project_id = nil, credentials = nil, scope: nil,
|
107
|
+
timeout: nil, client_config: nil
|
108
|
+
require "google/cloud/pubsub"
|
109
|
+
Google::Cloud::PubSub.new project_id: project_id, credentials: credentials,
|
110
|
+
scope: scope, timeout: timeout, client_config: client_config
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
# Set the default pubsub configuration
|
116
|
+
Google::Cloud.configure.add_config! :pubsub do |config|
|
117
|
+
default_project = Google::Cloud::Config.deferred do
|
118
|
+
ENV["PUBSUB_PROJECT"]
|
119
|
+
end
|
120
|
+
default_creds = Google::Cloud::Config.deferred do
|
121
|
+
Google::Cloud::Config.credentials_from_env(
|
122
|
+
"PUBSUB_CREDENTIALS", "PUBSUB_CREDENTIALS_JSON", "PUBSUB_KEYFILE", "PUBSUB_KEYFILE_JSON"
|
123
|
+
)
|
124
|
+
end
|
125
|
+
default_emulator = Google::Cloud::Config.deferred do
|
126
|
+
ENV["PUBSUB_EMULATOR_HOST"]
|
127
|
+
end
|
128
|
+
|
129
|
+
config.add_field! :project_id, default_project, match: String, allow_nil: true
|
130
|
+
config.add_alias! :project, :project_id
|
131
|
+
config.add_field! :credentials, default_creds, match: [String, Hash, Google::Auth::Credentials], allow_nil: true
|
132
|
+
config.add_alias! :keyfile, :credentials
|
133
|
+
config.add_field! :scope, nil, match: [String, Array]
|
134
|
+
config.add_field! :timeout, nil, match: Integer
|
135
|
+
config.add_field! :client_config, nil, match: Hash
|
136
|
+
config.add_field! :emulator_host, default_emulator, match: String, allow_nil: true
|
137
|
+
config.add_field! :on_error, nil, match: Proc
|
138
|
+
config.add_field! :endpoint, nil, match: String
|
139
|
+
end
|
@@ -0,0 +1,173 @@
|
|
1
|
+
# Copyright 2015 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"
|
17
|
+
require "google/cloud/pubsub/project"
|
18
|
+
require "google/cloud/config"
|
19
|
+
require "google/cloud/env"
|
20
|
+
|
21
|
+
module Google
|
22
|
+
module Cloud
|
23
|
+
##
|
24
|
+
# # Google Cloud Pub/Sub
|
25
|
+
#
|
26
|
+
# Google Cloud Pub/Sub is designed to provide reliable, many-to-many,
|
27
|
+
# asynchronous messaging between applications. Publisher applications can
|
28
|
+
# send messages to a "topic" and other applications can subscribe to that
|
29
|
+
# topic to receive the messages. By decoupling senders and receivers, Google
|
30
|
+
# Cloud Pub/Sub allows developers to communicate between independently
|
31
|
+
# written applications.
|
32
|
+
#
|
33
|
+
# See {file:OVERVIEW.md Google Cloud Pub/Sub Overview}.
|
34
|
+
#
|
35
|
+
module PubSub
|
36
|
+
# rubocop:disable Metrics/AbcSize
|
37
|
+
|
38
|
+
##
|
39
|
+
# Creates a new object for connecting to the Pub/Sub service.
|
40
|
+
# Each call creates a new connection.
|
41
|
+
#
|
42
|
+
# For more information on connecting to Google Cloud see the
|
43
|
+
# {file:AUTHENTICATION.md Authentication Guide}.
|
44
|
+
#
|
45
|
+
# @param [String] project_id Project identifier for the Pub/Sub service
|
46
|
+
# you are connecting to. If not present, the default project for the
|
47
|
+
# credentials is used.
|
48
|
+
# @param [String, Hash, Google::Auth::Credentials] credentials The path to
|
49
|
+
# the keyfile as a String, the contents of the keyfile as a Hash, or a
|
50
|
+
# Google::Auth::Credentials object. (See {PubSub::Credentials})
|
51
|
+
# @param [String, Array<String>] scope The OAuth 2.0 scopes controlling
|
52
|
+
# the set of resources and operations that the connection can access.
|
53
|
+
# See [Using OAuth 2.0 to Access Google
|
54
|
+
# APIs](https://developers.google.com/identity/protocols/OAuth2).
|
55
|
+
#
|
56
|
+
# The default scope is:
|
57
|
+
#
|
58
|
+
# * `https://www.googleapis.com/auth/pubsub`
|
59
|
+
# @param [Integer] timeout Default timeout to use in requests. Optional.
|
60
|
+
# @param [Hash] client_config A hash of values to override the default
|
61
|
+
# behavior of the API client. Optional.
|
62
|
+
# @param [String] endpoint Override of the endpoint host name. Optional.
|
63
|
+
# If the param is nil, uses the default endpoint.
|
64
|
+
# @param [String] emulator_host Pub/Sub emulator host. Optional.
|
65
|
+
# If the param is nil, uses the value of the `emulator_host` config.
|
66
|
+
# @param [String] project Alias for the `project_id` argument. Deprecated.
|
67
|
+
# @param [String] keyfile Alias for the `credentials` argument.
|
68
|
+
# Deprecated.
|
69
|
+
#
|
70
|
+
# @return [Google::Cloud::PubSub::Project]
|
71
|
+
#
|
72
|
+
# @example
|
73
|
+
# require "google/cloud/pubsub"
|
74
|
+
#
|
75
|
+
# pubsub = Google::Cloud::PubSub.new
|
76
|
+
#
|
77
|
+
# topic = pubsub.topic "my-topic"
|
78
|
+
# topic.publish "task completed"
|
79
|
+
#
|
80
|
+
def self.new project_id: nil, credentials: nil, scope: nil, timeout: nil, client_config: nil, endpoint: nil,
|
81
|
+
emulator_host: nil, project: nil, keyfile: nil
|
82
|
+
project_id ||= (project || default_project_id)
|
83
|
+
scope ||= configure.scope
|
84
|
+
timeout ||= configure.timeout
|
85
|
+
client_config ||= configure.client_config
|
86
|
+
endpoint ||= configure.endpoint
|
87
|
+
emulator_host ||= configure.emulator_host
|
88
|
+
|
89
|
+
if emulator_host
|
90
|
+
project_id = project_id.to_s # Always cast to a string
|
91
|
+
raise ArgumentError, "project_id is missing" if project_id.empty?
|
92
|
+
|
93
|
+
return PubSub::Project.new(
|
94
|
+
PubSub::Service.new(
|
95
|
+
project_id, :this_channel_is_insecure, host: emulator_host, timeout: timeout, client_config: client_config
|
96
|
+
)
|
97
|
+
)
|
98
|
+
end
|
99
|
+
|
100
|
+
credentials ||= (keyfile || default_credentials(scope: scope))
|
101
|
+
unless credentials.is_a? Google::Auth::Credentials
|
102
|
+
credentials = PubSub::Credentials.new credentials, scope: scope
|
103
|
+
end
|
104
|
+
|
105
|
+
project_id ||= credentials.project_id if credentials.respond_to? :project_id
|
106
|
+
project_id = project_id.to_s # Always cast to a string
|
107
|
+
raise ArgumentError, "project_id is missing" if project_id.empty?
|
108
|
+
|
109
|
+
PubSub::Project.new(
|
110
|
+
PubSub::Service.new(
|
111
|
+
project_id, credentials, timeout: timeout, host: endpoint, client_config: client_config
|
112
|
+
)
|
113
|
+
)
|
114
|
+
end
|
115
|
+
|
116
|
+
# rubocop:enable Metrics/AbcSize
|
117
|
+
|
118
|
+
##
|
119
|
+
# Configure the Google Cloud PubSub library.
|
120
|
+
#
|
121
|
+
# The following PubSub configuration parameters are supported:
|
122
|
+
#
|
123
|
+
# * `project_id` - (String) Identifier for a PubSub project. (The
|
124
|
+
# parameter `project` is considered deprecated, but may also be used.)
|
125
|
+
# * `credentials` - (String, Hash, Google::Auth::Credentials) The path to
|
126
|
+
# the keyfile as a String, the contents of the keyfile as a Hash, or a
|
127
|
+
# Google::Auth::Credentials object. (See {PubSub::Credentials}) (The
|
128
|
+
# parameter `keyfile` is considered deprecated, but may also be used.)
|
129
|
+
# * `scope` - (String, Array<String>) The OAuth 2.0 scopes controlling
|
130
|
+
# the set of resources and operations that the connection can access.
|
131
|
+
# * `retries` - (Integer) Number of times to retry requests on server
|
132
|
+
# error.
|
133
|
+
# * `timeout` - (Integer) Default timeout to use in requests.
|
134
|
+
# * `client_config` - (Hash) A hash of values to override the default
|
135
|
+
# behavior of the API client.
|
136
|
+
# * `endpoint` - (String) Override of the endpoint host name, or `nil`
|
137
|
+
# to use the default endpoint.
|
138
|
+
# * `emulator_host` - (String) Host name of the emulator. Defaults to
|
139
|
+
# `ENV["PUBSUB_EMULATOR_HOST"]`
|
140
|
+
# * `on_error` - (Proc) A Proc to be run when an error is encountered
|
141
|
+
# on a background thread. The Proc must take the error object as the
|
142
|
+
# single argument. (See {Subscriber.on_error}.)
|
143
|
+
#
|
144
|
+
# @return [Google::Cloud::Config] The configuration object the
|
145
|
+
# Google::Cloud::PubSub library uses.
|
146
|
+
#
|
147
|
+
def self.configure
|
148
|
+
yield Google::Cloud.configure.pubsub if block_given?
|
149
|
+
|
150
|
+
Google::Cloud.configure.pubsub
|
151
|
+
end
|
152
|
+
|
153
|
+
##
|
154
|
+
# @private Default project.
|
155
|
+
def self.default_project_id
|
156
|
+
Google::Cloud.configure.pubsub.project_id ||
|
157
|
+
Google::Cloud.configure.project_id ||
|
158
|
+
Google::Cloud.env.project_id
|
159
|
+
end
|
160
|
+
|
161
|
+
##
|
162
|
+
# @private Default credentials.
|
163
|
+
def self.default_credentials scope: nil
|
164
|
+
Google::Cloud.configure.pubsub.credentials ||
|
165
|
+
Google::Cloud.configure.credentials ||
|
166
|
+
PubSub::Credentials.default(scope: scope)
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
## Legacy namespace
|
171
|
+
Pubsub = PubSub unless const_defined? :Pubsub
|
172
|
+
end
|
173
|
+
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 "monitor"
|
17
|
+
require "concurrent"
|
18
|
+
require "google/cloud/pubsub/errors"
|
19
|
+
require "google/cloud/pubsub/async_publisher/batch"
|
20
|
+
require "google/cloud/pubsub/publish_result"
|
21
|
+
require "google/cloud/pubsub/service"
|
22
|
+
require "google/cloud/pubsub/convert"
|
23
|
+
|
24
|
+
module Google
|
25
|
+
module Cloud
|
26
|
+
module PubSub
|
27
|
+
##
|
28
|
+
# Used to publish multiple messages in batches to a topic. See
|
29
|
+
# {Google::Cloud::PubSub::Topic#async_publisher}
|
30
|
+
#
|
31
|
+
# @example
|
32
|
+
# require "google/cloud/pubsub"
|
33
|
+
#
|
34
|
+
# pubsub = Google::Cloud::PubSub.new
|
35
|
+
#
|
36
|
+
# topic = pubsub.topic "my-topic"
|
37
|
+
# topic.publish_async "task completed" do |result|
|
38
|
+
# if result.succeeded?
|
39
|
+
# log_publish_success result.data
|
40
|
+
# else
|
41
|
+
# log_publish_failure result.data, result.error
|
42
|
+
# end
|
43
|
+
# end
|
44
|
+
#
|
45
|
+
# topic.async_publisher.stop.wait!
|
46
|
+
#
|
47
|
+
# @attr_reader [String] topic_name The name of the topic the messages are published to. In the form of
|
48
|
+
# "/projects/project-identifier/topics/topic-name".
|
49
|
+
# @attr_reader [Integer] max_bytes The maximum size of messages to be collected before the batch is published.
|
50
|
+
# Default is 1,000,000 (1MB).
|
51
|
+
# @attr_reader [Integer] max_messages The maximum number of messages to be collected before the batch is
|
52
|
+
# published. Default is 100.
|
53
|
+
# @attr_reader [Numeric] interval The number of seconds to collect messages before the batch is published. Default
|
54
|
+
# is 0.01.
|
55
|
+
# @attr_reader [Numeric] publish_threads The number of threads used to publish messages. Default is 2.
|
56
|
+
# @attr_reader [Numeric] callback_threads The number of threads to handle the published messages' callbacks.
|
57
|
+
# Default is 4.
|
58
|
+
#
|
59
|
+
class AsyncPublisher
|
60
|
+
include MonitorMixin
|
61
|
+
|
62
|
+
attr_reader :topic_name, :max_bytes, :max_messages, :interval,
|
63
|
+
:publish_threads, :callback_threads
|
64
|
+
##
|
65
|
+
# @private Implementation accessors
|
66
|
+
attr_reader :service, :batch, :publish_thread_pool,
|
67
|
+
:callback_thread_pool
|
68
|
+
|
69
|
+
##
|
70
|
+
# @private Create a new instance of the object.
|
71
|
+
def initialize topic_name, service, max_bytes: 1_000_000, max_messages: 100, interval: 0.01, threads: {}
|
72
|
+
# init MonitorMixin
|
73
|
+
super()
|
74
|
+
@topic_name = service.topic_path topic_name
|
75
|
+
@service = service
|
76
|
+
|
77
|
+
@max_bytes = max_bytes
|
78
|
+
@max_messages = max_messages
|
79
|
+
@interval = interval
|
80
|
+
@publish_threads = (threads[:publish] || 2).to_i
|
81
|
+
@callback_threads = (threads[:callback] || 4).to_i
|
82
|
+
|
83
|
+
@published_at = nil
|
84
|
+
@publish_thread_pool = Concurrent::ThreadPoolExecutor.new max_threads: @publish_threads
|
85
|
+
@callback_thread_pool = Concurrent::ThreadPoolExecutor.new max_threads: @callback_threads
|
86
|
+
|
87
|
+
@ordered = false
|
88
|
+
@batches = {}
|
89
|
+
@cond = new_cond
|
90
|
+
|
91
|
+
@thread = Thread.new { run_background }
|
92
|
+
end
|
93
|
+
|
94
|
+
##
|
95
|
+
# Add a message to the async publisher to be published to the topic.
|
96
|
+
# Messages will be collected in batches and published together.
|
97
|
+
# See {Google::Cloud::PubSub::Topic#publish_async}
|
98
|
+
#
|
99
|
+
# @param [String, File] data The message payload. This will be converted
|
100
|
+
# to bytes encoded as ASCII-8BIT.
|
101
|
+
# @param [Hash] attributes Optional attributes for the message.
|
102
|
+
# @param [String] ordering_key Identifies related messages for which
|
103
|
+
# publish order should be respected.
|
104
|
+
# @yield [result] the callback for when the message has been published
|
105
|
+
# @yieldparam [PublishResult] result the result of the asynchronous
|
106
|
+
# publish
|
107
|
+
# @raise [Google::Cloud::PubSub::AsyncPublisherStopped] when the
|
108
|
+
# publisher is stopped. (See {#stop} and {#stopped?}.)
|
109
|
+
# @raise [Google::Cloud::PubSub::OrderedMessagesDisabled] when
|
110
|
+
# publishing a message with an `ordering_key` but ordered messages are
|
111
|
+
# not enabled. (See {#message_ordering?} and
|
112
|
+
# {#enable_message_ordering!}.)
|
113
|
+
# @raise [Google::Cloud::PubSub::OrderingKeyError] when publishing a
|
114
|
+
# message with an `ordering_key` that has already failed when
|
115
|
+
# publishing. Use {#resume_publish} to allow this `ordering_key` to be
|
116
|
+
# published again.
|
117
|
+
#
|
118
|
+
def publish data = nil, attributes = nil, ordering_key: nil, **extra_attrs, &callback
|
119
|
+
msg = Convert.pubsub_message data, attributes, ordering_key, extra_attrs
|
120
|
+
|
121
|
+
synchronize do
|
122
|
+
raise AsyncPublisherStopped if @stopped
|
123
|
+
raise OrderedMessagesDisabled if !@ordered && !msg.ordering_key.empty? # default is empty string
|
124
|
+
|
125
|
+
batch = resolve_batch_for_message msg
|
126
|
+
raise OrderingKeyError, batch.ordering_key if batch.canceled?
|
127
|
+
batch_action = batch.add msg, callback
|
128
|
+
if batch_action == :full
|
129
|
+
publish_batches!
|
130
|
+
elsif @published_at.nil?
|
131
|
+
# Set initial time to now to start the background counter
|
132
|
+
@published_at = Time.now
|
133
|
+
end
|
134
|
+
@cond.signal
|
135
|
+
end
|
136
|
+
|
137
|
+
nil
|
138
|
+
end
|
139
|
+
|
140
|
+
##
|
141
|
+
# Begins the process of stopping the publisher. Messages already in
|
142
|
+
# the queue will be published, but no new messages can be added. Use
|
143
|
+
# {#wait!} to block until the publisher is fully stopped and all
|
144
|
+
# pending messages have been published.
|
145
|
+
#
|
146
|
+
# @return [AsyncPublisher] returns self so calls can be chained.
|
147
|
+
def stop
|
148
|
+
synchronize do
|
149
|
+
break if @stopped
|
150
|
+
|
151
|
+
@stopped = true
|
152
|
+
publish_batches! stop: true
|
153
|
+
@cond.signal
|
154
|
+
@publish_thread_pool.shutdown
|
155
|
+
end
|
156
|
+
|
157
|
+
self
|
158
|
+
end
|
159
|
+
|
160
|
+
##
|
161
|
+
# Blocks until the publisher is fully stopped, all pending messages have
|
162
|
+
# been published, and all callbacks have completed, or until `timeout`
|
163
|
+
# seconds have passed.
|
164
|
+
#
|
165
|
+
# Does not stop the publisher. To stop the publisher, first call {#stop}
|
166
|
+
# and then call {#wait!} to block until the publisher is stopped
|
167
|
+
#
|
168
|
+
# @param [Number, nil] timeout The number of seconds to block until the
|
169
|
+
# publisher is fully stopped. Default will block indefinitely.
|
170
|
+
#
|
171
|
+
# @return [AsyncPublisher] returns self so calls can be chained.
|
172
|
+
def wait! timeout = nil
|
173
|
+
synchronize do
|
174
|
+
@publish_thread_pool.wait_for_termination timeout
|
175
|
+
|
176
|
+
@callback_thread_pool.shutdown
|
177
|
+
@callback_thread_pool.wait_for_termination timeout
|
178
|
+
end
|
179
|
+
|
180
|
+
self
|
181
|
+
end
|
182
|
+
|
183
|
+
##
|
184
|
+
# Stop this publisher and block until the publisher is fully stopped,
|
185
|
+
# all pending messages have been published, and all callbacks have
|
186
|
+
# completed, or until `timeout` seconds have passed.
|
187
|
+
#
|
188
|
+
# The same as calling {#stop} and {#wait!}.
|
189
|
+
#
|
190
|
+
# @param [Number, nil] timeout The number of seconds to block until the
|
191
|
+
# publisher is fully stopped. Default will block indefinitely.
|
192
|
+
#
|
193
|
+
# @return [AsyncPublisher] returns self so calls can be chained.
|
194
|
+
def stop! timeout = nil
|
195
|
+
stop
|
196
|
+
wait! timeout
|
197
|
+
end
|
198
|
+
|
199
|
+
##
|
200
|
+
# Forces all messages in the current batch to be published
|
201
|
+
# immediately.
|
202
|
+
#
|
203
|
+
# @return [AsyncPublisher] returns self so calls can be chained.
|
204
|
+
def flush
|
205
|
+
synchronize do
|
206
|
+
publish_batches!
|
207
|
+
@cond.signal
|
208
|
+
end
|
209
|
+
|
210
|
+
self
|
211
|
+
end
|
212
|
+
|
213
|
+
##
|
214
|
+
# Whether the publisher has been started.
|
215
|
+
#
|
216
|
+
# @return [boolean] `true` when started, `false` otherwise.
|
217
|
+
def started?
|
218
|
+
!stopped?
|
219
|
+
end
|
220
|
+
|
221
|
+
##
|
222
|
+
# Whether the publisher has been stopped.
|
223
|
+
#
|
224
|
+
# @return [boolean] `true` when stopped, `false` otherwise.
|
225
|
+
def stopped?
|
226
|
+
synchronize { @stopped }
|
227
|
+
end
|
228
|
+
|
229
|
+
##
|
230
|
+
# Enables message ordering for messages with ordering keys. When
|
231
|
+
# enabled, messages published with the same `ordering_key` will be
|
232
|
+
# delivered in the order they were published.
|
233
|
+
#
|
234
|
+
# See {#message_ordering?}. See {Topic#publish_async},
|
235
|
+
# {Subscription#listen}, and {Message#ordering_key}.
|
236
|
+
#
|
237
|
+
def enable_message_ordering!
|
238
|
+
synchronize { @ordered = true }
|
239
|
+
end
|
240
|
+
|
241
|
+
##
|
242
|
+
# Whether message ordering for messages with ordering keys has been
|
243
|
+
# enabled. When enabled, messages published with the same `ordering_key`
|
244
|
+
# will be delivered in the order they were published. When disabled,
|
245
|
+
# messages may be delivered in any order.
|
246
|
+
#
|
247
|
+
# See {#enable_message_ordering!}. See {Topic#publish_async},
|
248
|
+
# {Subscription#listen}, and {Message#ordering_key}.
|
249
|
+
#
|
250
|
+
# @return [Boolean]
|
251
|
+
#
|
252
|
+
def message_ordering?
|
253
|
+
synchronize { @ordered }
|
254
|
+
end
|
255
|
+
|
256
|
+
##
|
257
|
+
# Resume publishing ordered messages for the provided ordering key.
|
258
|
+
#
|
259
|
+
# @param [String] ordering_key Identifies related messages for which
|
260
|
+
# publish order should be respected.
|
261
|
+
#
|
262
|
+
# @return [boolean] `true` when resumed, `false` otherwise.
|
263
|
+
#
|
264
|
+
def resume_publish ordering_key
|
265
|
+
synchronize do
|
266
|
+
batch = resolve_batch_for_ordering_key ordering_key
|
267
|
+
return if batch.nil?
|
268
|
+
batch.resume!
|
269
|
+
end
|
270
|
+
end
|
271
|
+
|
272
|
+
protected
|
273
|
+
|
274
|
+
def run_background
|
275
|
+
synchronize do
|
276
|
+
until @stopped
|
277
|
+
if @published_at.nil?
|
278
|
+
@cond.wait
|
279
|
+
next
|
280
|
+
end
|
281
|
+
|
282
|
+
time_since_first_publish = Time.now - @published_at
|
283
|
+
if time_since_first_publish > @interval
|
284
|
+
# interval met, flush the batches...
|
285
|
+
publish_batches!
|
286
|
+
@cond.wait
|
287
|
+
else
|
288
|
+
# still waiting for the interval to publish the batch...
|
289
|
+
timeout = @interval - time_since_first_publish
|
290
|
+
@cond.wait timeout
|
291
|
+
end
|
292
|
+
end
|
293
|
+
end
|
294
|
+
end
|
295
|
+
|
296
|
+
def resolve_batch_for_message msg
|
297
|
+
@batches[msg.ordering_key] ||= Batch.new self, msg.ordering_key
|
298
|
+
end
|
299
|
+
|
300
|
+
def resolve_batch_for_ordering_key ordering_key
|
301
|
+
@batches[ordering_key]
|
302
|
+
end
|
303
|
+
|
304
|
+
def publish_batches! stop: nil
|
305
|
+
@batches.reject! { |_ordering_key, batch| batch.empty? }
|
306
|
+
@batches.values.each do |batch|
|
307
|
+
ready = batch.publish! stop: stop
|
308
|
+
publish_batch_async @topic_name, batch if ready
|
309
|
+
end
|
310
|
+
# Set published_at to nil to wait indefinitely
|
311
|
+
@published_at = nil
|
312
|
+
end
|
313
|
+
|
314
|
+
def publish_batch_async topic_name, batch
|
315
|
+
# TODO: raise unless @publish_thread_pool.running?
|
316
|
+
return unless @publish_thread_pool.running?
|
317
|
+
|
318
|
+
Concurrent::Promises.future_on(
|
319
|
+
@publish_thread_pool, topic_name, batch
|
320
|
+
) { |t, b| publish_batch_sync t, b }
|
321
|
+
end
|
322
|
+
|
323
|
+
# rubocop:disable Metrics/AbcSize
|
324
|
+
# rubocop:disable Metrics/MethodLength
|
325
|
+
|
326
|
+
def publish_batch_sync topic_name, batch
|
327
|
+
# The only batch methods that are safe to call from the loop are
|
328
|
+
# rebalance! and reset! because they are the only methods that are
|
329
|
+
# synchronized.
|
330
|
+
loop do
|
331
|
+
items = batch.rebalance!
|
332
|
+
|
333
|
+
unless items.empty?
|
334
|
+
grpc = @service.publish topic_name, items.map(&:msg)
|
335
|
+
items.zip Array(grpc.message_ids) do |item, id|
|
336
|
+
next unless item.callback
|
337
|
+
|
338
|
+
item.msg.message_id = id
|
339
|
+
publish_result = PublishResult.from_grpc item.msg
|
340
|
+
execute_callback_async item.callback, publish_result
|
341
|
+
end
|
342
|
+
end
|
343
|
+
|
344
|
+
break unless batch.reset!
|
345
|
+
end
|
346
|
+
rescue StandardError => e
|
347
|
+
items = batch.items
|
348
|
+
|
349
|
+
unless batch.ordering_key.empty?
|
350
|
+
retry if publish_batch_error_retryable? e
|
351
|
+
# Cancel the batch if the error is not to be retried.
|
352
|
+
begin
|
353
|
+
raise OrderingKeyError, batch.ordering_key
|
354
|
+
rescue OrderingKeyError => e
|
355
|
+
# The existing e variable is not set to OrderingKeyError
|
356
|
+
# Get all unsent messages for the callback
|
357
|
+
items = batch.cancel!
|
358
|
+
end
|
359
|
+
end
|
360
|
+
|
361
|
+
items.each do |item|
|
362
|
+
next unless item.callback
|
363
|
+
|
364
|
+
publish_result = PublishResult.from_error item.msg, e
|
365
|
+
execute_callback_async item.callback, publish_result
|
366
|
+
end
|
367
|
+
|
368
|
+
# publish will retry indefinitely, as long as there are unsent items.
|
369
|
+
retry if batch.reset!
|
370
|
+
end
|
371
|
+
|
372
|
+
# rubocop:enable Metrics/AbcSize
|
373
|
+
# rubocop:enable Metrics/MethodLength
|
374
|
+
|
375
|
+
PUBLISH_RETRY_ERRORS = [
|
376
|
+
GRPC::Cancelled, GRPC::DeadlineExceeded, GRPC::Internal,
|
377
|
+
GRPC::ResourceExhausted, GRPC::Unauthenticated, GRPC::Unavailable,
|
378
|
+
GRPC::Core::CallError
|
379
|
+
].freeze
|
380
|
+
|
381
|
+
def publish_batch_error_retryable? error
|
382
|
+
PUBLISH_RETRY_ERRORS.any? { |klass| error.is_a? klass }
|
383
|
+
end
|
384
|
+
|
385
|
+
def execute_callback_async callback, publish_result
|
386
|
+
return unless @callback_thread_pool.running?
|
387
|
+
|
388
|
+
Concurrent::Promises.future_on(
|
389
|
+
@callback_thread_pool, callback, publish_result
|
390
|
+
) do |cback, p_result|
|
391
|
+
cback.call p_result
|
392
|
+
end
|
393
|
+
end
|
394
|
+
end
|
395
|
+
end
|
396
|
+
|
397
|
+
Pubsub = PubSub unless const_defined? :Pubsub
|
398
|
+
end
|
399
|
+
end
|