Update records
Learn how to update existing records in Aerospike using the Developer SDK. This guide covers updating bins, using operations, conditional updates, and optimistic locking.
Update specific bins
Use update() to modify specific bins in an existing record:
DataSet users = DataSet.of("test", "users");
// Update only the email binsession.update(users.id("user-1")) .bin("email").setTo("newemail@example.com") .execute();
// Update multiple binssession.update(users.id("user-1")) .bin("email").setTo("newemail@example.com") .bin("phone").setTo("+1-555-1234") .bin("updated_at").setTo(System.currentTimeMillis()) .execute();import time
async def demo(session): users = DataSet.of("test", "users")
# Update only the email bin await ( session.update(users.id("user-1")) .bin("email").set_to("newemail@example.com") .execute() )
# Update multiple bins await ( session.update(users.id("user-1")) .bin("email").set_to("newemail@example.com") .bin("phone").set_to("+1-555-1234") .bin("updated_at").set_to(time.time()) .execute() )See also: Insert vs upsert vs other operations
Increment numbers
Atomically increment numeric values:
// Increment view count by 1session.update(users.id("user-1")) .bin("view_count").add(1) .execute();
// Increment by a larger amountsession.update(users.id("user-1")) .bin("points").add(100) .execute();
// Decrement (negative increment)session.update(users.id("user-1")) .bin("credits").add(-10) .execute();
// Multiple increments in one operationsession.update(users.id("user-1")) .bin("login_count").add(1) .bin("points").add(5) .bin("last_login").setTo(System.currentTimeMillis()) .execute();import time
async def demo(session): users = DataSet.of("test", "users")
# Increment view count by 1 await ( session.update(users.id("user-1")) .bin("view_count").increment_by(1) .execute() )
# Increment by a larger amount await ( session.update(users.id("user-1")) .bin("points").increment_by(100) .execute() )
# Decrement (negative increment) await ( session.update(users.id("user-1")) .bin("credits").increment_by(-10) .execute() )
# Multiple increments in one operation await ( session.update(users.id("user-1")) .bin("login_count").increment_by(1) .bin("points").increment_by(5) .bin("last_login").set_to(time.time()) .execute() )Read back after incrementing:
try (RecordStream writeResult = session.update(users.id("user-1")) .bin("view_count").add(1) .bin("view_count").get() .execute()) { Record updated = writeResult.getFirstRecord(); System.out.println("view_count: " + updated.getInt("view_count"));}async def demo(session): users = DataSet.of("test", "users")
# Increment first. stream = await session.update(users.id("user-1")) \ .bin("view_count").increment_by(1) \ .execute() stream.close()
# Read back the updated value. read_stream = await session.query(users.id("user-1")) \ .bins(["view_count"]) \ .execute() row = await read_stream.first_or_raise() record = row.record_or_raise() read_stream.close() print(f"view_count: {record.bins['view_count']}")Append to strings
Append text to string bins:
// Append to a log fieldsession.update(users.id("user-1")) .bin("activity_log").append("\n2024-01-15: Logged in") .execute();
// Prepend (add to beginning)session.update(users.id("user-1")) .bin("activity_log").prepend("Latest: ") .execute();async def demo(session): users = DataSet.of("test", "users") # Append to a log field await ( session.update(users.id("user-1")) .append("activity_log", "\n2024-01-15: Logged in") .execute() )
# Prepend (add to beginning) await ( session.update(users.id("user-1")) .prepend("activity_log", "Latest: ") .execute() )Update list/map bins (CDT)
Collection updates use the bin(...) builder with list/map path methods.
This lets you update nested structures without a full read-modify-write cycle.
import com.aerospike.client.sdk.DataSet;import com.aerospike.client.sdk.RecordResult;import com.aerospike.client.sdk.RecordStream;import com.aerospike.client.sdk.cdt.MapOrder;
DataSet hotels = DataSet.of("test", "hotels");String key = "hotel:1";
// CD-1 style: set a map entry value.session.update(hotels.id(key)) .bin("rooms").onMapKey("room1").setTo(150) .execute();
// CD-2 style: one round-trip with range read + count.try (RecordStream rs = session.update(hotels.id(key)) .bin("rooms").onMapKeyRange("room1", "room4").getKeysAndValues() .bin("rooms").onMapKeyRange("room1", "room4").count() .execute()) { var rec = rs.getFirstRecord(); List<?> roomData = rec.getList("rooms"); System.out.println("rooms: " + roomData.get(0)); System.out.println("room count: " + roomData.get(1)); // rangeResult = map entries in range, countResult = count}
// CD-3 style: append to list and read list size in one round-trip.try (RecordStream rs = session.update(hotels.id(key)) .bin("tags").listAppend("vip") .bin("tags").listSize() .execute()) { // Second result row contains size information.}
// CD-4 style: nested path update (rooms -> room1 -> rates).session.update(hotels.id(key)) .bin("rooms").onMapKey("room1", MapOrder.KEY_ORDERED) .onMapKey("rates").setTo(110) .execute();from aerospike_async import MapOrder
async def demo_cdt(session): hotels = DataSet.of("test", "hotels") key = hotels.id("hotel:1")
# CD-1 style: set a map entry value. await session.update(key).bin("rooms").on_map_key("room1").set_to(150).execute()
# CD-2 style: one round-trip with range read + count. stream = await ( session.update(key) .bin("rooms").on_map_key_range("room1", "room4").get_keys_and_values() .bin("rooms").on_map_key_range("room1", "room4").count() .execute() ) stream.close()
# CD-3 style: append to list and read list size in one round-trip. stream = await session.update(key).bin("tags").list_append("vip").bin("tags").list_size().execute() stream.close()
# CD-4 style: nested path update (rooms -> room1 -> rates). await ( session.update(key) .bin("rooms").on_map_key("room1", create_type=MapOrder.KEY_ORDERED) .on_map_key("rates").set_to(110) .execute() )Touch (update metadata only)
“Touch” a record to reset its TTL without changing bin values:
import java.time.Duration;import com.aerospike.client.sdk.AerospikeException;
// Reset TTL to 30 days from nowtry { session.touch(users.id("user-1")) .expireRecordAfter(Duration.ofDays(30)) .execute();} catch (AerospikeException e) { // If server/namespace TTL prerequisites are not met, you can get "Operation not allowed at this time". System.err.println("Touch failed: " + e.getMessage());}async def demo(session): users = DataSet.of("test", "users") # Reset TTL to 30 days from now await ( session.touch(users.id("user-1")) .expire_record_after_seconds(30 * 24 * 3600) .execute() )Delete bins
Remove specific bins from a record (without deleting the record):
// Remove the phone binsession.update(users.id("user-1")) .bin("phone").remove() .execute();
// Remove multiple binssession.update(users.id("user-1")) .bin("phone").remove() .bin("fax").remove() .bin("pager").remove() .execute();async def demo(session): users = DataSet.of("test", "users")
# Remove the phone bin await ( session.update(users.id("user-1")) .remove_bin("phone") .execute() )
# Remove multiple bins await ( session.update(users.id("user-1")) .remove_bin("phone") .remove_bin("fax") .remove_bin("pager") .execute() )Conditional update (optimistic locking)
Use generation checks to prevent concurrent update conflicts:
import com.aerospike.client.sdk.Record;import com.aerospike.client.sdk.RecordResult;import com.aerospike.client.sdk.RecordStream;
// Read current recordRecordStream readStream = session.query(users.id("user-1")).execute();Record user = readStream.getFirstRecord();
int currentGeneration = user.generation;
// Update only if generation hasn't changedtry { session.update(users.id("user-1")) .bin("email").setTo("newemail@example.com") .ensureGenerationIs(currentGeneration) .execute(); System.out.println("Update successful");} catch (GenerationException e) { System.out.println("Record was modified by another process");}from aerospike_sdk import GenerationError
async def demo(session): users = DataSet.of("test", "users") # Read current record stream = await session.query(users.id("user-1")).execute() row = await stream.first_or_raise() user = row.record_or_raise() stream.close() current_generation = user.generation
# Update only if generation hasn't changed try: await ( session.update(users.id("user-1")) .bin("email").set_to("newemail@example.com") .ensure_generation_is(current_generation) .execute() ) print("Update successful") except GenerationError: print("Record was modified by another process")Optimistic locking pattern
import com.aerospike.client.sdk.Record;import com.aerospike.client.sdk.RecordResult;import com.aerospike.client.sdk.RecordStream;
public void updateWithRetry(DataSet users, String userId, int maxRetries) { for (int attempt = 0; attempt < maxRetries; attempt++) { // Read current state RecordStream rs = session.query(users.id(userId)).execute(); Record user = rs.getFirstRecord();
int newBalance = user.getInt("balance") + 100;
try { // Attempt update with generation check session.update(users.id(userId)) .bin("balance").setTo(newBalance) .ensureGenerationIs(user.generation) .execute(); return; // Success } catch (GenerationException e) { // Retry on conflict System.out.println("Conflict, retrying..."); } } throw new RuntimeException("Max retries exceeded");}from aerospike_sdk import GenerationError
async def demo(session): users = DataSet.of("test", "users")
# Read current record stream = await session.query(users.id("user-1")).execute() row = await stream.first_or_raise() user = row.record_or_raise() stream.close()
current_generation = user.generation
# Update only if generation hasn't changed try: await ( session.update(users.id("user-1")) .bin("email").set_to("newemail@example.com") .ensure_generation_is(current_generation) .execute() ) print("Update successful") except GenerationError: print("Record was modified by another process")Update only if exists
Ensure the record exists before updating:
try { session.update(users.id("user-1")) .bin("email").setTo("newemail@example.com") .execute();} catch (RecordNotFoundException e) { System.out.println("Record doesn't exist");}from aerospike_sdk import AerospikeError, DataSetfrom aerospike_async.exceptions import ResultCode
async def demo(session): users = DataSet.of("test", "users") try: await ( session.update(users.id("user-1")) .bin("email").set_to("newemail@example.com") .execute() ) except AerospikeError as e: if e.result_code != ResultCode.KEY_NOT_FOUND_ERROR: raise print("Record doesn't exist")Complete example
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 UpdateRecordsExample { 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 key = "update-example-user";
// Seed data so the example is repeatable. session.upsert(users) .bins("name", "login_count", "points") .id(key).values("Alice Smith", 0, 0) .execute();
// Simple update session.update(users.id(key)) .bin("status").setTo("active") .bin("updated_at").setTo(System.currentTimeMillis()) .execute();
// Increment counters session.update(users.id(key)) .bin("login_count").add(1) .bin("points").add(10) .execute();
// Conditional update with generation check RecordStream rs = session.query(users.id(key)).execute(); Record user = rs.getFirstRecord(); session.update(users.id(key)) .bin("verified").setTo(true) .ensureGenerationIs(user.generation) .execute();
System.out.println("All updates completed!"); } }}import asyncioimport time
from aerospike_sdk import Behavior, DataSet, Client
async def main(): async with Client("localhost:3000") as client: session = client.create_session(Behavior.DEFAULT) users = DataSet.of("test", "users") key = users.id("update-example-user")
# Seed data so the example is repeatable. await session.upsert(key=key).put( {"name": "Alice Smith", "login_count": 0, "points": 0} ).execute()
# Simple update await ( session.update(key) .bin("status").set_to("active") .bin("updated_at").set_to(time.time()) .execute() )
# Increment counters await ( session.update(key) .bin("login_count").increment_by(1) .bin("points").increment_by(10) .execute() )
# Conditional update with generation check stream = await session.query(key).execute() row = await stream.first_or_raise() user = row.record_or_raise() stream.close() await ( session.update(key) .bin("verified").set_to(True) .ensure_generation_is(user.generation) .execute() )
print("All updates completed!")
if __name__ == "__main__": asyncio.run(main())API reference summary
| Method | Description | Link |
|---|---|---|
update() | Update an existing record, fails if the record doesn’t exist | Java · Python |
touch() | Update metadata/TTL only | Java · Python |
.bin(name).add(delta) / .bin(name).increment_by(delta) | Atomically increment a numeric bin | — |
.bin(name).append(text) / .prepend(text) | Append or prepend a string bin | — |
.bin(name).remove() | Remove a bin | — |
.ensureGenerationIs(gen) / .ensure_generation_is(gen) | Conditional update | — |
Next steps
Delete Records
Remove records from the database.
Batch Operations
Update multiple records efficiently.
Error Handling
Handle update conflicts and errors.