Skip to content

Batch operations

For the complete documentation index see: llms.txt

All documentation pages available in markdown.

Applies to

  • Aerospike Developer SDK (Java 21+ and Python 3.11+)
  • Aerospike Database 6.0 or later unless a section states otherwise

Learn how to perform multiple database operations efficiently in a single network request. Batch operations reduce latency and improve throughput when working with multiple records.

Except where noted, snippets on this page use the imports below. A snippet lists additional import lines only when it needs a type not shown here. When this page includes a Complete example section, that block is fully self-contained with every import required to run it.

import com.aerospike.client.sdk.DataSet;
import com.aerospike.client.sdk.Record;
import com.aerospike.client.sdk.RecordResult;
import com.aerospike.client.sdk.RecordStream;
import com.aerospike.client.sdk.ErrorStrategy;
import java.util.List;

Why use batch operations?

Approach100 RecordsNetwork Roundtrips
Individual operations~100ms+100
Batch operation~5-10ms1

Batch operations send multiple requests in a single network call, as well as sending requests to multiple servers concurrently, dramatically reducing latency.

Two kinds of batch

Batch commands come in two shapes, and which one you need depends on whether every key gets the same thing.

One operation applied to many keys. Pass a key list to the ordinary command and it runs once against every key in the list. The operation and its arguments are identical for all of them. This covers reads, deletes, existence checks, touches, UDF calls, and writes where every record gets the same bin values.

CommandWhat it does to every key in the list
query(keys)Reads the records
exists(keys)Reports whether each record exists
delete(keys)Deletes the records
touch(keys)Resets the time to expiration
insert(keys) / update(keys) / upsert(keys) / replace(keys)Applies the same bin values to each record
executeUdf(keys)Runs the same function with the same arguments

A different payload per key. When each record needs its own values, name the data set instead of a key list and supply one row per key. This is the batch-write shape, and it is what the legacy clients called a batch write.

DataSet users = DataSet.of("test", "users");
// One operation, many keys: every record gets tier="gold".
session.upsert(users.ids("user-1", "user-2", "user-3"))
.bin("tier").setTo("gold")
.execute();
// A different payload per key, still one request.
session.insert(users)
.bins("name", "score")
.id("user-1").values("Alice", 10)
.id("user-2").values("Bob", 20)
.id("user-3").values("Carol", 30)
.execute();

Batch read

Read multiple records in one request:

DataSet users = DataSet.of("test", "users");
RecordStream result = session.query(users.ids("user-1", "user-2", "user-3"))
.execute();
// Get all records
List<Record> records = new java.util.ArrayList<>();
result.forEach(rr -> {
if (rr.isOk()) {
records.add(rr.recordOrThrow());
}
});
for (Record record : records) {
System.out.println("Name: " + record.getString("name"));
}
// Or access by index
Record first = records.get(0); // user-1
Record second = records.get(1); // user-2

📖 API reference: DataSet.of(...) | DataSet.ids(...) | Session.query(List) | ChainableQueryBuilder.execute() | RecordStream.forEach(...) | RecordResult.isOk() | RecordResult.recordOrThrow() | Record.getString(...)

Batch write

Create or update multiple records:

DataSet users = DataSet.of("test", "users");
session.insert(users)
.bins("name", "email")
.id("user-1").values("Alice", "alice@example.com")
.id("user-2").values("Bob", "bob@example.com")
.id("user-3").values("Carol", "carol@example.com")
.execute();

📖 API reference: Session.insert(DataSet) | OperationObjectBuilder.bins(...) | IdValuesBuilder.id(...) | IdValuesRowBuilder.values(...) | ChainableQueryBuilder.execute()

Batch upsert

Insert or update multiple records:

DataSet users = DataSet.of("test", "users");
session
.upsert(users.ids("user-1","user-2"))
.bin("status").setTo("active")
.bin("balance").add(500)
.upsert(users.id("user-3"))
.bin("status").setTo("inactive")
.execute();

📖 API reference: DataSet.ids(...) | DataSet.id(...) | ChainableQueryBuilder.bin(...) | BinBuilder.add(...) | ChainableQueryBuilder.execute()

Batch delete

Delete multiple records:

DataSet users = DataSet.of("test", "users");
RecordStream result = session.delete(users.ids("user-1", "user-2", "user-3"))
.execute();
// Check which deletes succeeded
int i = 0;
while (result.hasNext()) {
RecordResult rr = result.next();
System.out.println("Record " + i++ + " deleted: " + rr.asBoolean());
}
result.close();

📖 API reference: DataSet.ids(...) | Session.delete(List) | Session.delete(Key) | ChainableQueryBuilder.execute() | RecordStream.hasNext() | RecordStream.next() | RecordStream.close()

Batch exists

Check whether several records exist, without reading their bins:

DataSet users = DataSet.of("test", "users");
RecordStream result = session.exists(users.ids("user-1", "user-2", "user-3")).execute();
while (result.hasNext()) {
RecordResult rr = result.next();
System.out.println(rr.getKey().userKey + " exists: " + rr.asBoolean());
}
result.close();

Batch touch

Reset the time to expiration on several records at once, without changing their bins:

DataSet users = DataSet.of("test", "users");
session.touch(users.ids("user-1", "user-2", "user-3")).execute();

Touch cannot create a record, because the server deletes empty records. The command fails for any key that does not exist. See Set time-to-live (TTL) for how the expiration is derived.

Mixed batch operations

Combine different operation types in one call. Note that the operations on each key are performed asynchronously across the nodes in the cluster, and hence the returned results may not be in the same order as the commands. Each returned item exposes a key and an index — key() and index() in Java, the key and index attributes in Python (row.key, row.index). key is the unique key of the record and index is the zero-based index of the command in the original list.

