쓰기 작업
Write
- Sync
- Async
import aerospike_py as aerospike
client = aerospike.client({"hosts": [("127.0.0.1", 3000)]}).connect()
key: tuple[str, str, str] = ("test", "demo", "user1")
# 단순 write
client.put(key, {"name": "Alice", "age": 30})
# 지원 타입: str, int, float, bytes, list, dict, bool, None
client.put(key, {
"str_bin": "hello",
"int_bin": 42,
"float_bin": 3.14,
"list_bin": [1, 2, 3],
"map_bin": {"nested": "dict"},
})
# TTL 포함
client.put(key, {"val": 1}, meta={"ttl": 300})
# Create only (존재 시 실패)
client.put(key, {"val": 1}, policy={"exists": aerospike.POLICY_EXISTS_CREATE_ONLY})
await client.put(key, {"name": "Alice", "age": 30})
await client.put(key, {"val": 1}, meta={"ttl": 300})
await client.put(key, {"val": 1}, policy={"exists": aerospike.POLICY_EXISTS_CREATE_ONLY})
Update
- Sync
- Async
client.increment(key, "age", 1)
client.increment(key, "score", 0.5)
client.append(key, "name", " Smith")
client.prepend(key, "greeting", "Hello, ")
await client.increment(key, "age", 1)
await client.append(key, "name", " Smith")
Delete
- Sync
- Async
client.remove(key)
# generation check 포함
client.remove(key, meta={"gen": 5}, policy={"gen": aerospike.POLICY_GEN_EQ})
# 특정 bin 만 제거
client.remove_bin(key, ["temp_bin", "debug_bin"])
await client.remove(key)
await client.remove_bin(key, ["temp_bin"])
Touch (TTL Reset)
client.touch(key, val=600) # 또는: await client.touch(key, val=600)
Multi-Operation (Operate)
한 record에 여러 operation을 atomic하게 실행합니다.
- Sync
- Async
ops: list[dict] = [
{"op": aerospike.OPERATOR_WRITE, "bin": "name", "val": "Bob"},
{"op": aerospike.OPERATOR_INCR, "bin": "counter", "val": 1},
{"op": aerospike.OPERATOR_READ, "bin": "counter", "val": None},
]
record = client.operate(key, ops)
print(record.bins["counter"])
# 순서 보존 결과
result = client.operate_ordered(key, ops)
for bt in result.ordered_bins:
print(f"{bt.name} = {bt.value}")
record = await client.operate(key, ops)
result = await client.operate_ordered(key, ops)
Batch Write
한 번의 batch call로 여러 record의 per-record bin을 씁니다. put()과 마찬가지로 record마다 서로 다른 bin name과 value를 사용할 수 있습니다.
- Sync
- Async
records = [
(("test", "demo", "user1"), {"name": "Alice", "age": 30}),
(("test", "demo", "user2"), {"name": "Bob", "age": 25}),
(("test", "demo", "user3"), {"name": "Charlie", "age": 35}),
]
results = client.batch_write(records)
for br in results.batch_records:
if br.result != 0:
print(f"Failed: {br.key}, code={br.result}, in_doubt={br.in_doubt}")
records = [
(("test", "demo", "user1"), {"name": "Alice", "age": 30}),
(("test", "demo", "user2"), {"name": "Bob", "age": 25}),
(("test", "demo", "user3"), {"name": "Charlie", "age": 35}),
]
results = await client.batch_write(records)
for br in results.batch_records:
if br.result != 0:
print(f"Failed: {br.key}, code={br.result}, in_doubt={br.in_doubt}")
TTL 이 있는 Batch Write
TTL 은 두 수준에서 설정 가능:
- Batch-level:
policy={"ttl": N}이 batch 의 모든 record 에 적용. - Per-record:
(key, bins, {"ttl": N})이 해당 record 에 대해 batch-level TTL 을 override.
- Sync
- Async
# Batch-level TTL — 모든 record 가 30일 후 만료
results = client.batch_write(records, policy={"ttl": 2592000})
# Per-record TTL — 각 record 가 자기 만료 시간
records_with_ttl = [
(("test", "demo", "user1"), {"name": "Alice"}, {"ttl": 3600}), # 1시간
(("test", "demo", "user2"), {"name": "Bob"}, {"ttl": 86400}), # 1일
(("test", "demo", "user3"), {"name": "Charlie"}), # namespace default
]
results = client.batch_write(records_with_ttl)
# Mix: batch-level default + per-record override
results = client.batch_write(
[
(("test", "demo", "user1"), {"name": "Alice"}), # batch-level TTL 사용
(("test", "demo", "user2"), {"name": "Bob"}, {"ttl": 3600}), # 1시간으로 override
],
policy={"ttl": 86400}, # default: 1일
)
# Batch-level TTL
results = await client.batch_write(records, policy={"ttl": 2592000})
# Per-record TTL
records_with_ttl = [
(("test", "demo", "user1"), {"name": "Alice"}, {"ttl": 3600}),
(("test", "demo", "user2"), {"name": "Bob"}, {"ttl": 86400}),
]
results = await client.batch_write(records_with_ttl)
자동 retry: retry를 설정하면 timeout, device overload, key busy 같은 transient error를 exponential backoff로 다시 시도합니다.
- Sync
- Async
# 실패 record 를 최대 5회 retry
results = client.batch_write(records, retry=5)
# 실패 record 를 최대 5회 retry
results = await client.batch_write(records, retry=5)
in_doubt flag
br.in_doubt가 True이면 error가 발생했어도 server에서 write를 완료했을 수 있습니다. Write를 보낸 뒤 timeout이 난 경우가 한 예입니다. Non-idempotent operation을 두 번 적용하지 않도록 retry 전에 in_doubt를 확인하세요.
Batch Operate / Remove
# Batch operate — BatchWriteResult (.batch_records: list[BatchRecord]) 반환.
# 참고: batch_read 는 LazyBatchRecords 를 반환; per-record `BatchRecord` row 만
# batch_write / batch_operate / batch_remove 와 공유.
ops = [{"op": aerospike.OPERATOR_INCR, "bin": "views", "val": 1}]
results = client.batch_operate(keys, ops)
for br in results.batch_records:
if br.result == 0 and br.record is not None:
print(br.record.bins)
# Batch remove
results = client.batch_remove(keys)
for br in results.batch_records:
if br.result != 0:
print(f"Failed to remove: {br.key}")
Optimistic Locking
from aerospike_py.exception import RecordGenerationError
record = client.get(key)
try:
client.put(
key,
{"val": record.bins["val"] + 1},
meta={"gen": record.meta.gen},
policy={"gen": aerospike.POLICY_GEN_EQ},
)
except RecordGenerationError:
print("Concurrent modification, retry needed")
팁
- Batch size: batch 당 100-5,000 key 가 최적. 너무 크면 timeout 가능.
- Timeout: 큰 batch operation 의 경우
total_timeout증가. - Error handling: Batch 안의 record는 각각 독립적으로 실패할 수 있습니다. 먼저
br.result를 확인하세요. 값이 0이 아니라면br.in_doubt를 확인해 non-idempotent write를 retry할 때 두 번 적용될 가능성이 있는지 판단합니다.br.record는 record를 반환하는 operation의 선택적 결과이며 성공 여부를 나타내지 않습니다.