# Behaviors

Learn how Behaviors decouple operational configuration from business logic, letting you tune performance and reliability without changing your code.

## What are behaviors?

A **Behavior** is a reusable configuration object that defines how operations execute:

-   **Timeouts** — How long to wait for a response
-   **Retries** — How many times to retry on transient failures
-   **Consistency** — Read from master or allow replicas
-   **Durability** — Wait for commits to complete

Instead of scattering timeout and retry settings throughout your code, you define Behaviors once and apply them where needed.

-   [Java](#tab-panel-2904)
-   [Python](#tab-panel-2905)

```java
import com.aerospike.client.sdk.RecordStream;

// Create a session with a pre-built behavior

Session session = cluster.createSession(Behavior.DEFAULT);

// All operations use DEFAULT settings by default

RecordStream stream = session.query(users.id("user-1")).execute();

stream.getFirst().ifPresent(r -> { /* ... */ });

session.insert(users)

    .bins("name")

    .id("user-2").values("Bob")

    .execute();
```

```python
# Create a session with a pre-built behavior

session = cluster.create_session(Behavior.DEFAULT)

# All operations use DEFAULT settings by default

stream = await session.query(users.id("user-1")).execute()

row = await stream.first()

if row is not None:

    # ... handle the record ...

    pass

stream.close()

await session.insert(key=users.id("user-2")).put({"name": "Bob"}).execute()
```

## Behavior profiles

Use these common behavior profiles as starting points:

| Behavior | Best For | Characteristics |
| --- | --- | --- |
| `DEFAULT` | General purpose | Built-in baseline behavior |
| `READ_FAST` (derived) | Read-heavy workloads | Lower latency, allows AP replica reads |
| `STRICT_READS` (Java derived) / `STRICTLY_CONSISTENT` (Python built-in) | SC workloads | Linearizable reads for SC namespaces |

### DEFAULT

The default choice for most applications. Provides reasonable timeouts and retry behavior for mixed workloads.

-   [Java](#tab-panel-2906)
-   [Python](#tab-panel-2907)

```java
Session session = cluster.createSession(Behavior.DEFAULT);

// Typical settings:

// - Socket timeout: 30ms

// - Total timeout: 1000ms

// - Max retries: 2

// - Read mode: Master only
```

```python
session = cluster.create_session(Behavior.DEFAULT)

# Built-in Behavior.DEFAULT:

# - Socket timeout: 5s

# - Total timeout: 30s

# - Max retries: 2

# - send_key: True

# - Read mode: Master only
```

**Use DEFAULT when**:

-   You’re not sure which behavior to choose
-   Your workload is a mix of reads and writes
-   You want sensible defaults without fine-tuning

### READ\_FAST

Optimized for read-heavy applications where speed matters more than perfect consistency.

-   [Java](#tab-panel-2908)
-   [Python](#tab-panel-2909)

```java
import java.time.Duration;

import com.aerospike.client.sdk.policy.ReadModeAP;

import com.aerospike.client.sdk.policy.Behavior.Selectors;

Behavior readFast = Behavior.DEFAULT.deriveWithChanges("READ_FAST", builder -> builder

    .on(Selectors.reads().get().ap(), ops -> ops

        .abandonCallAfter(Duration.ofMillis(100))

        .maximumNumberOfCallAttempts(2)

        .readMode(ReadModeAP.ALL)));

Session session = cluster.createSession(readFast);

// Typical settings:

// - Socket timeout: 10ms

// - Total timeout: 100ms

// - Max retries: 1

// - Read mode: Allow replicas (eventual consistency)
```

```python
session = cluster.create_session(Behavior.READ_FAST)

# Built-in Behavior.READ_FAST:

# - Socket timeout: 50ms

# - Total timeout: 200ms

# - Max retries: 3

# - Read mode: Allow replicas (eventual consistency)
```

**Use READ\_FAST when**:

-   Reading cached or frequently-accessed data
-   Milliseconds matter (real-time bidding, gaming)
-   You can tolerate reading slightly stale data
-   The cluster has high replication (RF ≥ 2)

::: eventual consistency
With `READ_FAST`, you might read from a replica that hasn’t received the latest write. If you need the most current data, use `DEFAULT`.
:::

### CRITICAL\_WRITES (custom)

Prioritizes data safety over speed. Use for financial transactions, audit logs, and other critical writes.

-   [Java](#tab-panel-2910)
-   [Python](#tab-panel-2911)

```java
import java.time.Duration;

import com.aerospike.client.sdk.policy.CommitLevel;

import com.aerospike.client.sdk.policy.Behavior.Selectors;

Behavior criticalWrites = Behavior.DEFAULT.deriveWithChanges("CRITICAL_WRITES", builder -> builder

    .on(Selectors.writes().retryable().point().ap(), ops -> ops

        .abandonCallAfter(Duration.ofSeconds(5))

        .maximumNumberOfCallAttempts(5)

        .delayBetweenRetries(Duration.ofMillis(50))

        .commitLevel(CommitLevel.COMMIT_ALL))

    .on(Selectors.writes().nonRetryable().point().ap(), ops -> ops

        .abandonCallAfter(Duration.ofSeconds(5))

        .maximumNumberOfCallAttempts(1)

        .commitLevel(CommitLevel.COMMIT_ALL))

);

Session session = cluster.createSession(criticalWrites);

// Typical settings:

// - Socket timeout: 100ms

// - Total timeout: 5000ms

// - Max retries: 3

// - Commit level: All replicas
```

```python
session = cluster.create_session(Behavior.DEFAULT)

# Typical settings:

# - Socket timeout: 100ms

# - Total timeout: 5000ms

# - Max retries: 3

# - Commit level: All replicas
```

**Use a critical-write custom behavior when**:

-   Writing financial transactions
-   Storing audit logs or compliance data
-   You need confirmation that data is safely persisted
-   Losing a write would cause business impact

## Custom behaviors

Create custom Behaviors when the pre-built options don’t fit your needs:

-   [Java](#tab-panel-2912)
-   [Python](#tab-panel-2913)

```java
import java.time.Duration;

import com.aerospike.client.sdk.policy.ReadModeAP;

import com.aerospike.client.sdk.policy.Behavior.Selectors;

// Build a custom read behavior with explicit Java APIs.

Behavior fastReads = Behavior.DEFAULT.deriveWithChanges("READ_FAST_CUSTOM", builder -> builder

    .on(Selectors.reads().get().ap(), ops -> ops

        .abandonCallAfter(Duration.ofMillis(100))

        .maximumNumberOfCallAttempts(2)

        .delayBetweenRetries(Duration.ofMillis(5))

        .readMode(ReadModeAP.ALL))

    .on(Selectors.reads().batch().ap(), ops -> ops

        .abandonCallAfter(Duration.ofMillis(200))

        .maximumNumberOfCallAttempts(3))

);

// Use it

Session session = cluster.createSession(fastReads);
```

```python
from datetime import timedelta

from aerospike_sdk import Behavior

from aerospike_sdk.policy import Settings

# Build a custom behavior with explicit scope settings.

fast_reads = Behavior.DEFAULT.derive_with_changes(

    "READ_FAST_CUSTOM",

    reads_ap=Settings(

        total_timeout=timedelta(milliseconds=100),

        max_retries=2,

        retry_delay=timedelta(milliseconds=5),

    ),

    reads_batch=Settings(

        total_timeout=timedelta(milliseconds=200),

        max_retries=3,

    ),

)

# Use it

session = cluster.create_session(fast_reads)
```

### Available configuration options (Java)

| Setting goal | Java behavior method |
| --- | --- |
| End-to-end call timeout | `abandonCallAfter(Duration)` |
| Retry count | `maximumNumberOfCallAttempts(int)` |
| Delay between retries | `delayBetweenRetries(Duration)` |
| AP read mode | `readMode(ReadModeAP)` |
| SC read mode | `consistency(ReadModeSC)` |
| AP write durability | `commitLevel(CommitLevel)` |

::: selector-scoped tuning in java
Use these methods inside selector scopes, for example `Selectors.reads().get().ap()` or `Selectors.writes().retryable().point().ap()`.  
Build selector chains from general to specific for best type-safety.
:::

### Available configuration options (Python)

Use `Settings(...)` values with `Behavior.derive_with_changes(...)` scope kwargs:

Python does not expose a `Selectors` class. Use scope keyword arguments such as `reads_ap=...`, `reads_batch=...`, `writes=...`, and `all=...` on `derive_with_changes(...)`.

| Setting goal | Python behavior fields |
| --- | --- |
| End-to-end call timeout | `Settings(total_timeout=timedelta(...))` |
| Retry count | `Settings(max_retries=int)` |
| Delay between retries | `Settings(retry_delay=timedelta(...))` |
| AP read scope | `reads_ap=Settings(...)` |
| SC read scope | `reads_sc=Settings(...)` |
| Batch read scope | `reads_batch=Settings(...)` |
| All operations scope | `all=Settings(...)` |

### Python recipes for common behavior tasks

Use these patterns directly when deriving behaviors in Python:

```python
from datetime import timedelta

from aerospike_sdk import Behavior

from aerospike_sdk.policy import Settings

# TC-1: 100ms total timeout for AP reads.

tc1_behavior = Behavior.DEFAULT.derive_with_changes(

    "TC1_AP_READ_100MS",

    reads_ap=Settings(total_timeout=timedelta(milliseconds=100)),

)

# TC-2: 3-second retry delay for all operations.

tc2_behavior = Behavior.DEFAULT.derive_with_changes(

    "TC2_ALL_DELAY_3S",

    all=Settings(retry_delay=timedelta(seconds=3)),

)

# TC-3: up to 7 attempts for batch reads.

tc3_behavior = Behavior.DEFAULT.derive_with_changes(

    "TC3_BATCH_READ_RETRIES",

    reads_batch=Settings(max_retries=7),

)
```

Behavior derivation does not require running a record read/write. If you add a read to demonstrate usage, ensure your key exists before calling `first_or_raise()`.

For Python batch reads, use `query(data_set.ids(...))`. The async `session.batch()` builder is for batch writes/deletes/operate chains and does not expose a `get(...)` read method.

### Timeout deep dive

Understanding the two timeout types is crucial:

```plaintext
┌─────────────────────────────────────────────────────────┐

│                    totalTimeout                         │

├─────────────┬─────────────┬─────────────┬──────────────┤

│  Attempt 1  │   Retry 1   │   Retry 2   │   Retry 3    │

│ (socket TO) │ (socket TO) │ (socket TO) │ (socket TO)  │

└─────────────┴─────────────┴─────────────┴──────────────┘
```

-   **abandonCallAfter(…)**: Max time for an operation call attempt before it is abandoned
-   **maximumNumberOfCallAttempts(…)**: Max attempts (initial attempt + retries)

-   [Java](#tab-panel-2914)
-   [Python](#tab-panel-2915)

```java
import java.time.Duration;

import com.aerospike.client.sdk.policy.Behavior.Selectors;

// Example: Allow up to 5 attempts, with a 2-second call cap.

Behavior patient = Behavior.DEFAULT.deriveWithChanges("PATIENT", builder -> builder

    .on(Selectors.reads().get().ap(), ops -> ops

        .abandonCallAfter(Duration.ofSeconds(2))

        .maximumNumberOfCallAttempts(5)

        .delayBetweenRetries(Duration.ofMillis(25))));
```

```python
from datetime import timedelta

from aerospike_sdk import Behavior

from aerospike_sdk.policy import Settings

# Example: Allow up to 5 attempts for AP reads, with a 2-second total timeout.

patient = Behavior.DEFAULT.derive_with_changes(

    "PATIENT",

    reads_ap=Settings(

        total_timeout=timedelta(seconds=2),

        max_retries=5,

        retry_delay=timedelta(milliseconds=25),

    ),

)
```

### Read consistency options

For clusters in **AP mode** (availability-prioritized):

| ReadModeAP | Behavior |
| --- | --- |
| `ONE` | Read from master only (strong consistency) |
| `ALL` | Read from any replica (eventual consistency, lower latency) |

For clusters in **SC mode** (strong consistency):

| ReadModeSC | Behavior |
| --- | --- |
| `SESSION` | Session consistency (your writes are visible to your reads) |
| `LINEARIZE` | Linearizable reads (strongest guarantee, highest latency) |

-   [Java](#tab-panel-2916)
-   [Python](#tab-panel-2917)

```java
import com.aerospike.client.sdk.policy.ReadModeAP;

import com.aerospike.client.sdk.policy.ReadModeSC;

import com.aerospike.client.sdk.policy.Behavior.Selectors;

// For AP mode cluster: allow replica reads.

Behavior eventualReads = Behavior.DEFAULT.deriveWithChanges("EVENTUAL_READS", builder -> builder

    .on(Selectors.reads().get().ap(), ops -> ops.readMode(ReadModeAP.ALL)));

// For SC mode cluster: linearizable reads.

Behavior strictReads = Behavior.DEFAULT.deriveWithChanges("STRICT_READS", builder -> builder

    .on(Selectors.reads().get().cp(), ops -> ops.consistency(ReadModeSC.LINEARIZE)));
```

```python
# For AP mode cluster: allow replica reads

eventual_reads = Behavior.READ_FAST

# For SC mode cluster: linearizable reads

strict_reads = Behavior.STRICTLY_CONSISTENT
```

## Session-level behavior selection

In Java, behavior is selected at session creation time. Use multiple sessions when a subset of operations needs a different behavior:

### Session-level (default for all operations)

Set a Behavior when creating the session. All operations inherit it:

-   [Java](#tab-panel-2918)
-   [Python](#tab-panel-2919)

```java
import com.aerospike.client.sdk.RecordStream;

// All operations on this session use critical-write settings

Behavior criticalWrites = Behavior.DEFAULT.deriveWithChanges("CRITICAL_WRITES", b -> {});

Session session = cluster.createSession(criticalWrites);

session.insert(users)

    .bins("name")

    .id("user-1").values("Alice")

    .execute();  // Durable

RecordStream read = session.query(users.id("user-1")).execute();  // Durable

read.getFirst().ifPresent(r -> { /* ... */ });

read.close();

session.delete(users.id("user-1")).execute().close();  // Durable
```

```python
from datetime import timedelta

from aerospike_sdk import Behavior

from aerospike_sdk.policy import Settings

# All operations on this session use custom critical-write settings

critical_writes = Behavior.DEFAULT.derive_with_changes(

    "CRITICAL_WRITES",

    writes=Settings(

        total_timeout=timedelta(seconds=5),

        max_retries=5,

        retry_delay=timedelta(milliseconds=50),

    ),

)

session = cluster.create_session(critical_writes)

await session.insert(key=users.id("user-1")).put({"name": "Alice"}).execute()  # Durable

stream = await session.query(users.id("user-1")).execute()  # Durable

await stream.first_or_raise()

stream.close()

stream = await session.delete(key=users.id("user-1")).execute()  # Durable

stream.close()
```

### Per-operation behavior choice (dedicated sessions)

Use a dedicated session for operations that need different retry/consistency settings:

-   [Java](#tab-panel-2920)
-   [Python](#tab-panel-2921)

```java
// Session uses DEFAULT by default.

Session defaultSession = cluster.createSession(Behavior.DEFAULT);

// Most operations use DEFAULT

RecordStream q1 = defaultSession.query(users.id("user-1")).execute();

q1.getFirst().ifPresent(r -> { /* ... */ });

q1.close();

// This specific write uses a dedicated critical-write session.

Behavior criticalWrites = Behavior.DEFAULT.deriveWithChanges("CRITICAL_WRITES", builder -> builder

    .on(Selectors.writes().retryable().point().ap(), ops -> ops.maximumNumberOfCallAttempts(5)));

Session criticalSession = cluster.createSession(criticalWrites);

criticalSession.insert(orders)

    .bins("total")

    .id("order-1").values(999.99)

    .execute();

// Back to DEFAULT session.

RecordStream q2 = defaultSession.query(users.id("user-2")).execute();

q2.getFirst().ifPresent(r -> { /* ... */ });

q2.close();
```

```python
from datetime import timedelta

from aerospike_sdk import Behavior

from aerospike_sdk.policy import Settings

# Session uses DEFAULT by default

session = cluster.create_session(Behavior.DEFAULT)

# Most operations use DEFAULT

stream = await session.query(users.id("user-1")).execute()

row = await stream.first()

# ... handle row if present ...

stream.close()

# This specific write uses a dedicated critical-write session

critical_writes = Behavior.DEFAULT.derive_with_changes(

    "CRITICAL_WRITES",

    writes=Settings(

        total_timeout=timedelta(seconds=5),

        max_retries=5,

        retry_delay=timedelta(milliseconds=50),

    ),

)

critical_session = cluster.create_session(critical_writes)

await critical_session.insert(key=orders.id("order-1")).put(

    {"total": 999.99}

).execute()

# Back to DEFAULT

stream = await session.query(users.id("user-2")).execute()

row = await stream.first()

# ... handle row if present ...

stream.close()
```

## Retry strategies

### Automatic retries

The Developer SDK automatically retries transient failures based on your Behavior configuration:

-   [Java](#tab-panel-2922)
-   [Python](#tab-panel-2923)

```java
import com.aerospike.client.sdk.RecordStream;

import com.aerospike.client.sdk.Session;

// DEFAULT behavior: automatic retries per policy

Session session = cluster.createSession(Behavior.DEFAULT);

// This operation will automatically retry on transient failures (per policy)

RecordStream stream = session.query(users.id("user-1")).execute();

stream.getFirst().ifPresent(r -> { /* ... */ });
```

```python
# DEFAULT behavior: automatic retries per policy

session = cluster.create_session(Behavior.DEFAULT)

# This operation will automatically retry on transient failures (per policy)

stream = await session.query(users.id("user-1")).execute()

await stream.first_or_raise()

stream.close()
```

### Manual retry with backoff

For application-level retries with exponential backoff:

-   [Java](#tab-panel-2924)
-   [Python](#tab-panel-2925)

```java
import java.time.Duration;

import com.aerospike.client.sdk.Record;

import com.aerospike.client.sdk.RecordResult;

import com.aerospike.client.sdk.RecordStream;

public <T> T retryWithBackoff(Supplier<T> operation, int maxRetries) {

    int attempt = 0;

    long delayMs = 100;

    while (true) {

        try {

            return operation.get();

        } catch (AerospikeException.Timeout | AerospikeException.Connection e) {

            attempt++;

            if (attempt >= maxRetries) {

                throw e;  // Exhausted retries

            }

            System.out.println("Retry " + attempt + " after " + delay.toMillis() + "ms");

            try {

                Thread.sleep(delayMs);

            } catch (InterruptedException ie) {

                Thread.currentThread().interrupt();

                throw new RuntimeException(ie);

            }

            // Exponential backoff with jitter

            delayMs = delayMs * 2 + ((long)(Math.random() * 100));

        }

    }

}

// Usage

Record user = retryWithBackoff(

    () -> {

        try (RecordStream stream = session.query(users.id("user-1")).execute()) {

            return stream.getFirstRecord();

        }

    },

    5  // Max 5 retries

);
```

```python
import asyncio

import random

import time

from aerospike_sdk import ConnectionError, TimeoutError

async def retry_with_backoff(operation, max_retries=5):

    attempt = 0

    delay = 0.1  # 100ms

    while True:

        try:

            return await operation()

        except (TimeoutError, ConnectionError):

            attempt += 1

            if attempt >= max_retries:

                raise  # Exhausted retries

            print(f"Retry {attempt} after {delay * 1000:.0f}ms")

            await asyncio.sleep(delay)

            # Exponential backoff with jitter

            delay = delay * 2 + random.uniform(0, 0.1)

# Usage

async def load_user():

    stream = await session.query(users.id("user-1")).execute()

    try:

        row = await stream.first_or_raise()

        return row.record_or_raise()

    finally:

        stream.close()

user = await retry_with_backoff(load_user, max_retries=5)
```

For more information on Aerospike exceptions, see [Error handling](https://aerospike.com/docs/develop/client/sdk/concepts/errors/).

## Multiple sessions pattern

A common pattern is to create multiple sessions with different behaviors:

-   [Java](#tab-panel-2926)
-   [Python](#tab-panel-2927)

```java
import com.aerospike.client.sdk.DataSet;

import com.aerospike.client.sdk.Record;

import com.aerospike.client.sdk.RecordResult;

import com.aerospike.client.sdk.RecordStream;

public class DatabaseService {

    private final DataSet users = DataSet.of("app", "users");

    private final DataSet payments = DataSet.of("app", "payments");

    private final Session readSession;

    private final Session writeSession;

    private final Session criticalSession;

    public DatabaseService(Cluster cluster) {

        Behavior readFast = Behavior.DEFAULT.deriveWithChanges("READ_FAST", builder -> builder

            .on(Selectors.reads().get().ap(), ops -> ops.maximumNumberOfCallAttempts(2)));

        Behavior criticalWrites = Behavior.DEFAULT.deriveWithChanges("CRITICAL_WRITES", builder -> builder

            .on(Selectors.writes().retryable().point().ap(), ops -> ops.maximumNumberOfCallAttempts(5)));

        // Fast reads for user-facing queries

        this.readSession = cluster.createSession(readFast);

        // Standard writes

        this.writeSession = cluster.createSession(Behavior.DEFAULT);

        // Critical operations (payments, audit logs)

        this.criticalSession = cluster.createSession(criticalWrites);

    }

    public Optional<Record> getUser(String userId) {

        RecordStream stream = readSession.query(users.id(userId)).execute();

        Optional<Record> out = stream.getFirst()

            .filter(RecordResult::isOk)

            .map(RecordResult::recordOrThrow);

        stream.close();

        return out;

    }

    public void updatePreferences(String userId, Map<String, Object> prefs) {

        writeSession.upsert(users)

            .bins("prefs")

            .id(userId).values(prefs)

            .execute();

    }

    public void recordPayment(String paymentId, double amount) {

        criticalSession.insert(payments)

            .bins("amount", "timestamp")

            .id(paymentId).values(amount, System.currentTimeMillis())

            .execute();

    }

}
```

```python
import time

from datetime import timedelta

from aerospike_sdk import Behavior, DataSet

from aerospike_sdk.policy import Settings

class DatabaseService:

    users = DataSet.of("app", "users")

    payments = DataSet.of("app", "payments")

    def __init__(self, cluster):

        read_fast = Behavior.DEFAULT.derive_with_changes(

            "READ_FAST",

            reads_ap=Settings(

                total_timeout=timedelta(milliseconds=100),

                max_retries=2,

            ),

        )

        critical_writes = Behavior.DEFAULT.derive_with_changes(

            "CRITICAL_WRITES",

            writes=Settings(

                total_timeout=timedelta(seconds=5),

                max_retries=5,

                retry_delay=timedelta(milliseconds=50),

            ),

        )

        # Fast reads for user-facing queries

        self.read_session = cluster.create_session(read_fast)

        # Standard writes

        self.write_session = cluster.create_session(Behavior.DEFAULT)

        # Critical operations (payments, audit logs)

        self.critical_session = cluster.create_session(critical_writes)

    async def get_user(self, user_id):

        stream = await self.read_session.query(self.users.id(user_id)).execute()

        row = await stream.first_or_raise()

        record = row.record_or_raise()

        stream.close()

        return record

    async def update_preferences(self, user_id, prefs):

        await self.write_session.upsert(key=self.users.id(user_id)).put(

            {"prefs": prefs}

        ).execute()

    async def record_payment(self, payment_id, amount):

        await self.critical_session.insert(key=self.payments.id(payment_id)).put(

            {"amount": amount, "timestamp": time.time()}

        ).execute()
```

## Choosing the right behavior

Use this decision tree:

```plaintext
What type of operation?

├── Read operation

│   ├── Need latest data? → DEFAULT

│   └── Speed critical, stale OK? → READ_FAST

│

└── Write operation

    ├── Critical data (payments, audit)? → custom critical-write behavior

    └── Normal data? → DEFAULT
```

### Quick reference

| Scenario | Recommended Behavior |
| --- | --- |
| User profile lookups | `READ_FAST` |
| Shopping cart updates | `DEFAULT` |
| Payment processing | custom critical-write behavior |
| Real-time game state | `READ_FAST` |
| Audit logging | custom critical-write behavior |
| Session management | `DEFAULT` |
| Analytics writes | `DEFAULT` |
| Cache reads | `READ_FAST` |

## Behavior anti-patterns

### ❌ Don’t: use critical-write settings for everything

```java
// Too conservative—unnecessarily slow

Behavior criticalWrites = Behavior.DEFAULT.deriveWithChanges("CRITICAL_WRITES", builder -> builder

    .on(Selectors.writes().retryable().point().ap(), ops -> ops.maximumNumberOfCallAttempts(5)));

Session session = cluster.createSession(criticalWrites);

RecordStream s = session.query(users.id("user-1")).execute();  // Reads don't need durable write settings

s.getFirst().ifPresent(r -> { /* ... */ });

s.close();
```

### ❌ Don’t: set zero timeouts

```java
// Will fail constantly

Behavior bad = Behavior.DEFAULT.deriveWithChanges("BAD_TIMEOUTS", builder -> builder

    .on(Selectors.all(), ops -> ops.abandonCallAfter(Duration.ZERO)));
```

### ❌ Don’t: ignore timeouts in production

```java
// Development might work, production will have network variance

Behavior risky = Behavior.DEFAULT.deriveWithChanges("RISKY_TIMEOUTS", builder -> builder

    .on(Selectors.all(), ops -> ops.abandonCallAfter(Duration.ofDays(1))));
```

### ✅ Do: match behavior to operation type

```java
// Different behaviors for different needs

Behavior readFast = Behavior.DEFAULT.deriveWithChanges("READ_FAST", builder -> builder

    .on(Selectors.reads().get().ap(), ops -> ops.maximumNumberOfCallAttempts(2)));

Behavior criticalWrites = Behavior.DEFAULT.deriveWithChanges("CRITICAL_WRITES", builder -> builder

    .on(Selectors.writes().retryable().point().ap(), ops -> ops.maximumNumberOfCallAttempts(5)));

Session reads = cluster.createSession(readFast);

Session writes = cluster.createSession(Behavior.DEFAULT);

Session critical = cluster.createSession(criticalWrites);
```

## Next steps

Data Model

Understand namespaces, sets, and records.

[Data Model →](https://aerospike.com/docs/develop/client/sdk/concepts/data-model)

Error Handling

Handle timeouts and retries gracefully.

[Error Handling →](https://aerospike.com/docs/develop/client/sdk/concepts/errors)

Connect to Aerospike

Configure your cluster connection.

[Connect →](https://aerospike.com/docs/develop/client/sdk/connect)

Async Operations

Scale with non-blocking operations.

[Async Operations →](https://aerospike.com/docs/develop/client/sdk/usage/async)