An opinionated Clojure library wrapping Aerospike Java Client.
- 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:
mostlynear 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.
Currently supports only single bin records.Does not expose batch/scan operations. Batch writes are supported viaput-multiple
.
Support batch asynchronous APIs.Support batch put asynchronous API.- Support Java 11
user=> (require '[aerospike-clj.client :as aero])
nil
user=> (def c (aero/init-simple-aerospike-client
#_=> ["aerospike-001.com", "aerospik-002.com"] "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
["localhost"]
"test"
{: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/db
user=> (require '[manifold.deferred :as d])
nil
user=> (aero/put c "index" "set-name" 42 1000)
<< … >>
user=> (def f (aero/get-single c "index" "set-name"))
#'user/f
user=> (d/chain (aero/get-single c "index" "set-name")
#_=> :ttl
#_=> aero/expiry-unix
#_=> #(java.time.Instant/ofEpochSecond %)
#_=> str
#_=> println)
<< … >>
2019-01-10T09:02:45Z
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
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
.
Usage:
(ns com-example.app
(: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.