Aerospike Python SDK - a modern, developer-friendly interface for Aerospike
Project description
Aerospike Python SDK
A modern, developer-friendly interface for Aerospike. Async-first, Pythonic API with a chainable session model, fluent query builder, and AEL string filters over the Aerospike Python Async Client.
Status: Public preview (alpha). Not yet production-ready; feedback welcome via GitHub Issues.
Resources
- PyPI: https://pypi.org/project/aerospike-sdk/
- Documentation: https://aerospike.com/docs/develop/client/sdk/
- API Reference: https://aerospike-python-sdk.readthedocs.io/
- Source: https://github.com/aerospike/aerospike-client-python-sdk
- Issues: https://github.com/aerospike/aerospike-client-python-sdk/issues
Installation
pip install aerospike-sdk
Pin to a specific release if you need reproducible builds:
pip install aerospike-sdk==0.9.0a2
This installs the SDK plus its dependency on the Aerospike Python Async Client
(aerospike-async). No Rust toolchain or git checkout required for ordinary
use — pre-built wheels are available for Linux, macOS, and Windows on Python
3.10–3.14.
Quick start
import asyncio
from aerospike_sdk import Behavior, Client, DataSet
async def main():
async with Client("localhost:3000") as client:
session = client.create_session(Behavior.DEFAULT)
users = DataSet.of("test", "users")
# High-level key-value writes
await session.upsert(users.id(1)).put({"name": "Alice", "age": 28, "country": "UK"}).execute()
await session.upsert(users.id(2)).put({"name": "Bob", "age": 35, "country": "US"}).execute()
# Filtered query with AEL — streams results memory-efficiently
results = await (
session.query(users)
.where("$.age > 25 and $.country == 'US'")
.execute()
)
async for row in results:
if row.is_ok and row.record is not None:
print(row.record.bins)
# Or drain the entire stream into a list
all_users = await session.query(users).execute()
rows = await all_users.collect()
asyncio.run(main())
Sync
The same surface is available without asyncio via SyncClient. No async/await,
no event loop — useful for sync codebases or when a dependency forbids asyncio.
from aerospike_sdk import Behavior, DataSet, SyncClient
def main():
with SyncClient("localhost:3000") as client:
session = client.create_session(Behavior.DEFAULT)
users = DataSet.of("test", "users")
# High-level key-value writes
session.upsert(users.id(1)).put({"name": "Alice", "age": 28, "country": "UK"}).execute()
session.upsert(users.id(2)).put({"name": "Bob", "age": 35, "country": "US"}).execute()
# Filtered query with AEL — same builder API as async
results = (
session.query(users)
.where("$.age > 25 and $.country == 'US'")
.execute()
)
for row in results:
if row.is_ok and row.record is not None:
print(row.record.bins)
main()
SyncClient is a thin façade over the PAC _blocking surface — there is no per-call
event loop and no per-thread loop runner. Sessions, behaviors, builders, and AEL
filters are identical to the async path.
See the Quick Start guide for a deeper walkthrough; the API reference covers every public class and method in detail.
Performance modes
PSDK offers two API shapes — pick based on what your code needs.
| API | Use when | Trade-off |
|---|---|---|
Chained builder (session.query(k).execute(), session.upsert(k).put(...).execute()) |
You need filters (where(...)), batch ops, error handlers, secondary-index queries, TTL overrides, generation checks, etc. Same shape as the Aerospike Java SDK. |
Builder + stream wrapping costs ~60 µs/op of Python overhead. |
Fast-path (session.get(key), session.put(key, bins)) |
Single-key reads/writes where you want the lowest per-op overhead. | Single-key only; no filters, no error-handler callbacks, no batch semantics. Errors raise directly. |
Both shapes work in sync and async modes. Use whichever fits each call site — they share the same Session and Behavior.
Free-threaded Python
For high-throughput multi-threaded workloads, run PSDK on the free-threaded build with the GIL disabled:
# uv example — install once, then always launch with PYTHON_GIL=0
uv python install 3.14.5+freethreaded
PYTHON_GIL=0 python my_app.py
Verify with sys._is_gil_enabled() == False after imports — if any non-FT-safe C extension is imported, the interpreter silently re-enables the GIL and your multi-threaded perf collapses 4-6×.
AsyncPool (multi-loop async) is a free-threading feature — don't use it on regular Python, it's slower than a single-client setup there. Each pool spawns N event loops on N OS threads, each with its own Client; coroutines submitted via pool.run(...) round-robin across loops:
from aerospike_sdk import AsyncPool, Behavior, Client, DataSet
async def main():
pool = AsyncPool(
client_factory=lambda: Client("localhost:3000"),
loop_count=4,
)
async with pool:
users = DataSet.of("test", "users")
# Submit a coroutine — picks an idle loop round-robin
await pool.run(
lambda client: client.create_session(Behavior.DEFAULT)
.upsert(users.id(1))
.put({"name": "Alice"})
.execute()
)
Tune loop_count based on your workload — os.cpu_count() is the default. Cluster-wide index metadata is shared via a single IndexesMonitor across the pool, so sindex-list load doesn't scale with loop_count.
For the full decision guide, the trade-offs, and measured TPS/latency across all modes, see docs/guide/performance.md. For the raw bench data and methodology, see docs/guide/benchmarking.md.
Documentation
- Guides and tutorials (user-facing, hand-curated): https://aerospike.com/docs/develop/client/sdk/
- API reference (auto-generated from docstrings, hosted on Read the Docs): https://aerospike-python-sdk.readthedocs.io/
The two complement each other: the guide site introduces concepts and works
through realistic examples, while the API reference is the exhaustive source
for Client, Session, query/update builders, AEL, behavior policies, and
every public symbol.
Versioning
PSDK follows SemVer. Pre-releases use the
MAJOR.MINOR.PATCH-{alpha,beta,rc}.N form (e.g. 0.9.0-alpha.1). PyPI
normalizes these on upload to the equivalent PEP 440 spelling (0.9.0a1).
The top-level VERSION file is the single source of truth; pyproject.toml
reads it dynamically, so the wheel and the working tree are guaranteed to
match. See the Development section below for the
bump procedure.
License
Apache License 2.0. See LICENSE for details.
Development / Contributing
The sections below are for SDK contributors. Downstream users do not need
any of this — pip install aerospike-sdk is sufficient to use the package.
Prerequisites
- Python 3.10 - 3.14, or 3.14t (free-threaded) for high-throughput /
AsyncPoolwork. Recommended installer:uv(uv python install 3.14.5+freethreaded) orpyenvwith a dedicated environment. Free-threaded wheels (cp313t/cp314t) ship across the same platform matrix as the regular CPython wheels starting withaerospike-asyncv0.5.0-alpha.1. - Aerospike server — required for integration tests
- Rust toolchain (
rustc+cargo) — required only when building the Aerospike Python Async Client from source (e.g. for an unreleased PAC feature) - Java 11+ — required for the one-time AEL parser build (
make generate-ael)
Setting up a dev environment
make generate-ael # one-time: build the ANTLR AEL parser (requires Java 11+)
pip install -e ".[dev]" # install with dev extras
make generate-ael only needs to be re-run if aerospike_sdk/ael/antlr4/Condition.g4 changes.
Local PAC checkout
To test against a sibling Aerospike Python Async Client working tree (e.g. for
a feature not yet on PyPI), install it editable first and pass --no-deps to
this SDK so pip doesn't try to re-resolve PAC from PyPI:
pip install -e /path/to/aerospike-client-python-async
pip install -e ".[dev]" --no-deps
Or use requirements-local.txt (gitignored path example).
Configuration
Copy aerospike.env.example to aerospike.env in the repo root and adjust
hosts or ports. aerospike.env is not committed.
cp aerospike.env.example aerospike.env
source aerospike.env
Pytest loads aerospike.env when present; otherwise conftest.py loads
aerospike.env.example for unset variables only (so CI env vars still win).
Running tests
make test # all tests
make test-unit # unit tests only
make test-int # integration tests only (requires running Aerospike server)
macOS file descriptor limit. On macOS, you may encounter OSError: [Errno 24] Too many open files when running the full test suite. The default limit
(256) is not enough for the concurrent async connections created during
testing.
ulimit -n 4096
To make this permanent, add it to your shell profile (~/.zshrc or
~/.bash_profile).
Building docs locally
API docs are built with Sphinx (Furo theme, MyST-Parser for Markdown). The same Sphinx config is what Read the Docs builds from.
pip install -e ".[docs]" # one-time: install Sphinx toolchain
make docs # build static HTML to docs/_build/html/
make docs-serve # live-reloading local preview
Docstrings use Google style with Sphinx cross-references (:meth:, :class:,
etc.).
Lint
ruff check .
Bumping the version
Bumps are manual and happen in PRs against dev. Promotion workflows
(dev → stage → main) do not mutate the version.
# 1. Edit VERSION:
# e.g. 0.9.0-alpha.1 → 0.9.0-alpha.2
echo '0.9.0-alpha.2' > VERSION
# 2. Confirm:
bin/get-version # prints 0.9.0-alpha.2
# 3. Open a PR against dev with just this change.
Bumping the PAC pin
PSDK depends on a published release of the
Aerospike Python Async Client
on PyPI as aerospike-async. On dev and downstream release branches the pin
must be a published PyPI version. The pin lives in pyproject.toml under
[project] dependencies:
[project]
dependencies = [
"aerospike-async==0.5.0a1",
# ...other deps
]
To bump: change the version to the new release on PyPI, then reinstall:
pip install --upgrade "aerospike-async==0.5.0aN"
Open the PR against dev. PSDK's own VERSION does not need to change for a
PAC pin bump unless the underlying API contract has shifted enough to warrant
it.
Mid-cycle: pinning a tagged but unpublished PAC
During a breaking-change cycle, the new PAC may be git-tagged on GitHub before
its PyPI wheel is published. Feature branches (not dev) may temporarily pin
to the git ref to validate against the new PAC before publish:
[project]
dependencies = [
#"aerospike-async==0.5.0a1", # ← restore this form before merging to dev
"aerospike-async @ git+ssh://git@github.com/aerospike/aerospike-client-python-async.git@v0.5.0-alpha.1",
# ...other deps
]
Use git refs only on feature branches; switch back to the ==X.Y.ZaN form
before opening a PR against dev. CI reads the PAC ref out of pyproject.toml
at job start, so both forms work transparently for the build matrix.
Reading the version programmatically
Anywhere a build script, CI step, or release tool needs the version:
bin/get-version # → 0.9.0-alpha.1
The script reads VERSION and trims trailing whitespace. No Python or
setuptools runtime dependency — usable from any shell, container, or CI
environment.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file aerospike_sdk-0.9.0a5.tar.gz.
File metadata
- Download URL: aerospike_sdk-0.9.0a5.tar.gz
- Upload date:
- Size: 264.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
275a8f3df3761b87676a02409e314a87f7218a0bb7f1ac28fd373b4d3d3a3486
|
|
| MD5 |
ec327cea50f58dca158411c922808efa
|
|
| BLAKE2b-256 |
932f2a04731ff95ea114c3148218ac43febb9f28361e7e21bf77d5e7464e9d77
|
Provenance
The following attestation bundles were made for aerospike_sdk-0.9.0a5.tar.gz:
Publisher:
publish-artifact.yaml on citrusleaf/artifact-publisher
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
aerospike_sdk-0.9.0a5.tar.gz -
Subject digest:
275a8f3df3761b87676a02409e314a87f7218a0bb7f1ac28fd373b4d3d3a3486 - Sigstore transparency entry: 1844255739
- Sigstore integration time:
-
Permalink:
citrusleaf/artifact-publisher@c53d7b14d525b5e20843bcb4d7fc7850e7df14c4 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/citrusleaf
-
Access:
internal
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-artifact.yaml@c53d7b14d525b5e20843bcb4d7fc7850e7df14c4 -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file aerospike_sdk-0.9.0a5-py3-none-any.whl.
File metadata
- Download URL: aerospike_sdk-0.9.0a5-py3-none-any.whl
- Upload date:
- Size: 309.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6ac1cfcae00c5e49117d081559f39855d4d1859fbb94e5fbb1769ead0e4acd19
|
|
| MD5 |
2c65c51c165c7ccbb560732f445d8c15
|
|
| BLAKE2b-256 |
e1e1a3e7671fc455fab91dfefe1eba3bf3f236d1174cdc704df61cbaa1021174
|
Provenance
The following attestation bundles were made for aerospike_sdk-0.9.0a5-py3-none-any.whl:
Publisher:
publish-artifact.yaml on citrusleaf/artifact-publisher
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
aerospike_sdk-0.9.0a5-py3-none-any.whl -
Subject digest:
6ac1cfcae00c5e49117d081559f39855d4d1859fbb94e5fbb1769ead0e4acd19 - Sigstore transparency entry: 1844256201
- Sigstore integration time:
-
Permalink:
citrusleaf/artifact-publisher@c53d7b14d525b5e20843bcb4d7fc7850e7df14c4 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/citrusleaf
-
Access:
internal
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-artifact.yaml@c53d7b14d525b5e20843bcb4d7fc7850e7df14c4 -
Trigger Event:
workflow_dispatch
-
Statement type: