diff --git a/Makefile b/Makefile
index bc368e1..02a5906 100644
--- a/Makefile
+++ b/Makefile
@@ -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)
diff --git a/README.md b/README.md
index e960877..8599418 100644
--- a/README.md
+++ b/README.md
@@ -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:
@@ -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
@@ -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`.
+See [`example.c`](example.c) for basic function usage.
+See [`performance.c`](performance.c) for concurrent usage.
## Contributors
diff --git a/example.c b/example.c
index 0c3277a..2262f3d 100644
--- a/example.c
+++ b/example.c
@@ -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};
diff --git a/logdb.h b/logdb.h
index 4524888..472bc59 100644
--- a/logdb.h
+++ b/logdb.h
@@ -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
* ---------------
*
@@ -74,7 +72,6 @@ SOFTWARE.
* etc checksum1 checksum2
* length1 length2
*
- *
* idx file format
* ---------------
*
@@ -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
@@ -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
diff --git a/performance.c b/performance.c
index 184dc8b..def1f56 100644
--- a/performance.c
+++ b/performance.c
@@ -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;
diff --git a/tests.c b/tests.c
index cac3e09..a797094 100644
--- a/tests.c
+++ b/tests.c
@@ -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)
@@ -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);
@@ -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");
@@ -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");