DataSet users = DataSet.of("test", "users");
RecordStream stream = session
.query(users.ids("user-1", "user-2"))
.upsert(users.id("user-3"))
.bin("status").setTo("active")
.delete(users.id("user-4"))
.execute();
stream.forEach(result -> {
switch (result.getIndex()) {
case 0 -> handleUser1(result);
case 1 -> handleUser2(result);
case 2 -> System.out.println("Upsert: " + (result.isOk() ? "ok" : result.getMessage()));
case 3 -> System.out.println("Delete: " + (result.isOk() ? "ok" : result.getMessage()));
}
});

📖 API reference: DataSet.ids(...) | DataSet.id(...) | ChainableQueryBuilder.bin(...) | ChainableQueryBuilder.execute() | RecordStream.forEach(...) | RecordResult.isOk()

Batch with selected bins

Read only specific bins in batch:

DataSet users = DataSet.of("test", "users");
RecordStream result = session.query(users.ids("user-1", "user-2", "user-3"))
.bins("name", "email")
.execute();

📖 API reference: DataSet.ids(...) | Session.query(List) | OperationObjectBuilder.bins(...) | ChainableQueryBuilder.execute()

Handle partial failures

Some operations in a batch may fail while others succeed:

DataSet users = DataSet.of("test", "users");
RecordStream result = session
.update(users.ids("user-1", "nonexistent", "user-3"))
.bin("lastSeen").setTo(System.currentTimeMillis())
.execute(ErrorStrategy.IN_STREAM); // Place errors in the RecordStream
result.forEach(rr -> {
String keyId = rr.getKey().userKey.toString();
if (!rr.isOk()) {
System.out.println(keyId + " failed: " + rr.getMessage());
} else {
System.out.println(keyId + ": updated");
}
});

📖 API reference: DataSet.ids(...) | ChainableQueryBuilder.bin(...) | ChainableQueryBuilder.execute() | RecordStream.forEach(...) | RecordResult.isOk() | ErrorStrategy | ErrorStrategy.IN_STREAM

Dynamic batch building

Build batches programmatically:

// Additional imports for this example:
import com.aerospike.client.sdk.IdValuesRowBuilder;
DataSet users = DataSet.of("test", "users");
IdValuesRowBuilder rows = session.upsert(users)
.bins("name", "score")
.id("user-1").values("User 1", 10);
for (int id = 2; id <= 50; id++) {
rows.id("user-" + id).values("User " + id, id * 10);
}
rows.execute();

📖 API reference: DataSet.id(...) | Session.upsert(DataSet) | OperationObjectBuilder.bins(...) | IdValuesBuilder.id(...) | IdValuesRowBuilder.values(...) | ChainableQueryBuilder.execute()

Complete example

This example is self-contained—it lists every import needed to run standalone.

import com.aerospike.client.sdk.Cluster;
import com.aerospike.client.sdk.ClusterDefinition;
import com.aerospike.client.sdk.DataSet;
import com.aerospike.client.sdk.Record;
import com.aerospike.client.sdk.RecordResult;
import com.aerospike.client.sdk.RecordStream;
import com.aerospike.client.sdk.Session;
import com.aerospike.client.sdk.policy.Behavior;
public class BatchOperationsExample {
public static void main(String[] args) {
try (Cluster cluster = new ClusterDefinition("localhost", 3000).connect()) {
Session session = cluster.createSession(Behavior.DEFAULT);
DataSet users = DataSet.of("test", "users");
String key1 = "batch-example-1";
String key2 = "batch-example-2";
String key3 = "batch-example-3";
// Cleanup so the example is repeatable.
session.delete(users.ids(key1, key2, key3)).execute().close();
// Batch insert
session.insert(users)
.bins("name", "age")
.id(key1).values("Alice", 28)
.id(key2).values("Bob", 35)
.id(key3).values("Carol", 22)
.execute();
System.out.println("Batch insert complete");
// Batch read
try (RecordStream readStream = session.query(users.ids(key1, key2, key3)).execute()) {
readStream.forEach(result -> {
Record record = result.recordOrThrow();
System.out.println(" - " + record.getString("name"));
});
}
// Batch update
session
.upsert(users.id(key1), users.id(key2))
.bin("status").setTo("active")
.upsert(users.id(key3))
.bin("status").setTo("inactive")
.execute().close();
System.out.println("\nBatch update complete");
// Batch delete
session.delete(users.ids(key1, key2, key3))
.execute().close();
System.out.println("Batch delete complete");
}
}
}

📖 API reference: ClusterDefinition(String,int) | ClusterDefinition.connect() | Cluster.createSession(Behavior) | Cluster.close() | DataSet.of(...) | DataSet.ids(...) | Session.insert(DataSet) | Session.delete(List) | Session.delete(Key) | Session.query(List) | OperationObjectBuilder.bins(...) | IdValuesBuilder.id(...) | IdValuesRowBuilder.values(...) | ChainableQueryBuilder.bin(...) | ChainableQueryBuilder.execute() | ChainableNoBinsBuilder.execute() | RecordStream.forEach(...) | RecordStream.close() | RecordResult.recordOrThrow() | Record.getString(...)

API reference summary

JavaPythonDescription
session.query(dataSet.ids(...))await session.query(data_set.ids(...)).execute()Batch-read multiple record IDs
session.insert(dataSet) + repeated .id().values()pendingBatch insert with one request
session.upsert(dataSet) + repeated .id().values()pendingBatch upsert with one request
session.delete(dataSet.ids(...))await session.delete(data_set.ids(...)).execute()Batch delete multiple IDs
.bins(...) (varargs).bins([...]) (list)Select projected bins for batch reads
RecordStream / forEachRecordStream / async forIterate per-record results

Next steps