• Stars
    star
    148
  • Rank 242,315 (Top 5 %)
  • Language
    C
  • License
    Other
  • Created over 9 years ago
  • Updated over 6 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

A Lightweight Durable HTTP Key-Value Pair Database in C
https://travis-ci.org/willemt/pearldb.png

What?

PearlDB is a durable HTTP Key-Value pair database. It uses LMDB for storing data, and H2O for HTTP.

PearlDB is completely written in C.

Persistent connections and pipelining are built-in.

PearlDB uses bmon to batch LMDB writes.

Goals

  • Speed
  • Low latency
  • Durability - An HTTP response means the write is on disk
  • Simplicity outside (RESTful inteface)
  • Simplicity inside (succinct codebase)
  • HTTP caching - Because the CRUD is RESTful you could hypothetically use an HTTP reverse proxy cache to scale out reads. You could use multiple caches to create an eventually consistent database

Ubuntu Quick Start

sudo add-apt-repository -y ppa:willemt/pearldb
sudo apt-get update
sudo apt-get install pearldb

Usage

Examples below make use of the excellent httpie

Starting

build/pearl --daemonize --port 8000 --db_size 1 --pid_file pearl.pid
echo daemonizing...
daemonizing...

Get

We obtain a value by GET'ng the key.

In this case the key is "x". But we get a 404 if it doesn't exist.

http -h --ignore-stdin 127.0.0.1:8000/x/
HTTP/1.1 404 NOT FOUND
Date: ..., ... .... ........ GMT
Connection: keep-alive
Server: h2o/2.2.4
content-length: 0

You MUST specify a path.

http -h --ignore-stdin 127.0.0.1:8000/ | head -n 1
HTTP/1.1 400 BAD PATH

Put

We use PUT for creating or updating a key value pair. PUTs are durable - we only respond when the change has been made to disk.

echo "MY VALUE" | http -h PUT 127.0.0.1:8000/x/
HTTP/1.1 200 OK
Date: ..., ... .... ........ GMT
Connection: keep-alive
Server: h2o/2.2.4
transfer-encoding: chunked

PUTs have an immediate change on the resource. There is full isolation, and therefore no dirty reads.

Now we can finally retrieve our data via a GET:

http --ignore-stdin 127.0.0.1:8000/x/
MY VALUE

The slash at the end is optional.

http --ignore-stdin 127.0.0.1:8000/x
MY VALUE

The user must specify the capacity of the database upfront. PearlDB does not support automatic resizing. A PUT will fail if it would put the database over capacity.

head -c 1000000 /dev/urandom | base64 > tmp_file
du -h tmp_file | awk '{ print $1 }'
cat tmp_file | http -h PUT 127.0.0.1:8000/1/
rm tmp_file
1.3M
HTTP/1.1 400 NOT ENOUGH SPACE
Date: ..., ... .... ........ GMT
Connection: keep-alive
Server: h2o/2.2.4
content-length: 0

You can't PUT under nested resources.

echo 'DATA' | http -h PUT 127.0.0.1:8000/x/nested_resource/
HTTP/1.1 400 BAD PATH
Date: ..., ... .... ........ GMT
Connection: keep-alive
Server: h2o/2.2.4
content-length: 0

Put without a key (POST)

If you want PearlDB to generate a key for you, just use POST.

echo "MY POSTED VALUE" | http -h POST 127.0.0.1:8000/ > posted.txt
cat posted.txt
HTTP/1.1 200 OK
Date: ..., ... .... ........ GMT
Connection: keep-alive
Server: h2o/2.2.4
location: ...
transfer-encoding: chunked

The Location header in the response has the URI of the newly created resource. The URI is the URL safe base64 encoded UUID4.

http --ignore-stdin -b GET 127.0.0.1:8000$(grep location: posted.txt | sed -e 's/location: //' | tr -d '\r\n')
MY POSTED VALUE

Providing a URL (ie. key) with POST doesn't make sense, and will result in a 400.

echo "MY POSTED VALUE" | http -h POST 127.0.0.1:8000/xxxx/
HTTP/1.1 400 BAD
Date: ..., ... .... ........ GMT
Connection: keep-alive
Server: h2o/2.2.4
content-length: 0

