---
title: "Best practices"
description: "Best practices for optimizing the Aerospike Rust client: single client reuse, batching, and key management."
---

# Best practices

> For the complete documentation index see: [llms.txt](https://aerospike.com/docs/llms.txt)
> 
> All documentation pages available in markdown.

Client version

This page applies to Rust client version 2.2.0.

## Sharing and reusing the client

### Reuse a single client instance

The Aerospike client is thread-safe (`Send` and `Sync`). We recommend creating one client per cluster and reusing it for the lifetime of your application.

-   **Do:** Create the client once (such as at startup) and pass references (`&Client`) or wrap in `Arc<Client>` when sharing across threads or async tasks.
-   **Don’t:** Create a new `Client` for every request or per thread. Each client manages its own connection pool and cluster state. Multiple clients add unnecessary overhead and connection churn.

### Single client in a multi-threaded or async environment

Because `Client` is `Send + Sync`, you can:

-   Share a reference across threads (such as `Arc<Client>`).
-   Use the same client from many concurrent async tasks (such as `&Client` in handlers).
-   Call client methods from a thread pool or a multi-threaded Tokio runtime. The client uses internal synchronization for connection and cluster state.

**Example: one client, many concurrent tasks (async)**

When you spawn tasks with `tokio::spawn`, the future’s output must be `Send`. Use `Box<dyn std::error::Error + Send + Sync>` (not bare `Box<dyn Error>`) if spawned tasks return errors:

```rust
use std::sync::Arc;

use aerospike::{Client, ClientPolicy, ReadPolicy, Bins, as_key};

#[tokio::main]

async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {

    let policy = ClientPolicy::default();

    let client = Client::new(&policy, &"127.0.0.1:3000".to_string()).await?;

    let client = Arc::new(client);

    // Spawn multiple tasks using the same client

    let mut handles = vec![];

    for i in 0..10 {

        let client = Arc::clone(&client);

        handles.push(tokio::spawn(async move {

            let key = as_key!("test", "myset", i);

            client.get(&ReadPolicy::default(), &key, Bins::All).await

        }));

    }

    for h in handles {

        let _ = h.await?;

    }

    Ok(())

}
```

> 📖 **API reference**: [`Client::new`](https://docs.rs/aerospike/latest/aerospike/struct.Client.html#method.new) | [`Client::get`](https://docs.rs/aerospike/latest/aerospike/struct.Client.html#method.get)

**Example: sync client with multiple threads**

With the `sync` feature, use the same pattern: one `Client` (such as inside `Arc`) and clone the `Arc` for each thread.

```rust
use std::sync::Arc;

use std::thread;

use aerospike::{Client, ClientPolicy, ReadPolicy, Bins, as_key};

fn main() -> Result<(), Box<dyn std::error::Error>> {

    let client = Client::new(&ClientPolicy::default(), &"127.0.0.1:3000".to_string())?;

    let client = Arc::new(client);

    let mut handles = vec![];

    for i in 0..4 {

        let client = Arc::clone(&client);

        handles.push(thread::spawn(move || {

            let key = as_key!("test", "myset", i);

            client.get(&ReadPolicy::default(), &key, Bins::All)

        }));

    }

    for h in handles {

        let _ = h.join().unwrap()?;

    }

    Ok(())

}
```

> 📖 **API reference**: [`Client::new`](https://docs.rs/aerospike/latest/aerospike/struct.Client.html#method.new) | [`Client::get`](https://docs.rs/aerospike/latest/aerospike/struct.Client.html#method.get)

### When to create a new client

Create a new client only when:

-   Connecting to a different cluster (such as another environment or region).
-   You have reconfigured the cluster (such as a new seed list) and need a fresh connection pool and cluster view.
-   The previous client has been closed and you need to reconnect.

For normal request handling, use one shared client and reuse it.

## User-defined keys and storage

Aerospike identifies records by a _digest_ (a hash of namespace, set, and your key). By default, only this digest is stored. The original user key (such as `"user_123"` or `42`) is not stored on the server. Single-record reads still work because your application already has the key and the client recomputes the digest.

If you need the key to be stored and returned (such as in scan/query results or when iterating), use one of these:

-   **Set `WritePolicy.send_key` to `true`:** The client sends the user key to the server on every write. The server stores it with the record and returns it on reads, scans, and queries. Use this when you want the key available everywhere (scans, queries, secondary indexes) without putting it in a bin.
    
-   **Store the key in a bin:** Write the key as normal bin data (such as a bin named `"user_key"`) and read it back when you read the record. The server still identifies the record by digest. The key is application data. Use this when you prefer to manage the key explicitly in your schema or need a different shape than the primary key.
    

## Batch operations on one record: `operate()`

Use [`Client::operate()`](https://docs.rs/aerospike/latest/aerospike/struct.Client.html#method.operate) to run multiple operations (such as add, get, put) on the same record in a single round-trip. The server runs the ops in order and returns the result (such as bins from read ops).

**Example: add then get in one call**

```rust
use aerospike::operations;

use aerospike::{Client, ClientPolicy, WritePolicy};

// One round-trip: add 10 to "counter", then read "counter" back.

let add_op = as_bin!("counter", 10i64);

let ops = vec![operations::add(&add_op), operations::get_bin("counter")];

let rec = client.operate(&wpolicy, &key, &ops).await?;

// rec.bins contains the result of the get
```

> 📖 **API reference**: [`Client::operate`](https://docs.rs/aerospike/latest/aerospike/struct.Client.html#method.operate) | [`operations::add`](https://docs.rs/aerospike/latest/aerospike/operations/fn.add.html) | [`operations::get_bin`](https://docs.rs/aerospike/latest/aerospike/operations/fn.get_bin.html)

## Replace mode for full-record overwrites

When you create or update all bins for a record in one command, use `Replace` mode so the server can overwrite the record without reading it first. Do not use `Replace` when you are only updating a subset of bins, or you will remove bins you didn’t send.

```rust
let mut policy = WritePolicy::default();

policy.record_exists_action = RecordExistsAction::Replace;

client.put(&policy, &key, &bins).await?;
```

> 📖 **API reference**: [`WritePolicy::default`](https://docs.rs/aerospike/latest/aerospike/struct.WritePolicy.html) | [`RecordExistsAction`](https://docs.rs/aerospike/latest/aerospike/enum.RecordExistsAction.html) | [`Client::put`](https://docs.rs/aerospike/latest/aerospike/struct.Client.html#method.put)

## Reuse policies

Each command takes a policy as the first argument. If the policy is the same for a group of commands, create it once and reuse it instead of creating a new policy for each call.

```rust
let mut policy = WritePolicy::default();

policy.record_exists_action = RecordExistsAction::Replace;

client.put(&policy, &key1, &bins1).await?;

client.put(&policy, &key2, &bins2).await?;
```

> 📖 **API reference**: [`WritePolicy::default`](https://docs.rs/aerospike/latest/aerospike/struct.WritePolicy.html) | [`RecordExistsAction`](https://docs.rs/aerospike/latest/aerospike/enum.RecordExistsAction.html) | [`Client::put`](https://docs.rs/aerospike/latest/aerospike/struct.Client.html#method.put)

## Error handling

Client methods return `Result<T, aerospike::Error>`. For the `Error` enum, pattern matching, propagation with `?`, using [`anyhow`](https://crates.io/crates/anyhow) or [`thiserror`](https://crates.io/crates/thiserror), and matching on specific errors before converting, see [Error handling](https://aerospike.com/docs/develop/client/rust/2.1.0/error-handling).

## Next steps

Usage examples

Ready for more code examples? We have usage examples for all the basic CRUD operations.

[Usage examples →](https://aerospike.com/docs/develop/client/rust/2.1.0/usage/atomic/create)

Error handling

Actionable errors with recovery suggestions. Know exactly what went wrong and how to fix it.

[Error handling →](https://aerospike.com/docs/develop/client/rust/2.1.0/error-handling)