Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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
}