Get keys

You can get the keys that match a prefix by using the /key/XXX/ nested resource.

echo '' | http PUT 127.0.0.1:8000/1/ > /dev/null
echo '' | http PUT 127.0.0.1:8000/199/ > /dev/null
echo '' | http PUT 127.0.0.1:8000/102/ > /dev/null
echo '' | http PUT 127.0.0.1:8000/2/ > /dev/null
http GET 127.0.0.1:8000/key/1/
1
102
199

Without a prefix you get all keys.

http GET 127.0.0.1:8000/key// | sed -e '/^.*=$/d'
1
102
199
2
x

Existence Check

To check for existence use the HEAD method. This is great, because PearlDB doesn't waste bandwidth sending the document body.

http -h --ignore-stdin HEAD 127.0.0.1:8000/x/
HTTP/1.1 200 OK
Date: ..., ... .... ........ GMT
Connection: keep-alive
Server: h2o/2.2.4

Delete

DELETEs are durable - we only respond when the change has been made to disk.

http -h --ignore-stdin DELETE 127.0.0.1:8000/x/
HTTP/1.1 200 OK
Date: ..., ... .... ........ GMT
Connection: keep-alive
Server: h2o/2.2.4
transfer-encoding: chunked

Of course, after a DELETE the key doesn't exist anymore:

http -h --ignore-stdin 127.0.0.1:8000/x/
HTTP/1.1 404 NOT FOUND
Date: ..., ... .... ........ GMT
Connection: keep-alive
Server: h2o/2.2.4
content-length: 0

Compare and Swap (CAS)

A form of opportunistic concurrency control is available through ETags.

When the client provides the Prefers: ETag header on a GET request we generate an ETag. A client can then use the If-Match header with the ETag to perform a conditional update, (ie. a CAS operation). If the ETag has changed then the PUT operation will fail. CAS operations are great because there is no locking; if a CAS operation fails for one client that means it has succeeded for another, ie. there has been progress.

Imagine two clients trying to update the same key. Client 1 requests an ETag. The ETag is provided via the etag header.

echo 'SWEET DATA' | http -h --ignore-stdin PUT 127.0.0.1:8000/x/ > /dev/null
http -h --ignore-stdin GET 127.0.0.1:8000/x/ Prefers:ETag > etag.txt
cat etag.txt
HTTP/1.1 200 OK
Date: ..., ... .... ........ GMT
Connection: keep-alive
Server: h2o/2.2.4
etag: ...
transfer-encoding: chunked

If client 1 requests an ETag again, the same ETag is sent:

http -h --ignore-stdin GET 127.0.0.1:8000/x/ Prefers:ETag > etag2.txt
cat etag2.txt
diff <(grep etag etag.txt) <(grep etag etag2.txt)
HTTP/1.1 200 OK
Date: ..., ... .... ........ GMT
Connection: keep-alive
Server: h2o/2.2.4
etag: ...
transfer-encoding: chunked

Client 2 does a PUT on x. This will invalidate the ETag.

echo 'SURPRISE' | http -h PUT 127.0.0.1:8000/x/
HTTP/1.1 200 OK
Date: ..., ... .... ........ GMT
Connection: keep-alive
Server: h2o/2.2.4
transfer-encoding: chunked

Client 1 uses a conditional PUT to update "x" using the If-Match tag. Because the ETag was invalidated, we don't commit, and respond with 412 Precondition Failed.

echo 'MY NEW VALUE BASED OFF OLD VALUE' | http -h PUT 127.0.0.1:8000/x/ If-Match:$(grep etag: etag.txt | sed -e 's/etag: //' | tr -d '\r\n')
HTTP/1.1 412 BAD ETAG
Date: ..., ... .... ........ GMT
Connection: keep-alive
Server: h2o/2.2.4
content-length: 0

Once this happens we can retry the PUT after we do a new GET.

http -h GET 127.0.0.1:8000/x/ Prefers:ETag > etag3.txt
cat etag3.txt
HTTP/1.1 200 OK
Date: ..., ... .... ........ GMT
Connection: keep-alive
Server: h2o/2.2.4
etag: ...
transfer-encoding: chunked

