Error handling
Sometimes applications need to branch based on the type and details of the error returned by the client library. This guide shows you how to write code to handle such errors.
Retryable errors: One of the most common reasons to handle errors in distributed systems is to retry requests that fail due to transient errors. The Google Cloud Client Libraries for Rust implement a policy-based retry loop. You only need to configure the policies to enable the retry loop, and the libraries implement common retry policies. Consult the Configuring Retry Policies section before implementing your own retry loop.
Prerequisites
The guide uses the Secret Manager service to demonstrate error handling, but the concepts apply to other services as well.
You may want to follow the service quickstart, which shows you how to enable the service and ensure that you've logged in and that your account has the necessary permissions.
For complete setup instructions for the Rust libraries, see Setting up your development environment.
Dependencies
Add the Secret Manager library to your Cargo.toml
file:
cargo add google-cloud-secretmanager-v1
In addition, this guide uses crc32c
to calculate the checksum:
cargo add crc32c
Motivation
In this guide you'll create a new secret version. Secret versions are contained in secrets. You must create the secret before adding secret versions. A common pattern in cloud services is to use a resource as if the container for it existed, and only create the container if there is an error. If the container exists most of the time, such an approach is more efficient than checking if the container exists before making the request. Checking if the container exists consumes more quota, results in more RPC charges, and is slower when the container already exists.
Handling the error
This section walks you through a function to update a secret. For the full code
sample, see update_secret
.
First make an attempt to create a new secret version:
match update_attempt(&client, project_id, secret_id, data.clone()).await {
If update_attempt
succeeds, you can just print the
successful result and return:
Ok(version) => {
println!("new version is {}", version.name);
Ok(version)
}
The request may have failed for many reasons: because the connection dropped before the request was fully sent, or the connection dropped before the response was received, or because it was impossible to create the authentication tokens.
The retry policies can deal with most of these errors. Here we are interested only in errors returned by the service:
Err(e) => {
if let Some(status) = e.status() {
and then only in errors that correspond to a missing secret:
use gax::error::rpc::Code;
if status.code == Code::NotFound {
If this is a "not found" error, you can try to create the secret. This simply returns on failure:
let _ = create_secret(&client, project_id, secret_id).await?;
Assuming create_secret
is successful, you can try to add the
secret version again, this time just returning an error if anything fails:
let version = update_attempt(&client, project_id, secret_id, data).await?;
println!("new version is {}", version.name);
return Ok(version);
What's next
Learn more about error handling:
Code samples
update_secret
pub async fn update_secret(
project_id: &str,
secret_id: &str,
data: Vec<u8>,
) -> crate::Result<sm::model::SecretVersion> {
let client = sm::client::SecretManagerService::builder().build().await?;
match update_attempt(&client, project_id, secret_id, data.clone()).await {
Ok(version) => {
println!("new version is {}", version.name);
Ok(version)
}
Err(e) => {
if let Some(status) = e.status() {
use gax::error::rpc::Code;
if status.code == Code::NotFound {
let _ = create_secret(&client, project_id, secret_id).await?;
let version = update_attempt(&client, project_id, secret_id, data).await?;
println!("new version is {}", version.name);
return Ok(version);
}
}
Err(e.into())
}
}
}
update_attempt
async fn update_attempt(
client: &sm::client::SecretManagerService,
project_id: &str,
secret_id: &str,
data: Vec<u8>,
) -> gax::Result<sm::model::SecretVersion> {
let checksum = crc32c::crc32c(&data) as i64;
client
.add_secret_version()
.set_parent(format!("projects/{project_id}/secrets/{secret_id}"))
.set_payload(
sm::model::SecretPayload::new()
.set_data(data)
.set_data_crc32c(checksum),
)
.send()
.await
}
create_secret
pub async fn create_secret(
client: &sm::client::SecretManagerService,
project_id: &str,
secret_id: &str,
) -> gax::Result<sm::model::Secret> {
use google_cloud_gax::options::RequestOptionsBuilder;
use google_cloud_gax::retry_policy::AlwaysRetry;
use google_cloud_gax::retry_policy::RetryPolicyExt;
use std::time::Duration;
client
.create_secret()
.set_parent(format!("projects/{project_id}"))
.with_retry_policy(
AlwaysRetry
.with_attempt_limit(5)
.with_time_limit(Duration::from_secs(15)),
)
.set_secret_id(secret_id)
.set_secret(
sm::model::Secret::new()
.set_replication(sm::model::Replication::new().set_replication(
sm::model::replication::Replication::Automatic(
sm::model::replication::Automatic::new().into(),
),
))
.set_labels([("integration-test", "true")]),
)
.send()
.await
}