Tansu is a distributed key/value and lock store

Tansu is a distributed key value store designed to maintain configuration and other data that must be highly available. It uses the Raft Consensus algorithm for leadership election and distribution of state amongst its members. By default node discovery is via mDNS and will automatically form a mesh of nodes sharing the same environment.

Features

Tansu has a REST interface to set, get or delete the value represented by a key. It also provides a HTTP Server Sent Event Stream
of changes to the store.

Tansu provides REST interface for simple Check And Set (CAS) operations.

Tansu provides test and set operations that can be used to operate locks through a simple REST based HTTP Server Sent Event Stream interface.

Quick Start

Tansu is packaged as a Docker container. To start a local 5 node cluster is as simple as:

for i in {1..5}; do
    docker run \
        --name tansu-$(printf %03d $i) \
        -d shortishly/tansu;
done

Tansu uses mDNS by default to discover other nodes and automatically forms a cluster. API requests can be made to any discovered node, which are internally routed to the appropriate node depending on the request type.

To demonstrate this, lets use the shell to randomly pick a node from our new cluster:

RANDOM_IP=$(docker inspect \
            --format={{.NetworkSettings.IPAddress}} \
            tansu-$(printf %03d $[1 + $[RANDOM % 5]]))

All key/value API operations are under the ‘/api/keys/…’ URL. We can create a stream of changes to a key (or a hierarchy of keys) before that key exists as follows:

curl \
    -i \
    -s \
    "http://${RANDOM_IP}/api/keys/hello?stream=true&children=true"

The key space in Tansu is a directory structure separated with ‘/’ characters. Any change to the key ‘hello’ will be reported in the above stream, and also any change in subdirectory below ‘hello’ will also be reported.

Leaving the stream curl running, in another shell lets assign the value “world” to the key “hello”:

curl \
    -X PUT \
    -i \
    -s \
    http://${RANDOM_IP}/api/keys/hello \
    -d value=world

Back in our stream, it will contain a ‘create’ notification:

id: 1
event: create
data: {
  "category":"user",
  "key":"/hello",
  "metadata":{
  "tansu":{
    "content_type":"text/plain",
    "created":1,
    "parent":"/",
    "updated":1}},
  "value":"world"}

Or a key that is below ‘hello’:

curl \
  -X PUT \
  -i \
  -s \
   http://${RANDOM_IP}/api/keys/hello/joe \
  -d value=mike

The stream will now contain a `create` notification:

id: 2
event: create
data: {
  "category":"user",
  "key":"/hello/joe",
  "metadata":{
    "tansu":{
      "content_type":"text/plain",
      "created":2,
      "parent":"/hello",
      "updated":2}},
  "value":"mike"}

In the above case Tansu will assume that the value has the ‘text/plain’ content type (as the value from a form url encoded body). Other content types (in particular JSON) are also supported:

curl \
  -X PUT \
  -H "Content-Type: application/json" \
  -i http://${RANDOM_IP}/api/keys/hello \
  --data-binary '{"stuff": true}'

With an update in the stream:

id: 3
event: set
data: {
  "category":"user",
  "key":"/hello",
  "metadata":{
    "tansu":{
      "content_type":"application/json",
      "created":1,
      "parent":"/",
      "updated":3}},
  "previous":"world",
  "value":{"stuff":true}}

GET

The current value of a key can be obtained simply by issuing a GET on that key:

curl \
  -i \
  -s \
  http://${RANDOM_IP}/api/keys/hello

{"stuff": true}

DELETE

Similarly a key is removed by issuing a DELETE request:

curl \
  -i \
  -X DELETE \
  http://${RANDOM_IP}/api/keys/hello

The stream will now contain a delete notification:

id: 5
event: delete
data: {
  "category":"user",
  "key":"/hello",
  "metadata":{
    "tansu":{
      "content_type":"application/json",
      "created":1,
      "parent":"/",
      "updated":5}},
  "value":{"stuff":true}}

TTL

A value can also be given a time to live by supplying a TTL header:

curl \
  -X PUT \
  -H "Content-Type: application/json" \
  -H "ttl: 10" \
  -i \
  http://${RANDOM_IP}/api/keys/hello \
  --data-binary '{"ephemeral": true}'

The event stream will contain details of the `create` together with a TTL
attribute:

id: 6
event: create
data: {
  "category":"user",
  "key":"/hello",
  "metadata":{
    "tansu":{
      "content_type":"application/json",
      "created":6,
      "parent":"/",
      "ttl":10,
      "updated":6}},
  "value":{"ephemeral":true}}

Ten seconds later when the time to live has expired:

id: 7
event: delete
data: {
  "category":"user",
  "key":"/hello",
  "metadata":{
    "tansu":{
      "content_type":"application/json",
      "created":6,
      "parent":"/",
      "ttl":0,
      "updated":7}},
  "value":{"ephemeral":true}}

Leave a Reply

Your email address will not be published. Required fields are marked *