From 765ffea018e6f3e65352287fe36d757e8c064676 Mon Sep 17 00:00:00 2001 From: John Sully Date: Sun, 1 Mar 2020 19:17:58 -0500 Subject: [PATCH 01/18] Rename cron command to KEYDB.CRON and add help Former-commit-id: 3eb847e28f6df45528dcebc6761290ff60248e78 --- src/help.h | 8 +++++++- src/server.cpp | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/help.h b/src/help.h index 34f4e77c7..d3870d490 100644 --- a/src/help.h +++ b/src/help.h @@ -1168,7 +1168,13 @@ struct commandHelp { "destination numkeys key [key ...] [WEIGHTS weight] [AGGREGATE SUM|MIN|MAX]", "Add multiple sorted sets and store the resulting sorted set in a new key", 4, - "2.0.0" } + "2.0.0" }, + { "KEYDB.CRON", + "name [single/repeat] [optional: start] delay script numkeys [key N] [arg N]", + "Run a specified script after start + delay, optionally repeating every delay interval. The job may be cancelled by deleting the key associated with the job (name parameter)", + 10, + "6.5.2" + } }; #endif diff --git a/src/server.cpp b/src/server.cpp index 837b1c38e..311f2bffa 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -1026,7 +1026,7 @@ struct redisCommand redisCommandTable[] = { "read-only fast noprop", 0,NULL,0,0,0,0,0,0}, - {"cron",cronCommand,-5, + {"keydb.cron",cronCommand,-5, "write use-memory", 0,NULL,1,1,1,0,0,0}, }; From de3d796eb765e038e9f330ef2797f29a8aa585c8 Mon Sep 17 00:00:00 2001 From: John Sully Date: Sun, 1 Mar 2020 21:42:01 -0500 Subject: [PATCH 02/18] Update tests to reflect new CRON name (keydb.cron) Former-commit-id: 83f585e30ab9d37408c79b74e2536664325a051f --- tests/unit/cron.tcl | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/tests/unit/cron.tcl b/tests/unit/cron.tcl index 554df9328..dddf78255 100644 --- a/tests/unit/cron.tcl +++ b/tests/unit/cron.tcl @@ -1,47 +1,47 @@ start_server {tags {"CRON"}} { - test {cron singleshot past tense} { + test {keydb.cron singleshot past tense} { r flushall - r cron testjob single 0 1 {redis.call("incr", "testkey")} 1 testkey + r keydb.cron testjob single 0 1 {redis.call("incr", "testkey")} 1 testkey after 300 assert_equal 1 [r get testkey] assert_equal 0 [r exists testjob] } - test {cron repeat past tense next exec is in the future} { + test {keydb.cron repeat past tense next exec is in the future} { r flushall - r cron testjob repeat 0 1000000 {redis.call("incr", "testkey")} 1 testkey + r keydb.cron testjob repeat 0 1000000 {redis.call("incr", "testkey")} 1 testkey after 300 assert_equal 1 [r get testkey] assert_equal 1 [r exists testjob] r del testjob } - test {cron repeat works} { + test {keydb.cron repeat works} { r flushall - r cron testjob repeat 0 600 {redis.call("incr","testkey")} + r keydb.cron testjob repeat 0 600 {redis.call("incr","testkey")} after 1000 assert_equal 2 [r get testkey] } - test {cron overwrite works} { + test {keydb.cron overwrite works} { r flushall - r cron testjob single 500 {redis.call("set","testkey","a")} 1 testkey - r cron testjob single 500 {redis.call("set","anotherkey","b")} 1 anotherkey + r keydb.cron testjob single 500 {redis.call("set","testkey","a")} 1 testkey + r keydb.cron testjob single 500 {redis.call("set","anotherkey","b")} 1 anotherkey after 1000 assert_equal 0 [r exists testkey] assert_equal b [r get anotherkey] } - test {cron delete key stops job} { + test {keydb.cron delete key stops job} { r flushall - r cron testjob single 500 {redis.call("set","testkey","a")} + r keydb.cron testjob single 500 {redis.call("set","testkey","a")} r del testjob after 1000 assert_equal 0 [r exists testkey] } - test {cron zero interval rejected} { - catch {r cron testjob single 0 0 {redis.call("incr","testkey")} 1 testkey} e + test {keydb.cron zero interval rejected} { + catch {r keydb.cron testjob single 0 0 {redis.call("incr","testkey")} 1 testkey} e assert_match {ERR*} $e } } From 87b36b00dfbe9c11f3f386dd39e2152e4d8b0db4 Mon Sep 17 00:00:00 2001 From: John Sully Date: Sun, 1 Mar 2020 22:16:25 -0500 Subject: [PATCH 03/18] Add option to build pro without license checks Former-commit-id: 0713518d2f34559a06db31a9023228b91ce60af3 --- src/Makefile | 5 +++++ src/server.cpp | 2 ++ 2 files changed, 7 insertions(+) diff --git a/src/Makefile b/src/Makefile index 45c70f6d6..cef895987 100644 --- a/src/Makefile +++ b/src/Makefile @@ -108,6 +108,11 @@ ifeq ($(USE_JEMALLOC),no) MALLOC=libc endif + +ifeq ($(NO_LICENSE_CHECK),yes) + CXXFLAGS+=-DNO_LICENSE_CHECK=1 +endif + # Override default settings if possible -include .make-settings diff --git a/src/server.cpp b/src/server.cpp index 53a16e54d..82f334fda 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -1103,6 +1103,7 @@ void serverLog(int level, const char *fmt, ...) { static void checkTrialTimeout() { +#ifndef NO_LICENSE_CHECK if (cserver.license_key != nullptr && FValidKey(cserver.license_key, strlen(cserver.license_key))) return; time_t curtime = time(NULL); @@ -1118,6 +1119,7 @@ static void checkTrialTimeout() { serverLog(LL_WARNING, "Trial timeout in %ld:%02ld minutes", remaining/60, remaining % 60); } +#endif } /* Log a fixed message without printf-alike capabilities, in a way that is From 53a66f6a72f03d0268daec5a6f71eae0e8886b50 Mon Sep 17 00:00:00 2001 From: John Sully Date: Tue, 3 Mar 2020 13:58:48 -0800 Subject: [PATCH 04/18] Update README.md Former-commit-id: c0882864f2fff0a32899e983ad4fe060eb71c1ad --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 70b879d52..d33b7e61d 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,8 @@ ##### Need Help? Check out our extensive [documentation](https://docs.keydb.dev). +##### Have feedback? Take our quick survey: https://www.surveymonkey.com/r/Y9XNS93 + What is KeyDB? -------------- From bffd982853db1c4088e56ee29fefb9a83a8417f6 Mon Sep 17 00:00:00 2001 From: John Sully Date: Wed, 4 Mar 2020 15:14:41 -0500 Subject: [PATCH 05/18] min-clients-per-thread needs to use the new config code Former-commit-id: fed4bd5269a95a24f99d4542ceece09b9ad66d2c --- src/config.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/config.cpp b/src/config.cpp index 8f6cfbad5..de32ae85f 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -540,11 +540,6 @@ void loadServerConfigFromString(char *config) { } else if (!strcasecmp(argv[0],"enable-pro")) { cserver.fUsePro = true; break; - } else if (!strcasecmp(argv[0],"min-clients-per-thread") && argc == 2) { - cserver.thread_min_client_threshold = atoi(argv[1]); - if (cserver.thread_min_client_threshold < 0 || cserver.thread_min_client_threshold > 400) { - err = "min-thread-client must be between 0 and 400"; goto loaderr; - } } else { err = "Bad directive or wrong number of arguments"; goto loaderr; } @@ -2315,6 +2310,7 @@ standardConfig configs[] = { createIntConfig("hz", NULL, MODIFIABLE_CONFIG, 0, INT_MAX, g_pserver->config_hz, CONFIG_DEFAULT_HZ, INTEGER_CONFIG, NULL, updateHZ), createIntConfig("min-replicas-to-write", "min-slaves-to-write", MODIFIABLE_CONFIG, 0, INT_MAX, g_pserver->repl_min_slaves_to_write, 0, INTEGER_CONFIG, NULL, updateGoodSlaves), createIntConfig("min-replicas-max-lag", "min-slaves-max-lag", MODIFIABLE_CONFIG, 0, INT_MAX, g_pserver->repl_min_slaves_max_lag, 10, INTEGER_CONFIG, NULL, updateGoodSlaves), + createIntConfig("min-clients-per-thread", NULL, MODIFIABLE_CONFIG, 0, 400, cserver.thread_min_client_threshold, 50, INTEGER_CONFIG, NULL, NULL), /* Unsigned int configs */ createUIntConfig("maxclients", NULL, MODIFIABLE_CONFIG, 1, UINT_MAX, g_pserver->maxclients, 10000, INTEGER_CONFIG, NULL, updateMaxclients), From 21fb6018511ba1e9ccd2cc8e09dc3210e66829db Mon Sep 17 00:00:00 2001 From: John Sully Date: Wed, 4 Mar 2020 16:22:55 -0500 Subject: [PATCH 06/18] MOTD refactor Former-commit-id: 6c78ec7c41223b3381e19fdf78478f66b4959dc9 --- src/Makefile | 8 ++-- src/asciilogo.h | 5 +-- src/redis-cli.c | 115 +----------------------------------------------- src/server.cpp | 7 ++- 4 files changed, 14 insertions(+), 121 deletions(-) diff --git a/src/Makefile b/src/Makefile index a0e48831c..9ab3a8a72 100644 --- a/src/Makefile +++ b/src/Makefile @@ -105,7 +105,7 @@ endif FINAL_CFLAGS=$(STD) $(WARN) $(OPT) $(DEBUG) $(CFLAGS) $(REDIS_CFLAGS) FINAL_CXXFLAGS=$(CXX_STD) $(WARN) $(OPT) $(DEBUG) $(CXXFLAGS) $(REDIS_CFLAGS) FINAL_LDFLAGS=$(LDFLAGS) $(REDIS_LDFLAGS) $(DEBUG) -FINAL_LIBS=-lm +FINAL_LIBS=-lm -lcurl DEBUG=-g -ggdb ifeq ($(uname_S),SunOS) @@ -213,9 +213,9 @@ endif REDIS_SERVER_NAME=keydb-server REDIS_SENTINEL_NAME=keydb-sentinel -REDIS_SERVER_OBJ=adlist.o quicklist.o ae.o anet.o dict.o server.o sds.o zmalloc.o lzf_c.o lzf_d.o pqsort.o zipmap.o sha1.o ziplist.o release.o networking.o util.o object.o db.o replication.o rdb.o t_string.o t_list.o t_set.o t_zset.o t_hash.o config.o aof.o pubsub.o multi.o debug.o sort.o intset.o syncio.o cluster.o crc16.o endianconv.o slowlog.o scripting.o bio.o rio.o rand.o memtest.o crc64.o bitops.o sentinel.o notify.o setproctitle.o blocked.o hyperloglog.o latency.o sparkline.o redis-check-rdb.o redis-check-aof.o geo.o lazyfree.o module.o evict.o expire.o geohash.o geohash_helper.o childinfo.o defrag.o siphash.o rax.o t_stream.o listpack.o localtime.o acl.o storage.o rdb-s3.o fastlock.o new.o tracking.o cron.o $(ASM_OBJ) +REDIS_SERVER_OBJ=adlist.o quicklist.o ae.o anet.o dict.o server.o sds.o zmalloc.o lzf_c.o lzf_d.o pqsort.o zipmap.o sha1.o ziplist.o release.o networking.o util.o object.o db.o replication.o rdb.o t_string.o t_list.o t_set.o t_zset.o t_hash.o config.o aof.o pubsub.o multi.o debug.o sort.o intset.o syncio.o cluster.o crc16.o endianconv.o slowlog.o scripting.o bio.o rio.o rand.o memtest.o crc64.o bitops.o sentinel.o notify.o setproctitle.o blocked.o hyperloglog.o latency.o sparkline.o redis-check-rdb.o redis-check-aof.o geo.o lazyfree.o module.o evict.o expire.o geohash.o geohash_helper.o childinfo.o defrag.o siphash.o rax.o t_stream.o listpack.o localtime.o acl.o storage.o rdb-s3.o fastlock.o new.o tracking.o cron.o motd.o $(ASM_OBJ) REDIS_CLI_NAME=keydb-cli -REDIS_CLI_OBJ=anet.o adlist.o dict.o redis-cli.o redis-cli-cpphelper.o zmalloc.o release.o anet.o ae.o crc64.o siphash.o crc16.o storage-lite.o fastlock.o new.o $(ASM_OBJ) +REDIS_CLI_OBJ=anet.o adlist.o dict.o redis-cli.o redis-cli-cpphelper.o zmalloc.o release.o anet.o ae.o crc64.o siphash.o crc16.o storage-lite.o fastlock.o new.o motd.o $(ASM_OBJ) REDIS_BENCHMARK_NAME=keydb-benchmark REDIS_BENCHMARK_OBJ=ae.o anet.o redis-benchmark.o adlist.o dict.o zmalloc.o siphash.o redis-benchmark.o storage-lite.o fastlock.o new.o $(ASM_OBJ) REDIS_CHECK_RDB_NAME=keydb-check-rdb @@ -285,7 +285,7 @@ $(REDIS_CHECK_AOF_NAME): $(REDIS_SERVER_NAME) # keydb-cli $(REDIS_CLI_NAME): $(REDIS_CLI_OBJ) - $(REDIS_LD) -o $@ $^ ../deps/hiredis/libhiredis.a ../deps/linenoise/linenoise.o $(FINAL_LIBS) -lcurl + $(REDIS_LD) -o $@ $^ ../deps/hiredis/libhiredis.a ../deps/linenoise/linenoise.o $(FINAL_LIBS) # keydb-benchmark $(REDIS_BENCHMARK_NAME): $(REDIS_BENCHMARK_OBJ) diff --git a/src/asciilogo.h b/src/asciilogo.h index c8b18a0ac..1cbcce142 100644 --- a/src/asciilogo.h +++ b/src/asciilogo.h @@ -36,7 +36,6 @@ const char *ascii_logo = " Port: %d\n" " PID: %ld\n" " \n" -" Like KeyDB? Star us on GitHub! \n" +" %s\n" " \n" -" https://github.com/JohnSully/KeyDB \n" -" \n\n"; +" \n"; diff --git a/src/redis-cli.c b/src/redis-cli.c index 7a1f06341..7332c40e0 100644 --- a/src/redis-cli.c +++ b/src/redis-cli.c @@ -57,6 +57,7 @@ #include "anet.h" #include "ae.h" #include "storage.h" +#include "motd.h" #include "redis-cli.h" @@ -6536,118 +6537,6 @@ static void intrinsicLatencyMode(void) { } } -/*------------------------------------------------------------------------------ - * Message of the day - *--------------------------------------------------------------------------- */ -#ifdef MOTD -#include - -static const char *szMotdCachePath() -{ - static sds sdsMotdCachePath = NULL; - if (sdsMotdCachePath != NULL) - return sdsMotdCachePath; - - struct passwd *pw = getpwuid(getuid()); - if (pw == NULL) - return ""; - const char *homedir = pw->pw_dir; - sdsMotdCachePath = sdsnew(homedir); - sdsMotdCachePath = sdscat(sdsMotdCachePath, "/.keydb-cli-motd"); - return sdsMotdCachePath; -} -static size_t motd_write_callback(void *ptr, size_t size, size_t nmemb, sds *str) -{ - *str = sdscatlen(*str, ptr, size*nmemb); - return (size*nmemb); -} - -static char *fetchMOTDFromCache() -{ - struct stat attrib; - if (stat(szMotdCachePath(), &attrib) != 0) - return NULL; - time_t t = attrib.st_mtim.tv_sec; - time_t now = time(NULL); - if ((now - t) < 14400) - { - // If our cache was updated no more than 4 hours ago use it instead of fetching the MOTD - FILE *pf = fopen(szMotdCachePath(), "rb"); - if (pf == NULL) - return NULL; - fseek(pf, 0L, SEEK_END); - long cb = ftell(pf); - fseek(pf, 0L, SEEK_SET); // rewind - sds str = sdsnewlen(NULL, cb); - size_t cbRead = fread(str, 1, cb, pf); - fclose(pf); - if ((long)cbRead != cb) - { - sdsfree(str); - return NULL; - } - return str; - } - return NULL; -} - -static void setMOTDCache(const char *sz) -{ - FILE *pf = fopen(szMotdCachePath(), "wb"); - if (pf == NULL) - return; - size_t celem = fwrite(sz, strlen(sz), 1, pf); - (void)celem; // best effort - fclose(pf); -} - -static char *fetchMOTD() -{ - sds str; - CURL *curl; - CURLcode res; - - /* First try and get the string from the cache */ - str = fetchMOTDFromCache(); - if (str != NULL) - return str; - - str = sdsnew(""); - curl = curl_easy_init(); - if(curl) { - curl_easy_setopt(curl, CURLOPT_URL, "http://api.keydb.dev/motd/motd.txt"); - curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); // follow redirects - curl_easy_setopt(curl, CURLOPT_TIMEOUT, 2); // take no more than two seconds - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, motd_write_callback); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, &str); - - /* Perform the request, res will get the return code */ - res = curl_easy_perform(curl); - /* Check for errors */ - if(res != CURLE_OK) - { - sdsfree(str); - str = NULL; - } - - /* always cleanup */ - curl_easy_cleanup(curl); - - if (str != NULL) - setMOTDCache(str); - } - return str; -} - -#else - -static char *fetchMOTD() -{ - return NULL; -} - -#endif - /*------------------------------------------------------------------------------ * Program main() *--------------------------------------------------------------------------- */ @@ -6818,7 +6707,7 @@ int main(int argc, char **argv) { if (argc == 0 && !config.eval) { /* Show the message of the day if we are interactive */ if (config.output == OUTPUT_STANDARD) { - char *szMotd = fetchMOTD(); + char *szMotd = fetchMOTD(1 /* cache */); if (szMotd != NULL) { printf("Message of the day:\n %s\n", szMotd); sdsfree(szMotd); diff --git a/src/server.cpp b/src/server.cpp index 311f2bffa..d6fe1ba1f 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -62,6 +62,7 @@ #include #include #include "aelocker.h" +#include "motd.h" int g_fTestMode = false; @@ -4749,14 +4750,18 @@ void redisAsciiArt(void) { mode, g_pserver->port ); } else { + sds motd = fetchMOTD(true); snprintf(buf,1024*16,ascii_logo, KEYDB_REAL_VERSION, redisGitSHA1(), strtol(redisGitDirty(),NULL,10) > 0, (sizeof(long) == 8) ? "64" : "32", mode, g_pserver->port, - (long) getpid() + (long) getpid(), + motd ? motd : "" ); + if (motd) + sdsfree(motd); serverLogRaw(LL_NOTICE|LL_RAW,buf); } zfree(buf); From de2b08c3f855c97c5ac159d12c8cc07505ff86f6 Mon Sep 17 00:00:00 2001 From: John Sully Date: Wed, 4 Mar 2020 17:09:12 -0500 Subject: [PATCH 07/18] Add extra logging when reporting errors from masters - especially in rreplay Former-commit-id: 5397f0b03312b8cace07a85333d8f035bdfb8d57 --- src/networking.cpp | 35 +++++++++++++++++++++++++++++++++++ src/replication.cpp | 2 ++ src/server.h | 1 + 3 files changed, 38 insertions(+) diff --git a/src/networking.cpp b/src/networking.cpp index 00322df72..58bd79bcc 100644 --- a/src/networking.cpp +++ b/src/networking.cpp @@ -177,6 +177,7 @@ client *createClient(int fd, int iel) { c->bufposAsync = 0; c->client_tracking_redirection = 0; c->casyncOpsPending = 0; + c->master_error = 0; memset(c->uuid, 0, UUID_BINARY_LEN); listSetFreeMethod(c->pubsub_patterns,decrRefCountVoid); @@ -432,6 +433,34 @@ void addReplyProtoAsync(client *c, const char *s, size_t len) { addReplyProtoCore(c, s, len, true); } +std::string escapeString(sds str) +{ + std::string newstr; + size_t len = sdslen(str); + for (size_t ich = 0; ich < len; ++ich) + { + char ch = str[ich]; + switch (ch) + { + case '\n': + newstr += "\\n"; + break; + + case '\t': + newstr += "\\t"; + break; + + case '\r': + newstr += "\\r"; + break; + + default: + newstr += ch; + } + } + return newstr; +} + /* Low level function called by the addReplyError...() functions. * It emits the protocol for a Redis error, in the form: * @@ -464,6 +493,12 @@ void addReplyErrorLengthCore(client *c, const char *s, size_t len, bool fAsync) serverLog(LL_WARNING,"== CRITICAL == This %s is sending an error " "to its %s: '%s' after processing the command " "'%s'", from, to, s, cmdname); + + if (c->querybuf && sdslen(c->querybuf)) { + std::string str = escapeString(c->querybuf); + serverLog(LL_WARNING, "\tquerybuf: %s", str.c_str()); + } + c->master_error = 1; } } diff --git a/src/replication.cpp b/src/replication.cpp index d7e92d308..2bdc21bf0 100644 --- a/src/replication.cpp +++ b/src/replication.cpp @@ -3482,6 +3482,8 @@ void replicaReplayCommand(client *c) cFake->flags &= ~(CLIENT_MASTER | CLIENT_PREVENT_REPL_PROP); bool fExec = ccmdPrev != serverTL->commandsExecuted; cFake->lock.unlock(); + if (cFake->master_error) + addReplyError(c, "Error in rreplay command, please check logs"); if (fExec || cFake->flags & CLIENT_MULTI) { addReply(c, shared.ok); diff --git a/src/server.h b/src/server.h index 58ea3e620..1e6632576 100644 --- a/src/server.h +++ b/src/server.h @@ -1337,6 +1337,7 @@ typedef struct client { int iel; /* the event loop index we're registered with */ struct fastlock lock; + int master_error; } client; struct saveparam { From 3dbe23f513688d84d4859b8a13f450249ca48acb Mon Sep 17 00:00:00 2001 From: John Sully Date: Wed, 4 Mar 2020 17:13:18 -0500 Subject: [PATCH 08/18] Add missing files from last checkin Former-commit-id: 60c0d4104de3ff904758c200342e4d132b19501c --- src/motd.cpp | 121 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/motd.h | 11 +++++ 2 files changed, 132 insertions(+) create mode 100644 src/motd.cpp create mode 100644 src/motd.h diff --git a/src/motd.cpp b/src/motd.cpp new file mode 100644 index 000000000..e46ba8819 --- /dev/null +++ b/src/motd.cpp @@ -0,0 +1,121 @@ +#include "sds.h" +#include +#include +#include +#include +#include +#include "motd.h" + +/*------------------------------------------------------------------------------ + * Message of the day + *--------------------------------------------------------------------------- */ +#ifdef MOTD +#include + +static const char *szMotdCachePath() +{ + static sds sdsMotdCachePath = NULL; + if (sdsMotdCachePath != NULL) + return sdsMotdCachePath; + + struct passwd *pw = getpwuid(getuid()); + if (pw == NULL) + return ""; + const char *homedir = pw->pw_dir; + sdsMotdCachePath = sdsnew(homedir); + sdsMotdCachePath = sdscat(sdsMotdCachePath, "/.keydb-cli-motd"); + return sdsMotdCachePath; +} +static size_t motd_write_callback(void *ptr, size_t size, size_t nmemb, sds *str) +{ + *str = sdscatlen(*str, ptr, size*nmemb); + return (size*nmemb); +} + +static char *fetchMOTDFromCache() +{ + struct stat attrib; + if (stat(szMotdCachePath(), &attrib) != 0) + return NULL; + time_t t = attrib.st_mtim.tv_sec; + time_t now = time(NULL); + if ((now - t) < 14400) + { + // If our cache was updated no more than 4 hours ago use it instead of fetching the MOTD + FILE *pf = fopen(szMotdCachePath(), "rb"); + if (pf == NULL) + return NULL; + fseek(pf, 0L, SEEK_END); + long cb = ftell(pf); + fseek(pf, 0L, SEEK_SET); // rewind + sds str = sdsnewlen(NULL, cb); + size_t cbRead = fread(str, 1, cb, pf); + fclose(pf); + if ((long)cbRead != cb) + { + sdsfree(str); + return NULL; + } + return str; + } + return NULL; +} + +static void setMOTDCache(const char *sz) +{ + FILE *pf = fopen(szMotdCachePath(), "wb"); + if (pf == NULL) + return; + size_t celem = fwrite(sz, strlen(sz), 1, pf); + (void)celem; // best effort + fclose(pf); +} + +extern "C" char *fetchMOTD(int cache) +{ + sds str; + CURL *curl; + CURLcode res; + + /* First try and get the string from the cache */ + if (cache) { + str = fetchMOTDFromCache(); + if (str != NULL) + return str; + } + + str = sdsnew(""); + curl = curl_easy_init(); + if(curl) { + curl_easy_setopt(curl, CURLOPT_URL, "http://api.keydb.dev/motd/motd.txt"); + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); // follow redirects + curl_easy_setopt(curl, CURLOPT_TIMEOUT, 2); // take no more than two seconds + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, motd_write_callback); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &str); + + /* Perform the request, res will get the return code */ + res = curl_easy_perform(curl); + /* Check for errors */ + if(res != CURLE_OK) + { + sdsfree(str); + str = NULL; + } + + /* always cleanup */ + curl_easy_cleanup(curl); + + if (str != NULL && cache) + setMOTDCache(str); + } + return str; +} + +#else + +extern "C" char *fetchMOTD(int /* cache */) +{ + return NULL; +} + +#endif \ No newline at end of file diff --git a/src/motd.h b/src/motd.h new file mode 100644 index 000000000..70fa7bb1d --- /dev/null +++ b/src/motd.h @@ -0,0 +1,11 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +char *fetchMOTD(int fCache); + +#ifdef __cplusplus +} +#endif \ No newline at end of file From a26eec507a2b46225e98a77073733d31f98fccf1 Mon Sep 17 00:00:00 2001 From: John Sully Date: Wed, 4 Mar 2020 17:22:57 -0500 Subject: [PATCH 09/18] finish merge Former-commit-id: 66063a19f6cdbc87ead8ecbd4887134438ee1ccd --- src/Makefile | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/Makefile b/src/Makefile index 27c910264..1b8695cfb 100644 --- a/src/Makefile +++ b/src/Makefile @@ -270,11 +270,7 @@ endif REDIS_SERVER_NAME=keydb-pro-server REDIS_SENTINEL_NAME=keydb-sentinel -<<<<<<< HEAD -REDIS_SERVER_OBJ=adlist.o quicklist.o ae.o anet.o dict.o server.o sds.o zmalloc.o lzf_c.o lzf_d.o pqsort.o zipmap.o sha1.o ziplist.o release.o networking.o util.o object.o db.o replication.o rdb.o t_string.o t_list.o t_set.o t_zset.o t_hash.o config.o aof.o pubsub.o multi.o debug.o sort.o intset.o syncio.o cluster.o crc16.o endianconv.o slowlog.o scripting.o bio.o rio.o rand.o memtest.o crc64.o bitops.o sentinel.o notify.o setproctitle.o blocked.o hyperloglog.o latency.o sparkline.o redis-check-rdb.o redis-check-aof.o geo.o lazyfree.o module.o evict.o expire.o geohash.o geohash_helper.o childinfo.o defrag.o siphash.o rax.o t_stream.o listpack.o localtime.o acl.o storage.o rdb-s3.o fastlock.o new.o tracking.o cron.o connection.o tls.o sha256.o AsyncWorkQueue.o snapshot.o storage/rocksdb.o storage/rocksdbfactory.o storage/teststorageprovider.o keydbutils.o $(ASM_OBJ) -======= -REDIS_SERVER_OBJ=adlist.o quicklist.o ae.o anet.o dict.o server.o sds.o zmalloc.o lzf_c.o lzf_d.o pqsort.o zipmap.o sha1.o ziplist.o release.o networking.o util.o object.o db.o replication.o rdb.o t_string.o t_list.o t_set.o t_zset.o t_hash.o config.o aof.o pubsub.o multi.o debug.o sort.o intset.o syncio.o cluster.o crc16.o endianconv.o slowlog.o scripting.o bio.o rio.o rand.o memtest.o crc64.o bitops.o sentinel.o notify.o setproctitle.o blocked.o hyperloglog.o latency.o sparkline.o redis-check-rdb.o redis-check-aof.o geo.o lazyfree.o module.o evict.o expire.o geohash.o geohash_helper.o childinfo.o defrag.o siphash.o rax.o t_stream.o listpack.o localtime.o acl.o storage.o rdb-s3.o fastlock.o new.o tracking.o cron.o connection.o tls.o sha256.o motd.o $(ASM_OBJ) ->>>>>>> redis_6_merge +REDIS_SERVER_OBJ=adlist.o quicklist.o ae.o anet.o dict.o server.o sds.o zmalloc.o lzf_c.o lzf_d.o pqsort.o zipmap.o sha1.o ziplist.o release.o networking.o util.o object.o db.o replication.o rdb.o t_string.o t_list.o t_set.o t_zset.o t_hash.o config.o aof.o pubsub.o multi.o debug.o sort.o intset.o syncio.o cluster.o crc16.o endianconv.o slowlog.o scripting.o bio.o rio.o rand.o memtest.o crc64.o bitops.o sentinel.o notify.o setproctitle.o blocked.o hyperloglog.o latency.o sparkline.o redis-check-rdb.o redis-check-aof.o geo.o lazyfree.o module.o evict.o expire.o geohash.o geohash_helper.o childinfo.o defrag.o siphash.o rax.o t_stream.o listpack.o localtime.o acl.o storage.o rdb-s3.o fastlock.o new.o tracking.o cron.o connection.o tls.o sha256.o AsyncWorkQueue.o snapshot.o storage/rocksdb.o storage/rocksdbfactory.o storage/teststorageprovider.o keydbutils.o motd.o $(ASM_OBJ) REDIS_CLI_NAME=keydb-cli REDIS_CLI_OBJ=anet.o adlist.o dict.o redis-cli.o redis-cli-cpphelper.o zmalloc.o release.o anet.o ae.o crc64.o siphash.o crc16.o storage-lite.o fastlock.o new.o motd.o $(ASM_OBJ) REDIS_BENCHMARK_NAME=keydb-benchmark From b3e33daf7dbcb873aaab113b3a0f29ceb20a442a Mon Sep 17 00:00:00 2001 From: John Sully Date: Wed, 4 Mar 2020 17:23:40 -0500 Subject: [PATCH 10/18] Fix CLANG build break Former-commit-id: c2a98a60f964f28455a5d04e18b88a9a45c9e586 --- src/server.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/server.h b/src/server.h index 1e6632576..94d8a0d11 100644 --- a/src/server.h +++ b/src/server.h @@ -55,6 +55,7 @@ #include #include #include +#include #ifdef __cplusplus extern "C" { #include From 0114827642eedea1639dd0c45156f2e8a94fb7aa Mon Sep 17 00:00:00 2001 From: John Sully Date: Wed, 4 Mar 2020 17:23:40 -0500 Subject: [PATCH 11/18] Fix CLANG build break Former-commit-id: 5e63c0955d2861ab0ebe2055d4f2d2e8989ea4f3 --- src/server.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/server.h b/src/server.h index a668cbcf7..73a7d8451 100644 --- a/src/server.h +++ b/src/server.h @@ -55,6 +55,7 @@ #include #include #include +#include #ifdef __cplusplus extern "C" { #include From 282dd54eb638386b3a2e5da34b1d3b7380981f5a Mon Sep 17 00:00:00 2001 From: John Sully Date: Sun, 15 Mar 2020 18:26:46 -0400 Subject: [PATCH 12/18] Change the default for maxmemory samples to 10 to reduce issues with FLASH storage and eviction failure Former-commit-id: 8571d8aca9b462d600b78854c0795c8b33520088 --- src/config.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/config.cpp b/src/config.cpp index 4e2153290..06aad3649 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -306,6 +306,7 @@ bool initializeStorageProvider(const char **err) if (!strcasecmp(g_sdsProvider, "flash") && g_sdsArgs != nullptr) { // Create The Storage Factory (if necessary) + serverLog(LL_NOTICE, "Initializing FLASH storage provider (this may take a long time)"); g_pserver->m_pstorageFactory = CreateRocksDBStorageFactory(g_sdsArgs, cserver.dbnum); } else if (!strcasecmp(g_sdsProvider, "test") && g_sdsArgs == nullptr) @@ -2365,7 +2366,7 @@ standardConfig configs[] = { createIntConfig("lfu-decay-time", NULL, MODIFIABLE_CONFIG, 0, INT_MAX, g_pserver->lfu_decay_time, 1, INTEGER_CONFIG, NULL, NULL), createIntConfig("replica-priority", "slave-priority", MODIFIABLE_CONFIG, 0, INT_MAX, g_pserver->slave_priority, 100, INTEGER_CONFIG, NULL, NULL), createIntConfig("repl-diskless-sync-delay", NULL, MODIFIABLE_CONFIG, 0, INT_MAX, g_pserver->repl_diskless_sync_delay, 5, INTEGER_CONFIG, NULL, NULL), - createIntConfig("maxmemory-samples", NULL, MODIFIABLE_CONFIG, 1, INT_MAX, g_pserver->maxmemory_samples, 5, INTEGER_CONFIG, NULL, NULL), + createIntConfig("maxmemory-samples", NULL, MODIFIABLE_CONFIG, 1, INT_MAX, g_pserver->maxmemory_samples, 10, INTEGER_CONFIG, NULL, NULL), createIntConfig("timeout", NULL, MODIFIABLE_CONFIG, 0, INT_MAX, cserver.maxidletime, 0, INTEGER_CONFIG, NULL, NULL), /* Default client timeout: infinite */ createIntConfig("replica-announce-port", "slave-announce-port", MODIFIABLE_CONFIG, 0, 65535, g_pserver->slave_announce_port, 0, INTEGER_CONFIG, NULL, NULL), createIntConfig("tcp-backlog", NULL, IMMUTABLE_CONFIG, 0, INT_MAX, g_pserver->tcp_backlog, 511, INTEGER_CONFIG, NULL, NULL), /* TCP listen backlog. */ From 006a9565d3c2c4f80654fee72dc61ab9dcea855d Mon Sep 17 00:00:00 2001 From: John Sully Date: Sun, 15 Mar 2020 18:27:10 -0400 Subject: [PATCH 13/18] Add fallback if we fail to evict normally with FLASH (flush everything) Former-commit-id: 66ee85a8d5e24514083aa3478dcf0cf3da86520e --- src/evict.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/evict.cpp b/src/evict.cpp index 792a39f87..ddf8e1818 100644 --- a/src/evict.cpp +++ b/src/evict.cpp @@ -666,6 +666,21 @@ int freeMemoryIfNeeded(void) { return C_OK; cant_free: + if (!cserver.delete_on_evict) + { + for (int idb = 0; idb < cserver.dbnum; ++idb) + { + redisDb *db = g_pserver->db[idb]; + if (db->FStorageProvider()) + { + serverLog(LL_WARNING, "Failed to evict keys, falling back to flushing entire cache. Consider increasing maxmemory-samples."); + db->removeAllCachedValues(); + if (((mem_reported - zmalloc_used_memory()) + mem_freed) >= mem_tofree) + return C_OK; + } + } + } + /* We are here if we are not able to reclaim memory. There is only one * last thing we can try: check if the lazyfree thread has jobs in queue * and wait... */ From 3b1f36a521d4d33a6833a41a49a547ba7f64473e Mon Sep 17 00:00:00 2001 From: John Sully Date: Sun, 15 Mar 2020 18:32:05 -0400 Subject: [PATCH 14/18] Update MOTD URL Former-commit-id: cd76a71de393eb527f98024ea85a9492ef0c36ca --- src/motd.cpp | 2 +- src/motd.h | 2 ++ src/redis-cli.c | 2 ++ src/server.cpp | 1 + 4 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/motd.cpp b/src/motd.cpp index e46ba8819..8ad0b9caa 100644 --- a/src/motd.cpp +++ b/src/motd.cpp @@ -87,7 +87,7 @@ extern "C" char *fetchMOTD(int cache) str = sdsnew(""); curl = curl_easy_init(); if(curl) { - curl_easy_setopt(curl, CURLOPT_URL, "http://api.keydb.dev/motd/motd.txt"); + curl_easy_setopt(curl, CURLOPT_URL, motd_url); curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); // follow redirects curl_easy_setopt(curl, CURLOPT_TIMEOUT, 2); // take no more than two seconds curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, motd_write_callback); diff --git a/src/motd.h b/src/motd.h index 70fa7bb1d..31479c9b2 100644 --- a/src/motd.h +++ b/src/motd.h @@ -1,5 +1,7 @@ #pragma once +extern const char *motd_url; + #ifdef __cplusplus extern "C" { #endif diff --git a/src/redis-cli.c b/src/redis-cli.c index 7332c40e0..c8aaab6db 100644 --- a/src/redis-cli.c +++ b/src/redis-cli.c @@ -93,6 +93,8 @@ int spectrum_palette_size; int g_fInCrash = 0; +const char *motd_url = "http://api.keydb.dev/motd/motd_cli.txt"; + /*------------------------------------------------------------------------------ * Utility functions *--------------------------------------------------------------------------- */ diff --git a/src/server.cpp b/src/server.cpp index d6fe1ba1f..1ec2c0d7a 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -65,6 +65,7 @@ #include "motd.h" int g_fTestMode = false; +const char *motd_url = "http://api.keydb.dev/motd/motd_server.txt"; /* Our shared "common" objects */ From 34fca59d4a7f8e798cba749888480dfb6345c17f Mon Sep 17 00:00:00 2001 From: John Sully Date: Sun, 15 Mar 2020 18:34:42 -0400 Subject: [PATCH 15/18] Update MOTD url for Pro Former-commit-id: feed518042f3046016c26beda1c08cc721914943 --- src/server.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server.cpp b/src/server.cpp index b9e62cbb4..b8ea9650f 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -66,7 +66,7 @@ #include "motd.h" int g_fTestMode = false; -const char *motd_url = "http://api.keydb.dev/motd/motd_server.txt"; +const char *motd_url = "http://api.keydb.dev/motd/motd_server_pro.txt"; /* Our shared "common" objects */ From 15df3ed91d1f739bffbf74eec93d43256c60715b Mon Sep 17 00:00:00 2001 From: John Sully Date: Sun, 15 Mar 2020 18:47:01 -0400 Subject: [PATCH 16/18] Handle HTTP error codes with MOTD Former-commit-id: 2ec0b2cd206f2c73fabcda5d59751b013aa8cfbf --- src/motd.cpp | 13 ++++++++++++- src/motd.h | 1 + src/redis-cli.c | 1 + src/server.cpp | 1 + 4 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/motd.cpp b/src/motd.cpp index 8ad0b9caa..2fbef9526 100644 --- a/src/motd.cpp +++ b/src/motd.cpp @@ -23,7 +23,7 @@ static const char *szMotdCachePath() return ""; const char *homedir = pw->pw_dir; sdsMotdCachePath = sdsnew(homedir); - sdsMotdCachePath = sdscat(sdsMotdCachePath, "/.keydb-cli-motd"); + sdsMotdCachePath = sdscat(sdsMotdCachePath, motd_cache_file); return sdsMotdCachePath; } static size_t motd_write_callback(void *ptr, size_t size, size_t nmemb, sds *str) @@ -101,6 +101,17 @@ extern "C" char *fetchMOTD(int cache) sdsfree(str); str = NULL; } + else + { + long response_code; + curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code); + if ((response_code / 100) != 2) + { + // An error code not in the 200s implies an error + sdsfree(str); + str = NULL; + } + } /* always cleanup */ curl_easy_cleanup(curl); diff --git a/src/motd.h b/src/motd.h index 31479c9b2..c57e63726 100644 --- a/src/motd.h +++ b/src/motd.h @@ -1,6 +1,7 @@ #pragma once extern const char *motd_url; +extern const char *motd_cache_file; #ifdef __cplusplus extern "C" { diff --git a/src/redis-cli.c b/src/redis-cli.c index c8aaab6db..96f920ae1 100644 --- a/src/redis-cli.c +++ b/src/redis-cli.c @@ -94,6 +94,7 @@ int spectrum_palette_size; int g_fInCrash = 0; const char *motd_url = "http://api.keydb.dev/motd/motd_cli.txt"; +const char *motd_cache_file = "/.keydb-cli-motd"; /*------------------------------------------------------------------------------ * Utility functions diff --git a/src/server.cpp b/src/server.cpp index 1ec2c0d7a..3d7aa3a41 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -66,6 +66,7 @@ int g_fTestMode = false; const char *motd_url = "http://api.keydb.dev/motd/motd_server.txt"; +const char *motd_cache_file = "/.keydb-server-motd"; /* Our shared "common" objects */ From 42027c533a404cccd3f6b8353609aac0a7aeb0b3 Mon Sep 17 00:00:00 2001 From: John Sully Date: Mon, 16 Mar 2020 20:43:46 -0400 Subject: [PATCH 17/18] Fix issue #153: Config get replicaof returns a corrupt response Former-commit-id: 5aef606d3bc1d748ca8d2df1d36feccaa2e583c0 --- src/config.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/config.cpp b/src/config.cpp index 2c9a9d518..4035ca376 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -1496,8 +1496,15 @@ void configGetCommand(client *c) { listIter li; listNode *ln; listRewind(g_pserver->masters, &li); + bool fFirst = true; while ((ln = listNext(&li))) { + if (!fFirst) + { + addReplyBulkCString(c,optname); + matches++; + } + fFirst = false; struct redisMaster *mi = (struct redisMaster*)listNodeValue(ln); snprintf(buf,sizeof(buf),"%s %d", mi->masterhost, mi->masterport); From ebe0b918bc7fa16fe229e6d17620795ef1cb353f Mon Sep 17 00:00:00 2001 From: John Sully Date: Tue, 17 Mar 2020 17:11:41 -0400 Subject: [PATCH 18/18] Cache count of keys in database Former-commit-id: 7cd266030ad82b5fddd4668d666adc95e1bed475 --- src/server.cpp | 63 ++++++++++++++++++++++++++++++++++ src/storage/rocksdb.cpp | 9 +++++ src/storage/rocksdb.h | 3 ++ src/storage/rocksdbfactory.cpp | 50 ++++++++++++++++++++++++--- src/version.h | 25 ++++++++++++++ 5 files changed, 145 insertions(+), 5 deletions(-) diff --git a/src/server.cpp b/src/server.cpp index f7ac7545e..f59288a3a 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -1646,6 +1646,62 @@ int clientsCronResizeQueryBuffer(client *c) { return 0; } +SymVer parseVersion(const char *version) +{ + SymVer ver = {-1,-1,-1}; + long versions[3] = {-1,-1,-1}; + const char *start = version; + const char *end = nullptr; + + for (int iver = 0; iver < 3; ++iver) + { + end = start; + while (*end != '\0' && *end != '.') + ++end; + + if (start >= end) + return ver; + + if (!string2l(start, end - start, versions + iver)) + return ver; + if (*end != '\0') + start = end+1; + else + break; + } + ver.major = versions[0]; + ver.minor = versions[1]; + ver.build = versions[2]; + + return ver; +} + +VersionCompareResult compareVersion(SymVer *pver) +{ + SymVer symVerThis = parseVersion(KEYDB_REAL_VERSION); + for (int iver = 0; iver < 3; ++iver) + { + long verThis, verOther; + switch (iver) + { + case 0: + verThis = symVerThis.major; verOther = pver->major; + break; + case 1: + verThis = symVerThis.minor; verOther = pver->minor; + break; + case 2: + verThis = symVerThis.build; verOther = pver->build; + } + + if (verThis < verOther) + return VersionCompareResult::NewerVersion; + if (verThis > verOther) + return VersionCompareResult::OlderVersion; + } + return VersionCompareResult::EqualVerison; +} + /* This function is used in order to track clients using the biggest amount * of memory in the latest few seconds. This way we can provide such information * in the INFO output (clients section), without having to do an O(N) scan for @@ -5397,6 +5453,13 @@ int main(int argc, char **argv) { std::set_terminate(OnTerminate); + { + SymVer version; + version = parseVersion(KEYDB_REAL_VERSION); + serverAssert(version.major >= 0 && version.minor >= 0 && version.build >= 0); + serverAssert(compareVersion(&version) == VersionCompareResult::EqualVerison); + } + #ifdef USE_MEMKIND storage_init(NULL, 0); #endif diff --git a/src/storage/rocksdb.cpp b/src/storage/rocksdb.cpp index 386ae7397..dd88a86a8 100644 --- a/src/storage/rocksdb.cpp +++ b/src/storage/rocksdb.cpp @@ -99,6 +99,15 @@ const IStorage *RocksDBStorageProvider::clone() const RocksDBStorageProvider::~RocksDBStorageProvider() { + if (m_spbatch != nullptr) + endWriteBatch(); + + if (m_spdb != nullptr && m_psnapshot == nullptr) + { + insert(count_key, sizeof(count_key), &m_count, sizeof(m_count), false); + flush(); + } + if (m_spdb != nullptr) { if (m_psnapshot != nullptr) diff --git a/src/storage/rocksdb.h b/src/storage/rocksdb.h index b5d3308fd..21e57db92 100644 --- a/src/storage/rocksdb.h +++ b/src/storage/rocksdb.h @@ -5,6 +5,9 @@ #include #include "../fastlock.h" +static const char count_key[] = "\0__keydb__count\1"; +static const char version_key[] = "\0__keydb__version\1"; + class RocksDBStorageProvider : public IStorage { std::shared_ptr m_spdb; // Note: This must be first so it is deleted last diff --git a/src/storage/rocksdbfactory.cpp b/src/storage/rocksdbfactory.cpp index 8c5cacef2..f02701130 100644 --- a/src/storage/rocksdbfactory.cpp +++ b/src/storage/rocksdbfactory.cpp @@ -1,4 +1,5 @@ #include "rocksdb.h" +#include "../version.h" #include #include #include @@ -14,6 +15,9 @@ public: virtual IStorage *create(int db) override; virtual const char *name() const override; + +private: + void setVersion(rocksdb::ColumnFamilyHandle*); }; IStorageFactory *CreateRocksDBStorageFactory(const char *path, int dbnum) @@ -64,9 +68,26 @@ RocksDBStorageFactory::RocksDBStorageFactory(const char *dbfile, int dbnum) if (!status.ok()) throw status.ToString(); - for (auto handle : handles) - m_vecspcols.emplace_back(handle); m_spdb = std::shared_ptr(db); + for (auto handle : handles) + { + std::string strVersion; + auto status = m_spdb->Get(rocksdb::ReadOptions(), handle, rocksdb::Slice(version_key, sizeof(version_key)), &strVersion); + if (!status.ok()) + { + setVersion(handle); + } + else + { + SymVer ver = parseVersion(strVersion.c_str()); + auto cmp = compareVersion(&ver); + if (cmp == NewerVersion) + throw "Cannot load FLASH database created by newer version of KeyDB"; + if (cmp == OlderVersion) + setVersion(handle); + } + m_vecspcols.emplace_back(handle); + } } RocksDBStorageFactory::~RocksDBStorageFactory() @@ -74,14 +95,33 @@ RocksDBStorageFactory::~RocksDBStorageFactory() m_spdb->SyncWAL(); } +void RocksDBStorageFactory::setVersion(rocksdb::ColumnFamilyHandle *handle) +{ + auto status = m_spdb->Put(rocksdb::WriteOptions(), handle, rocksdb::Slice(version_key, sizeof(version_key)), rocksdb::Slice(KEYDB_REAL_VERSION, strlen(KEYDB_REAL_VERSION)+1)); + if (!status.ok()) + throw status.ToString(); +} + IStorage *RocksDBStorageFactory::create(int db) { ++db; // skip default col family std::shared_ptr spcolfamily(m_vecspcols[db].release()); size_t count = 0; - std::unique_ptr it = std::unique_ptr(m_spdb->NewIterator(rocksdb::ReadOptions(), spcolfamily.get())); - for (it->SeekToFirst(); it->Valid(); it->Next()) { - ++count; + + std::string value; + auto status = m_spdb->Get(rocksdb::ReadOptions(), spcolfamily.get(), rocksdb::Slice(count_key, sizeof(count_key)), &value); + if (status.ok() && value.size() == sizeof(size_t)) + { + count = *reinterpret_cast(value.data()); + m_spdb->Delete(rocksdb::WriteOptions(), spcolfamily.get(), rocksdb::Slice(count_key, sizeof(count_key))); + } + else + { + printf("\tDatabase was not shutdown cleanly, recomputing metrics\n"); + std::unique_ptr it = std::unique_ptr(m_spdb->NewIterator(rocksdb::ReadOptions(), spcolfamily.get())); + for (it->SeekToFirst(); it->Valid(); it->Next()) { + ++count; + } } return new RocksDBStorageProvider(m_spdb, spcolfamily, nullptr, count); } diff --git a/src/version.h b/src/version.h index db32931f1..793078590 100644 --- a/src/version.h +++ b/src/version.h @@ -1,3 +1,28 @@ #define KEYDB_REAL_VERSION "0.0.0" extern const char *KEYDB_SET_VERSION; // Unlike real version, this can be overriden by the config +enum VersionCompareResult +{ + EqualVerison, + OlderVersion, + NewerVersion, +}; + +struct SymVer +{ + long major; + long minor; + long build; +}; + +#ifdef __cplusplus +extern "C" +{ +#endif + +struct SymVer parseVersion(const char *version); +enum VersionCompareResult compareVersion(struct SymVer *pver); + +#ifdef __cplusplus +} +#endif \ No newline at end of file