The PUT will succeed because the ETag is still valid.

echo 'NEW VALUE' | http -h PUT 127.0.0.1:8000/x/ If-Match:$(grep etag: etag3.txt | sed -e 's/etag: //' | tr -d '\r\n')
HTTP/1.1 200 OK
Date: ..., ... .... ........ GMT
Connection: keep-alive
Server: h2o/2.2.4
transfer-encoding: chunked

However, if we use the ETag again it will fail.

echo 'NEW VALUE2' | http -h PUT 127.0.0.1:8000/x/ If-Match:$(grep etag: etag3.txt | sed -e 's/etag: //' | tr -d '\r\n')
HTTP/1.1 412 BAD ETAG
Date: ..., ... .... ........ GMT
Connection: keep-alive
Server: h2o/2.2.4
content-length: 0

Notes about ETags:

  • On reboots, PearlDB loses all ETag information
  • On launch PearlDB generates a random ETag prefix
  • ETags are expected to have a short life (ie. < 1 day)

OPTIONS

You can check what HTTP methods are available to a resource using the OPTIONS method. This is useful as some systems like HAProxy use the OPTIONS method as a healthcheck.

http -h --ignore-stdin OPTIONS 127.0.0.1:8000/x/
HTTP/1.1 200 OK
Date: ..., ... .... ........ GMT
Connection: keep-alive
Server: h2o/2.2.4
allow: HEAD,GET,PUT,DELETE,OPTIONS
transfer-encoding: chunked
http -h --ignore-stdin OPTIONS 127.0.0.1:8000/
HTTP/1.1 200 OK
Date: ..., ... .... ........ GMT
Connection: keep-alive
Server: h2o/2.2.4
allow: POST,OPTIONS
transfer-encoding: chunked

Shutting down

cat pearl.pid | xargs kill -9
echo shutdown
shutdown

Building

sudo apt-get install git cmake automake libtool libssl-dev
make libuv
make libh2o
make libck
make

More Repositories

1

raft

C implementation of the Raft Consensus protocol, BSD licensed
C
1,090
star
2

cbuffer

A circular buffer written in C using Posix calls to create a contiguously mapped memory space. BSD Licensed.
C
248
star
3

ticketd

A distributed durable unique 64bit ID server
C
193
star
4

bipbuffer

A circular buffer alternative written in C under a BSD license
C
131
star
5

yabtorrent

Cross platform Bittorrent library written in C with a BSD license. Intended to be used as a framework for embedding bittorrent functionality
C
68
star
6

pytest-asyncio-cooperative

Run all your asynchronous tests cooperatively.
Python
68
star
7

heapless-bencode

Bencode reader library that doesn't use the heap. Written in C with a BSD license
C
49
star
8

heap

Heap priority queue written in C, licensed under a BSD license
C
31
star
9

bmon

Batch Monitor - Gain performance by combining work from multiple threads into a single batch
C
31
star
10

uv_multiplex

Take 1 TCP listen socket and duplicate it across N threads so you get some sweet multi-thread action happening
C
24
star
11

py2graphql

Python to GraphQL: A Pythonic GraphQL client
Python
20
star
12

array-avl-tree

An AVL tree implemented with an array, because cache misses keep you awake at night. Using a BSD license
C
18
star
13

duraqueue

Durable dead simple queue that allows zero copy semantics. Durable under failure!
C
18
star
14

linked-list-queue

A simple queue using a linked list written in C under the BSD license.
C
18
star
15

virtraft

Raft network simulator, for QA
C
16
star
16

splay-tree

A splaytree ADT written in C with a BSD license
C
16
star
17

skiplist

Dictionary implemented through a skiplist
C
15
star
18

bitfield

A bitfield ADT, used for easily managing bits, written in C under a BSD licence.
C
15
star
19

bitstream

A collection of functions for making writing to a bitstream eaisier; written in C under a BSD license
C
14
star
20

quadtree

A quadtree written in C. Perfect for culling things! BSD licensed
C
12
star
21

dogebox

C
11
star
22

arrayqueue

