Skip to content
Open
1 change: 1 addition & 0 deletions guide/samples/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ pub mod error_handling;
pub mod examine_error_details;
pub mod gemini;
pub mod logging;
pub mod occ;
pub mod observability;
pub mod pagination;
pub mod retry_policies;
Expand Down
15 changes: 15 additions & 0 deletions guide/samples/src/occ.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Copyright 2026 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

pub mod set_iam_policy;
81 changes: 81 additions & 0 deletions guide/samples/src/occ/set_iam_policy.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
// Copyright 2026 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// [BEGIN rust_occ_loop]
use google_cloud_gax::error::rpc::Code;
use google_cloud_iam_v1::model::Binding;
use google_cloud_iam_v1::model::Policy;
use google_cloud_secretmanager_v1::client::SecretManagerService;
use google_cloud_wkt::FieldMask;

/// Executes an Optimistic Concurrency Control (OCC) loop to safely update a resource.
///
/// This function demonstrates the core Read-Modify-Write-Retry pattern. It uses the secret manager
/// service and a hard-coded role. The principles apply to any other service or role.
///
/// # Parameters
/// * `project_id` The Google Cloud Project ID (e.g., "my-project-123").
/// * `secret_id` The Google Cloud Project ID (e.g., "my-secret").
/// * `member` The member to add (e.g., "user:user@example.com").
///
/// # Returns
/// The new IAM policy.
pub async fn sample(project_id: &str, secret_id: &str, member: &str) -> anyhow::Result<Policy> {
// ANCHOR: occ-loop
const ROLE: &str = "roles/secretmanager.secretAccessor";
const ATTEMPTS: u32 = 5;

let secret_name = format!("projects/{project_id}/secrets/{secret_id}");
let client = SecretManagerService::builder().build().await?;
for _attempt in 0..ATTEMPTS {
let mut current = client
.get_iam_policy()
.set_resource(&secret_name)
.send()
.await?;

match current.bindings.iter_mut().find(|b| b.role == ROLE) {
None => current
.bindings
.push(Binding::new().set_role(ROLE).set_members([member])),
Some(b) => {
if b.members.iter().find(|m| *m == member).is_some() {
return Ok(current);
}
b.members.push(member.to_string());
}
};
let updated = client
.set_iam_policy()
.set_resource(&secret_name)
.set_policy(current)
.set_update_mask(FieldMask::default().set_paths(["bindings"]))
.send()
.await;
match updated {
Ok(p) => return Ok(p),
Err(e)
if e.status().is_some_and(|s| {
s.code == Code::Aborted || s.code == Code::FailedPrecondition
}) =>
{
continue;
}
Err(e) => return Err(e.into()),
}
}
anyhow::bail!("could not set IAM policy after {ATTEMPTS} attempts")
// ANCHOR_END: occ-loop
}
// [END rust_occ_loop]
55 changes: 55 additions & 0 deletions guide/src/configure_client.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# How to configure a client

The Google Cloud Rust Client Libraries let you configure client behavior
using a configuration object passed to the client constructor. This configuration
is typically handled by a `ClientConfig` or `Config` struct provided by the
specific service crate.

## 1. Customizing the API endpoint

See [Override the default endpoint][override-endpoint].

## 2. Authentication configuration

While the client attempts to find [Application Default Credentials (ADC)][adc]
automatically, you can explicitly provide them using the `with_auth` or
`with_api_key` methods on the configuration object. See
[`Override the default authentication method`][authentication] for details and
examples.

## 3. Logging

See [Enable logging][enable-logging].

## 4. Configuring a proxy

To configure a proxy, you can take advantage of the
[standard environment variables][envvars] supported by `reqwest`. You
don't need to configure this in the Rust code itself. Set the following
environment variables in your shell or container:

```bash
export http_proxy="http://proxy.example.com:3128"
export https_proxy="http://proxy.example.com:3128"
```

## 5. Configuring retries

See [Configuring retry policies](/configuring_retry_policies.md)

## 6. Other common configuration options

To override the default authentication, including using API keys, see
[Override the default authentication method][override-authentication]. To
override the default endpoint, see
[Override the default endpoint][override-endpoint].

**NOTE**: To use API keys, you can use the

[adc]: https://cloud.google.com/docs/authentication/application-default-credentials
[authentication]: https://docs.cloud.google.com/rust/override-default-authentication
[enable-logging]: https://docs.cloud.google.com/rust/enable-logging
[envvars]: https://grpc.github.io/grpc/core/md_doc_environment_variables.html
[override-endpoint]: https://docs.cloud.google.com/rust/override-default-endpoint
[override-authentication]: https://docs.cloud.google.com/rust/override-default-authentication
[override-api-keys]: https://docs.cloud.google.com/rust/override-default-authentication#override_the_default_credentials_api_keys
93 changes: 93 additions & 0 deletions guide/src/occ.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
<!--
Copyright 2025 Google LLC

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

https://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->

# How to work with Optimistic Concurrency Control (OCC)

