Liking cljdoc? Tell your friends :D


An opinionated Clojure library wrapping Aerospike Java Client.

Clojars Project

Build Status

Coverage Status


Generated docs



More advanced docs:


  • Java 8
  • Clojure 1.8


  • Converts Java client's callback model into a future (manifold/deferred) based API.
  • Expose passing functional transcoders over payloads (both put/get).
  • Health-check utility.
  • Functions return Clojure records.


  • stable. Although not 1.x, the API will remain stable.


  • Feature completeness: ~~mostly~~ near complete.
  • Stability: production ready. We actively use this library in production.


  • Non blocking only: Expose only the non-blocking API. Block with deref if you like.
  • Futures instead of callbacks. Futures (and functional chaining) are more composable and less cluttered. If a synchronous behaviour is still desired, the calling code can still deref (@) the returned future object. For a more sophisticated coordination, a variety of control mechanisms are supplied by manifold/deferred, or via the library using transcoders or hooks.
  • Follows the method names of the underlying Java APIs.
  • TTLs should be explicit, and developers should think about them. Forces passing a ttl and not use the cluster default.
  • Minimal dependencies.
  • Single client per Aerospike namespace. Namespaces in Aerospike usually indicate different cluster configurations. In order to reduce overhead for clusters with more than a single namespace create 2 client objects and share an event loop between them.

Limitations/ caveats

  • ~~Currently supports only single bin records.~~
  • ~~Does not expose batch/scan operations. Batch writes are supported via put-multiple.~~


  • ~~Support batch asynchronous APIs.~~
  • ~~Support batch put asynchronous API.~~
  • Support Java 11


Most of the time just create a simple client (single cluster)

user=> (require '[aerospike-clj.client :as aero])
user=> (def c (aero/init-simple-aerospike-client
  #_=>          ["", ""] "my-ns" {:enable-logging true}))

It is possible to inject additional asynchronous user-defined behaviour. To do that add an instance of ClientEvents. Some useful info is passed in in-order to support metering and to read client configuration. op-start-time is (System/nanoTime) more here.

(let [c (aero/init-simple-aerospike-client
          {:client-events (reify ClientEvents
                            (on-success [_ op-name op-result index op-start-time db]
                              (when (:enable-logging? db)
                                (println op-name "success!")))
                            (on-failure [_  op-name op-ex index op-start-time db]
                              (println "oh-no" op-name "failed on index" index)))})]

  (get-single c "index" "set-name"))


For demo purposes we will use a docker based local DB:

$ sudo docker run -d --name aerospike -p 3000:3000 -p 3001:3001 -p 3002:3002 -p 3003:3003 aerospike

And connect to it:

user=> (def c (aero/init-simple-aerospike-client ["localhost"] "test"))
user=> (require '[manifold.deferred :as d])
user=> (aero/put c "index" "set-name" 42 1000)
<< … >>
user=> (def f (aero/get-single c "index" "set-name"))
user=> (d/chain (aero/get-single c "index" "set-name")
  #_=>          :ttl
  #_=>          aero/expiry-unix
  #_=>          #(java.time.Instant/ofEpochSecond %)
  #_=>          str
  #_=>          println)
<< … >>

We actually get back a record with the payload, the DB generation and the ttl (in an Aerospike style EPOCH format).

user=> @(aero/get-single c "index" "set-name")
#aerospike_clj.client.AerospikeRecord{:payload 42, :gen 1, :ttl 285167713}


Aerospike returns a TTL on the queried records that is Epoch style, but with a different "beginning of time" which is "2010-01-01T00:00:00Z". Call expiry-unix with the returned TTL to get a UNIX TTL if you want to convert it later to a more standard timestamp.


Testing is performed against a local Aerospike running in the latest docker

$ sudo docker run -d --name aerospike -p 3000:3000 -p 3001:3001 -p 3002:3002 -p 3003:3003 aerospike
$ lein test

Mocking in application unit tests

When performing unit tests in application code, it is most times undesirable to launch a full aerospike container to run tests against. For those cases the library exposes a mock client that replaces all the calls to aerospike-clj.client.


  (:require [clojure.test :refer [deftest use-fixtures]]
            [aerospike-clj.mock-client :refer [init-mock]]))

(use-fixtures :each init-mock)

(deftest ...) ;; define your application unit tests as usual

The sample code executes on every test run. It initializes the mock and runs the test within a with-redefs - rebinding all the calls to functions in aerospike-clj.client to the mock.

Note: If the production client is initiated using a state management framework, you would also need to stop and restart the state on each test run.


PRs are welcome!


Distributed under the Apache 2.0 License - found here.

Can you improve this documentation? These fine people already did:
Ido Barkan, yaron.elyashiv, Yaron Elyashiv & Sharone Zitzman
Edit on GitHub

cljdoc is a website building & hosting documentation for Clojure/Script libraries

× close