Skip to content

Commit

Permalink
Code review
Browse files Browse the repository at this point in the history
  • Loading branch information
torrentg committed May 1, 2024
1 parent d5b4004 commit 1b52501
Show file tree
Hide file tree
Showing 6 changed files with 89 additions and 53 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
CFLAGS= -std=c99 -D_POSIX_C_SOURCE=200809L -Wall -Wextra -Wpedantic -Wnull-dereference -pthread
LDFLAGS= -lpthread

all: tests coverage cppcheck valgrind example
all: tests example performance

tests: logdb.h tests.c
$(CC) -g $(CFLAGS) -DRUNNING_ON_VALGRIND -o tests tests.c $(LDFLAGS)
Expand Down
60 changes: 39 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
# logdb

A simple log-structured database.
It is a header-only C embeded database with no dependencies.

It is a header-only C embeded database with no dependencies.
The logdb goal is to cover the following case:
Logdb is a simple database with the following characteristics:

* Need to persist sequentially ordered data
* Most operations are write type
* Data is rarely read or searched
* Allow to revert last entries (rollback)
* Eventually purge obsolete entries (purge)
* Variable length record type
* Records uniquely identified by a sequential number (seqnum)
* Records are indexed by timestamp (monotonic non-decreasing field)
* There are no other indexes other than seqnum and timestamp.
* Records can be appended, read, and searched
* Records can not be updated nor deleted
* Allows to revert last entries (rollback)
* Allows to remove obsolete entries (purge)
* Read-write concurrency supported (multi-thread)
* Automatic data recovery on catastrofic event
* Minimal memory footprint

Use cases:
Expand All @@ -19,19 +24,8 @@ Use cases:

## Description

Logdb is a simple database with the following characteristics:

* Records have variable length (non-fixed record size)
* Record identifier is a sequential number
* Record are indexed by timestamp (monotonic non-decreasing field)
* Only append function is supported (no update, no delete)
* Just after insertion data is flushed to disk (no delayed writes)
* Automatic data recovery on catastrofic event
* Records can be read (retrieved by seqnum)
* Records can be searched by id (seqnum)
* Records can be searched by timestamp
* Rollback means to remove X records from top
* Can be purged (removing X records from bottom)
Basically, logdb is an append-only data file (\*.dat) with an index file (\*.idx) used to speed up lookups. No complex data structures, no sofisticated algorithms, only basic file
access. We rely on the filesystem cache (managed by the operating system) to ensure read performance.

### dat file format

Expand All @@ -58,7 +52,31 @@ Logdb is a simple database with the following characteristics:

Drop off [`logdb.h`](logdb.h) in your project and start using it.

See [`example.c`](example.c).
```
#define LDB_IMPL
#include "logdb.h"
ldb_db_t db = {0};
ldb_entry_t wentries[MAX_ENTRIES] = {{0}};
ldb_entry_t rentries[MAX_ENTRIES] = {{0}};
ldb_open("/my/directory", "example", &db, true);
// on write-thread
fill_entries(wentries, MAX_ENTRIES);
ldb_append(&db, wentries, MAX_ENTRIES, NULL);
// on read-thread
ldb_read(&db, 1, rentries, MAX_ENTRIES, NULL);
process_entries(rentries, MAX_ENTRIES);
ldb_free_entries(rentries, MAX_ENTRIES);
ldb_close(&db);
```

Read functions documentation in `logdb.h`.<br/>
See [`example.c`](example.c) for basic function usage.<br/>
See [`performance.c`](performance.c) for concurrent usage.

## Contributors

Expand Down
4 changes: 2 additions & 2 deletions example.c
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,8 @@ int main(void)
int rc = 0;

// don't mix read and write entries, they have distinct memory-owner
ldb_entry_t wentries[MAX_ENTRIES] = {0};
ldb_entry_t rentries[MAX_ENTRIES] = {0};
ldb_entry_t wentries[MAX_ENTRIES] = {{0}};
ldb_entry_t rentries[MAX_ENTRIES] = {{0}};
ldb_entry_t wentry = {0};
ldb_entry_t rentry = {0};

Expand Down
46 changes: 23 additions & 23 deletions logdb.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,30 +35,28 @@ SOFTWARE.

