Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Twom database format and first implementation #5157

Merged
merged 53 commits into from
Feb 11, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
645a819
cyr_dbtool: add options and fix documentation to be all aligned
brong Jan 8, 2025
cd2b717
add xxhash.h
brong Dec 3, 2024
940cb84
add twom library to libcyrus
brong Jan 8, 2025
a441d76
cyrusdb: add a twom database backend
brong Jan 16, 2025
7aff6fc
cyrudbbench: add twom support
brong Jan 8, 2025
553be8f
cuint: add additional read/delete test
brong Jan 9, 2025
faba46b
twom: rework DELETE to be a lead-in at level 0 for the previous value
brong Jan 16, 2025
69af0ce
twom: new locking paradigm, per suggestion by robm
brong Jan 17, 2025
b74a99f
README doc for twom format
brong Jan 16, 2025
2dde416
cyrusdb_twom: format attribute for printf
brong Jan 21, 2025
fb730ce
twom: format attribute for printf
brong Jan 21, 2025
5d1f557
twom: all levels are uint8_t
brong Jan 21, 2025
b3e0fa7
twom: cleanup cursor error cases properly
brong Jan 21, 2025
64a9567
dbtool: remove excess documentation line
brong Jan 21, 2025
7088187
cyrusdb: built in lock file
brong Jan 21, 2025
288e0d8
cyrusdb: lock conversion section, and add cyrusdb_autconvert config
brong Jan 21, 2025
a0c4fab
capitalisation
brong Jan 21, 2025
d76fa2f
cyrusdb: fix twom detection
brong Jan 21, 2025
ea2a94b
remove bogus CHECKSUM_ENGINE field
brong Jan 21, 2025
616be0a
twom: rewrite documentation some
brong Jan 21, 2025
99fb5d9
fcntl comment
brong Jan 21, 2025
1924256
twom: create strerror function
brong Jan 22, 2025
6641a5f
comment on flags and tighten up errors
brong Jan 22, 2025
35b20b6
better consistent name and bool for should_repack
brong Jan 22, 2025
0279f16
rename to twom_db_open
brong Jan 22, 2025
3345466
twom_open_data
brong Jan 22, 2025
23fd3eb
remove unused SETUP_FLAGS
brong Jan 22, 2025
3378191
twom: add comment about clearing dirty early
brong Jan 25, 2025
70e4126
twom: add errno to error handler
brong Jan 25, 2025
404a899
twom: remove 'extern' from header file, it's meaningless
brong Jan 25, 2025
207577c
twom: make noyield and twom_db_yield consistent and available at both…
brong Jan 25, 2025
93c5db3
twom: pass all flags when recreating txn
brong Jan 25, 2025
455dede
twom: comment about open with flags
brong Jan 25, 2025
f4f0747
fix twom documentation about db_yield
brong Jan 25, 2025
debc485
twom: KEYPTR - return a zero length string for non-tail items
brong Jan 25, 2025
2501183
twom: use typedefs in init data
brong Jan 25, 2025
c4c2b58
dbtool: use lockopen for readonly support
brong Jan 25, 2025
2949786
cyrusdb: pass through TWOM_SHARED to open
brong Jan 25, 2025
1452bd1
cyr_dbtool: print error message!
brong Jan 25, 2025
b761a98
fix indent
brong Jan 28, 2025
dc18eee
cunit: add test for replace before a deleted record
brong Jan 28, 2025
e714c24
twom: reset the deleted_offset after writing an ADD or REPLACE
brong Jan 28, 2025
c80f338
write_lock: also need to unlock when file changed!
brong Jan 29, 2025
d0ad886
comment improvements
brong Jan 29, 2025
ed4852f
twom: split SET0 and SETN
brong Jan 29, 2025
1a61255
more documentation about the setloc behaviour at level 0
brong Jan 29, 2025
66c8125
cyrusdb: handle CONVERT without noise
brong Jan 30, 2025
c15dad8
twom: locked is only an INFO syslog
brong Feb 1, 2025
daf7530
twom: call xsyslog, not syslog
rsto Feb 3, 2025
cdcd355
twom: switch to using offset to mean exactmatch
brong Feb 9, 2025
809d700
twom: assert that the file is always big enough
brong Feb 10, 2025
ac17e49
twom: move the lock dance BEFORE we stat the file
brong Feb 10, 2025
fb2c6d7
twom: add changes/next file
brong Feb 11, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -805,6 +805,7 @@ include_HEADERS = \
lib/stristr.h \
lib/times.h \
lib/tok.h \
lib/twom.h \
lib/wildmat.h \
lib/xmalloc.h \
lib/xsha1.h \
Expand Down Expand Up @@ -1473,6 +1474,7 @@ lib_libcyrus_la_SOURCES = \
lib/cyrusdb_quotalegacy.c \
lib/cyrusdb_skiplist.c \
lib/cyrusdb_twoskip.c \
lib/cyrusdb_twom.c \
lib/glob.c \
lib/htmlchar.c \
lib/htmlchar.h \
Expand All @@ -1495,6 +1497,7 @@ lib_libcyrus_la_SOURCES = \
lib/signals.c \
lib/stristr.c \
lib/times.c \
lib/twom.c \
lib/wildmat.c
if USE_CYRUSDB_SQL
lib_libcyrus_la_SOURCES += lib/cyrusdb_sql.c
Expand Down
9 changes: 5 additions & 4 deletions bench/cyrdbbench.c
Original file line number Diff line number Diff line change
Expand Up @@ -368,12 +368,12 @@ static size_t do_write(int txnmode, int insmode)
return bytes;
}

