Configuring polling policies
The Google Cloud Client Libraries for Rust provide helper functions to simplify waiting and monitoring the progress of LROs (Long-Running Operations). These helpers use policies to configure the polling frequency and to determine what polling errors are transient and may be ignored until the next polling event.
This guide will walk you through the configuration of these policies for all the long-running operations started by a client, or just for one specific request.
There are two different policies controlling the behavior of the LRO loops:
- The polling backoff policy controls how long the loop waits before polling the status of a LRO that is still in progress.
- The polling error policy controls what to do on an polling error. Some polling errors are unrecoverable, and indicate that the operation was aborted or the caller has no permissions to check the status of the LRO. Other polling errors are transient, and indicate a temporary problem in the client network or the service.
Each one of these policies can be set independently, and each one can be set for all the LROs started on a client or changed for just one request.
Prerequisites
The guide uses the Speech-To-Text V2 service to keep the code snippets concrete. The same ideas work for any other service using LROs.
We recommend you first follow one of the service guides, such as Transcribe speech to text by using the command line. These guides will cover critical topics such as ensuring your project has the API enabled, your account has the right permissions, and how to set up billing for your project (if needed). Skipping the service guides may result in problems that are hard to diagnose.
Dependencies
As it is usual with Rust, you must declare the dependency in your
Cargo.toml
file. We use:
google-cloud-speech-v2 = { version = "0.2", path = "../../src/generated/cloud/speech/v2" }
Configuring the polling frequency for all requests in a client
If you are planning to use the same polling backoff policy for all (or even most) requests with the same client then consider setting this as a client option.
To configure the polling frequency you use a type implementing the PollingBackoffPolicy trait. The client libraries provide ExponentialBackoff:
use google_cloud_gax::exponential_backoff::ExponentialBackoffBuilder;
use google_cloud_gax::options::ClientConfig;
Then initialize the client with the configuration you want:
let client = speech::client::Speech::new_with_config(
ClientConfig::default().set_polling_backoff_policy(
ExponentialBackoffBuilder::new()
.with_initial_delay(Duration::from_millis(250))
.with_maximum_delay(Duration::from_secs(10))
.build()?,
),
)
.await?;
Unless you override the policy with a per-request setting this policy will be in effect for any long-running operation started with the client. In this example, if you make a call such as:
let mut operation = client
.batch_recognize(/* stuff */)
/* more stuff */
.send()
.await?;
The client library will first wait for 500ms, after the first polling attempt, then for 1,000ms (or 1s) for the second attempt, and sub-sequent attempts will wait 2s, 4s, 8s and then all attempts will wait 10s.
See below for the complete code.
Configuring the polling frequency for a specific request
As described in the previous section. We need a type implementing the PollingBackoffPolicy trait to configure the polling frequency. We will also use ExponentialBackoff in this example:
use google_cloud_gax::exponential_backoff::ExponentialBackoffBuilder;
use std::time::Duration;
The configuration of the request will require bringing a trait within scope:
use google_cloud_gax::options::RequestOptionsBuilder;
You create the request builder as usual:
let response = client
.batch_recognize(format!(
"projects/{project_id}/locations/global/recognizers/_"
))
And then configure the polling backoff policy:
.with_polling_backoff_policy(
ExponentialBackoffBuilder::new()
.with_initial_delay(Duration::from_millis(250))
.with_maximum_delay(Duration::from_secs(10))
.build()?,
)
You can issue this request as usual. For example:
.poller()
.until_done()
.await?;
println!("LRO completed, response={response:?}");
See below for the complete code.
Configuring the retryable polling errors for all requests in a client
To configure the retryable errors we need to use a type implementing the PollingErrorPolicy trait. The client libraries provide a number of them, a conservative choice is Aip194Strict:
use google_cloud_gax::options::ClientConfig;
use google_cloud_gax::polling_error_policy::Aip194Strict;
use google_cloud_gax::polling_error_policy::PollingErrorPolicyExt;
use std::time::Duration;
If you are planning to use the same polling policy for all (or even most) requests with the same client then consider setting this as a client option.
Initialize the client with the configuration you want:
let client = speech::client::Speech::new_with_config(
ClientConfig::default().set_polling_error_policy(
Aip194Strict
.with_attempt_limit(100)
.with_time_limit(Duration::from_secs(300)),
),
)
.await?;
Unless you override the policy with a per-request setting this policy will be in effect for any long-running operation started with the client. In this example, if you make a call such as:
let mut operation = client
.batch_recognize(/* stuff */)
/* more stuff */
.send()
.await?;
The client library will only treat UNAVAILABLE
(see AIP-194) as a retryable
error, and will stop polling after 100 attempts or 300 seconds, whichever comes
first.
See below for the complete code.
Configuring the retryable polling errors for a specific request
To configure the retryable errors we need to use a type implementing the PollingErrorPolicy trait. The client libraries provide a number of them, a conservative choice is Aip194Strict:
use google_cloud_gax::polling_error_policy::Aip194Strict;
use google_cloud_gax::polling_error_policy::PollingErrorPolicyExt;
use std::time::Duration;
The configuration of the request will require bringing a trait within scope:
use google_cloud_gax::options::RequestOptionsBuilder;
You create the request builder as usual:
let response = client
.batch_recognize(format!(
"projects/{project_id}/locations/global/recognizers/_"
))
And then configure the polling backoff policy:
.with_polling_error_policy(
Aip194Strict
.with_attempt_limit(100)
.with_time_limit(Duration::from_secs(300)),
)
.set_files([speech::model::BatchRecognizeFileMetadata::new()
.set_uri("gs://cloud-samples-data/speech/hello.wav")])
.set_recognition_output_config(
speech::model::RecognitionOutputConfig::new()
.set_inline_response_config(speech::model::InlineOutputConfig::new()),
)
.set_processing_strategy(
speech::model::batch_recognize_request::ProcessingStrategy::DYNAMIC_BATCHING,
)
.set_config(
speech::model::RecognitionConfig::new()
.set_language_codes(["en-US"])
.set_model("short")
.set_auto_decoding_config(speech::model::AutoDetectDecodingConfig::new()),
)
.poller()
.until_done()
.await?;
println!("LRO completed, response={response:?}");
Ok(())
}
You can issue this request as usual. For example:
.poller()
.until_done()
.await?;
println!("LRO completed, response={response:?}");
See below for the complete code.
Configuring the polling frequency for all requests in a client: complete code
pub async fn client_backoff(project_id: &str) -> crate::Result<()> {
use google_cloud_gax::exponential_backoff::ExponentialBackoffBuilder;
use google_cloud_gax::options::ClientConfig;
use speech::Poller;
use std::time::Duration;
let client = speech::client::Speech::new_with_config(
ClientConfig::default().set_polling_backoff_policy(
ExponentialBackoffBuilder::new()
.with_initial_delay(Duration::from_millis(250))
.with_maximum_delay(Duration::from_secs(10))
.build()?,
),
)
.await?;
let response = client
.batch_recognize(format!(
"projects/{project_id}/locations/global/recognizers/_"
))
.set_files([speech::model::BatchRecognizeFileMetadata::new()
.set_uri("gs://cloud-samples-data/speech/hello.wav")])
.set_recognition_output_config(
speech::model::RecognitionOutputConfig::new()
.set_inline_response_config(speech::model::InlineOutputConfig::new()),
)
.set_processing_strategy(
speech::model::batch_recognize_request::ProcessingStrategy::DYNAMIC_BATCHING,
)
.set_config(
speech::model::RecognitionConfig::new()
.set_language_codes(["en-US"])
.set_model("short")
.set_auto_decoding_config(speech::model::AutoDetectDecodingConfig::new()),
)
.poller()
.until_done()
.await?;
println!("LRO completed, response={response:?}");
Ok(())
}
Configuring the polling frequency for a specific request: complete code
pub async fn rpc_backoff(project_id: &str) -> crate::Result<()> {
use google_cloud_gax::exponential_backoff::ExponentialBackoffBuilder;
use std::time::Duration;
use google_cloud_gax::options::RequestOptionsBuilder;
use speech::Poller;
let client = speech::client::Speech::new().await?;
let response = client
.batch_recognize(format!(
"projects/{project_id}/locations/global/recognizers/_"
))
.with_polling_backoff_policy(
ExponentialBackoffBuilder::new()
.with_initial_delay(Duration::from_millis(250))
.with_maximum_delay(Duration::from_secs(10))
.build()?,
)
.set_files([speech::model::BatchRecognizeFileMetadata::new()
.set_uri("gs://cloud-samples-data/speech/hello.wav")])
.set_recognition_output_config(
speech::model::RecognitionOutputConfig::new()
.set_inline_response_config(speech::model::InlineOutputConfig::new()),
)
.set_processing_strategy(
speech::model::batch_recognize_request::ProcessingStrategy::DYNAMIC_BATCHING,
)
.set_config(
speech::model::RecognitionConfig::new()
.set_language_codes(["en-US"])
.set_model("short")
.set_auto_decoding_config(speech::model::AutoDetectDecodingConfig::new()),
)
.poller()
.until_done()
.await?;
println!("LRO completed, response={response:?}");
Ok(())
}
Configuring the retryable polling errors for all requests in a client: complete code
pub async fn client_backoff(project_id: &str) -> crate::Result<()> {
use google_cloud_gax::exponential_backoff::ExponentialBackoffBuilder;
use google_cloud_gax::options::ClientConfig;
use speech::Poller;
use std::time::Duration;
let client = speech::client::Speech::new_with_config(
ClientConfig::default().set_polling_backoff_policy(
ExponentialBackoffBuilder::new()
.with_initial_delay(Duration::from_millis(250))
.with_maximum_delay(Duration::from_secs(10))
.build()?,
),
)
.await?;
let response = client
.batch_recognize(format!(
"projects/{project_id}/locations/global/recognizers/_"
))
.set_files([speech::model::BatchRecognizeFileMetadata::new()
.set_uri("gs://cloud-samples-data/speech/hello.wav")])
.set_recognition_output_config(
speech::model::RecognitionOutputConfig::new()
.set_inline_response_config(speech::model::InlineOutputConfig::new()),
)
.set_processing_strategy(
speech::model::batch_recognize_request::ProcessingStrategy::DYNAMIC_BATCHING,
)
.set_config(
speech::model::RecognitionConfig::new()
.set_language_codes(["en-US"])
.set_model("short")
.set_auto_decoding_config(speech::model::AutoDetectDecodingConfig::new()),
)
.poller()
.until_done()
.await?;
println!("LRO completed, response={response:?}");
Ok(())
}
Configuring the retryable polling errors for a specific request: complete code
pub async fn rpc_backoff(project_id: &str) -> crate::Result<()> {
use google_cloud_gax::exponential_backoff::ExponentialBackoffBuilder;
use std::time::Duration;
use google_cloud_gax::options::RequestOptionsBuilder;
use speech::Poller;
let client = speech::client::Speech::new().await?;
let response = client
.batch_recognize(format!(
"projects/{project_id}/locations/global/recognizers/_"
))
.with_polling_backoff_policy(
ExponentialBackoffBuilder::new()
.with_initial_delay(Duration::from_millis(250))
.with_maximum_delay(Duration::from_secs(10))
.build()?,
)
.set_files([speech::model::BatchRecognizeFileMetadata::new()
.set_uri("gs://cloud-samples-data/speech/hello.wav")])
.set_recognition_output_config(
speech::model::RecognitionOutputConfig::new()
.set_inline_response_config(speech::model::InlineOutputConfig::new()),
)
.set_processing_strategy(
speech::model::batch_recognize_request::ProcessingStrategy::DYNAMIC_BATCHING,
)
.set_config(
speech::model::RecognitionConfig::new()
.set_language_codes(["en-US"])
.set_model("short")
.set_auto_decoding_config(speech::model::AutoDetectDecodingConfig::new()),
)
.poller()
.until_done()
.await?;
println!("LRO completed, response={response:?}");
Ok(())
}