memstore
=====
Simple memory database for Erlang application using
[MVCC](https://en.wikipedia.org/wiki/Multiversion_concurrency_control) to store the data.
## Design
Data are stored in an ETS table, writes are collected in a simple ERLANG process to allows atomic batch. Reads
are none blocking and directly access to the table
Active version sand running databases snapshots are maintained via another Erlang OTP gen_server. Snapshots
allows the user to maintain a stable view of the database during the queries. Until a snapshot or an iterator
is released deleted keys/values associated to this database version won't be deleted.
Garbage collection (the removal of old versions) is done in background.
## Basic Operations
### Open a simple database
```erlang
Db = test,
Options = [],
%% open the database
ok = memstore:open(Db, Options),
%% try to read an write a key/value
Key = a,
Value = b,
%% value is not yet created, fetching it return not_found
not_found = memstore:get(Db, Key),
%% create the key
ok = memstore:put(Db, Key, Value),
%% fetch the value for this key
{ok, b} = memstore:get(Db, Key),
%% delete the Key
ok = memstore:delete(Db, Key),
not_found = memstore:get(Db, Key),
%% close the database
ok = memstore:close(Db).
```
### Atomic updates
```erlang
ok = memstore:open(test, []),
ok = memstore:write_batch(test,
[
{put, a, b},
{put, c, d}
]
),
{ok, b} = memstore:get(test, a),
{ok, d} = memstore:get(test, c),
ok = memstore:write_batch(test,
[
{delete, a},
{delete, c}
]
),
not_found = memstore:get(test, a),
not_found = memstore:get(test, c),
ok = memstore:close(test).
```
### Snapshots
Snapshots provide consistent read-only views over the entire state of the key-value store.
```erlang
ok = memstore:open(test, []),
not_found = memstore:get(test, a),
ok = memstore:put(test, a, b),
{ok, b} = memstore:get(test, a),
Snapshot = memstore:new_snapshot(test),
1 = memstore:get_snapshot_sequence(Snapshot),
ok = memstore:put(test, c, d),
{ok, b} = memstore:get(test, a, [{snapshot, Snapshot}]),
not_found = memstore:get(test, c, [{snapshot, Snapshot}]),
{ok, d} = memstore:get(test, c),
ok = memstore:close(test).
```
> Note that when a snapshot is no longer needed, it should be released using the
`memstore:release_snapshot/1` function. This allows the implementation to get rid
of state that was being maintained just to support reading as of that snapshot.
### Iteration
The following example shows how to iterate all the keys in the database
```erlang
%% create some keys
ok = memstore:open(test, []),
ok = memstore:put(test, <<"a">>, <<"x">>),
ok = memstore:put(test, <<"b">>, <<"y">>),
{ok, <<"x">>} = memstore:get(test, <<"a">>),
%% create an iterator
{ok, I} = memstore:iterator(test, []),
%% move to an empty key
{ok, <<"a">>, <<"x">>} = memstore:iterator_move(I, {seek, <<>>}),
%% move forward
{ok, <<"b">>, <<"y">>} = memstore:iterator_move(I, next),
%% move backward
{ok, <<"a">>, <<"x">>} = memstore:iterator_move(I, prev),
%% close the iterator
ok = memstore:iterator_close(I),
ok = memstore:close(test).
```
## Build
$ rebar3 compile