Home

Awesome

PlumDB

[[TOC]]

PlumDB is a globally replicated database using eventual consistency. It uses Epidemic Broadcast Trees and lasp-lang’s Partisan, an alternative runtime system for improved scalability and reduced latency in distributed Erlang applications.

It is an offspring of Helium's Plumtree – a descendant of Riak Core's Metadata Store – and Partisan.

The original Plumtree project was the result of extracting the Metadata Store from Riak Core and replacing the cluster membership state by an ORSWOT CRDT.

PlumDB builds on top of Plumtree but changes its architecture offering additional features.

FeaturePlumDBPlumtree
Cluster membership statePartisan's membership state which uses an AWSETORSWOT (riak_dt)
Data modelRiak Core Metadata (dvvset)Riak Core Metadata (dvvset)
Persistenceleveldb. A key is sharded across N instances of a store. Stores can be in-memory (ets), on disk (Rocksdb) or both. N is configurable at deployment time.Each prefix has its own ets and dets table.
APIA simplification of the Riak Core Metadata API. A single function to iterate over the whole database i.e. across one or all shards and across a single or many prefixes.Riak Core Metadata API (plumtree_metadata_manager) is used to iterate over prefixes whereas plumtree_metadata is used to iterate over keys within each prefix. The API is confusing and is the result of having a store (ets + dets) per prefix.
Active anti-entropyBased on Riak Core Metadata AAE, uses a separate instance of leveldb to store a merkle tree on disk. Updated to use the new API and gen_statemBased on Riak Core Metadata AAE, uses a separate instance of leveldb to store a merkle tree on disk.
PubsubBased on a combination of gen_event and gproc, allowing to register a Callback module or function to be executed when an event is generated. gproc dependency allows to pattern match events using a match specBased on gen_event, allowing to register a Callback module or function to be executed when an event is generated

Installation

You will use PlumDB as a dependency in your Erlang application.

Requirements

Configuration

PlumDB is configured using the standard Erlang sys.config.

The following is an example configuration:

{plum_db, [
    {aae_enabled, true},
    {store_open_retries_delay, 2000},
    {store_open_retry_Limit, 30},
    {data_exchange_timeout, 60000},
    {hashtree_timer, 10000},
    {data_dir, "data"},
    {partitions, 8},
    {prefixes, [
        {foo, ram},
        {bar, ram_disk},
        {<<"baz">>, disk}
    ]}
]}

At the moment additional configuration is required for Partisan and Plumtree dependencies:

{partisan, [
    {peer_port, 18086}, % port for inter-node communication
    {parallelism, 4} % number of tcp connections
]}
{plumtree, [
    {broadcast_exchange_timer, 60000} % Perform AAE exchange every 1 min.
]}

Usage

Learn more by reading the source code Documentation.

Standalone testing

We have three rebar3 release profiles that you can use for testing PlumDB itself.

Running a 3-node cluster

To run a three node cluster do the following in three separate shells.

In shell #1:

$ rebar3 as dev1 run

In shell #2:

$ rebar3 as dev2 run

In shell #3:

$ rebar3 as dev3 run

Make node 2 and 3 join node 1

In node #2:

> {ok, Peer} = partisan:node_spec('plum_db1@127.0.0.1', {{127,0,0,1}, 18086}).
> partisan_peer_service:join(Peer).

In node #3:

> {ok, Peer} = partisan:node_spec('plum_db1@127.0.0.1', {{127,0,0,1}, 18086}).
> partisan_peer_service:join(Peer).

Check that the other two nodes are visible in each node

In node #1:

> partisan_peer_service:members().
{ok,['plum_db3@127.0.0.1','plum_db2@127.0.0.1']}

In node #2:

> partisan_peer_service:members().
{ok,['plum_db3@127.0.0.1','plum_db1@127.0.0.1']}

In node #3:

> partisan_peer_service:members().
{ok,['plum_db2@127.0.0.1','plum_db1@127.0.0.1']}

In node #1:

> [plum_db:put({foo, a}, x, 1).
ok

In node #2:

> plum_db:put({foo, a}, y, 2).
ok

In node #3:

> plum_db:put({foo, a}, z, 3).
ok

Do the following on each node to check they now all have the three elements:

> plum_db:fold(fun(Tuple, Acc) -> [Tuple|Acc] end, [], {'_', '_'}).
[{x,1},{y,2},{z,3}]

We are folding over the whole database (all shards) using the full prefix wildcard {'_', '_'}.

The following are examples of prefix wildcards:

Notice that the pattern {'_', bar} is NOT allowed.