Basic CRUD (Create, Read, Update, and Delete) operations in Aerospike, and how multiple operations on a record are performed in a single request.
This notebook requires that Aerospike datbase is running.
!asd >& /dev/null
!pgrep -x asd >/dev/null && echo "Aerospike database is running!" || echo "**Aerospike database is not running!**"
Aerospike database is running!
Initialize the client and connect it to the database.
import aerospike
import sys
config = {
'hosts': [ ('127.0.0.1', 3000) ]
}
try:
client = aerospike.client(config).connect()
except:
import sys
print("failed to connect to the cluster with", config['hosts'])
sys.exit(1)
print('Client initialized and connected to database')
Client initialized and connected to database
Data in Aerospike consists of records. A record belongs to a namespace (equivalent to a database) and optionally to a set (equivalent to a table). A record has multiple bins (or fields), which are named, strongly-typed containers that hold both atomic (string, integer, bytes) and complex (map, list) data types.
A record has two metadata values:
Expired records are garbage-collected by the database. On a write or touch operations, a record's ttl is updated based on the specified policy.
The following code cell illustrates the record structure.
Note:
And also:
# Record structure
# key: (namespace, set, user_key, digest), digest is computed from first three values
# metadata: {'gen': generation, 'ttl': ttl}
# bins: key-value pairs of data bins dictionary
namespace = 'test'
demoset = 'demo'
user_key = 'foo'
meta = {'ttl': 0}
bins = {'name': 'John Doe', 'age': 15, 'gpa': 4.3 }
policy = {'key': aerospike.POLICY_KEY_SEND} # policy to store the user_key along with the record
# insert/update the record
try:
client.put((namespace, demoset, user_key), bins, meta, policy)
except:
print('failed to put record')
sys.exit(1)
print('Successfully wrote the record.')
# read back the record
try:
(key, metadata, bins)= client.get((namespace, demoset, user_key), policy)
except:
print('failed to get record')
sys.exit(1)
print('Successfully read the record.')
print ('Key: ', key)
print ('Metadata: ', metadata)
print ('Bins: ', bins)
Successfully wrote the record. Successfully read the record. Key: ('test', 'demo', 'foo', bytearray(b'\xf5~\xc1\x835\xf7\x10\x0c\x04X\xf8\xa6D\xbc\xbcvm\x93G\x1e')) Metadata: {'ttl': 2592000, 'gen': 1} Bins: {'name': 'John Doe', 'age': 15, 'gpa': 4.3}
# create the key tuple identifying the record
key = ('test', 'characters', 'bender')
Specify record data in a dict, where the top-level object fields represent the bin names, and field values correspond to bin values.
This example writes six bins: name, serialnum, lastsentence, composition, apartment, and quote_cnt.
# The record data to write to the cluster
bins = {
'name': 'Bender',
'serialnum': 2716057,
'lastsentence': {
'BBS': "Well, we're boned",
'TBwaBB': 'I love you, meatbags!',
'BG': 'Whip harder, Professor!',
'ltWGY': 'Into the breach, meatbags. Or not, whatever'},
'composition': [ "40% zinc", "40% titanium", "30% iron", "40% dolomite" ],
'apartment': bytearray(b'\x24'),
'quote_cnt': 47
}
Store the record in the database with put.
# Put the record to the database.
try:
client.put(key, bins)
except:
print('failed to put record')
sys.exit(1)
print('Successfully stored the record.')
try:
(key, metadata, bins) = client.get(key, policy)
except:
print('failed to get record')
sys.exit(1)
print ('Bins: ', bins)
Successfully stored the record. Bins: {'name': 'Bender', 'serialnum': 2716057, 'lastsentence': {'ltWGY': 'Into the breach, meatbags. Or not, whatever', 'BG': 'Whip harder, Professor!', 'BBS': "Well, we're boned", 'TBwaBB': 'I love you, meatbags!'}, 'composition': ['40% zinc', '40% titanium', '30% iron', '40% dolomite'], 'apartment': bytearray(b'$'), 'quote_cnt': 47}
The following APIs are used to prepend and append string bins, and increment integer bins:
try:
client.prepend(key, 'name', 'Dr. ')
client.append(key, 'name', ' Bending Rodriguez')
client.increment(key, 'quote_cnt', 3)
except:
print('failed to get record')
sys.exit(1)
try:
(key, metadata, bins)= client.get(key, policy)
except:
print('failed to get record')
sys.exit(1)
print ('Bins: ', bins)
Bins: {'name': 'Dr. Bender Bending Rodriguez', 'serialnum': 2716057, 'lastsentence': {'ltWGY': 'Into the breach, meatbags. Or not, whatever', 'BG': 'Whip harder, Professor!', 'BBS': "Well, we're boned", 'TBwaBB': 'I love you, meatbags!'}, 'composition': ['40% zinc', '40% titanium', '30% iron', '40% dolomite'], 'apartment': bytearray(b'$'), 'quote_cnt': 50}
There are multiple ways to read records from Aerospike database, "get" being the simplest one. You will need the key as the record unique identifier as discussed above. Note the record was written above.
It returns:
Meta and bins are None if the record is not found.
# key of the record
key = ('test', 'demo', 'foo')
# Retrieve the record using the key.
try:
(key, meta, bins) = client.get(key, policy)
except:
print('failed to get record')
sys.exit(1)
print('Successfully read the record.')
print ('Key: ', key)
print ('Metadata: ', metadata)
print ('Bins: ', bins)
Successfully read the record. Key: ('test', 'demo', 'foo', bytearray(b'\xf5~\xc1\x835\xf7\x10\x0c\x04X\xf8\xa6D\xbc\xbcvm\x93G\x1e')) Metadata: {'ttl': 2592000, 'gen': 4} Bins: {'name': 'John Doe', 'age': 15, 'gpa': 4.3}
It is possible to project or read specific bins of a record. The following example illustrates this. Note that the second argument is a tuple of bin names to project.
# retrieve only specified bins in the record
try:
(key, meta, bins) = client.select(('test','demo','foo'), ('age', 'gpa'))
except:
print('failed to get record')
sys.exit(1)
print ('Bins: ', bins)
Bins: {'age': 15, 'gpa': 4.3}
Use "exists" to check existence of a record in the database. Note, meta is None if the record is not found.
# Retrieve the record using a non-existent key.
(key, metadata) = client.exists(('test','demo','foo'), policy)
print('User-key, Metadata: ', key[2], metadata)
(key, metadata) = client.exists(('test','demo','nonexistent'), policy)
print('User-key, Metadata: ', key[2], metadata)
User-key, Metadata: foo {'ttl': 2592000, 'gen': 1} User-key, Metadata: nonexistent None
The get_many and exists_many batch operations allow the application to access multiple records.
import pprint
pp = pprint.PrettyPrinter(depth=4)
keys = []
for i in range(1,3):
key = ('test', 'demo', 'key' + str(i))
client.put(key, {'batch': i}, policy)
for i in range(1,4):
key = ('test', 'demo', 'key' + str(i))
keys.append(key)
records = client.get_many(keys)
pp.pprint (records)
[(('test', 'demo', 'key1', bytearray(b'\xec\x91\x19-K\x7f\x8c\xe3]]x\xd3K\xcae\xcb\xaa\xaa\xc9`')), {'gen': 1, 'ttl': 2592000}, {'batch': 1}), (('test', 'demo', 'key2', bytearray(b'&o\xc5\xc9\x94\xab\x9d\n\xa1\xc3g\xd9\x9a\xbc\xe6\xd8' b'\x9e\x97p\x83')), {'gen': 1, 'ttl': 2592000}, {'batch': 2}), (('test', 'demo', 'key3', bytearray(b'cN\x96\xf7\xff\\\xd9hS8\x18\xdc"v\x18\xe2\xe4D\'\xa5')), None, None)]
To delete records from the database, there is "remove" operation.
An exception is thrown if the record does not exist.
# Key of the record to be deleted
key1 = ('test', 'demo', 'key1')
# Delete the record
try:
client.remove(key1)
except:
print('failed to delete record: ', key1)
sys.exit(1)
(key, metadata) = client.exists(key1, policy)
print('Key, metadata: ', key1, metadata)
print('Successfully deleted ', key1)
# will throw an exception
nokey = ('test', 'demo', 'non-existent')
try:
client.remove(key)
except:
print('failed to delete record: ', nokey)
Key, metadata: ('test', 'demo', 'key1') None Successfully deleted ('test', 'demo', 'key1') failed to delete record: ('test', 'demo', 'non-existent')
Multiple operations on a single a record can be conveniently performed in a single transaction. Multiple updates as well as reads of a record may be performed atomically in a single transaction. Operations are performed in the order they are specified. Below is a full example of a multi-op request.
from __future__ import print_function
import aerospike
from aerospike_helpers.operations import operations as op_helpers
from aerospike import exception as ex
import sys
config = { 'hosts': [('127.0.0.1', 3000)] }
client = aerospike.client(config).connect()
try:
key = ('test', 'demo', 1)
client.put(key, {'age': 25, 'career': 'delivery boy'})
ops = [
op_helpers.increment("age", 1000),
op_helpers.write("name", "J."),
op_helpers.prepend("name", "Phillip "),
op_helpers.append("name", " Fry"),
op_helpers.read("name"),
op_helpers.read("career"),
op_helpers.read("age")
]
(key, metadata, bins) = client.operate(key, ops, {'ttl':0}, {'total_timeout':500})
except ex.AerospikeError as e:
print("Error: {0} [{1}]".format(e.msg, e.code))
sys.exit(1)
print('Key: ', key)
print('--------------------------')
print('Metadata: ', metadata)
print('--------------------------')
print('Bins: ', bins) # shows only bins specified in read operations
Key: ('test', 'demo', 1, bytearray(b'\xb7\xf4\xb88\x89\xe2\xdag\xdeh>\x1d\xf6\x91\x9a\x1e\xac\xc4F\xc8')) -------------------------- Metadata: {'ttl': 2592000, 'gen': 2} -------------------------- Bins: {'name': 'Phillip J. Fry', 'career': 'delivery boy', 'age': 1025}
The example shows reading and deleting a record in a single operation. The record was create above.
from aerospike_helpers.operations import operations
key = ('test', 'demo', 1)
ops = [ operations.read('name'),
operations.delete() ]
_, _, bins = client.operate(key, ops)
print('Bins: ', bins)
# will throw an exception
(key_, metadata) = client.exists(key, policy)
print('Key, metadata: ', key, metadata)
print('Successfully deleted ', key)
Bins: {'name': 'Phillip J. Fry'} Key, metadata: ('test', 'demo', 1) None Successfully deleted ('test', 'demo', 1)
Visit Aerospike notebooks repo to run additional Aerospike notebooks. To run a different notebook, download the notebook from the repo to your local machine, and then click on File->Open, and select Upload.