---
title: "Batched commands"
description: "Guide to executing batched reads, writes, and complex multi-operation commands using the Aerospike C# client."
---

# Batched commands

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

Jump to the [Code block](#code-block) for a combined complete example.

[Batched commands](https://aerospike.com/docs/develop/learn/batch) execute against multiple records issued as a single request. Batch reads support `get`, `exists`, `getHeader`, and `operate` requests. Batch writes, introduced in Aerospike 6.0.0, allow write requests against any keys, including updates, deletes, UDFs, and multi-operation `operate` commands.

## Setup

The following examples will use the setup and record structure below to illustrate batch operations in an Aerospike database.

```csharp
using Aerospike.Client;

using System;

using System.Collections;

// Define host configuration

Host config = new Host("127.0.0.1", 3000);

// Establishes a connection to the server

AerospikeClient client = new AerospikeClient(null, config);
```

The record structure:

```asciidoc
Occurred: Integer

Reported: Integer

Posted: Integer

Report: Map

{

    shape: List,

    summary: String,

    city: String,

    state: String,

    duration: String

}

Location: GeoJSON
```

## Policies

Policies are defined for the batch parent policy as well as batch read, batch write, batch delete, and batch UDF operations. [Filter Expressions](https://aerospike.com/docs/develop/client/csharp/usage/atomic/expressions) can be defined within each type of batch operation policy and the batch parent policy, along with other operation specific policies.

```csharp
// Create a new batch policy

BatchPolicy batchPolicy = new BatchPolicy();

batchPolicy.filterExp = Exp.Build(

    // An example that will always return true

    Exp.GT(Exp.Val(2), Exp.Val(1))

);

// Create the batch write policy

BatchWritePolicy batchWritePolicy = new BatchWritePolicy();

batchWritePolicy.filterExp = Exp.Build(

    // An example that will always return true

    Exp.GT(Exp.Val(2), Exp.Val(1))

);
```

Two `BatchPolicy` fields shape how a batch actually runs:

-   `maxConcurrentThreads`
    
    Defaults to `1`, which sends each node’s portion of the batch serially. Set it to `0` to issue all node requests in parallel, or to a positive number to cap the worker threads. Raising it trades client threads and connections for latency, so size it against `ClientPolicy.maxConnsPerNode`.
    
-   `respondAllKeys`
    
    Defaults to `true`, which tells the server to return a result for every key even after one of them fails. Setting it to `false` lets the server stop early, so some records come back with no result at all.
    

```csharp
BatchPolicy batchPolicy = new(client.BatchParentPolicyWriteDefault)

{

  // 0 issues all node requests in parallel. Positive values cap the thread count.

  maxConcurrentThreads = 4

};
```

## Handling partial failures

A batch is not all-or-nothing. Individual keys can fail while the rest of the batch succeeds, so a batch command that does not throw has not necessarily done everything you asked. Which signals you get depends on the overload:

-   `Get(BatchPolicy, Key[])` returns `Record[]`.
    
    A `null` entry means the record was not found. There are no per-key result codes on this overload, so genuine failures throw instead.
    
-   `Get(BatchPolicy, List<BatchRead>)` and the `Operate()` overloads
    
    Set `BatchRecord.resultCode` per key. `ResultCode.KEY_NOT_FOUND_ERROR` is an ordinary miss. Any other non-`OK` code is a failure for that key.
    
-   `Operate()` also reports an aggregate.
    
    The `BatchResults` overload sets `BatchResults.status`, and the `List<BatchRecord>` overload returns a `bool`. Both are `true` only when every subcommand succeeded, and both are easy to discard by accident.
    
-   `BatchRecord.inDoubt`
    
    Means a write may have been applied even though the client could not confirm it. Resolve an in-doubt record before retrying a non-idempotent operation.
    

An `AerospikeException` from a batch call signals a command-level or node-level failure, which is a different thing from a key that was not found. The examples below show each of these signals in place.

## Requests

### Exists

The following example creates an array of ten keys and checks for their existence in the database.

```csharp
// Create batch of keys

Key[] keys = new Key[10];

for (int i = 0; i < 10; i++)

{

    keys[i] = new Key("sandbox", "ufodata", (i + 4995));

}

// Check if records exist

bool[] exists = client.Exists(batchPolicy, keys);

for (int i = 0; i < exists.Length; i++)

{

    if(!exists[i])

    {

        // Do something

        Console.WriteLine("Key: {0} does not exist", keys[i].userKey);

    }

}

// Close the connection to the server

client.Close();
```

### Read records

The following example creates an array of ten keys and reads the records from the database; returning either the whole record or the specified `report` and `location` bins.

```csharp
// Create batch of keys

Key[] keys = new Key[10];

for (int i = 0; i < 10; i++)

{

    keys[i] = new Key("sandbox", "ufodata", (i + 1));

}

// Read each whole record

Record[] records = client.Get(batchPolicy, keys);

// Or specifiy bins

// Record[] records = client.Get(batchPolicy, keys, "report", "location");

// Access the records

foreach (Record record in records)

{

    if(record != null)

    {

        // Do something

        Console.WriteLine("Record: {0}\\n", record.ToString().Split("bins:")[1]);

    }

}

// Close the connection to the server

client.Close();
```

### Missing records and command failures

For list-based batch reads, inspect each record’s `resultCode`. `KEY_NOT_FOUND_ERROR` is an ordinary missing key. Other per-record codes indicate failures. A batch exception represents a command or node failure and is distinct from a missing record.

```csharp
try

{

  client.Get(null, records);

  foreach (BatchRead read in records)

  {

    Key key = read.key;

    if (read.resultCode == ResultCode.OK)

    {

      Console.WriteLine($"Record: ns={key.ns} set={key.setName} key={key.userKey} bin={BinName} value={read.record?.GetValue(BinName)}");

    }

    else if (read.resultCode == ResultCode.KEY_NOT_FOUND_ERROR)

    {

      Console.WriteLine($"Record not found: ns={key.ns} set={key.setName} key={key.userKey}");

    }

    else

    {

      Console.Error.WriteLine($"Record failed: key={key.userKey} result={ResultCode.GetResultString(read.resultCode)}");

    }

  }

}

catch (AerospikeException ae)

{

  // A batch exception represents a command/node failure, not an ordinary missing key.

  Console.Error.WriteLine($"Batch command failed: {ae.Message}");

  throw;

}
```

### Read commands

The following example creates an array of ten keys and accesses the `city` and `state` map keys to return their respective values from the `report` bin, for each record.

```csharp
// Create batch of keys

Key[] keys = new Key[10];

for (int i = 0; i < 10; i++)

{

    keys[i] = new Key("sandbox", "ufodata", (i + 1));

}

// Create map key list

string[] mapKeys = {"city", "state"};

// Get 'city' and 'state' from report map for each record

BatchResults batchResult = client.Operate(batchPolicy, batchWritePolicy, keys,

    MapOperation.GetByKeyList("report", mapKeys, MapReturnType.VALUE)

);

// Access the records

foreach (BatchRecord batchRecord in batchResult.records)

{

    Record record = batchRecord.record;

    if(record != null)

    {

        // Do something

        Console.WriteLine("Record: {0}\\n", record.ToString().Split("bins:")[1]);

    }

}

// Close the connection to the server

client.Close();
```

### Inspect aggregate and per-record status

`BatchResults.status` reports whether every subcommand succeeded. Always retain it, then inspect `resultCode` and `inDoubt` for each failed record. `inDoubt` means a write may have been applied even though the client could not confirm the result.

```csharp
BatchResults batch = client.Operate(batchPolicy, null, keys,

  ListOperation.Append(ListPolicy.Default, BinName3, Value.Get(999)),

  ListOperation.Size(BinName3),

  ListOperation.GetByIndex(BinName3, -1, ListReturnType.VALUE));

Console.WriteLine($"All batch sub-commands succeeded: {batch.status}");

for (int i = 0; i < batch.records.Length; i++)

{

  BatchRecord br = batch.records[i];

  if (br.resultCode == 0)

  {

    IList results = br.record.GetList(BinName3);

    Console.WriteLine($"Result[{i}]: {results[1]},{results[2]}");

  }

  else

  {

    Console.WriteLine(

      $"Result[{i}]: error={ResultCode.GetResultString(br.resultCode)} " +

      $"inDoubt={br.inDoubt}");

  }

}
```

### Read/write commands

The following example creates an array of ten keys and

1.  Defines an [Operation Expression](https://aerospike.com/docs/develop/client/csharp/usage/atomic/expressions#operation-expressions) that compares the `occurred` bin value against the provided value, `20211231`, and verifies the `posted` bin exists to determine the boolean value of the new `recent` key being added to the `report` map.
2.  Returns the `report` bin.

```csharp
// Create batch of keys

Key[] keys = new Key[10];

for (int i = 0; i < 10; i++)

{

    keys[i] = new Key("sandbox", "ufodata", (i + 1));

}

// Define Operation Expressions

Expression exp = Exp.Build(

    MapExp.Put(MapPolicy.Default, Exp.Val("recent"),

        Exp.And(

            Exp.GT(Exp.IntBin("occurred"), Exp.Val(20211231)),

            Exp.BinExists("posted")

        ),

        Exp.MapBin("report")

    )

);

// Execute the write operation and return the report bin

BatchResults batchResult = client.Operate(batchPolicy, batchWritePolicy, keys,

    ExpOperation.Write("report", exp, ExpWriteFlags.DEFAULT),

    Operation.Get("report")

);

// Access the records

foreach (BatchRecord batchRecord in batchResult.records)

{

    Record record = batchRecord.record;

    if(record != null)

    {

        // Do something

        Console.WriteLine("Record: {0}\\n", record.ToString().Split("bins:")[1]);

    }

}

// Close the connection to the server

client.Close();
```

### Deletes

The following example deletes the records from the database.

```csharp
// Create batch of keys

Key[] keys = new Key[10];

for (int i = 0; i < 10; i++)

{

    keys[i] = new Key("sandbox", "ufodata", (i + 1));

}

// Delete records passing null to use the default BatchDeletePolicy

BatchResults batchResults = client.Delete(batchPolicy, null, keys);

// Close the connection to the server

client.Close();
```

### Complex batched commands

The following example creates a list of four batch records that each use a differing set of operations.

#### The record with user defined key `4000`

1.  uses the `ops1` array that combines the [Operation Expression](https://aerospike.com/docs/develop/client/csharp/usage/atomic/expressions#operation-expressions).
2.  uses `exp1` which compares the `occurred` bin value against the provided value, `20211231` and verifies the `posted` bin exists to determine the boolean value of the new `recent` key being added to the `report` map.
3.  returns the `report` bin.

#### The record with user defined key `4001`

1.  uses the `ops2` array which contains a read [Operation Expression](https://aerospike.com/docs/develop/client/csharp/usage/atomic/expressions#operation-expressions) that gets the length of the `shape` list from the `report` map and returns the value in a computed bin named `numShapes`.

#### The record with user defined key `4002`

1.  uses the `ops3` array which combines a write operation that updates the `posted` bin value, with a map operation that updates the `city` value in the `report` map.
2.  returns both the `posted` and `report` bins.

#### The record with user defined key `4003` is deleted from the database.

```csharp
// Define Operation Expressions

Expression exp1 = Exp.Build(

    MapExp.Put(MapPolicy.Default, Exp.Val("recent"),

        Exp.And(

            Exp.GT(Exp.IntBin("occurred"), Exp.Val(20211231)),

            Exp.BinExists("posted")

        ),

        Exp.MapBin("report")

    )

);

Expression exp2 = Exp.Build(

    ListExp.Size(

        MapExp.GetByKey(MapReturnType.VALUE, Exp.Type.LIST, Exp.Val("shape"), Exp.MapBin("report"))

    )

);

// Define operations

Operation[] ops1 = Operation.Array(

    ExpOperation.Write("report", exp1, ExpWriteFlags.DEFAULT),

    Operation.Get("report")

);

Operation[] ops2 = Operation.Array(ExpOperation.Read("numShapes", exp2, ExpReadFlags.DEFAULT));

Operation[] ops3 = Operation.Array(

    Operation.Put(new Bin("posted", 20201108)),

    MapOperation.Put(MapPolicy.Default, "report", Value.Get("city"), Value.Get("Cedarville")),

    Operation.Get("posted"),

    Operation.Get("report")

);

// Create list of batch records to process

List<BatchRecord> batchRecordList = new List<BatchRecord>();

batchRecordList.Add(new BatchWrite(new Key("sandbox", "ufodata", 4000), ops1));

batchRecordList.Add(new BatchRead(new Key("sandbox", "ufodata", 4001), ops2));

batchRecordList.Add(new BatchWrite(new Key("sandbox", "ufodata", 4002), ops3));

batchRecordList.Add(new BatchDelete(new Key("sandbox", "ufodata", 4003)));

// Process the batch and retain whether every sub-command succeeded.

bool allSucceeded = client.Operate(batchPolicy, batchRecordList);

Console.WriteLine("All batch sub-commands succeeded: {0}", allSucceeded);

// Access the results

foreach (BatchRecord batchRecord in batchRecordList)

{

    Record record = batchRecord.record;

    if (batchRecord.resultCode == ResultCode.OK)

    {

        // Do something

        Console.WriteLine("Record: {0}\\n", record?.ToString().Split("bins:")[1]);

    }

    else

    {

        Console.WriteLine("Result: {0} | In doubt: {1}",

            ResultCode.GetResultString(batchRecord.resultCode), batchRecord.inDoubt);

    }

}

// Close the connection to the server

client.Close();
```

## Code block

Expand this section for a single code block to execute a batch read/write operation

```csharp
using Aerospike.Client;

using System;

using System.Collections;

// Define host configuration

Host config = new Host("127.0.0.1", 3000);

// Establishes a connection to the server

AerospikeClient client = new AerospikeClient(null, config);

// Create a new batch policy

BatchPolicy batchPolicy = new BatchPolicy();

batchPolicy.filterExp = Exp.Build(

    // An example that will always return true

    Exp.GT(Exp.Val(2), Exp.Val(1))

);

// Create the batch write policy

BatchWritePolicy batchWritePolicy = new BatchWritePolicy();

batchWritePolicy.filterExp = Exp.Build(

    // An example that will always return true

    Exp.GT(Exp.Val(2), Exp.Val(1))

);

// Create batch of keys

Key[] keys = new Key[10];

for (int i = 0; i < 10; i++)

{

    keys[i] = new Key("sandbox", "ufodata", (i + 1));

}

// Define Operation Expressions

Expression exp = Exp.Build(

    MapExp.Put(MapPolicy.Default, Exp.Val("recent"),

        Exp.And(

            Exp.GT(Exp.IntBin("occurred"), Exp.Val(20211231)),

            Exp.BinExists("posted")

        ),

        Exp.MapBin("report")

    )

);

// Execute the write operation and return the report bin

BatchResults batchResult = client.Operate(batchPolicy, batchWritePolicy, keys,

    ExpOperation.Write("report", exp, ExpWriteFlags.DEFAULT),

    Operation.Get("report")

);

// Access the records

foreach (BatchRecord batchRecord in batchResult.records)

{

    Record record = batchRecord.record;

    if(record != null)

    {

        // Do something

        Console.WriteLine("Record: {0}\\n", record.ToString().Split("bins:")[1]);

    }

}

// Close the connection to the server

client.Close();
```