Optimistic Concurrency Control (OCC) is a strategy used to manage shared
resources and prevent "lost updates" or race conditions when multiple users or
processes attempt to modify the same resource simultaneously.

As an example, consider systems like Google Cloud IAM, where
the shared resource is an **IAM Policy** applied to a resource (like a Project,
Bucket, or Service). To implement OCC, systems typically use a version number or
an `etag` (entity tag) field on the resource struct.

## Introduction to OCC

Imagine two processes, A and B, try to update a shared resource at the same
time:

1. Process **A** reads the current state of the resource.

2. Process **B** reads the *same* current state.

3. Process **A** modifies its copy and writes it back to the server.

4. Process **B** modifies its copy and writes it back to the server.

Because Process **B** overwrites the resource *without* knowing that Process
**A** already changed it, Process **A**'s updates are **lost**.

OCC solves this by introducing a unique fingerprint which changes every time an
entity is modified. In many systems (like IAM), this is done
using an `etag`. The server checks this tag on every write:

1. When you read the resource, the server returns an `etag` (a unique
fingerprint).

2. When you send the modified resource back, you must include the original
`etag`.

3. If the server finds that the stored `etag` does **not** match the `etag` you
sent (meaning someone else modified the resource since you read it), the write
operation fails with an `ABORTED` or `FAILED_PRECONDITION` error.

This failure forces the client to **retry** the entire process—re-read the *new*
state, re-apply the changes, and try the write again with the new `etag`.

## Implementing the OCC loop

The core of the OCC implementation is a loop that handles the retry logic. You
should set a reasonable maximum number of retries to prevent infinite loops in
cases of high contention.

### Steps of the loop:

| **Step** | **Action** | **Implementation example** |
| --- | --- | --- |
| **Read** | Fetch the current resource state, including the `etag`. | `let mut policy = client.get_iam_policy(request).await?;` |
| **Modify** | Apply the changes to the local struct. | `policy.bindings.push(new_binding);` |
| **Write/Check** | Attempt to save the modified resource using the old `etag`. This action is checked for specific error codes. | `match client.set_iam_policy(request).await { Ok(p) => return Ok(p), Err(e) => { /* retry logic */ } }` |
| **Success/Retry** | If the write succeeds, exit the loop. If it fails with a concurrency error, increment the retry counter and continue the loop (go back to the Read step). | |

The following code provides an example of how to implement the OCC loop using an
IAM policy on a Project resource as the target.

**Note**: This example assumes the use of the Secret Manager client, but the
same OCC pattern applies to any service or database that implements versioned
updates.

### Example

As usual with Rust, you must declare the dependency in your `Cargo.toml` file:

```shell
cargo add google-cloud-secretmanager-v1
```

```rust,ignore
{{#include ../samples/src/occ/set_iam_policy.rs:occ-loop}}
```
65 changes: 65 additions & 0 deletions guide/src/troubleshooting.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# Troubleshoot the Google Cloud Rust Client Library

{% block body %}

## Debug logging

The best way to troubleshoot is by enabling logging. See
[Enabling Logging][enable-logging] for more
information.

## How can I trace gRPC issues?

When working with libraries that use gRPC, you can use the underlying gRPC
environment variables to enable logging. Most Rust clients use pure-Rust gRPC
implementations like `tonic`.

### Prerequisites

Ensure your crate includes the necessary features for the gRPC transport. You
can verify your dependencies in `Cargo.toml`.

### Transport logging with gRPC

The primary method for debugging gRPC calls in Rust is using the `tracing`
subscriber filters. You can target specific gRPC crates to see underlying
transport details.

NOTE: The `tracing` crate requires that you first initialize a
[`tracing_subscriber`][tracing_subscriber].

For example, setting the `RUST_LOG` environment variable to include
`tonic=debug` or `h2=debug` will dump a lot of information regarding the gRPC
and HTTP/2 layers.

```sh
RUST_LOG=debug,tonic=debug,h2=debug cargo run --example your_program
```

## How can I diagnose proxy issues?

See [Client Configuration: Configuring a Proxy][client-configuration].

## Reporting a problem

If your issue is still not resolved, ask for help. If you have a support
contract with Google, create an issue in the
[support console][support] instead of filing on GitHub.
This will ensure a timely response.

Otherwise, file an issue on GitHub. Although there are multiple GitHub
repositories associated with the Google Cloud Libraries, we recommend filing
an issue in
[https://github.com/googleapis/google-cloud-rust][google-cloud-rust]
unless you are certain that it belongs elsewhere. The maintainers may move it to
a different repository where appropriate, but you will be notified of this using
the email associated with your GitHub account.

When filing an issue, include as much of the following information as possible.
This will enable us to help you quickly.

[client-configuration]: /configure_client.md
[google-cloud-rust]: https://github.com/googleapis/google-cloud-rust
[support]: https://cloud.google.com/support/
[tracing_subscriber]: https://docs.rs/tracing-subscriber/latest/tracing_subscriber/fmt/index.html
[enable-logging]: https://docs.cloud.google.com/rust/enable-logging
Loading