5. Key/Value store

5.1 About Key/Value store

ActorDB can also act as a key/value store by maintaining a sharded table across all clusters. The syntax is a bit verbose, but quite powerful. Unlike most KV stores, ActorDB KV uses structured values. A value can have multiple columns and even foreign tables.

Lets create a schema file (counters.sql) and save it to ActorDB using: actordb_console -f counters.sql

-- create a KV namespace named counters.
actor counters kv
-- Every KV type must have a table named actors. id and hash are mandatory. 
-- id is our key, hash is used by ActorDB, val is our data.
CREATE TABLE actors (id TEXT PRIMARY KEY, hash INTEGER, val INTEGER) WITHOUT ROWID

Important:

- "counters" is a key/value namespace. You can have multiple namespaces with different table schemas.

- Table must be named "actors" and it must have id and hash columns. id is key, hash is hash of id, everything after that is up to you.

- You can create as many columns as you wish.

- You can have multiple tables (but they must use foreign keys to actors table).


Lets insert a few values:

actordb> ACTOR counters(mykey);
actordb (1)> INSERT INTO actors VALUES ('mykey', {{hash(mykey)}}, 42);
actordb (2)> c
Rowid: 4, Rows changed: 1
actordb> ACTOR counters(mykey1);
actordb (1)> INSERT INTO actors VALUES ('mykey1', {{hash(mykey1)}}, 43);
actordb (2)> c
Rowid: 9, Rows changed: 1
actordb> ACTOR counters(mykey2);
actordb (1)> INSERT INTO actors VALUES ('mykey2', {{hash(mykey2)}}, 44);
actordb (2)> c
Rowid: 4, Rows changed: 1

Very similar to how actors are queried. First line sets namespace and key. Second line is what gets executed. It is important to always include id and hash on inserts. Hash must be written as:

Let's return a row we inserted earlier:

actordb> actor counters(mykey);
actordb (1)> select * from actors where id='mykey';
actordb (2)> c
**********************
hash       id    val |
----------------------
2721926833 mykey 42  |
----------------------

You can return an entire KV namespace with a simple query:

actordb> actor counters(*);
actordb (1)> {{RESULT}}SELECT * FROM actors;
actordb (2)> c
**********************************
actor      hash       id     val |
----------------------------------
2147483646 2721926833 mykey  42  |
2147483646 2724604765 mykey1 43  |
0          535595634  mykey2 43  |
----------------------------------

Note how mykey and mykey1 were added to the same KV shard (actor column), mykey2 was added to a different one.

5.2 Use case: reliable distributed counters

Say you have an analytics application that needs to be able count pageviews. Single points of failure or data loss are out of the question. You basically have two operations: increment and read counter.

If you have 2 clusters of 3 servers. Create 12 counter IDs (24 would probably be better).

Pick key for increment: N = RandomNumber % 12.

Increment like so:

ACTOR counters(N);
UPDATE actors SET val = val+1 WHERE id='N';

Read all counters

ACTOR counters(1,2,3,4,5,6,7,8,9,10,11,12);
{{RESULT}}SELECT * FROM actors where id='{{curactor}}';

This will return row for every key. You have to sum them manually however.

<< Previous     (4. Query model) (6. Efficiency)     Next >>