A queue implemented on an array, written in C using the BSD licence.
C
11
star
23

bag

Bag ADT written in C so that you can take randomly, licensed under a BSD license
C
11
star
24

meanqueue

A queue that calculates the running mean of its integer contents
C
11
star
25

pseudolru

A pseudo LRU cache written in C with a BSD license
C
10
star
26

streaming-bencode

Bencode reader that loves working with streams. Written in C with a BSD license
C
9
star
27

expboff

Simple function for doing exponential backoff in C
C
9
star
28

farraylist

An array list written in C that doesn't move objects once there is a "hole" between slots. BSD licensed.
C
8
star
29

texture-atlas

A simple texture atlas written in C. Graphics library agnostic. Under the BSD license
C
8
star
30

tearen

2D multi-platform OpenGL renderer written in C, licensed under the BSD license
C
7
star
31

taileff

tail -f for humans
Python
7
star
32

linked-list-hashmap

A hashmap which uses linked list nodes. Written in C and licensed under a BSD license
C
7
star
33

pidfile

Create a pidfile
C
6
star
34

torrent-reader

A simple torrent reader written in C under a BSD license
C
6
star
35

chunkybar

An ADT for accounting the status of a piece of data which can be progressed in "chunks" in any random order, written in C under a BSD licence.
C
6
star
36

pwp

A Bittorrent peer wire protocol implementation written in C under a BSD licence.
C
6
star
37

aqueue-noptr

An in-place queue implemented on an array, written in C using the BSD licence.
C
6
star
38

tracker-client

A simple Bittorrent tracker client, written in C under a BSD licence.
C
6
star
39

fe

Flip the endianess of integers
C
5
star
40

raft_amalgamation

Single file implementation of the Raft consensus protocol
C
5
star
41

defold-cognito

Objective-C
5
star
42

file2str

file2str simply reads a file and returns the contents as a null terminated string.
C
5
star
43

ds

ds is sudo alternative. Privileged commands execute once approved by other users.
Python
4
star
44

weak-reference-pool

An ADT and set of function calls that allow C programmers to make use of weak references; written in C under a BSD licence.
C
4
star
45

minmax

min and max functions for C
C++
4
star
46

yabtorrent-cli

Bittorrent client using the yabtorrent library
C
4
star
47

docopt2ragel

Define your CLI in docopt and output into a sweet C FSM using Ragel
Python
4
star
48

jz

Responsive JSON database presented as a REST server
Python
4
star
49

quadratic-probing-hashmap

C
3
star
50

comp-object

A framework for working with actors and composition in C
C
3
star
51

pip-versions

Get version information about a pip from pypi (and private repos)
Python
3
star
52

SDLProducerConsumerMonitor

A producer/consumer threading monitor; written in C under a BSD licence.
C
2
star
53

rsttst

rsttst makes your reStructuredText testable
Python
2
star
54

cutest

A simple TAP producing fork of the cutest C unit test framework.
C
2
star
55

SDLReadWriterMonitor

A read/writer threading monitoring for SDL; written in C under a BSD licence.
C
2
star
56

aiorelational

Async relational iterators/generators for manipulating data streams
Python
2
star
57

pyariable

Simplify your test assertions with placeholder variables
Python
2
star
58

chrootize

Automatically place a command's binary and libraries in a chroot jail. For OS/X.
Shell
1
star
59

dynamodb-pep249

PEP249 Database wrapper for DynamoDB
Python
1
star
60

config-re

A re-entrant safe fork of a config library
C
1
star
61

fff

Friendlier File Follower is a file alteration monitor that listens to as many files as possible while trying to play nice with the system's file watcher limits. Let's use libuv to keep it cross platform!
C
1
star
62

SDLIteratorMonitor

A threading monitor that allows iteration over an iterator; written in C under a BSD licence.
C
1
star
63

stubfile

A module for managing the creation of files where the content of the files will be written to randomly, written in C under a BSD licence.
C
1
star
64

Teeworlds-Fobik

A recreation of the Fobik Quake 3 mod for Teeworlds
1
star
65

event-timer

A event timer, written in C under a BSD licence.
C
1
star