Skip to main content

Batch operations

A batch runs several operations of mixed types, puts, gets, and deletes together, in one round trip. This article shows you how to build a KvBatchRequest, execute it, and read the per-operation results, including the values returned by the gets.

Use a batch when you have related operations of different kinds to send together. Bulk operations move many keys of one operation type; a batch lets you combine a put, a get, and a delete in a single call.

This article uses types and helpers from two namespaces beyond your client bootstrap. Add both:

using Clustron.Zaris.Abstractions;    // KvBatchRequest, KvBatchItem, KvBatchOp
using Clustron.Zaris.Client.Helpers; // the GetValue<T>() extension method

Build and execute a batch

Construct a KvBatchRequest with a list of KvBatchItem. Give each item an Index, an Op (KvBatchOp.Put, KvBatchOp.Get, or KvBatchOp.Delete), and a Key. Put operations also set PutValue. Then pass the request to ExecuteBatchAsync<T>.

var batch = new KvBatchRequest
{
Items = new List<KvBatchItem>
{
new KvBatchItem { Index = 0, Op = KvBatchOp.Put, Key = "user:1", PutValue = "Ali" },
new KvBatchItem { Index = 1, Op = KvBatchOp.Put, Key = "user:2", PutValue = "Sarah" },
new KvBatchItem { Index = 2, Op = KvBatchOp.Delete, Key = "user:3" },
new KvBatchItem { Index = 3, Op = KvBatchOp.Get, Key = "user:1" }
}
};

var response = await client.ExecuteBatchAsync<string>(batch);

The signature is:

Task<KvBatchResponse> ExecuteBatchAsync<T>(
KvBatchRequest request,
CancellationToken ct = default);

The type parameter T is the type that get results decode to. In the example above, T is string because the get reads a string. If your batch reads values of different types, see Read values from a mixed-type batch.

Read the results

ExecuteBatchAsync returns a KvBatchResponse whose Results list holds one KvBatchItemResult per operation. Each result carries the Index you assigned, so match results back to the operations you sent by Index rather than by position.

foreach (var result in response.Results)
{
if (result.Op == KvBatchOp.Get && result.Success)
{
string? value = result.GetValue<string>();
Console.WriteLine($"[{result.Index}] {value}");
}
else if (!result.Success)
{
Console.WriteLine($"[{result.Index}] {result.Op} failed: {result.Status}");
}
}

Each KvBatchItemResult exposes:

MemberTypeDescription
IndexintThe index you assigned to the source item. Use it to correlate results with requests.
OpKvBatchOpThe operation this result came from.
SuccessboolWhether this operation succeeded.
StatusKvStatusThe specific outcome, for example NotFound for a get on a missing key.
Errorstring?A message when the operation failed.

To read the value from a get result, call the GetValue<T>() extension method, which deserializes the returned bytes into the type you ask for.

Read values from a mixed-type batch

When a batch reads values of different types, pick a general type argument for ExecuteBatchAsync (such as object) and decode each get result with GetValue<T>(), naming the concrete type per item.

var response = await client.ExecuteBatchAsync<object>(batch);

var customer = response.Results
.First(r => r.Index == 3)
.GetValue<Customer>();

GetValue<T>() deserializes the result's bytes and works the same whether the store is in-process or remote. Prefer it over reading ValueObject directly, because ValueObject is populated as an in-process convenience and is decoded as the batch's single type argument T.

Execution semantics

The client sends the items together and the store executes them as a group, one round trip per owning node. Two properties matter when you rely on a batch:

  • Operations execute in order, and results preserve that order through the Index field.
  • A batch is not transactional. Some operations can succeed while others fail, so you must check Success on each result.
warning

A batch groups operations for efficiency; it does not make them atomic. If a batch reads a value and then writes based on it, another writer can change the key in between. When you need all-or-nothing semantics, use a transaction instead.

Batch versus bulk

Both send multiple operations in one call. The difference is what they group.

AspectBulk operationsBatch operations
Operation typesOne type per call (PutManyAsync, GetManyAsync, DeleteManyAsync)Mixed (put, get, delete together)
Value typesOne type T per callCan differ per item; decode with GetValue<T>()
Result shapeIReadOnlyList<KvResult>KvBatchResponse.Results
Best forMoving many keys of the same shapeGrouping related operations of different kinds
TransactionalNoNo

Next steps