Avout: Distributed State in Clojure
Latest commit 06f3e00 Jun 30, 2014 @liebke Merge pull request #16 from holguinj/issue-2
Make zk-atoms actually persist
Failed to load latest commit information.


About Avout

Avout brings Clojure's in-memory model of state to distributed application development by providing a distributed implementation of Clojure's Multiversion Concurrency Control (MVCC) STM along with distributable, durable, and extendable versions of Clojure's Atom and Ref concurrency primitives.

Avout enables techniques that require synchronous, coordinated (i.e. transactional) management of distributed state (see also JavaSpaces), complementing approaches that focus on asynchronous, uncoordinated communication between distributed components, e.g. message queues (0MQ, RabbitMQ, HornetQ), event-driven approaches (Netty, Aleph), and actors (Erlang, Akka).

Much has been written [1, 2, 3] on functional programming and the advantages of designing programs that emphasize pure functions and immutable values, and that minimize or eliminate, wherever possible, mutable state. Of course, it's not always possible to completely eliminate the need for mutable state, and that's where Clojure's precise model of time, identity, and state becomes powerful.

Likewise, when designing distributed applications, it is desirable to create components that are loosely coupled and that communicate with each other asynchronously, but this too is also not always possible. There are times when you need coordinated access to state across systems in a distributed application, and this is where Avout comes in.

Avout uses ZooKeeper and zookeeper-clj to coordinate state change, and also includes distributed implementations of java.util.concurrent.lock.Lock and java.util.concurrent.lock.ReadWriteLock.

Using Avout

To get started, you'll need to run ZooKeeper, and include Avout as a dependency by adding the following to your project.clj file:

[avout "0.5.3"]

Below is the Avout equivalent of *Hello World*.

(use 'avout.core)
(def client (connect ""))

(def r0 (zk-ref client "/r0" 0))
(def r1 (zk-ref client "/r1" []))

(dosync!! client
  (alter!! r0 inc)
  (alter!! r1 conj @r0))

Start by creating a ZooKeeper client with the connect function, then create two ZooKeeper-backed distributed Refs using the zk-ref function. Finally, perform a dosync!! transaction that updates both Refs with alter!!. Using Avout isn't much different than using Clojure's in-memory Atoms and Refs.

Avout Atoms and Refs implement Clojure's IRef interface, and therefore support functions that operate on IRefs, including: deref (and its reader-macro, @), set-validator!, add-watch, and remove-watch.

Avout also provides "double-bang" versions of the remaining core Atom and Ref functions (reset!, swap!, dosync, ref-set, alter, commute) for use with distributed Atoms and Refs, reset!!, swap!!, dosync!!, ref-set!!, alter!!, commute!!.

Note: Avout Refs cannot participate in in-memory dosync transactions, but Avout's **local-ref** provides the equivalent of an in-memory Ref that can participate in dosync!! transactions with distributed Refs.

Extending Avout

Two types of Atoms, zk-atom and mongo-atom, and three types of Refs, zk-ref, mongo-ref, and local-ref have been implemented, and Avout can be extended with additional types of Atoms and Refs that use different containers for their state, durable or not, including (No)SQL databases, (distributed) filesystems, in-memory data structures, and RESTful webservices. The types of values supported by each depends on both the backend store and the method of serialization used, and transactions containing different types of Avout Refs are supported.

New types of Atoms can be created by implementing the avout.state.StateContainer protocol,

(defprotocol StateContainer
  (initStateContainer [this])
  (destroyStateContainer [this])
  (getState [this])
  (setState [this value]))

and new Ref types can be created by implementing avout.state.VersionedStateContainer.

(defprotocol VersionedStateContainer
  (initVersionedStateContainer [this])
  (destroyVersionedStateContainer [this])
  (getStateAt [this version])
  (setStateAt [this value version])
  (deleteStateAt [this version]))

More on Avout

To learn more about Avout and distributed-state in Clojure, visit avout.io


Although Avout is not part of Clojure-Contrib, it follows the same guidelines for contributing, which includes signing a Clojure Contributor Agreement (CA) before contributions can be accepted.


Avout is distributed under the Eclipse Public License, the same as Clojure.


Avout is Copyright © 2011 David Liebke and Relevance, Inc