static int parse_options(int argc, char **argv, const struct option *options)
static int parse_options(int argc, char **argv, const struct option *options __attribute__((unused)))
{
int option;
int option_index;

while ((option = getopt_long(argc, argv, "d:b:t:h?",
while ((option = getopt_long(argc, argv, "d:b:n:t:h?",
long_options, &option_index)) != -1) {
switch (option) {
case 'b':
Expand Down Expand Up @@ -480,11 +480,12 @@ int main(int argc, char *argv[])
goto done;
}

if (strncmp(BACKEND, "twoskip", strlen("twoskip")) == 0) {
if (strncmp(BACKEND, "twoskip", strlen("twoskip")) == 0 ||
strncmp(BACKEND, "twom", strlen("twom")) == 0) {
fprintf(stderr, "Running benchmarks for `%s` backend\n", BACKEND);
} else {
fprintf(stderr, "%s is not a valid CyrusDB backend. ", BACKEND);
fprintf(stderr, "Only `twoskip` is supported.\n");
fprintf(stderr, "Choose between `twom` or `zeroskip`.\n");
ret = EXIT_FAILURE;
goto done;
}
Expand Down
30 changes: 30 additions & 0 deletions changes/next/twom
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
Description:

twom: a new cyrusdb backend


Config changes:

A new backend called 'twom' is added to most of the database engine options
in imapd.conf. Twom is a database format similar to twoskip, with some
performance improvements.

There's also a new boolean option 'cyrusdb_autoconvert', which - if set to
true, will cause Cyrus to convert the database on first access if the
existing file can be detected (by looking at the first few bytes for file
magic). This works for skiplist, twoskip and twom source formats. The
conversions are performed under a global lock, so this can slow the server
down somewhat while the conversions are happening.


Upgrade instructions:

No changes are required if you don't want to switch to twom. No defaults have
been changed at this time. If you do want to change, you can optionally set
cyrusdb_autoconvert (otherwise watch the logs for files being opened with the
wrong format and convert them manually), and also update the DB engine for
individual database types.

GitHub issue:

No issue number.
134 changes: 133 additions & 1 deletion cunit/aaa-db.testc
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ struct binary_result
size_t datalen;
};

static const char *backend = CUNIT_PARAM("skiplist,flat,twoskip");
static const char *backend = CUNIT_PARAM("skiplist,flat,twom,twoskip");
static char *filename;
static char *filename2;

Expand Down Expand Up @@ -82,6 +82,15 @@ static char *make_basedir(const char * const *reldirs)
CU_ASSERT_EQUAL(r, CYRUSDB_OK); \
CU_ASSERT_PTR_NOT_NULL(txn);

#define CANDELETE(key, keylen) \
r = cyrusdb_delete(db, key, keylen, &txn, 1); \
CU_ASSERT_EQUAL(r, CYRUSDB_OK); \
CU_ASSERT_PTR_NOT_NULL(txn);

#define ISCONSISTENT() \
r = cyrusdb_consistent(db); \
CU_ASSERT_EQUAL(r, CYRUSDB_OK);

#define BADDATA ((const char *)0xdeadbeef)
#define BADLEN ((int)0xcafebabe)

Expand Down Expand Up @@ -150,6 +159,18 @@ static char *make_basedir(const char * const *reldirs)
CU_ASSERT_NOT_EQUAL(_datalen, BADLEN); \
}

#define CANNOTFETCH_NOTXN(key, keylen, experror) \
{ \
const char *_data = BADDATA; \
size_t _datalen = BADLEN; \
r = cyrusdb_fetch(db, key, keylen, &_data, &_datalen, NULL); \
CU_ASSERT_EQUAL(r, experror); \
CU_ASSERT_PTR_NULL(_data); \
CU_ASSERT_PTR_NOT_EQUAL(_data, BADDATA); \
CU_ASSERT_EQUAL(_datalen, 0); \
CU_ASSERT_NOT_EQUAL(_datalen, BADLEN); \
}

#define CANNOTFETCHNEXT(key, keylen, experror) \
{ \
r = cyrusdb_fetchnext(db, key, keylen, NULL, 0, NULL, 0, &txn); \
Expand Down Expand Up @@ -311,6 +332,117 @@ static void test_multiopen(void)
CU_ASSERT_EQUAL(fexists(filename), 0);
}

static void test_read_and_delete(void)
{
struct db *db = NULL;
struct txn *txn = NULL;
int r;
/* data courtesy hipsteripsum.me */
static const char KEY1[] = "mustache";
static const char DATA1[] = "blog lomo";
static const char KEY2[] = "cred";
static const char DATA2[] = "beard ethical";
static const char KEY3[] = "leggings";
static const char DATA3[] = "tumblr salvia";
static const char KEY3CHILD[] = "leggings.biodiesel";
static const char KEY4[] = "occupy";
static const char DATA4[] = "etsy tote bag";

CU_ASSERT_EQUAL(fexists(filename), -ENOENT);

/* open() with _CREATE succeeds and creates the db */
r = cyrusdb_open(backend, filename, CYRUSDB_CREATE, &db);
CU_ASSERT_EQUAL(r, CYRUSDB_OK);
CU_ASSERT_PTR_NOT_NULL(db);
CU_ASSERT_EQUAL(fexists(filename), 0);

/* 1st txn starts */
CANSTORE(KEY1, strlen(KEY1), DATA1, strlen(DATA1));
CANSTORE(KEY2, strlen(KEY2), DATA2, strlen(DATA2));
CANSTORE(KEY3, strlen(KEY3), DATA3, strlen(DATA3));
CANSTORE(KEY4, strlen(KEY4), DATA4, strlen(DATA4));
CANCOMMIT();
ISCONSISTENT();
/* 1st txn ends */

/* 2nd txn starts - try to fetch the immediately preceding value */
CANNOTFETCH(KEY3CHILD, strlen(KEY3CHILD), CYRUSDB_NOTFOUND);
CANFETCH(KEY3, strlen(KEY3), DATA3, strlen(DATA3));
CANDELETE(KEY3, strlen(KEY3));
CANFETCH(KEY1, strlen(KEY1), DATA1, strlen(DATA1));
CANDELETE(KEY1, strlen(KEY1));
CANCOMMIT();
ISCONSISTENT();

/* what is left? */
CANNOTFETCH_NOTXN(KEY1, strlen(KEY1), CYRUSDB_NOTFOUND);
CANFETCH_NOTXN(KEY2, strlen(KEY2), DATA2, strlen(DATA2));
CANNOTFETCH_NOTXN(KEY3, strlen(KEY3), CYRUSDB_NOTFOUND);
CANFETCH_NOTXN(KEY4, strlen(KEY4), DATA4, strlen(DATA4));
ISCONSISTENT();

/* closing succeeds and leaves the file in place */
r = cyrusdb_close(db);
CU_ASSERT_EQUAL(r, CYRUSDB_OK);
CU_ASSERT_EQUAL(fexists(filename), 0);
}

static void test_replace_before_delete(void)
{
struct db *db = NULL;
struct txn *txn = NULL;
int r;
/* data courtesy hipsteripsum.me */
static const char KEY1[] = "alphabet";
static const char DATA1[] = "blog lomo";
static const char KEY2[] = "blanket";
static const char DATA2[] = "beard ethical";
static const char KEY3[] = "cobra";
static const char DATA3[] = "prius toke";
static const char KEY4[] = "dynamo";
static const char DATA4[] = "etsy tote bag";

CU_ASSERT_EQUAL(fexists(filename), -ENOENT);

/* open() with _CREATE succeeds and creates the db */
r = cyrusdb_open(backend, filename, CYRUSDB_CREATE, &db);
CU_ASSERT_EQUAL(r, CYRUSDB_OK);
CU_ASSERT_PTR_NOT_NULL(db);
CU_ASSERT_EQUAL(fexists(filename), 0);

/* 1st txn */
CANSTORE(KEY1, strlen(KEY1), DATA1, strlen(DATA1));
CANSTORE(KEY3, strlen(KEY3), DATA3, strlen(DATA3));
CANSTORE(KEY4, strlen(KEY4), DATA4, strlen(DATA4));
CANCOMMIT();
ISCONSISTENT();

/* 2nd txn */
CANDELETE(KEY3, strlen(KEY3));
CANCOMMIT();
ISCONSISTENT();

/* 3rd txn */
CANSTORE(KEY2, strlen(KEY2), DATA3, strlen(DATA3));
CANCOMMIT();
ISCONSISTENT();

/* 4th txn (new value) */
CANSTORE(KEY2, strlen(KEY2), DATA2, strlen(DATA2));
CANCOMMIT();
ISCONSISTENT();

CANFETCH_NOTXN(KEY1, strlen(KEY1), DATA1, strlen(DATA1));
CANFETCH_NOTXN(KEY2, strlen(KEY2), DATA2, strlen(DATA2));
CANNOTFETCH_NOTXN(KEY3, strlen(KEY3), CYRUSDB_NOTFOUND);
CANFETCH_NOTXN(KEY4, strlen(KEY4), DATA4, strlen(DATA4));

/* closing succeeds and leaves the file in place */
r = cyrusdb_close(db);
CU_ASSERT_EQUAL(r, CYRUSDB_OK);
CU_ASSERT_EQUAL(fexists(filename), 0);
}

static void test_opentwo(void)
{
struct db *db1 = NULL;
Expand Down
Loading