Skip to content

Latest commit

 

History

History
255 lines (172 loc) · 8.47 KB

README.md

File metadata and controls

255 lines (172 loc) · 8.47 KB

scakka-zoo-cache Build Status Maven Central Maven Central

A library that caches ZooKeeper data. The cached data is used only for read operations, namely get Data and get Children. It does not support write operations, these should be done via the ZooKeeper Client.

The library is intended to be used by applications that heavily use ZooKeeper for read operations and can cope with eventual consistency.

It is written in Scala, the cache synchronization is internally done using Akka and it provides APIs for Scala, Java and Akka, all of them easily accessible via a factory object.

The paths to be cached are defined using the library's API. Subtrees and children of the defined paths to cache are automatically cached as well.

Whenever something changes in the ZooKeeper, watches are activated and the cache eventually follows the change (add/remove nodes and data).

Features

  • Built on top of the ZooKeeper API
  • Offering the required abstractions in order to use other ZooKeper frameworks (like Apache Curator)
  • Cache the whole ZooKeeper tree, or just parts of it
  • Data synchronization using Akka Actors
  • Access with Scala, Akka, or Java APIs.
  • Deployed in the Maven Central:
<dependency>
  <groupId>com.github.astonbitecode</groupId>
  <artifactId>scakka-zoo-cache</artifactId>
  <version>0.2.1</version>
</dependency>

Latest snapshot:

<dependency>
  <groupId>com.github.astonbitecode</groupId>
  <artifactId>scakka-zoo-cache</artifactId>
  <version>0.2.2-SNAPSHOT</version>
</dependency>

API Usage

The APIs of the ScakkaZooCache are offered for Scala, Akka and Java, depending on how the cache is created (which com.github.astonbitecode.zoocache.ScakkaZooCacheFactory method will be used for the instantiation of the cache).

They all support the following:

  • Adding a path to the cache

    After adding a path to the cache (eg. /path/one, all the ZooKeeper changes under the path /path/one will be monitored. So, for example when getting the data of the path /path/one/child1 the Data of the zNode will be returned from the cache. Of course, this will happen if and only if the path really exists in the ZooKeeper. In the opposite case, the call will throw a NotCachedException.

  • Reading children of a path.

  • Getting data of a node in a path.

  • Removing a path from the cache

    Stop monitoring a path and remove it from the cache.

  • Stopping the cache

Note: Writing to the ZooKeeper is not done via the ScakkaZooCache. It should be done by using the ZooKeeper client itself. The cache is used only for reading operations.


Scala API Usage

Initialize the cache

Assuming that zk is a ZooKeeper class instance, a ScakkaZooCache can be created like following:

1. Using simple initialization

import com.github.astonbitecode.zoocache.ScakkaZooCacheFactory

val zooCache = ScakkaZooCacheFactory.scala(zk)

2. Defining an ActorSystem

import com.github.astonbitecode.zoocache.ScakkaZooCacheFactory

val actorSystem = ActorSystem("myActorSystem")
val zooCache = ScakkaZooCacheFactory.scala(zk, actorSystem)

3. Creating from inside an Akka Actor, using the ActorContext

import com.github.astonbitecode.zoocache.ScakkaZooCacheFactory

// This is one Actor
class MyActor extends Actor {

  // Create a zoocache instance
  val zooCache = ScakkaZooCacheFactory.scala(zk, context)

  // Handle messages
  override def receive(): Receive = {
    case _ => ... // Use the zoo cache
  }
}

Add a Path to the cache

Simply call the addPathToCache:

zooCache.addPathToCache("/path/one")

Use the cache

// Get the children of a path
val children = zooCache.getChildren("/a/path")

// Get the data of a path
val data = zooCache.getData("/a/path")

// Find all the paths that match a regex and return a List of results
val cacheResults = zooCache.find("(^\\/a\\/path\\/[\\w]*)")

Akka API Usage

Create the Actor that handles the Akka API messages

import com.github.astonbitecode.zoocache.ScakkaZooCacheFactory

// Create your ActorSystem
val actorSystem = ActorSystem("myActorSystem")

// Create a ScakkaZooCache
val zooCache = ScakkaZooCacheFactory.scala(zk, actorSystem)

// Get the Akka Props from the factory
val props = ScakkaZooCacheFactory.props()

// Create the ActorRef
val zooCacheActorRef = actorSystem.actorOf(props)

// Contact the ActorRef
zooCacheActorRef ! GetChildren("/a/path")

Add a path to the cache

zooCacheActorRef ! AddPathToCache("/a/path")

Use the cache

// Get the children of a path
zooCacheActorRef ! GetChildren("/a/path")

// Get the data of a path
zooCacheActorRef ! GetData("/a/path")

// Find all the paths that match a regex and return a List of results
zooCacheActorRef ! Find("(^\\/a\\/path\\/[\\w]*)")

Akka messaging

The available messages for the Akka API exist in the com.github.astonbitecode.zoocache.api.akka package.

Each one of the available Akka messages has its corresponding response. This message is sent by the Actor that handles the Akka API as a response to a request. (You can consult the Scaladocs for more details).

For example, when sending a GetChildren message, a response of GetChildrenResponse will be received:

val askFuture = zooCacheActorRef ? GetChildren("/a/path")
val children: GetChildrenResponse = Await.result(askFuture, 30 seconds)

Request - Response correlation

Akka users are not obliged to use the Ask pattern. All the Akka messages offered by the ScakkaZooCache API have an Optional parameter that can be used for Request-Response correlation:

import com.github.astonbitecode.zoocache.ScakkaZooCacheFactory
import com.github.astonbitecode.zoocache.api.akka._

// This is one Actor
class MyActor extends Actor {

  // Create a zoocache instance
  val zooCache = ScakkaZooCacheFactory.scala(zk, context)
  // Create the zoocache Actor to handle the Akka API messages
  val zooCacheActorRef = context.actorOf(ScakkaZooCacheFactory.props(zooCache))

  // Send a message to the zooCacheActor
  zooCacheActorRef ! GetChildren("/a/path", Some("123"))

  // Handle the Responses
  override def receive(): Receive = {
    case getChildrenResponse: GetChildrenResponse => {
      if (getChildrenResponse.correlation == Some("123")) {
        println("OK")
      } else {
        println("CORRELATION ERROR")
      }
    }
    case _ =>
  }
}

Failure cases

In case of failure of any of the Akka API messages, the sender will receive a CacheFailure.

This message contains the failure details along with any correlation object the respective request contained.

Java API Usage

Initialize the cache

import com.github.astonbitecode.zoocache.ScakkaZooCacheFactory;
import com.github.astonbitecode.zoocache.api.java.JScakkaZooCache;

JScakkaZooCache zooCache = ScakkaZooCacheFactory.java(zk);

Add a Path to the cache

Simply call the addPathToCache:

import java.util.concurrent.Future;

Future<BoxedUnit> f = zooCache.addPathToCache("/a/path");
f.get();

Use the cache

// Get the children of a path
List<String> children = zooCache.getChildren("/a/path");

// Get the data of a path
byte[] data = zooCache.getData("/a/path");

// Find all the paths that match a regex and return a List of results
import com.github.astonbitecode.zoocache.api.dtos.JCacheResult;

List<JCacheResult> cacheResults = zooCache.find("(^\\/a\\/path\\/[\\w]*)");