Derefable, automatically reconnecting zookeeper connection, for Clojure
Switch branches/tags
Nothing to show
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Failed to load latest commit information.
src Make ClientLoop object Closeable Sep 28, 2015
test
.gitignore Add derefable Zookeeper client connection loop. May 12, 2014
LICENSE Add derefable Zookeeper client connection loop. May 12, 2014
README.md
project.clj Update version and dependencies Sep 28, 2015

README.md

zookeeper-loop

A Clojure library designed to have an automatically reconnecting Zookeeper client, for use with for example zookeeper-clj.

Usage

Add the following dependency to your project:

[functionalbytes/zookeeper-loop "0.1.0"]

Create a client loop like so:

(require '[zookeeper-loop :refer (client-loop close-loop)])

(def client (client-loop "localhost:2181"))

One can pass options to the client-loop as one would to zookeeper/connect of zookeeper-clj. One extra option is :client-atom. This will then be used to hold the currently used Zookeeper instance. Use at your own risk.

Now that you have a client-loop, one can use it as follows:

;; from zookeeper-clj
(require '[zookeeper :as zk])

;; Just deref the client-loop, and a connected instance will be returned.
(zk/create-all @client "/foo/bar")

;; A client may be in a 'connecting' state forever, so a more sensible approach
;; cound be to use timeouts.
(zk/data (deref client 2000 ::fail) "/foo/bar")

A connection-related exception might still be thrown in above statement, for instance when the client is closed by you, authentication failed, or the connection changed between the deref and the actual execution of the expression. Dealing with these cases is the responsibility of the user. One can of course easily wrap these statements with more sophisticated retry logic (e.g. the patterns below).

To close the client, and stop the loop:

(close-loop client)

Patterns

The zookeeper-loop.patterns namespace contains some frequently used Zookeeper patterns. These patterns use a client-loop, so they are failure-friendly. Currently the following patterns are available:

retry

Tries to execute the given function on a connected Zookeeper client as the first argument. When the connection to Zookeeper cannot be (re)established within timeout (msec) while processing the function, an exception is thrown. Note that in rare cases the retry pattern may take longer than the given timeout, when a newly established connection gets disconnected right away, i.e. just before the given function can be executed. Returns the value of the evaluated function. For example:

(retry client 1000 zk/delete "/path/to/delete")

ensure-path

Ensures the given path exists, based on the retry pattern above. Returns the path when created, or nil when it already exists. For example:

(ensure-path client 1000 "/path/to/check/or/create")

swap!

Applies the given function atomically (a compare-and-set loop) to the data of the given path, together with the args. This is based on the retry pattern above. Requires *deserializer* and *serializer* to be bound to functions taking a byte-array and a value respectively. Returns the new value. For example:

;; Atomically add an exclamation to a String.
(binding [*deserializer* #(String. % "UTF-8")
          *serializer* #(.getBytes % "UTF-8")]
  (swap! client 1000 "/path/to/a/string/node" str "!"))

TODO

  • Have more influence on how to deal with expired clients when derefing?
  • Have deserializer and serializer default to identity?