Skip to main content

google_cloud_pubsub/publisher/
client_builder.rs

1// Copyright 2026 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
15use super::base_publisher::BasePublisher;
16use gaxi::options::ClientConfig;
17use google_cloud_gax::backoff_policy::BackoffPolicyArg;
18use google_cloud_gax::client_builder::Result as BuilderResult;
19use google_cloud_gax::retry_policy::RetryPolicyArg;
20use google_cloud_gax::retry_throttler::RetryThrottlerArg;
21
22/// A builder for [`BasePublisher`].
23///
24/// # Example
25/// ```
26/// # use google_cloud_pubsub::client::BasePublisher;
27/// # async fn sample() -> anyhow::Result<()> {
28/// let builder = BasePublisher::builder();
29/// let client = builder
30///     .with_endpoint("https://pubsub.googleapis.com")
31///     .build()
32///     .await?;
33/// # Ok(()) }
34/// ```
35#[derive(Clone, Debug)]
36pub struct BasePublisherBuilder {
37    pub(super) config: ClientConfig,
38}
39
40impl BasePublisherBuilder {
41    pub(super) fn new() -> Self {
42        let mut config = ClientConfig::default();
43        config.backoff_policy = Some(std::sync::Arc::new(
44            super::backoff_policy::default_backoff_policy(),
45        ));
46        config.retry_policy = Some(std::sync::Arc::new(
47            super::retry_policy::default_retry_policy(),
48        ));
49        Self { config }
50    }
51
52    /// Creates a new client.
53    ///
54    /// # Example
55    /// ```
56    /// # use google_cloud_pubsub::client::BasePublisher;
57    /// # async fn sample() -> anyhow::Result<()> {
58    /// let client = BasePublisher::builder().build().await?;
59    /// # Ok(()) }
60    /// ```
61    pub async fn build(self) -> BuilderResult<BasePublisher> {
62        BasePublisher::new(self).await
63    }
64
65    /// Sets the endpoint.
66    ///
67    /// # Example
68    /// ```
69    /// # use google_cloud_pubsub::client::BasePublisher;
70    /// # async fn sample() -> anyhow::Result<()> {
71    /// let client = BasePublisher::builder()
72    ///     .with_endpoint("https://private.googleapis.com")
73    ///     .build()
74    ///     .await?;
75    /// # Ok(()) }
76    /// ```
77    pub fn with_endpoint<V: Into<String>>(mut self, v: V) -> Self {
78        self.config.endpoint = Some(v.into());
79        self
80    }
81
82    /// Configure the universe domain.
83    ///
84    /// The universe domain is the default service domain for a given cloud universe.
85    /// The default value is "googleapis.com".
86    ///
87    /// # Example
88    /// ```
89    /// # use google_cloud_pubsub::client::BasePublisher;
90    /// # async fn sample() -> anyhow::Result<()> {
91    /// let client = BasePublisher::builder()
92    ///     .with_universe_domain("googleapis.com")
93    ///     .build()
94    ///     .await?;
95    /// # Ok(()) }
96    /// ```
97    pub fn with_universe_domain<V: Into<String>>(mut self, v: V) -> Self {
98        self.config.universe_domain = Some(v.into());
99        self
100    }
101
102    /// Enables tracing.
103    ///
104    /// The client libraries can be dynamically instrumented with the Tokio
105    /// [tracing] framework. Setting this flag enables this instrumentation.
106    ///
107    /// # Example
108    /// ```
109    /// # use google_cloud_pubsub::client::BasePublisher;
110    /// # async fn sample() -> anyhow::Result<()> {
111    /// let client = BasePublisher::builder()
112    ///     .with_tracing()
113    ///     .build()
114    ///     .await?;
115    /// # Ok(()) }
116    /// ```
117    ///
118    /// [tracing]: https://docs.rs/tracing/latest/tracing/
119    pub fn with_tracing(mut self) -> Self {
120        self.config.tracing = true;
121        self
122    }
123
124    /// Configure the authentication credentials.
125    ///
126    /// Most Google Cloud services require authentication, though some services
127    /// allow for anonymous access, and some services provide emulators where
128    /// no authentication is required. More information about valid credentials
129    /// types can be found in the [google-cloud-auth] crate documentation.
130    ///
131    /// # Example
132    /// ```
133    /// # use google_cloud_pubsub::client::BasePublisher;
134    /// # async fn sample() -> anyhow::Result<()> {
135    /// use google_cloud_auth::credentials::mds;
136    /// let client = BasePublisher::builder()
137    ///     .with_credentials(
138    ///         mds::Builder::default()
139    ///             .with_scopes(["https://www.googleapis.com/auth/cloud-platform.read-only"])
140    ///             .build()?)
141    ///     .build()
142    ///     .await?;
143    /// # Ok(()) }
144    /// ```
145    ///
146    /// [google-cloud-auth]: https://docs.rs/google-cloud-auth
147    pub fn with_credentials<V: Into<gaxi::options::Credentials>>(mut self, v: V) -> Self {
148        self.config.cred = Some(v.into());
149        self
150    }
151
152    /// Configure the retry policy.
153    ///
154    /// The client libraries can automatically retry operations that fail. The
155    /// retry policy controls what errors are considered retryable, sets limits
156    /// on the number of attempts or the time trying to make attempts.
157    ///
158    /// # Example
159    /// ```
160    /// # use google_cloud_pubsub::client::BasePublisher;
161    /// # async fn sample() -> anyhow::Result<()> {
162    /// use google_cloud_gax::retry_policy::RetryPolicyExt;
163    /// use google_cloud_pubsub::retry_policy::RetryableErrors;
164    /// let client = BasePublisher::builder()
165    ///     .with_retry_policy(RetryableErrors.with_attempt_limit(3))
166    ///     .build()
167    ///     .await?;
168    /// # Ok(()) };
169    /// ```
170    pub fn with_retry_policy<V: Into<RetryPolicyArg>>(mut self, v: V) -> Self {
171        self.config.retry_policy = Some(v.into().into());
172        self
173    }
174
175    /// Configure the retry backoff policy.
176    ///
177    /// The client libraries can automatically retry operations that fail. The
178    /// backoff policy controls how long to wait in between retry attempts.
179    ///
180    /// # Example
181    /// ```
182    /// # use google_cloud_pubsub::client::BasePublisher;
183    /// # async fn sample() -> anyhow::Result<()> {
184    /// use google_cloud_gax::exponential_backoff::ExponentialBackoff;
185    /// use std::time::Duration;
186    /// let policy = ExponentialBackoff::default();
187    /// let client = BasePublisher::builder()
188    ///     .with_backoff_policy(policy)
189    ///     .build()
190    ///     .await?;
191    /// # Ok(()) }
192    /// ```
193    pub fn with_backoff_policy<V: Into<BackoffPolicyArg>>(mut self, v: V) -> Self {
194        self.config.backoff_policy = Some(v.into().into());
195        self
196    }
197
198    /// Configure the retry throttler.
199    ///
200    /// Advanced applications may want to configure a retry throttler to
201    /// [Address Cascading Failures] and when [Handling Overload] conditions.
202    /// The client libraries throttle their retry loop, using a policy to
203    /// control the throttling algorithm. Use this method to fine tune or
204    /// customize the default retry throttler.
205    ///
206    /// [Handling Overload]: https://sre.google/sre-book/handling-overload/
207    /// [Address Cascading Failures]: https://sre.google/sre-book/addressing-cascading-failures/
208    ///
209    /// # Example
210    /// ```
211    /// # use google_cloud_pubsub::client::BasePublisher;
212    /// # async fn sample() -> anyhow::Result<()> {
213    /// use google_cloud_gax::retry_throttler::AdaptiveThrottler;
214    /// let client = BasePublisher::builder()
215    ///     .with_retry_throttler(AdaptiveThrottler::default())
216    ///     .build()
217    ///     .await?;
218    /// # Ok(()) };
219    /// ```
220    pub fn with_retry_throttler<V: Into<RetryThrottlerArg>>(mut self, v: V) -> Self {
221        self.config.retry_throttler = v.into().into();
222        self
223    }
224
225    /// Configure the number of gRPC subchannels.
226    ///
227    /// # Example
228    /// ```
229    /// # use google_cloud_pubsub::client::BasePublisher;
230    /// # async fn sample() -> anyhow::Result<()> {
231    /// let client = BasePublisher::builder()
232    ///     .with_grpc_subchannel_count(4)
233    ///     .build()
234    ///     .await?;
235    /// # Ok(()) }
236    /// ```
237    ///
238    /// gRPC-based clients may exhibit high latency if many requests need to be
239    /// demuxed over a single HTTP/2 connection (often called a *subchannel* in
240    /// gRPC).
241    ///
242    /// Consider using more subchannels if your application makes many
243    /// concurrent requests. Consider using fewer subchannels if your
244    /// application needs the file descriptors for other purposes.
245    pub fn with_grpc_subchannel_count(mut self, v: usize) -> Self {
246        self.config.grpc_subchannel_count = Some(v);
247        self
248    }
249}
250
251#[cfg(test)]
252mod tests {
253    use super::*;
254    use google_cloud_auth::credentials::anonymous::Builder as Anonymous;
255
256    #[test]
257    fn defaults() -> anyhow::Result<()> {
258        let builder = BasePublisherBuilder::new();
259        assert!(builder.config.endpoint.is_none(), "{builder:?}");
260        assert!(builder.config.cred.is_none(), "{builder:?}");
261        assert!(builder.config.universe_domain.is_none(), "{builder:?}");
262        assert!(!builder.config.tracing);
263        assert!(
264            format!("{:?}", builder.config).contains("AdaptiveThrottler"),
265            "{:?}",
266            builder.config
267        );
268        assert!(builder.config.backoff_policy.is_some(), "{builder:?}");
269        let debug_str = format!("{:?}", builder.config);
270        assert!(
271            debug_str.contains("initial_delay: 100ms"),
272            "actual: {debug_str}"
273        );
274        assert!(
275            debug_str.contains("maximum_delay: 60s"),
276            "actual: {debug_str}"
277        );
278        assert!(debug_str.contains("scaling: 4.0"), "actual: {debug_str}");
279        assert!(builder.config.retry_policy.is_some(), "{builder:?}");
280        assert!(
281            builder.config.grpc_subchannel_count.is_none(),
282            "{builder:?}"
283        );
284
285        Ok(())
286    }
287
288    #[tokio::test]
289    async fn setters() -> anyhow::Result<()> {
290        use google_cloud_gax::retry_policy::{AlwaysRetry, RetryPolicyExt};
291        let builder = BasePublisherBuilder::new()
292            .with_endpoint("test-endpoint.com")
293            .with_universe_domain("test-ud.com")
294            .with_credentials(Anonymous::new().build())
295            .with_tracing()
296            .with_retry_policy(AlwaysRetry.with_attempt_limit(3))
297            .with_backoff_policy(
298                google_cloud_gax::exponential_backoff::ExponentialBackoff::default(),
299            )
300            .with_retry_throttler(google_cloud_gax::retry_throttler::CircuitBreaker::default())
301            .with_grpc_subchannel_count(16);
302        assert_eq!(
303            builder.config.endpoint,
304            Some("test-endpoint.com".to_string())
305        );
306        assert_eq!(
307            builder.config.universe_domain,
308            Some("test-ud.com".to_string())
309        );
310        assert!(builder.config.cred.is_some(), "{builder:?}");
311        assert!(builder.config.tracing);
312        assert!(
313            format!("{:?}", builder.config).contains("CircuitBreaker"),
314            "{:?}",
315            builder.config
316        );
317        assert!(builder.config.retry_policy.is_some(), "{builder:?}");
318        assert!(builder.config.backoff_policy.is_some(), "{builder:?}");
319        assert_eq!(builder.config.grpc_subchannel_count, Some(16));
320
321        Ok(())
322    }
323}