API Changes
Version 11.0.1
[CLIENT-2267] Revert adding base64 methods to aerospike module
These methods have been removed from the aerospike module due to memory errors when they are called:
aerospike.get_expression_base64()
aerospike.get_cdtctx_base64()
Course of action:
Call these methods from a client instance instead of the aerospike
module. Assuming client
is an instance of aerospike.Client
, the above calls should be replaced by:
client.get_expression_base64()
client.get_cdtctx_base64()
Version 11.0.0
[CLIENT-701] Batch methods: stop accepting a tuple of keys and bins
For the following functions:
client.get_many()
client.exists_many()
The keys
parameter no longer takes in a tuple of keys. It only takes in a list of keys.
In addition, client.select_many()
no longer takes in a tuple for the keys
and bins
parameters. Those parameters only take in a list of keys and bins, respectively.
Course of action:
Change code such as this:
keys = (("test", "demo", 1), ("test", "demo", 2))
bins = ("bin1", "bin2")
client.select_many(keys, bins)
...to this instead:
keys = [("test", "demo", 1), ("test", "demo", 2)]
bins = ["bin1", "bin2"]
client.select_many(keys, bins)
[CLIENT-2144] Expressions: add support for comparing KeyOrderedDicts (new server feature)
Before server 6.3, it is possible to compare maps in expressions if the maps were nested inside a list. For example, this code would work before server 6.3:
from aerospike_helpers.expressions import base as expr
client.put(key, {"bin": [{"a": 1}]})
exp = expr.Eq(expr.ListBin("bin"), [{"a": 1}]).compile()
record = client.get(key, {"expressions": exp})
print(record[2])
# {'bin': [{'a': 1}]}
This is now unsupported in server 6.3 because comparing unordered maps can potentially lead to inconsistent results. However, it is possible in server 6.3 to compare key-ordered maps in expressions.
Course of action:
For those using a server version < 6.3, no action is necessary. But it is recommended not to compare unordered maps in expressions.
For those upgrading to server 6.3, maps stored in the server must be key-ordered in order to be compared against in expressions. If the maps in the server are already key-ordered, and you would like to compare them in expressions, you must wrap any dictionaries in expressions with the KeyOrderedDict class.
For example, the code above must store the map as a key ordered map before comparing it in an expression:
from aerospike_helpers.expressions import base as expr
from aerospike import KeyOrderedDict
client.put(key, {"bin": [KeyOrderedDict({"a": 1})]})
exp = expr.Eq(expr.ListBin("bin"), [KeyOrderedDict({"a": 1})]).compile()
record = client.get(key, {"expressions": exp})
print(record[2])
# {'bin': [{'a': 1}]}
[CLIENT-2197] Return AEROSPIKE_ERR_NAMESPACE_NOT_FOUND instead of AEROSPIKE_ERR_CLIENT when a namespace cannot be found
Course of action: Change code such as this:
from aerospike import exception as exc
key = ("nonexistent_namespace", "demo", 1)
try:
client.get(key)
except exc.ClientError:
print("Incorrect namespace")
...to this instead:
from aerospike import exception as exc
key = ("nonexistent_namespace", "demo", 1)
try:
client.get(key)
except exc.NamespaceNotFound:
print("Incorrect namespace")
[CLIENT-2143] Return last error code received when scan/query maxRetries is exceeded
When running a query or scan, if max_retries is exceeded, the transaction will return the last suberror that was received instead of a MaxRetriesExceeded error. For example, if you try to run a query on a non-indexed bin, the client will return an IndexNotFound error from the last attempt to query the bin.
This code will no longer work:
query = client.query("test", "demo")
query.select("bin_without_index")
query.where(p.equals("bin_without_index", 1))
def callback(input_tuple):
pass
try:
query.foreach(callback)
except exc.MaxRetriesExceeded:
print("Query failed")
Course of action:
When handling a MaxRetriesExceeded exception, change it to the exact error that is expected to get thrown during the last query attempt. In this case, it is an IndexNotFound error:
try:
query.foreach(callback)
except exc.IndexNotFound:
print("Query failed")
Version 10.0.0
[CLIENT-1989] get_cdtctx_base64(): take in context directly instead of a dictionary.
When passing in a context array to get_cdtctx_base64()
, you must to pass it in directly as a parameter instead of embedding it in a dictionary.
This change is to make the API more easy to use and consistent with the other API methods.
Course of action:
This code:
get_cdtctx_base64({'ctx': ctx})
should be changed to:
get_cdtctx_base64(ctx)
Version 9.0.0
[CLIENT-2074] Change default send_bool_as constant to AS_BOOL
By default, the client now sends the boolean as an Aerospike server boolean type (aerospike.AS_BOOL) instead of a Python boolean type (aerospike.PY_BYTES).
For developers using server 5.6 and later
The server encodes Python booleans as the "blob" type and server booleans as the "booleans" type. Because of the ordering of types in a Collection Data Type (CDT), this may break rank operations for CDTs that store a mix of booleans and other types of values.
Course of action: Check that rank operations on CDTs containing both boolean and integers are handled properly.
For developers using server version earlier than 5.6
The server will fail to perform the write operation if the client uses the default send_bool_as
value, aerospike.AS_BOOL
, to write a boolean value to the server.
Course of actions:
- Change the default
send_bool_as
to a value other thanaerospike.AS_BOOL
. - Upgrade the server to version 5.6 to support server-native boolean types.
[CLIENT-2008] batch_get_ops()
: Remove meta field
batch_get_ops()
previously had this function signature:
batch_get_ops(keys, ops[, meta, policy: dict]) → [key, meta, bins]
Now the meta
parameter has been removed:
batch_get_ops(keys, ops[, policy: dict]) → [key, meta, bins]
[CLIENT-2012] scan_apply()
: Report correct error value and message if scan wait fails
When scan_apply()
is called and it internally calls scan_wait()
and fails, it previously returned this error:
"error": "(-2, 'Unable to perform scan_wait on the scan', 'src/main/client/scan.c', 215, False)"
Now it returns an error from scan_wait()
directly in order to identify the cause of the error.
Version 8.0.0
Connect when calling client constructor and conditionally no-op connect()
- Calling the
aerospike.Client
constructor establishes the connection.- If user authentication is required, pass in a username and password in the client configuration dictionary by using the keys
"username"
and"password"
.
- If user authentication is required, pass in a username and password in the client configuration dictionary by using the keys
aerospike.Client.connect()
only runs if the connection was closed by callingclose()
beforehand. Otherwise, usingconnect()
does nothing.
Before version 8.0.0:
config = {
'hosts': hosts,
'policies': {'auth_mode': aerospike.AUTH_INTERNAL},
}
client = aerospike.client(config)
client.connect(user, password)
At version 8.0.0:
config = {
'hosts': hosts,
'policies': {'auth_mode': aerospike.AUTH_INTERNAL},
'user': user,
'password': password
}
client = aerospike.client(config)
# following is no-op
client.connect(user, password)
Having the client connect to the server when the constructor is called makes the Python client's behavior more consistent with our other clients, and it also removes the possibility of an application trying to perform server operations with the client without a connection.
If you are using a try/except
block and have the constructor call outside of the try
block, move
the constructor call into the try
block. If an exception happens, it would be coming from the
constructor and not the connect() method.
This line of code:
client = aerospike.client(config).connect()
should not have an issue.
But code such as this:
client = aerospike.client(config)
try:
client.connect()
except Exception:
# eat exception and do something
Should be changed to this instead:
try:
client = aerospike.client(config)
except Exception:
# eat exception and do something
Version 7.0.0
Remove old predexp
Old predexp functionality has been removed. Code using old predexp must be converted to the new expression syntax. See: predexp to expression
Removed support for Debian 8
Debian 8 has reached its end of life. Python client installs on debian 8 may fail.
IndexNotFound and IndexNotReadable errors can now trigger retries
Previously these errors would cause transactions to fail immediately. Now they trigger retries if that transaction's policy allows for them. If your application uses transactions with retries and checks for the IndexNotFound or IndexNotReadable errors, switch to checking for the MaxRetriesExceeded error instead.
Bytes blobs read from the database will now be read as bytes instead of bytearray
Bytes and Bytearray objects written by the Python client to Aerospike are stored on the server as bytes blobs. Previously, these bytes blobs were always read as bytearrays. When bytes were written as dictionary keys, they would be read as bytearrays causing an unhashable key error. If your application reads bytes blobs and type checks the result expecting bytearray, the expected type should change to bytes. If your application relies on bytearray results being mutable and encounters errors like "TypeError: 'bytes' object does not support item assignment" the result has probably switched to bytes. bytes objects are not mutable so should not be directly modified."
Query policies max_records and records_per_second are now fields of the Query class
Query policies max_records and records_per_second have been moved from the query policy to be fields of the Query class. max_records and records_per_second policies will be ignored, so if your code uses them, change to Query.max_records and Query.records_per_second instead.
Version 7.0.0 - Replace predicate expressions with Aerospike expressions
The old predexp functionality has been replaced by Expressions. Expressions include all previous predicate expression functionality (with a different, improved syntax) plus support for record metadata expressions and list/map/bit/hyperloglog expression methods analogous to those used in operations.
See the Python client expressions documentation for more details.
Use of the new expressions API require server version 5.2.0.4 or later. Convert code using old predexp to the new syntax. Here is an example:
import aerospike
from aerospike_helpers import expressions as exp
from aerospike import predexp
# (c >= 11 and c <= 20) or (d >= 3 and d <= 5)
# Using old removed predexp.
predexps = [
predexp.integer_bin("c"),
predexp.integer_value(11),
predexp.integer_greatereq(),
predexp.integer_bin("c"),
predexp.integer_value(20),
predexp.integer_lesseq(),
predexp.predexp_and(2)
predexp.integer_bin("d"),
predexp.integer_value(3),
predexp.integer_greatereq(),
predexp.integer_bin("d"),
predexp.integer_value(5),
predexp.integer_lesseq(),
predexp.predexp_and(2),
predexp.predexp_or(2)
]
# Converted to new expressions.
expression = exp.Or(
exp.And(
exp.GE(exp.IntBin("c"), 11),
exp.LE(exp.IntBin("c"), 20)
),
exp.And(
exp.GE(exp.IntBin("d"), 3),
exp.LE(exp.IntBin("d"), 5)
)
).compile()
Version 6.0.0
Info_node() no longer works when security is enabled
- Because of changes in client authentication internals in C client 5.2.0, info_node() will fail if used when authentication is enabled. Checkout info_single_node() for a replacement.
Dropped support for scan/query options percent, priority, fail_on_cluster_change
- These options have been dropped and are no longer sent to the server. They will need to be removed from application code.
Dropped support for CentOS 6 and Ubuntu 16.04
- CentOS 6 and Ubuntu 16.04 have reached their support end of life. Aerospike Python client installs on CentOS6 and Ubuntu 16.04 will fail.
Python 3.5 Support Discontinued
- Python 3.5 has reached end of life. The Aerospike Python client will no longer be distributed for Python 3.5.
Version 5.0.0
Aerospike Server 4.9 and up Required
- Aerospike Python client 5.0.0 requires Aerospike server 4.9 or later. Attemping to connect to a server older than 4.9 will yield the error "-10, Failed to connect".
Python 2.7 and 3.4 Support Discontinued
- Python 2.7 and 3.4 have reached end of life. The Aerospike Python client will no longer be distributed for them.
Version 4.0.0
Scan policy changes
- Policy key
fail_on_cluster_change
has no effect for Aerospike server versions >= 4.9.
Scan option changes
- Option key
percent
no longer accepts 0 as a valid percent.
Version 3.7.0
Read Consistency Level Changes
Read policy changes for transactions involving AP (availability) namespaces
- Policy key
consistency_level
was replaced byread_mode_ap
for the read, batch and, operate policies.POLICY_CONSISTENCY_ALL
was replaced byPOLICY_READ_MODE_AP_ALL
POLICY_CONSISTENCY_ONE
was replaced byPOLICY_READ_MODE_AP_ONE
Read policy changes for transactions involving SC (strong consistency) namespaces
- Policy key
linearize_read
was replaced byread_mode_sc
for the read, batch, and operate policies. Addedaerospike.POLICY_READ_MODE_SC_SESSION
,aerospike.POLICY_READ_MODE_SC_LINEARIZE
,aerospike.POLICY_READ_MODE_SC_ALLOW_REPLICA
, andaerospike.POLICY_READ_MODE_SC_ALLOW_UNAVAILABLE
as options for this key.
Version 3.6.0
Backwards Incompatible API changes
Shared memory layout change
Shared memory layout has changed, and accordingly the default SHM key has changed from 0xA7000000
to 0xA8000000
. If manually specifiying an
SHM key, it is crucial to ensure that a separate key is used in order to prevent this version's client from sharing memory with a previous version.
Removed Unused Exceptions
The BinExistsError
, BinNotFound
exceptions have been removed.
Version 3.2.0
Additional Features
- Updated to C client
4.3.11
- Added
client.map_get_by_value_list
andclient.map_get_by_key_list
These methods require Aerospike Server version >= 3.16.0.1 .
Version 3.1.0
Additional Features
- Updated to C client
4.3.6
- Added
exists
policy field to operate policies
Backwards Incompatible API changes
Updated the args passed to AerospikeError constructor internally to contain 5 arguments. The arguments previously were
error code
,error message
,error file
,error line
. A fifth argumentin_doubt
has been added to the internal calls. so the arguments passed to the constructor are now :error_code
,error_message
,error_file
,error_line
,in_doubt
This means that code such as the following will now raise a ValueError
try:
client.get(key)
except AerospikeError as e:
code, msg, file, line = e.args
print(code, msg, file, line)
This can be fixed by unpacking the fifth value from the Error's args
tuple
try:
client.get(key)
except AerospikeError as e:
code, msg, file, line, in_doubt = e.args
print(code, msg, file, line)
Version 3.0.0
Additional Features
- Updated to C client
4.3.1
- Added a new list increment operation OP_LIST_INCREMENT . It can be used to increase an element of a list by a provided amount.
- Added the option to specify a specific node to run on a scan on, via a new optional nodename parameter.
- Added the option to specify that Query.results and Query.foreach should not return bins, via a new optional parameter options to both methods.
- Added aerospike.info_all() to allow sending of an info command to all nodes in the current cluster.
- Added linearize_read option to read policies. Requires Enterprise server >= 4.0.0
Deprecations
client#info
has been deprecated. In order to send requests to the entire cluster, the new methodclient#info_all
should be used. In order to send requests to hosts specified in a list, we recommend a loop invoking multiple calls to aerospike.info_node. See below for an example implementation:
def info_to_host_list(client, request, hosts, policy=None):
output = {}
for host in hosts:
try:
response = client.info_node(request, host, policy)
output[host] = response
except Exception as e:
# Handle the error gracefully here
output[host] = e
return output
- Setting of global policy defaults via top level entries in the 'policies' dictionary in the constructor config dict has been deprecated. See the constructor documentation for the new recommended method of specifying defaults.
Backwards Incompatible API changes
LDT Removal
Removed LDT (client#llist
) methods and support. Server version 3.14 is the last version of the Aerospike server to support the functionality.
Index Methods
Methods which create indexes, (
index_string_create
,index_integer_create
,index_list_create
,index_map_keys_create
,index_map_values_create
and,index_geo2dsphere_create
), will now raise anIndexFoundError
if the specified bin has already been indexed, or if an index with the same name already exists.Methods which drop indexes (
index_remove
), will now raise anIndexNotFound
if the named index does not exist.
Shared memory layout change
Shared memory layout has changed, and accordingly the default SHM key has changed from 0xA6000000
to 0xA7000000
. If manually specifiying an
SHM key, it is crucial to ensure that a separate key is used in order to prevent this version's client from sharing memory with a previous version.
Changed and removed policy field names:
In all policies, (except for info
and admin
), timeout
has been split into total_timeout
and socket_timeout
total_timeout
is an int representing total transaction timeout in milliseconds. The total_timeout
is tracked on the client and sent to the server along with the transaction in the wire protocol. The client will most likely timeout first, but the server also has the capability to timeout the transaction. If total_timeout
is not zero and total_timeout
is reached before the transaction completes, the transaction will return error TimeoutError
. If total_timeout
is zero, there will be no total time limit. See the documentation for individual policies for the default values.
socket_timeout
is an int representing socket idle timeout in milliseconds when processing a database command. If socket_timeout
is not zero and the socket has been idle for at least socket_timeout
, both max_retries and total_timeout
are checked. If max_retries
and total_timeout
are not exceeded, the transaction is retried. If both socket_timeout
and total_timeout
are non-zero and socket_timeout > total_timeout
, then socket_timeout
will be set to total_timeout
. If socket_timeout
is zero, there will be no socket idle limit. See the documentation for individual policies for the default values.
retry
has beeen removed.
max_retries
has been added, it is an integer specifying the number of times a transaction should be retried before aborting. If max_retries
is exceeded, TimeoutError
will be raised.
WARNING: Database writes that are not idempotent (such as client#increment
) should not be retried because the write operation may be performed multiple times if the client timed out previous transaction attempts. It’s important to use a distinct write policy for non-idempotent writes which sets max_retries
= 0;
The default value for max_retries
is 2.
Changes in policy defaults for aerospike.client
constructor
In this version, individual config dictionaries (read
, write
, apply
, operate
, scan
, query
, batch
, remove
) for method types should be used inside of the top level policies
dictionary for setting policies. In previous versions, individual policies were set at the top level policies
dictionary rather than in per method type dictionaries. The type of policy which affects each method can be found in the documenation. See the main documentation for the keys and values available for these new configuration dictionaries. See below for an example of the change in constructor usage:
# Pre Python client 3.0.0
hosts = [('localhost', 3000)]
timeout = 2345
key_policy = aerospike.POLICY_KEY_SEND
# This changes defaults for all methods, requiring a config dictionary to be passed in to all methods which
# should use different values.
policies = {timeout': timeout, 'key': key_policy, 'retry': aerospike.POLICY_RETRY_ONCE}
config = {'hosts': hosts, 'policies': policies}
client = aerospike.client(config)
# Post Python client 3.0.0
hosts = [('localhost', 3000)]
write_total_timeout = 4321
read_total_timeout = 1234
write_key_policy = aerospike.POLICY_KEY_SEND
read_policy = {'total_timeout': read_total_timeout, 'max_retries': 1}
write_policy = {'total_timeout': write_total_timeout, 'key': write_key_policy, 'max_retries': 1}
# operate policies affect methods such as client#increment, so these should not be retried since they are not idempotent.
operate_policy = {'max_retries': 0}
# Change the defaults for read, write, and operate methods, all other methods will use builtin defaults.
policies = {'read': read_policy, 'write': write_policy, 'operate': operate_policy}
config = {'hosts': hosts, 'policies': policies}
client = aerospike.client(config)