Compare commits

...
Author SHA1 Message Date
Malavan Sotheeswaran 9f4c6e6f15 using forked bg save causes more harm than good 2023-02-10 01:23:52 -08:00
Malavan Sotheeswaran 6c26f91bcd rdb_child_pid isn't the correct value 2023-02-10 01:13:12 -08:00
Malavan Sotheeswaran 932fc207ea make bgsave fork by default 2023-02-09 12:22:29 -08:00
Malavan Sotheeswaran 0cba0ed30b refactor aeReleaseForkLockChild to capture releaseRead case 2023-02-09 12:06:27 -08:00
Malavan Sotheeswaran b5ab1d64e2 need child specific release that doesn't trigger cv 2023-02-09 10:28:22 -08:00
Malavan Sotheeswaran 3492615c6a don't release lock on child as it can hang 2023-02-09 09:39:25 -08:00
Malavan Sotheeswaran f3a9d2e0cd technically possible for child_type == CHILD_TYPE_AOF without active child 2023-02-06 20:33:21 -08:00
Malavan Sotheeswaran a1978ce04c Add support to notify modules of keys loaded by flash on startup (#536) 2023-02-06 12:52:32 -05:00
Malavan Sotheeswaran 5123e2b3a1 change hasActiveChildProcess to return true only when there is an actual child process (#558)
change hasActiveChildProcess to return true only when there is an actual child process, add hasActiveChildProcessOrBGSave to catch case of forkless bgsave
2023-02-03 13:36:06 -05:00
John Sully 687850a612 Make semi-ordered-set rehashing configurable to aid in latency tuning 2023-01-31 11:30:39 -08:00
John Sully d4555a6e38 We need to check for AE_DONT_WAIT after it is set in beforeSleep otherwise we'll miss it 2023-01-31 11:27:59 -08:00
Malavan Sotheeswaran fc8e4804b2 remove extra call to handleBlockedClientsTimeout (#557) 2023-01-30 16:23:00 -05:00
benschermel 1113488991 fix typo & discrepancy 2023-01-25 11:42:36 -05:00
Malavan Sotheeswaran 694fc59f2a move memory check before rocksdb to avoid crash in child 2023-01-23 13:26:27 -08:00
Malavan Sotheeswaran 7e91711086 fix debian 12 test failure 2023-01-23 13:26:24 -08:00
Malavan Sotheeswaran ff7991951f Fix optional flash build (#549)
Currently flash is always built, with this change it is actually optional.
2023-01-23 16:22:01 -05:00
Malavan Sotheeswaran d7c34a8ced Properly handle case where we delete a key that doesn't exist while a write batch is active (#550) 2023-01-23 16:21:27 -05:00
22 changed files with 231 additions and 74 deletions

No files matched your search

+2 -2
View File
@@ -141,9 +141,9 @@ To append a suffix to KeyDB program names, use:
***Note that the following dependencies may be needed:
% sudo apt-get install autoconf autotools-dev libnuma-dev libtool
To buik=ld with TLS support, use:
KeyDB by default is built with TLS enabled. To build without TLS support, use:
% make BUILD_TLS=yes
% make BUILD_TLS=no
Running the tests with TLS enabled (you will need `tcl-tls`
installed):
+1
View File
@@ -37,5 +37,6 @@ $TCLSH tests/test_helper.tcl \
--single unit/moduleapi/hash \
--single unit/moduleapi/zset \
--single unit/moduleapi/stream \
--single unit/moduleapi/load \
--config server-threads 3 \
"${@}"
+3 -3
View File
@@ -20,7 +20,7 @@ release_hdr := $(shell sh -c './mkreleasehdr.sh')
uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not')
uname_M := $(shell sh -c 'uname -m 2>/dev/null || echo not')
OPTIMIZATION?=-O2 -flto
DEPENDENCY_TARGETS=hiredis linenoise lua hdr_histogram rocksdb
DEPENDENCY_TARGETS=hiredis linenoise lua hdr_histogram
NODEPS:=clean distclean
# Default settings
@@ -376,7 +376,7 @@ endif
REDIS_SERVER_NAME=keydb-server$(PROG_SUFFIX)
REDIS_SENTINEL_NAME=keydb-sentinel$(PROG_SUFFIX)
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 t_nhash.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 crcspeed.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_server.o timeout.o setcpuaffinity.o AsyncWorkQueue.o snapshot.o storage/rocksdb.o storage/rocksdbfactory.o storage/teststorageprovider.o keydbutils.o StorageCache.o monotonic.o cli_common.o mt19937-64.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 t_nhash.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 crcspeed.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_server.o timeout.o setcpuaffinity.o AsyncWorkQueue.o snapshot.o storage/teststorageprovider.o keydbutils.o StorageCache.o monotonic.o cli_common.o mt19937-64.o $(ASM_OBJ) $(STORAGE_OBJ)
KEYDB_SERVER_OBJ=SnapshotPayloadParseState.o
REDIS_CLI_NAME=keydb-cli$(PROG_SUFFIX)
REDIS_CLI_OBJ=anet.o adlist.o dict.o redis-cli.o redis-cli-cpphelper.o zmalloc.o release.o anet.o ae.o crcspeed.o crc64.o siphash.o crc16.o storage-lite.o fastlock.o motd_client.o monotonic.o cli_common.o mt19937-64.o $(ASM_OBJ)
@@ -437,7 +437,7 @@ endif
# keydb-server
$(REDIS_SERVER_NAME): $(REDIS_SERVER_OBJ) $(KEYDB_SERVER_OBJ)
$(REDIS_LD) -o $@ $^ ../deps/hiredis/libhiredis.a ../deps/lua/src/liblua.a ../deps/rocksdb/librocksdb.a $(FINAL_LIBS)
$(REDIS_LD) -o $@ $^ ../deps/hiredis/libhiredis.a ../deps/lua/src/liblua.a $(FINAL_LIBS)
# keydb-sentinel
$(REDIS_SENTINEL_NAME): $(REDIS_SERVER_NAME)
+10 -5
View File
@@ -716,11 +716,6 @@ int aeProcessEvents(aeEventLoop *eventLoop, int flags)
}
}
if (eventLoop->flags & AE_DONT_WAIT) {
tv.tv_sec = tv.tv_usec = 0;
tvp = &tv;
}
if (eventLoop->beforesleep != NULL && flags & AE_CALL_BEFORE_SLEEP) {
std::unique_lock<decltype(g_lock)> ulock(g_lock, std::defer_lock);
if (!(eventLoop->beforesleepFlags & AE_SLEEP_THREADSAFE)) {
@@ -731,6 +726,11 @@ int aeProcessEvents(aeEventLoop *eventLoop, int flags)
eventLoop->beforesleep(eventLoop);
}
if (eventLoop->flags & AE_DONT_WAIT) {
tv.tv_sec = tv.tv_usec = 0;
tvp = &tv;
}
/* Call the multiplexing API, will return only on timeout or when
* some event fires. */
numevents = aeApiPoll(eventLoop, tvp);
@@ -859,6 +859,11 @@ void aeReleaseForkLock()
g_forkLock.downgradeWrite();
}
void aeForkLockInChild()
{
g_forkLock.setNotify(false);
}
int aeThreadOwnsLock()
{
return fOwnLockOverride || g_lock.fOwnLock();
+1
View File
@@ -171,6 +171,7 @@ int aeTryAcquireLock(int fWeak);
void aeThreadOffline();
void aeReleaseLock();
void aeReleaseForkLock();
void aeForkLockInChild();
int aeThreadOwnsLock();
void aeSetThreadOwnsLockOverride(int fOverride);
int aeLockContested(int threshold);
+6 -6
View File
@@ -285,7 +285,7 @@ int startAppendOnly(void) {
strerror(errno));
return C_ERR;
}
if (hasActiveChildProcess() && g_pserver->child_type != CHILD_TYPE_AOF) {
if (hasActiveChildProcessOrBGSave() && g_pserver->child_type != CHILD_TYPE_AOF) {
g_pserver->aof_rewrite_scheduled = 1;
serverLog(LL_WARNING,"AOF was enabled but there is already another background operation. An AOF background was scheduled to start when possible.");
} else {
@@ -438,7 +438,7 @@ void flushAppendOnlyFile(int force) {
* useful for graphing / monitoring purposes. */
if (sync_in_progress) {
latencyAddSampleIfNeeded("aof-write-pending-fsync",latency);
} else if (hasActiveChildProcess()) {
} else if (hasActiveChildProcessOrBGSave()) {
latencyAddSampleIfNeeded("aof-write-active-child",latency);
} else {
latencyAddSampleIfNeeded("aof-write-alone",latency);
@@ -535,7 +535,7 @@ void flushAppendOnlyFile(int force) {
try_fsync:
/* Don't fsync if no-appendfsync-on-rewrite is set to yes and there are
* children doing I/O in the background. */
if (g_pserver->aof_no_fsync_on_rewrite && hasActiveChildProcess())
if (g_pserver->aof_no_fsync_on_rewrite && hasActiveChildProcessOrBGSave())
return;
/* Perform the fsync if needed. */
@@ -752,7 +752,7 @@ void feedAppendOnlyFile(struct redisCommand *cmd, int dictid, robj **argv, int a
* accumulate the differences between the child DB and the current one
* in a buffer, so that when the child process will do its work we
* can append the differences to the new append only file. */
if (g_pserver->child_type == CHILD_TYPE_AOF)
if (hasActiveChildProcess() && g_pserver->child_type == CHILD_TYPE_AOF)
aofRewriteBufferAppend((unsigned char*)buf,sdslen(buf));
sdsfree(buf);
@@ -1887,7 +1887,7 @@ void aofClosePipes(void) {
int rewriteAppendOnlyFileBackground(void) {
pid_t childpid;
if (hasActiveChildProcess()) return C_ERR;
if (hasActiveChildProcessOrBGSave()) return C_ERR;
if (aofCreatePipes() != C_OK) return C_ERR;
if ((childpid = redisFork(CHILD_TYPE_AOF)) == 0) {
char tmpfile[256];
@@ -1930,7 +1930,7 @@ int rewriteAppendOnlyFileBackground(void) {
void bgrewriteaofCommand(client *c) {
if (g_pserver->child_type == CHILD_TYPE_AOF) {
addReplyError(c,"Background append only file rewriting already in progress");
} else if (hasActiveChildProcess()) {
} else if (hasActiveChildProcessOrBGSave()) {
g_pserver->aof_rewrite_scheduled = 1;
addReplyStatus(c,"Background append only file rewriting scheduled");
} else if (rewriteAppendOnlyFileBackground() == C_OK) {
+1 -1
View File
@@ -322,7 +322,7 @@ handle_wrap:
return 1;
}
int checkSignedBitfieldOverflow(int64_t value, int64_t incr, uint64_t bits, int owtype, int64_t *limit) {
int checkSignedBitfieldOverflow(int64_t value, int64_t incr, int bits, int owtype, int64_t *limit) {
int64_t max = (bits == 64) ? INT64_MAX : (((int64_t)1<<(bits-1))-1);
int64_t min = (-max)-1;
+2
View File
@@ -40,6 +40,7 @@
#endif
const char *KEYDB_SET_VERSION = KEYDB_REAL_VERSION;
size_t g_semiOrderedSetTargetBucketSize = 0; // Its a header only class so nowhere else for this to go
/*-----------------------------------------------------------------------------
* Config file name-value maps.
@@ -2910,6 +2911,7 @@ standardConfig configs[] = {
createBoolConfig("allow-write-during-load", NULL, MODIFIABLE_CONFIG, g_pserver->fWriteDuringActiveLoad, 0, NULL, NULL),
createBoolConfig("force-backlog-disk-reserve", NULL, MODIFIABLE_CONFIG, cserver.force_backlog_disk, 0, NULL, NULL),
createBoolConfig("soft-shutdown", NULL, MODIFIABLE_CONFIG, g_pserver->config_soft_shutdown, 0, NULL, NULL),
createSizeTConfig("semi-ordered-set-bucket-size", NULL, MODIFIABLE_CONFIG, 0, 1024, g_semiOrderedSetTargetBucketSize, 0, INTEGER_CONFIG, NULL, NULL),
#ifdef USE_OPENSSL
createIntConfig("tls-port", NULL, MODIFIABLE_CONFIG, 0, 65535, g_pserver->tls_port, 0, INTEGER_CONFIG, NULL, updateTLSPort), /* TCP port. */
+11 -3
View File
@@ -93,7 +93,7 @@ static void lookupKeyUpdateObj(robj *val, int flags)
/* Update the access time for the ageing algorithm.
* Don't do it if we have a saving child, as this will trigger
* a copy on write madness. */
if (!hasActiveChildProcess() && !(flags & LOOKUP_NOTOUCH))
if (!hasActiveChildProcessOrBGSave() && !(flags & LOOKUP_NOTOUCH))
{
if (g_pserver->maxmemory_policy & MAXMEMORY_FLAG_LFU) {
updateLFU(val);
@@ -2607,6 +2607,14 @@ void clusterStorageLoadCallback(const char *rgchkey, size_t cch, void *)
slotToKeyUpdateKeyCore(rgchkey, cch, true /*add*/);
}
void moduleLoadCallback(const char * rgchKey, size_t cchKey, void *data) {
if (g_pserver->cluster_enabled) {
clusterStorageLoadCallback(rgchKey, cchKey, data);
}
robj *keyobj = createEmbeddedStringObject(rgchKey, cchKey);
moduleNotifyKeyspaceEvent(NOTIFY_LOADED, "loaded", keyobj, *(int *)data);
}
void redisDb::initialize(int id)
{
redisDbPersistentData::initialize();
@@ -2625,8 +2633,8 @@ void redisDb::storageProviderInitialize()
{
if (g_pserver->m_pstorageFactory != nullptr)
{
IStorageFactory::key_load_iterator itr = (g_pserver->cluster_enabled) ? clusterStorageLoadCallback : nullptr;
this->setStorageProvider(StorageCache::create(g_pserver->m_pstorageFactory, id, itr, nullptr));
IStorageFactory::key_load_iterator itr = moduleLoadCallback;
this->setStorageProvider(StorageCache::create(g_pserver->m_pstorageFactory, id, itr, &id));
}
}
+9 -6
View File
@@ -1679,17 +1679,15 @@ int rdbSaveBackground(rdbSaveInfo *rsi) {
pthread_t child;
long long start;
if (hasActiveChildProcess()) return C_ERR;
if (hasActiveChildProcessOrBGSave()) return C_ERR;
g_pserver->dirty_before_bgsave = g_pserver->dirty;
g_pserver->lastbgsave_try = time(NULL);
openChildInfoPipe();
start = ustime();
latencyStartMonitor(g_pserver->rdb_save_latency);
g_pserver->stat_fork_time = ustime()-start;
g_pserver->stat_fork_rate = (double) zmalloc_used_memory() * 1000000 / g_pserver->stat_fork_time / (1024*1024*1024); /* GB per second. */
if (launchRdbSaveThread(child, rsi) != C_OK) {
closeChildInfoPipe();
g_pserver->lastbgsave_status = C_ERR;
@@ -1697,6 +1695,9 @@ int rdbSaveBackground(rdbSaveInfo *rsi) {
strerror(errno));
return C_ERR;
}
g_pserver->stat_fork_time = ustime()-start;
g_pserver->stat_fork_rate = (double) zmalloc_used_memory() * 1000000 / g_pserver->stat_fork_time / (1024*1024*1024); /* GB per second. */
latencyAddSampleIfNeeded("fork",g_pserver->stat_fork_time/1000);
serverLog(LL_NOTICE,"Background saving started");
g_pserver->rdb_save_time_start = time(NULL);
@@ -3601,6 +3602,8 @@ static void backgroundSaveDoneHandlerDisk(int exitcode, bool fCancelled) {
g_pserver->dirty = g_pserver->dirty - g_pserver->dirty_before_bgsave;
g_pserver->lastsave = time(NULL);
g_pserver->lastbgsave_status = C_OK;
latencyEndMonitor(g_pserver->rdb_save_latency);
latencyAddSampleIfNeeded("rdb-save",g_pserver->rdb_save_latency);
} else if (!fCancelled && exitcode != 0) {
serverLog(LL_WARNING, "Background saving error");
g_pserver->lastbgsave_status = C_ERR;
@@ -3691,7 +3694,7 @@ void killRDBChild(bool fSynchronous) {
serverAssert(GlobalLocksAcquired());
if (cserver.fForkBgSave) {
kill(g_pserver->rdb_child_pid,SIGUSR1);
kill(g_pserver->child_pid,SIGUSR1);
} else {
g_pserver->rdbThreadVars.fRdbThreadCancel = true;
if (g_pserver->rdb_child_type == RDB_CHILD_TYPE_SOCKET) {
@@ -3782,7 +3785,7 @@ int rdbSaveToSlavesSockets(rdbSaveInfo *rsi) {
int pipefds[2];
rdbSaveSocketThreadArgs *args = nullptr;
if (hasActiveChildProcess()) return C_ERR;
if (hasActiveChildProcessOrBGSave()) return C_ERR;
/* Even if the previous fork child exited, don't start a new one until we
* drained the pipe. */
+9 -2
View File
@@ -8,6 +8,7 @@ class readWriteLock {
int m_readCount = 0;
int m_writeCount = 0;
bool m_writeWaiting = false;
bool m_notify = true;
public:
readWriteLock(const char *name) : m_readLock(name), m_writeLock(name) {}
@@ -65,7 +66,8 @@ public:
void releaseRead() {
std::unique_lock<fastlock> rm(m_readLock);
m_readCount--;
m_cv.notify_all();
if (m_notify)
m_cv.notify_all();
}
void releaseWrite(bool exclusive = true) {
@@ -74,7 +76,8 @@ public:
if (exclusive)
m_writeLock.unlock();
m_writeCount--;
m_cv.notify_all();
if (m_notify)
m_cv.notify_all();
}
void downgradeWrite(bool exclusive = true) {
@@ -82,6 +85,10 @@ public:
acquireRead();
}
void setNotify(bool notify) {
m_notify = notify;
}
bool hasReader() {
return m_readCount > 0;
}
+1
View File
@@ -326,6 +326,7 @@ static const RedisModuleEvent
#define REDISMODULE_SUBEVENT_LOADING_ENDED 3
#define REDISMODULE_SUBEVENT_LOADING_FAILED 4
#define _REDISMODULE_SUBEVENT_LOADING_NEXT 5
#define REDISMODULE_SUBEVENT_LOADING_FLASH_START 6
#define REDISMODULE_SUBEVENT_CLIENT_CHANGE_CONNECTED 0
#define REDISMODULE_SUBEVENT_CLIENT_CHANGE_DISCONNECTED 1
+2 -2
View File
@@ -1538,7 +1538,7 @@ void syncCommand(client *c) {
} else {
/* We don't have a BGSAVE in progress, let's start one. Diskless
* or disk-based mode is determined by replica's capacity. */
if (!hasActiveChildProcess()) {
if (!hasActiveChildProcessOrBGSave()) {
startBgsaveForReplication(c->slave_capa);
} else {
serverLog(LL_NOTICE,
@@ -4984,7 +4984,7 @@ void replicationStartPendingFork(void) {
* In case of diskless replication, we make sure to wait the specified
* number of seconds (according to configuration) so that other slaves
* have the time to arrive before we start streaming. */
if (!hasActiveChildProcess()) {
if (!hasActiveChildProcessOrBGSave()) {
time_t idle, max_idle = 0;
int slaves_waiting = 0;
int mincapa = -1;
+6 -2
View File
@@ -27,6 +27,7 @@ namespace keydbutils
template<>
size_t hash(const sdsview &);
}
extern size_t g_semiOrderedSetTargetBucketSize;
template<typename T, typename T_KEY = T, bool MEMMOVE_SAFE = false>
class semiorderedset
@@ -41,11 +42,14 @@ class semiorderedset
size_t idxRehash = (1ULL << bits_min);
int cfPauseRehash = 0;
constexpr size_t targetElementsPerBucket()
inline size_t targetElementsPerBucket()
{
// Aim for roughly 4 cache lines per bucket (determined by imperical testing)
// lower values are faster but use more memory
return std::max((64/sizeof(T))*8, (size_t)2);
if (g_semiOrderedSetTargetBucketSize == 0)
return std::max((64/sizeof(T))*8, (size_t)2);
else
return g_semiOrderedSetTargetBucketSize;
}
public:
+44 -35
View File
@@ -1706,7 +1706,7 @@ int redisDbPersistentData::incrementallyRehash() {
* for dict.c to resize the hash tables accordingly to the fact we have an
* active fork child running. */
void updateDictResizePolicy(void) {
if (!hasActiveChildProcess() || (g_pserver->FRdbSaveInProgress() && !cserver.fForkBgSave))
if (!hasActiveChildProcess())
dictEnableResize();
else
dictDisableResize();
@@ -1725,7 +1725,11 @@ const char *strChildType(int type) {
/* Return true if there are active children processes doing RDB saving,
* AOF rewriting, or some side process spawned by a loaded module. */
int hasActiveChildProcess() {
return g_pserver->FRdbSaveInProgress() || g_pserver->child_pid != -1;
return g_pserver->child_pid != -1;
}
int hasActiveChildProcessOrBGSave() {
return g_pserver->FRdbSaveInProgress() || hasActiveChildProcess();
}
void resetChildState() {
@@ -2074,7 +2078,7 @@ void databasesCron(bool fMainThread) {
/* Perform hash tables rehashing if needed, but only if there are no
* other processes saving the DB on disk. Otherwise rehashing is bad
* as will cause a lot of copy-on-write of memory pages. */
if (!hasActiveChildProcess() || g_pserver->FRdbSaveInProgress()) {
if (!hasActiveChildProcess()) {
/* We use global counters so if we stop the computation at a given
* DB we'll be able to start from the successive in the next
* cron loop iteration. */
@@ -2468,14 +2472,14 @@ int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) {
/* Start a scheduled AOF rewrite if this was requested by the user while
* a BGSAVE was in progress. */
if (!hasActiveChildProcess() &&
if (!hasActiveChildProcessOrBGSave() &&
g_pserver->aof_rewrite_scheduled)
{
rewriteAppendOnlyFileBackground();
}
/* Check if a background saving or AOF rewrite in progress terminated. */
if (hasActiveChildProcess() || ldbPendingChildren())
if (hasActiveChildProcessOrBGSave() || ldbPendingChildren())
{
run_with_period(1000) receiveChildInfo();
checkChildrenDone();
@@ -2517,7 +2521,7 @@ int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) {
/* Trigger an AOF rewrite if needed. */
if (g_pserver->aof_state == AOF_ON &&
!hasActiveChildProcess() &&
!hasActiveChildProcessOrBGSave() &&
g_pserver->aof_rewrite_perc &&
g_pserver->aof_current_size > g_pserver->aof_rewrite_min_size)
{
@@ -2601,7 +2605,7 @@ int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) {
* Note: this code must be after the replicationCron() call above so
* make sure when refactoring this file to keep this order. This is useful
* because we want to give priority to RDB savings for replication. */
if (!hasActiveChildProcess() &&
if (!hasActiveChildProcessOrBGSave() &&
g_pserver->rdb_bgsave_scheduled &&
(g_pserver->unixtime-g_pserver->lastbgsave_try > CONFIG_BGSAVE_RETRY_DELAY ||
g_pserver->lastbgsave_status == C_OK))
@@ -2815,9 +2819,6 @@ void beforeSleep(struct aeEventLoop *eventLoop) {
runAndPropogateToReplicas(processClients);
/* Handle precise timeouts of blocked clients. */
handleBlockedClientsTimeout();
/* Just call a subset of vital functions in case we are re-entering
* the event loop from processEventsWhileBlocked(). Note that in this
* case we keep track of the number of events we are processing, since
@@ -4067,12 +4068,6 @@ void initServer(void) {
}
}
/* We have to initialize storage providers after the cluster has been initialized */
for (int idb = 0; idb < cserver.dbnum; ++idb)
{
g_pserver->db[idb]->storageProviderInitialize();
}
saveMasterStatusToStorage(false); // eliminate the repl-offset field
/* Initialize ACL default password if it exists */
@@ -4085,6 +4080,15 @@ void initServer(void) {
* Thread Local Storage initialization collides with dlopen call.
* see: https://sourceware.org/bugzilla/show_bug.cgi?id=19329 */
void InitServerLast() {
/* We have to initialize storage providers after the cluster has been initialized */
moduleFireServerEvent(REDISMODULE_EVENT_LOADING, REDISMODULE_SUBEVENT_LOADING_FLASH_START, NULL);
for (int idb = 0; idb < cserver.dbnum; ++idb)
{
g_pserver->db[idb]->storageProviderInitialize();
}
moduleFireServerEvent(REDISMODULE_EVENT_LOADING, REDISMODULE_SUBEVENT_LOADING_ENDED, NULL);
bioInit();
set_jemalloc_bg_thread(cserver.jemalloc_bg_thread);
g_pserver->initial_memory_usage = zmalloc_used_memory();
@@ -6884,6 +6888,7 @@ int redisFork(int purpose) {
latencyAddSampleIfNeeded("fork-lock",(ustime()-startWriteLock)/1000);
if ((childpid = fork()) == 0) {
/* Child */
aeForkLockInChild();
aeReleaseForkLock();
g_pserver->in_fork_child = purpose;
setOOMScoreAdj(CONFIG_OOM_BGCHILD);
@@ -7566,6 +7571,29 @@ int main(int argc, char **argv) {
validateConfiguration();
if (!g_pserver->sentinel_mode) {
#ifdef __linux__
linuxMemoryWarnings();
#if defined (__arm64__)
int ret;
if ((ret = linuxMadvFreeForkBugCheck())) {
if (ret == 1)
serverLog(LL_WARNING,"WARNING Your kernel has a bug that could lead to data corruption during background save. "
"Please upgrade to the latest stable kernel.");
else
serverLog(LL_WARNING, "Failed to test the kernel for a bug that could lead to data corruption during background save. "
"Your system could be affected, please report this error.");
if (!checkIgnoreWarning("ARM64-COW-BUG")) {
serverLog(LL_WARNING,"KeyDB will now exit to prevent data corruption. "
"Note that it is possible to suppress this warning by setting the following config: ignore-warnings ARM64-COW-BUG");
exit(1);
}
}
#endif /* __arm64__ */
#endif /* __linux__ */
}
const char *err;
if (!initializeStorageProvider(&err))
{
@@ -7591,25 +7619,6 @@ int main(int argc, char **argv) {
if (!g_pserver->sentinel_mode) {
/* Things not needed when running in Sentinel mode. */
serverLog(LL_WARNING,"Server initialized");
#ifdef __linux__
linuxMemoryWarnings();
#if defined (__arm64__)
int ret;
if ((ret = linuxMadvFreeForkBugCheck())) {
if (ret == 1)
serverLog(LL_WARNING,"WARNING Your kernel has a bug that could lead to data corruption during background save. "
"Please upgrade to the latest stable kernel.");
else
serverLog(LL_WARNING, "Failed to test the kernel for a bug that could lead to data corruption during background save. "
"Your system could be affected, please report this error.");
if (!checkIgnoreWarning("ARM64-COW-BUG")) {
serverLog(LL_WARNING,"KeyDB will now exit to prevent data corruption. "
"Note that it is possible to suppress this warning by setting the following config: ignore-warnings ARM64-COW-BUG");
exit(1);
}
}
#endif /* __arm64__ */
#endif /* __linux__ */
moduleInitModulesSystemLast();
moduleLoadFromQueue();
ACLLoadUsersAtStartup();
+3 -1
View File
@@ -2470,7 +2470,8 @@ struct redisServer {
time_t lastbgsave_try; /* Unix time of last attempted bgsave */
time_t rdb_save_time_last; /* Time used by last RDB save run. */
time_t rdb_save_time_start; /* Current RDB save start time. */
pid_t rdb_child_pid = -1; /* Used only during fork bgsave */
mstime_t rdb_save_latency; /* Used to track end to end latency of rdb save*/
pid_t rdb_child_pid = -1; /* Used only during fork bgsave */
int rdb_bgsave_scheduled; /* BGSAVE when possible if true. */
int rdb_child_type; /* Type of save by active child. */
int lastbgsave_status; /* C_OK or C_ERR */
@@ -3254,6 +3255,7 @@ void receiveChildInfo(void);
void executeWithoutGlobalLock(std::function<void()> func);
int redisFork(int type);
int hasActiveChildProcess();
int hasActiveChildProcessOrBGSave();
void resetChildState();
int isMutuallyExclusiveChildType(int type);
+6 -4
View File
@@ -100,14 +100,14 @@ bool RocksDBStorageProvider::erase(const char *key, size_t cchKey)
{
rocksdb::Status status;
std::unique_lock<fastlock> l(m_lock);
if (!FKeyExists(key, cchKey))
return false;
if (m_spbatch != nullptr)
{
status = m_spbatch->Delete(m_spcolfamily.get(), rocksdb::Slice(key, cchKey));
}
else
{
if (!FKeyExists(key, cchKey))
return false;
status = m_spdb->Delete(WriteOptions(), m_spcolfamily.get(), rocksdb::Slice(key, cchKey));
}
if (status.ok())
@@ -202,12 +202,12 @@ rocksdb::WriteOptions RocksDBStorageProvider::WriteOptions() const
void RocksDBStorageProvider::beginWriteBatch()
{
m_lock.lock();
m_spbatch = std::make_unique<rocksdb::WriteBatch>();
m_spbatch = std::make_unique<rocksdb::WriteBatchWithIndex>();
}
void RocksDBStorageProvider::endWriteBatch()
{
m_spdb->Write(WriteOptions(), m_spbatch.get());
m_spdb->Write(WriteOptions(), m_spbatch.get()->GetWriteBatch());
m_spbatch = nullptr;
m_lock.unlock();
}
@@ -230,5 +230,7 @@ void RocksDBStorageProvider::flush()
bool RocksDBStorageProvider::FKeyExists(const char *key, size_t cch) const
{
rocksdb::PinnableSlice slice;
if (m_spbatch)
return m_spbatch->GetFromBatchAndDB(m_spdb.get(), ReadOptions(), m_spcolfamily.get(), rocksdb::Slice(key, cch), &slice).ok();
return m_spdb->Get(ReadOptions(), m_spcolfamily.get(), rocksdb::Slice(key, cch), &slice).ok();
}
+2 -1
View File
@@ -3,6 +3,7 @@
#include <memory>
#include "../IStorage.h"
#include <rocksdb/db.h>
#include <rocksdb/utilities/write_batch_with_index.h>
#include "../fastlock.h"
#define INTERNAL_KEY_PREFIX "\x00\x04\x03\x00\x05\x02\x04"
@@ -15,7 +16,7 @@ class RocksDBStorageProvider : public IStorage
{
RocksDBStorageFactory *m_pfactory;
std::shared_ptr<rocksdb::DB> m_spdb; // Note: This must be first so it is deleted last
std::unique_ptr<rocksdb::WriteBatch> m_spbatch;
std::unique_ptr<rocksdb::WriteBatchWithIndex> m_spbatch;
const rocksdb::Snapshot *m_psnapshot = nullptr;
std::shared_ptr<rocksdb::ColumnFamilyHandle> m_spcolfamily;
rocksdb::ReadOptions m_readOptionsTemplate;
+2 -1
View File
@@ -39,7 +39,8 @@ TEST_MODULES = \
defragtest.so \
hash.so \
zset.so \
stream.so
stream.so \
load.so
.PHONY: all
+1
View File
@@ -212,6 +212,7 @@ void loadingCallback(RedisModuleCtx *ctx, RedisModuleEvent e, uint64_t sub, void
case REDISMODULE_SUBEVENT_LOADING_RDB_START: keyname = "loading-rdb-start"; break;
case REDISMODULE_SUBEVENT_LOADING_AOF_START: keyname = "loading-aof-start"; break;
case REDISMODULE_SUBEVENT_LOADING_REPL_START: keyname = "loading-repl-start"; break;
case REDISMODULE_SUBEVENT_LOADING_FLASH_START: keyname = "loading-flash-start"; break;
case REDISMODULE_SUBEVENT_LOADING_ENDED: keyname = "loading-end"; break;
case REDISMODULE_SUBEVENT_LOADING_FAILED: keyname = "loading-failed"; break;
}
+94
View File
@@ -0,0 +1,94 @@
/* Server hooks API example
*
* -----------------------------------------------------------------------------
*
* Copyright (c) 2019, Salvatore Sanfilippo <antirez at gmail dot com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Redis nor the names of its contributors may be used
* to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#define REDISMODULE_EXPERIMENTAL_API
#include "redismodule.h"
size_t count, finalCount;
/* Client state change callback. */
void loadCallback(RedisModuleCtx *ctx, RedisModuleEvent e, uint64_t sub, void *data) {
REDISMODULE_NOT_USED(ctx);
REDISMODULE_NOT_USED(e);
REDISMODULE_NOT_USED(data);
if (sub == REDISMODULE_SUBEVENT_LOADING_FLASH_START || sub == REDISMODULE_SUBEVENT_LOADING_RDB_START || sub == REDISMODULE_SUBEVENT_LOADING_AOF_START || sub == REDISMODULE_SUBEVENT_LOADING_REPL_START) {
count = 0;
finalCount = 0;
} else if (sub == REDISMODULE_SUBEVENT_LOADING_ENDED) {
finalCount = count;
}
}
int loadKeyCallback(RedisModuleCtx *ctx, int type, const char *event, RedisModuleString *key) {
REDISMODULE_NOT_USED(ctx);
REDISMODULE_NOT_USED(type);
REDISMODULE_NOT_USED(event);
const char *keyname = RedisModule_StringPtrLen(key, NULL);
RedisModule_Log(ctx, REDISMODULE_LOGLEVEL_NOTICE, "Loaded key: %s", keyname);
count++;
return 0;
}
int LoadCount_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
REDISMODULE_NOT_USED(argv);
RedisModule_AutoMemory(ctx); /* Use automatic memory management. */
if (argc != 1) return RedisModule_WrongArity(ctx);
RedisModule_ReplyWithLongLong(ctx, finalCount);
return REDISMODULE_OK;
}
/* This function must be present on each Redis module. It is used in order to
* register the commands into the Redis server. */
int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
REDISMODULE_NOT_USED(argv);
REDISMODULE_NOT_USED(argc);
if (RedisModule_Init(ctx,"load",1,REDISMODULE_APIVER_1)
== REDISMODULE_ERR) return REDISMODULE_ERR;
RedisModule_SubscribeToServerEvent(ctx,
RedisModuleEvent_Loading, loadCallback);
RedisModule_SubscribeToKeyspaceEvents(ctx,
REDISMODULE_NOTIFY_LOADED, loadKeyCallback);
if (RedisModule_CreateCommand(ctx, "load.count",
LoadCount_RedisCommand,"readonly",1,1,1) == REDISMODULE_ERR)
return REDISMODULE_ERR;
return REDISMODULE_OK;
}
+15
View File
@@ -0,0 +1,15 @@
set testmodule [file normalize tests/modules/load.so]
if {$::flash_enabled} {
start_server [list tags [list "modules"] overrides [list storage-provider {flash ./rocks.db.master} databases 256 loadmodule $testmodule]] {
test "Module is notified of keys loaded from flash" {
r flushall
r set foo bar
r set bar foo
r set foobar barfoo
assert_equal [r load.count] 0
r debug reload
assert_equal [r load.count] 3
}
}
}