/**
* Logdb is a simple database with the following characteristics:
* - Records have variable length (non-fixed record size)
* - Record identifier is a sequential number
* - Record are indexed by timestamp (monotonic non-decreasing field)
* - Only append function is supported (no update, no delete)
* - Just after insertion data is flushed to disk (no delayed writes)
* - Automatic data recovery on catastrofic event
* - Records can be read (retrieved by seqnum)
* - Records can be searched by id (seqnum)
* - Records can be searched by timestamp
* - Rollback means to remove X records from top
* - Can be purged (removing X records from bottom)
*
* Logdb is intended in the following case:
* - Need to persist sequentially ordered data
* - Most operations are write type
* - Data is rarely read or searched
* - Variable length record type
* - Records uniquely identified by a sequential number (seqnum)
* - Records are indexed by timestamp (monotonic non-decreasing field)
* - There are no other indexes other than seqnum and timestamp.
* - Records can be appended, read, and searched
* - Records can not be updated nor deleted
* - Allows to revert last entries (rollback)
* - Eventually purge obsolete entries (purge)
* - Allows to remove obsolete entries (purge)
* - Read-write concurrency supported (multi-thread)
* - Automatic data recovery on catastrofic event
* - Minimal memory footprint
*
* Use cases:
* - Storage engine in a raft library (fault-tolerant distributed applications)
* - Storage engine for journal-based apps
*
* Basically, logdb is an append-only data file (\*.dat) with
* an index file (\*.idx) used to speed up lookups. No complex
* data structures, no sofisticated algorithms, only basic file
* access. We rely on the filesystem cache (managed by the operating
* system) to ensure read performance.
*
* dat file format
* ---------------
*
Expand All @@ -74,7 +72,6 @@ SOFTWARE.
* etc checksum1 checksum2
* length1 length2
*
*
* idx file format
* ---------------
*
Expand Down Expand Up @@ -126,8 +123,8 @@ SOFTWARE.
* └ search() R R
*/

#define LDB_VERSION_MAJOR 0
#define LDB_VERSION_MINOR 5
#define LDB_VERSION_MAJOR 1
#define LDB_VERSION_MINOR 0
#define LDB_VERSION_PATCH 0

#define LDB_OK 0
Expand Down Expand Up @@ -474,9 +471,12 @@ typedef struct {
#define LDB_INLINE /**/
#endif

#if __has_attribute(__fallthrough__)
# define fallthrough __attribute__((__fallthrough__))
#else
#if defined __has_attribute
#if __has_attribute(__fallthrough__)
# define fallthrough __attribute__((__fallthrough__))
#endif
#endif
#ifndef fallthrough
# define fallthrough do {} while (0) /* fallthrough */
#endif

Expand Down
4 changes: 3 additions & 1 deletion performance.c
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,9 @@ static void * run_write(void *args)
uint64_t time0 = ldb_get_millis();
size_t num = 0;

// all records have always the same content
// Logdb supports records of variable length.
// In this case we use fixed-length records filled with 0's
// to avoid to deal with memory alloc and fill it with random content.
for (size_t i = 0; i < num_entries; i++) {
entries[i].metadata_len = 0;
entries[i].metadata = NULL;
Expand Down
26 changes: 21 additions & 5 deletions tests.c
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,26 @@ void append_entries(ldb_db_t *db, uint64_t seqnum1, uint64_t seqnum2)
}
}

void test_version(void) {
void test_version(void)
{
const char *version = ldb_version();
TEST_ASSERT(version != NULL);
TEST_CHECK(strcmp(version, "0.5.0") == 0);

size_t len = strlen(version);
TEST_ASSERT(len >= 5);

TEST_ASSERT(version[0] != '.');
TEST_ASSERT(version[len-1] != '.');

size_t num_dots = 0;
for (size_t i = 0; i < len; i++) {
if (version[i] == '.')
num_dots++;
else if (!isdigit(version[i]))
TEST_ASSERT(false);
}

TEST_CHECK(num_dots == 2);
}

void test_strerror(void)
Expand Down Expand Up @@ -991,7 +1007,7 @@ bool check_entry(ldb_entry_t *entry, uint64_t seqnum, const char *metadata, cons
void test_read_invalid_args(void)
{
ldb_db_t db = {0};
ldb_entry_t entries[3] = {0};
ldb_entry_t entries[3] = {{0}};

TEST_ASSERT(ldb_read(NULL, 1, entries, 3, NULL) == LDB_ERR_ARG);
TEST_ASSERT(ldb_read(&db, 1, NULL, 3, NULL) == LDB_ERR_ARG);
Expand All @@ -1002,7 +1018,7 @@ void test_read_invalid_args(void)
void test_read_empty_db(void)
{
ldb_db_t db = {0};
ldb_entry_t entries[3] = {0};
ldb_entry_t entries[3] = {{0}};
size_t num = 10;

remove("test.dat");
Expand All @@ -1025,7 +1041,7 @@ void test_read_empty_db(void)
void test_read_nominal_case(void)
{
ldb_db_t db = {0};
ldb_entry_t entries[10] = {0};
ldb_entry_t entries[10] = {{0}};
size_t num = 0;

remove("test.dat");
Expand Down

0 comments on commit 1b52501

Please sign in to comment.