Compare commits

..
58 Commits
Author SHA1 Message Date
Ben Schermel 478ed26bde update deb changelog 2022-01-15 03:26:05 +00:00
MalavanEQAlpha dabb81960f Merge pull request #386 from EQ-Alpha/fix_rdb_hang
add readwrite lock for forking
2022-01-14 13:19:33 -08:00
christianEQ 35dc4df1df bump version 2022-01-13 00:07:03 +00:00
christianEQ e69f1023a9 Merge branch 'unstable' into RELEASE_6 2022-01-10 23:54:30 +00:00
John Sully b4528b797d Remove double free in keydb-benchmark 2021-12-23 14:20:41 -05:00
John Sully e070651039 Fix deadlock issue #183 cause by lock inversion 2021-12-23 13:17:14 -05:00
John Sully 7a32ec39fd Apple build break fix 2021-12-23 13:12:59 -05:00
John Sully 50dfee2039 Fix module test break 2021-12-23 13:12:34 -05:00
benschermel 5169f974b1 update deb changelog 2021-11-17 18:22:36 +00:00
VivekSainiEQ 2d25e62ece bump version 2021-11-15 23:45:46 +00:00
VivekSainiEQ b0b81c60d9 Merge branch 'unstable' into RELEASE_6 2021-11-15 18:08:01 +00:00
Vivek Saini 4b6bdcbcce Merge pull request #375 from VivekSainiEQ/time_thread_backport
Backport the time thread code from the enterprise version of KeyDB.

Also fix an issue with clustering where connections are potentially written to after being closed.
2021-11-15 12:57:15 -05:00
benschermel 244af9451c Merge pull request #377 from EQ-Alpha/systemd_rel
update release packaging
2021-11-13 13:16:48 -05:00
benschermel 639179471e update release packaging 2021-11-13 17:50:42 +00:00
benschermel 31c0e38d86 Merge pull request #345 from EQ-Alpha/pkg_update_tools
add keydb-diagnostic-tool to packaging
2021-11-10 22:16:09 -05:00
benschermel 8f5b94c75f Merge pull request #376 from EQ-Alpha/systemd_build
Systemd build
2021-11-10 22:14:24 -05:00
VivekSainiEQ b03eab2a06 Backported time thread code from enterprise 2021-11-05 00:30:34 +00:00
VivekSainiEQ 6a0a005ff2 Check that cluster connection is valid before setting write handler 2021-11-02 17:43:17 +00:00
benschermel 03a52dade1 update pkg conf file 2021-08-22 22:18:22 +00:00
benschermel 971725cc8c add keydb-diagnostic-tool to packaging 2021-08-16 23:21:07 +00:00
benschermel 45bb6e801f add keydb-diagnostic-tool to packaging 2021-08-16 22:10:31 +00:00
MalavanEQAlpha 1b73bc76c0 Merge pull request #344 from EQ-Alpha/fix_pbuild_ubuntu18.04
don't use flags that aren't supported by certain versions of OPENSSL
2021-08-13 23:59:50 -04:00
malavan 0bfd2ea8a6 don't use flags that aren't supported by certain versions of OPENSSL 2021-08-13 20:02:06 +00:00
malavan 11c6d585ca Merge branch 'RELEASE_6' of https://github.com/EQ-Alpha/KeyDB into RELEASE_6 2021-08-13 15:46:24 +00:00
malavan 2911145df7 Remove duplicate code 2021-08-13 15:46:09 +00:00
benschermel afd8ac5c38 update deb master changelog 2021-08-13 00:49:41 +00:00
benschermel 7260ebf5c1 bump version 2021-08-13 00:40:46 +00:00
malavan aae676dc7d bump version 2021-08-11 23:23:39 +00:00
malavan 16897d36b8 Merge remote-tracking branch 'origin/unstable' into RELEASE_6 2021-08-11 19:59:28 +00:00
malavan 4705235739 Merge branch 'unstable' into RELEASE_6 2021-08-09 18:17:11 +00:00
John Sully bf5b2d78d9 Fix issue #300 2021-03-30 23:30:17 +00:00
benschermel f660cd9357 bump version 2021-03-27 18:30:52 +00:00
benschermel 4272a7de24 bump version 2021-03-26 22:57:19 +00:00
benschermel f52815d309 update rpm script for centos8 2021-03-26 22:55:12 +00:00
John Sully 0b7b5332f2 Merge branch 'unstable' into RELEASE_6 2021-02-07 23:55:58 +00:00
John Sully d30fcfbe8a Bump version 2021-01-31 21:24:49 +00:00
John Sully 32b11ab809 Merge branch 'unstable' into RELEASE_6 2021-01-31 21:24:07 +00:00
Ben Schermel 63c47a09e9 update deb changelog 2020-09-28 23:42:54 +00:00
Ben Schermel 087af94c22 update deb changelog 2020-09-28 20:51:31 +00:00
John Sully bf3a3225fc Drop min-clients-per-thread to a more reasonable number 2020-09-28 19:19:47 +00:00
John Sully dab9296d7c bump version 6.0.16 2020-09-28 19:14:17 +00:00
John Sully a6f5a391a1 Merge branch 'unstable' into RELEASE_6 2020-09-28 18:55:24 +00:00
benschermel a6782a3c0d update deb changelog 2020-07-13 01:35:25 -04:00
John Sully f7d7baccc9 Bump version 2020-07-13 03:59:16 +00:00
John Sully eff294e374 Merge branch 'unstable' into RELEASE_6 2020-07-13 01:05:36 +00:00
John Sully f7c2006bf6 Merge branch 'unstable' into RELEASE_6 2020-07-13 01:04:49 +00:00
John Sully 42c4d85d72 Merge PKG files from unstable
No code change
2020-06-24 00:16:21 -04:00
John Sully 4a3e1f3b9b Bump version 2020-06-07 16:43:07 -04:00
John Sully 575fc98010 Solo tests should work with loopn 2020-06-07 16:43:07 -04:00
John Sully eb18345a61 Stream tests rely on deferring clients, mitigate races caused by them 2020-06-07 16:43:07 -04:00
John Sully d528c5c2c2 replication test race 2020-06-07 16:43:07 -04:00
John Sully fa03a539f3 fix race in test 2020-06-07 16:43:07 -04:00
John Sully 9646b1d944 Make lazyfree more reliable 2020-06-07 16:43:07 -04:00
John Sully cc5e906ebd Detect issues like #189 2020-06-07 16:43:07 -04:00
John Sully b0e14683b2 Ensure CRON jobs run in a clean environment 2020-06-07 16:43:07 -04:00
John Sully 8d5cc4cf0c Undecorated new is OK to use 2020-06-07 16:43:07 -04:00
John Sully b608acb349 RELEASE endurance tests should run offset from unstable 2020-06-01 17:02:16 -04:00
John Sully 8dbea2a050 Bump version 2020-06-01 17:01:50 -04:00
15 changed files with 394 additions and 147 deletions

No files matched your search

+1 -1
View File
@@ -2,7 +2,7 @@ name: Endurance
on:
schedule:
- cron: '30 * * * *'
- cron: '0 * * * *'
jobs:
+1
View File
@@ -5,3 +5,4 @@ src/keydb-check-rdb /usr/bin
src/keydb-cli /usr/bin
src/keydb-server /usr/bin
src/keydb-sentinel /usr/bin
src/keydb-diagnostic-tool /usr/bin
+1
View File
@@ -5,3 +5,4 @@ src/keydb-check-aof /usr/bin
src/keydb-check-rdb /usr/bin
src/keydb-cli /usr/bin
src/keydb-sentinel /usr/bin
src/keydb-diagnostic-tool /usr/bin
+69
View File
@@ -1,3 +1,72 @@
keydb (6:6.2.2-1distribution_placeholder) codename_placeholder; urgency=medium
* Acquire lock in module.cpp to fix module test break
* Fix usage of nanosleep() on Apple hardware
* Fix networking deadlock (#183)
* Remove redundant zfree() in keydb-benchmark
* Issues resolved: #378, #384
-- Ben Schermel <ben@eqalpha.com> Sat, 15 Jan 2022 20:00:37 +0000
keydb (6:6.2.1-1distribution_placeholder) codename_placeholder; urgency=medium
* This release of KeyDB is at parity with Redis 6.2.6. In addition, the following changes were included:
* Systemd support for release packages
* Fixes to buffer overflows in the `BITOP SHIFT` and `CRON` commands
* Server times are now computed on a seperate thread to improve performance
* Now enforces syslog identity and facility as soon as possible
* Removed erroneous use of LOG_... flags when using syslog
* Fixed erroneous `#endif` leading to build errors on some platforms
* Fixed the incorrect counting of client connections
* Issues resolved: #355, #370
-- Ben Schermel <ben@eqalpha.com> Wed, 17 Nov 2021 20:00:37 +0000
keydb (6:6.2.0-1distribution_placeholder) codename_placeholder; urgency=medium
* Removed unused command line options(-a, -d, -P, -r, -q, --csv, -l, -I, -e, --precision, --cluster, --enable-tracking)
* --ms option changed to --time, added --clients, --host, --port, and --threads options
* Added keydb-diagnostic-tool
* Improved test reliability
* Fixed memory leak in keydb-benchmark
* Fixed a number of race conditions
* Fixed OSX build issues
* Feature parity with Redis 6.2.3
* Fixed Issues: #323 #325 #276
-- Ben Schermel <ben@eqalpha.com> Thu, 12 Aug 2021 20:00:37 +0000
keydb (6:6.0.18-1distribution_placeholder) codename_placeholder; urgency=medium
* 6.0.18 - This release contains a number of improvements to stability and performance.
* Additional tests were added with improvements to existing test reliability
* Merged Redis 6.0.10 into KeyDB
* Issues resolved in this release: #214, #222, #238, #257, #273, #276, #285
-- Ben Schermel <ben@eqalpha.com> Fri, 26 Mar 2021 16:00:00 +0000
keydb (6:6.0.16-1distribution_placeholder) codename_placeholder; urgency=medium
* 6.0.16 - This release contains significant performance improvements as well as fixes to the following issues: #234, #236, #233, #207, #229, #231
* Reduced memory consumption when Active Replication is not used
* Reduced CPU consumption
* Improved TLS performance
* replica-quorum config for multi-master.Intended to be used with "replica-serve-stale-data no", if at least N replicas are connected we will consider our master link up and serve data.
* Subkey expires performance improvement for big sets
-- Ben Schermel <ben@eqalpha.com> Mon, 28 Sep 2020 16:00:00 +0000
keydb (6:6.0.13-1distribution_placeholder) codename_placeholder; urgency=medium
* 6.0.13 New features added: KEYDB.MEXISTS, REPLICAOF REMOVE, multi-master-no-forward configuration option
* KEYDB.MEXISTS - return exactly which keys exist in the database not just the count
* REPLICAOF REMOVE - Remove specific masters in a multi master setup
* multi-master-no-forward configuration option - Reduce network traffic in mesh topology multimaster setups
* Redis 6.0.5 feature parity
* Fixed Issues: 194 Bad directive or wrong number of arguments error on 'lazyfree-lazy-user-del no'; 209 Timeout using modules; 210 Databases are not merged on multi-master sync; 211 rpm packages now signed; 212 rpm file permissions updated
-- Ben Schermel <ben@eqalpha.com> Mon, 13 Jul 2020 8:00:00 +0000
keydb (6:6.0.9-1distribution_placeholder) codename_placeholder; urgency=medium
* 6.0.9 Addressing issues 187 - cpu lockup with subkey expire, 190 - missing sentinel binary in keydb-tools.
+7 -1
View File
@@ -25,7 +25,13 @@ mkdir -p $DIR/keydb_build/keydb_rpm/var/log/keydb
# move binaries to bin
rm $DIR/keydb_build/keydb_rpm/usr/bin/*
cp $DIR/../../src/keydb-* $DIR/keydb_build/keydb_rpm/usr/bin/
cp $DIR/../../src/keydb-server $DIR/keydb_build/keydb_rpm/usr/bin/
cp $DIR/../../src/keydb-sentinel $DIR/keydb_build/keydb_rpm/usr/bin/
cp $DIR/../../src/keydb-cli $DIR/keydb_build/keydb_rpm/usr/bin/
cp $DIR/../../src/keydb-benchmark $DIR/keydb_build/keydb_rpm/usr/bin/
cp $DIR/../../src/keydb-check-aof $DIR/keydb_build/keydb_rpm/usr/bin/
cp $DIR/../../src/keydb-check-rdb $DIR/keydb_build/keydb_rpm/usr/bin/
cp $DIR/../../src/keydb-diagnostic-tool $DIR/keydb_build/keydb_rpm/usr/bin/
# update spec file with build info
sed -i '2d' $DIR/keydb_build/keydb.spec
-1
View File
@@ -592,7 +592,6 @@ void handleClientsBlockedOnKeys(void) {
* lookup, invalidating the first one.
* See https://github.com/redis/redis/pull/6554. */
serverTL->fixed_time_expire++;
updateCachedTime(0);
/* Serve clients blocked on the key. */
robj *o = lookupKeyWrite(rl->db,rl->key);
+4 -1
View File
@@ -2447,7 +2447,10 @@ void clusterSendMessage(clusterLink *link, unsigned char *msg, size_t msglen) {
if (sdslen(link->sndbuf) == 0 && msglen != 0)
{
aePostFunction(g_pserver->rgthreadvar[IDX_EVENT_LOOP_MAIN].el, [link] {
connSetWriteHandlerWithBarrier(link->conn, clusterWriteHandler, 1);
/* The connection could be timed out before this posted function executes (thanks to TCP keepalive).
* So check that the connection is still there before setting the write handler, otherwise you segfault */
if (link->conn != nullptr)
connSetWriteHandlerWithBarrier(link->conn, clusterWriteHandler, 1);
});
}
+19 -53
View File
@@ -365,11 +365,7 @@ typedef struct RedisModuleCommandFilter {
static list *moduleCommandFilters;
/* Module GIL Variables */
static int s_cAcquisitionsServer = 0;
static int s_cAcquisitionsModule = 0;
static std::mutex s_mutex;
static std::condition_variable s_cv;
static std::recursive_mutex s_mutexModule;
static readWriteLock s_moduleGIL;
thread_local bool g_fModuleThread = false;
typedef void (*RedisModuleForkDoneHandler) (int exitcode, int bysignal, void *user_data);
@@ -4289,7 +4285,14 @@ RedisModuleCallReply *RM_Call(RedisModuleCtx *ctx, const char *cmdname, const ch
if (!(flags & REDISMODULE_ARGV_NO_REPLICAS))
call_flags |= CMD_CALL_PROPAGATE_REPL;
}
{
aeAcquireLock();
std::unique_lock<fastlock> ul(c->lock);
call(c,call_flags);
aeReleaseLock();
}
g_pserver->replication_allowed = prev_replication_allowed;
serverAssert((c->flags & CLIENT_BLOCKED) == 0);
@@ -5962,95 +5965,58 @@ void RM_ThreadSafeContextUnlock(RedisModuleCtx *ctx) {
// as the server thread acquisition is sufficient. If we did try to lock we would deadlock
static bool FModuleCallBackLock(bool fServerThread)
{
return !fServerThread && aeThreadOwnsLock() && !g_fModuleThread && s_cAcquisitionsServer > 0;
return !fServerThread && aeThreadOwnsLock() && !g_fModuleThread && s_moduleGIL.hasReader();
}
void moduleAcquireGIL(int fServerThread, int fExclusive) {
std::unique_lock<std::mutex> lock(s_mutex);
int *pcheck = fServerThread ? &s_cAcquisitionsModule : &s_cAcquisitionsServer;
if (FModuleCallBackLock(fServerThread)) {
return;
}
while (*pcheck > 0)
s_cv.wait(lock);
if (fServerThread)
{
++s_cAcquisitionsServer;
s_moduleGIL.acquireRead();
}
else
{
// only try to acquire the mutexModule in exclusive mode
if (fExclusive){
// It is possible that another module thread holds the GIL (and s_mutexModule as a result).
// When said thread goes to release the GIL, it will wait for s_mutex, which this thread owns.
// This thread is however waiting for the GIL (and s_mutexModule) that the other thread owns.
// As a result, a deadlock has occured.
// We release the lock on s_mutex and wait until we are able to safely acquire the GIL
// in order to prevent this deadlock from occuring.
while (!s_mutexModule.try_lock())
s_cv.wait(lock);
}
++s_cAcquisitionsModule;
fModuleGILWlocked++;
s_moduleGIL.acquireWrite(fExclusive);
}
}
int moduleTryAcquireGIL(bool fServerThread, int fExclusive) {
std::unique_lock<std::mutex> lock(s_mutex, std::defer_lock);
if (!lock.try_lock())
return 1;
int *pcheck = fServerThread ? &s_cAcquisitionsModule : &s_cAcquisitionsServer;
if (FModuleCallBackLock(fServerThread)) {
return 0;
}
if (*pcheck > 0)
return 1;
if (fServerThread)
{
++s_cAcquisitionsServer;
if (!s_moduleGIL.tryAcquireRead())
return 1;
}
else
{
// only try to acquire the mutexModule in exclusive mode
if (fExclusive){
if (!s_mutexModule.try_lock())
return 1;
}
++s_cAcquisitionsModule;
fModuleGILWlocked++;
if (!s_moduleGIL.tryAcquireWrite(fExclusive))
return 1;
}
return 0;
}
void moduleReleaseGIL(int fServerThread, int fExclusive) {
std::unique_lock<std::mutex> lock(s_mutex);
if (FModuleCallBackLock(fServerThread)) {
return;
}
if (fServerThread)
{
--s_cAcquisitionsServer;
s_moduleGIL.releaseRead();
}
else
{
// only try to release the mutexModule in exclusive mode
if (fExclusive)
s_mutexModule.unlock();
--s_cAcquisitionsModule;
fModuleGILWlocked--;
s_moduleGIL.releaseWrite(fExclusive);
}
s_cv.notify_all();
}
int moduleGILAcquiredByModule(void) {
return fModuleGILWlocked > 0;
return s_moduleGIL.hasWriter();
}
+27 -72
View File
@@ -1942,7 +1942,9 @@ void ProcessPendingAsyncWrites()
bool fResult = c->postFunction([](client *c) {
c->fPendingAsyncWriteHandler = false;
clientInstallWriteHandler(c);
c->lock.unlock();
handleClientsWithPendingWrites(c->iel, g_pserver->aof_state);
c->lock.lock();
}, false);
if (!fResult)
@@ -2461,6 +2463,8 @@ void processInputBuffer(client *c, int callFlags) {
if (c->flags & CLIENT_EXECUTING_COMMAND) break;
if (c->flags & CLIENT_EXECUTING_COMMAND) break;
/* Don't process input from the master while there is a busy script
* condition on the replica. We want just to accumulate the replication
* stream (instead of replying -BUSY like we do with other clients) and
@@ -3859,86 +3863,37 @@ int checkClientPauseTimeoutAndReturnIfPaused(void) {
*
* The function returns the total number of events processed. */
void processEventsWhileBlocked(int iel) {
serverAssert(GlobalLocksAcquired());
int iterations = 4; /* See the function top-comment. */
std::vector<client*> vecclients;
listIter li;
listNode *ln;
listRewind(g_pserver->clients, &li);
/* Update our cached time since it is used to create and update the last
* interaction time with clients and for other important things. */
updateCachedTime(0);
// All client locks must be acquired *after* the global lock is reacquired to prevent deadlocks
// so unlock here, and save them for reacquisition later
while ((ln = listNext(&li)) != nullptr)
{
client *c = (client*)listNodeValue(ln);
if (c->lock.fOwnLock()) {
serverAssert(c->flags & CLIENT_PROTECTED); // If the client is not protected we have no gurantee they won't be free'd in the event loop
c->lock.unlock();
vecclients.push_back(c);
int eventsCount = 0;
executeWithoutGlobalLock([&](){
int iterations = 4; /* See the function top-comment. */
try
{
ProcessingEventsWhileBlocked = 1;
while (iterations--) {
long long startval = g_pserver->events_processed_while_blocked;
long long ae_events = aeProcessEvents(g_pserver->rgthreadvar[iel].el,
AE_FILE_EVENTS|AE_DONT_WAIT|
AE_CALL_BEFORE_SLEEP|AE_CALL_AFTER_SLEEP);
/* Note that g_pserver->events_processed_while_blocked will also get
* incremeted by callbacks called by the event loop handlers. */
eventsCount += ae_events;
long long events = eventsCount - startval;
if (!events) break;
}
ProcessingEventsWhileBlocked = 0;
}
}
/* Since we're about to release our lock we need to flush the repl backlog queue */
bool fReplBacklog = g_pserver->repl_batch_offStart >= 0;
if (fReplBacklog) {
flushReplBacklogToClients();
g_pserver->repl_batch_idxStart = -1;
g_pserver->repl_batch_offStart = -1;
}
long long eventsCount = 0;
aeReleaseLock();
serverAssert(!GlobalLocksAcquired());
try
{
ProcessingEventsWhileBlocked = 1;
while (iterations--) {
long long startval = g_pserver->events_processed_while_blocked;
long long ae_events = aeProcessEvents(g_pserver->rgthreadvar[iel].el,
AE_FILE_EVENTS|AE_DONT_WAIT|
AE_CALL_BEFORE_SLEEP|AE_CALL_AFTER_SLEEP);
/* Note that g_pserver->events_processed_while_blocked will also get
* incremeted by callbacks called by the event loop handlers. */
eventsCount += ae_events;
long long events = eventsCount - startval;
if (!events) break;
catch (...)
{
ProcessingEventsWhileBlocked = 0;
throw;
}
ProcessingEventsWhileBlocked = 0;
}
catch (...)
{
// Caller expects us to be locked so fix and rethrow
ProcessingEventsWhileBlocked = 0;
AeLocker locker;
locker.arm(nullptr);
locker.release();
for (client *c : vecclients)
c->lock.lock();
throw;
}
AeLocker locker;
locker.arm(nullptr);
locker.release();
});
g_pserver->events_processed_while_blocked += eventsCount;
whileBlockedCron();
// Restore it so the calling code is not confused
if (fReplBacklog) {
g_pserver->repl_batch_idxStart = g_pserver->repl_backlog_idx;
g_pserver->repl_batch_offStart = g_pserver->master_repl_offset;
}
for (client *c : vecclients)
c->lock.lock();
// If a different thread processed the shutdown we need to abort the lua command or we will hang
if (serverTL->el->stop)
throw ShutdownException();
+113
View File
@@ -0,0 +1,113 @@
#pragma once
#include <condition_variable>
class readWriteLock {
std::mutex m_readLock;
std::recursive_mutex m_writeLock;
std::condition_variable m_cv;
int m_readCount = 0;
int m_writeCount = 0;
bool m_writeWaiting = false;
public:
void acquireRead() {
std::unique_lock<std::mutex> rm(m_readLock);
while (m_writeCount > 0 || m_writeWaiting)
m_cv.wait(rm);
m_readCount++;
}
bool tryAcquireRead() {
std::unique_lock<std::mutex> rm(m_readLock, std::defer_lock);
if (!rm.try_lock())
return false;
if (m_writeCount > 0 || m_writeWaiting)
return false;
m_readCount++;
return true;
}
void acquireWrite(bool exclusive = true) {
std::unique_lock<std::mutex> rm(m_readLock);
m_writeWaiting = true;
while (m_readCount > 0)
m_cv.wait(rm);
if (exclusive) {
/* Another thread might have the write lock while we have the read lock
but won't be able to release it until they can acquire the read lock
so release the read lock and try again instead of waiting to avoid deadlock */
while(!m_writeLock.try_lock())
m_cv.wait(rm);
}
m_writeCount++;
m_writeWaiting = false;
}
void upgradeWrite(bool exclusive = true) {
std::unique_lock<std::mutex> rm(m_readLock);
m_writeWaiting = true;
while (m_readCount > 1)
m_cv.wait(rm);
if (exclusive) {
/* Another thread might have the write lock while we have the read lock
but won't be able to release it until they can acquire the read lock
so release the read lock and try again instead of waiting to avoid deadlock */
while(!m_writeLock.try_lock())
m_cv.wait(rm);
}
m_writeCount++;
m_readCount--;
m_writeWaiting = false;
}
bool tryAcquireWrite(bool exclusive = true) {
std::unique_lock<std::mutex> rm(m_readLock, std::defer_lock);
if (!rm.try_lock())
return false;
if (m_readCount > 0)
return false;
if (exclusive)
if (!m_writeLock.try_lock())
return false;
m_writeCount++;
return true;
}
void releaseRead() {
std::unique_lock<std::mutex> rm(m_readLock);
serverAssert(m_readCount > 0);
m_readCount--;
m_cv.notify_all();
}
void releaseWrite(bool exclusive = true) {
std::unique_lock<std::mutex> rm(m_readLock);
serverAssert(m_writeCount > 0);
if (exclusive)
m_writeLock.unlock();
m_writeCount--;
m_cv.notify_all();
}
void downgradeWrite(bool exclusive = true) {
std::unique_lock<std::mutex> rm(m_readLock);
serverAssert(m_writeCount > 0);
if (exclusive)
m_writeLock.unlock();
m_writeCount--;
while (m_writeCount > 0 || m_writeWaiting)
m_cv.wait(rm);
m_readCount++;
}
bool hasReader() {
return m_readCount > 0;
}
bool hasWriter() {
return m_writeCount > 0;
}
bool writeWaiting() {
return m_writeWaiting;
}
};
-1
View File
@@ -1984,7 +1984,6 @@ int main(int argc, const char **argv) {
} while(config.loop);
zfree(data);
zfree(data);
if (config.redis_config != NULL) freeRedisConfig(config.redis_config);
return 0;
+127 -14
View File
@@ -63,9 +63,11 @@
#include <algorithm>
#include <uuid/uuid.h>
#include <mutex>
#include <condition_variable>
#include "aelocker.h"
#include "motd.h"
#include "t_nhash.h"
#include "readwritelock.h"
#ifdef __linux__
#include <sys/prctl.h>
#include <sys/mman.h>
@@ -90,10 +92,16 @@ double R_Zero, R_PosInf, R_NegInf, R_Nan;
/* Global vars */
namespace GlobalHidden {
struct redisServer server; /* Server global state */
readWriteLock forkLock;
}
redisServer *g_pserver = &GlobalHidden::server;
readWriteLock *g_forkLock = &GlobalHidden::forkLock;
struct redisServerConst cserver;
__thread struct redisServerThreadVars *serverTL = NULL; // thread local server vars
std::mutex time_thread_mutex;
std::condition_variable time_thread_cv;
int sleeping_threads = 0;
void wakeTimeThread();
/* Our command table.
*
@@ -2007,7 +2015,7 @@ void databasesCron(bool fMainThread) {
* info or not using the 'update_daylight_info' argument. Normally we update
* such info only when calling this function from serverCron() but not when
* calling it from call(). */
void updateCachedTime(int update_daylight_info) {
void updateCachedTime() {
long long t = ustime();
__atomic_store(&g_pserver->ustime, &t, __ATOMIC_RELAXED);
t /= 1000;
@@ -2020,12 +2028,10 @@ void updateCachedTime(int update_daylight_info) {
* context is safe since we will never fork() while here, in the main
* thread. The logging function will call a thread safe version of
* localtime that has no locks. */
if (update_daylight_info) {
struct tm tm;
time_t ut = g_pserver->unixtime;
localtime_r(&ut,&tm);
__atomic_store(&g_pserver->daylight_active, &tm.tm_isdst, __ATOMIC_RELAXED);
}
struct tm tm;
time_t ut = g_pserver->unixtime;
localtime_r(&ut,&tm);
__atomic_store(&g_pserver->daylight_active, &tm.tm_isdst, __ATOMIC_RELAXED);
}
void checkChildrenDone(void) {
@@ -2152,9 +2158,6 @@ int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) {
* handler if we don't return here fast enough. */
if (g_pserver->watchdog_period) watchdogScheduleSignal(g_pserver->watchdog_period);
/* Update the time cache. */
updateCachedTime(1);
g_pserver->hz = g_pserver->config_hz;
/* Adapt the g_pserver->hz value to the number of configured clients. If we have
* many clients, we want to call serverCron() with an higher frequency. */
@@ -2412,7 +2415,6 @@ int serverCronLite(struct aeEventLoop *eventLoop, long long id, void *clientData
void blockingOperationStarts() {
if(!g_pserver->blocking_op_nesting++){
updateCachedTime(0);
g_pserver->blocked_last_cron = g_pserver->mstime;
}
}
@@ -2623,6 +2625,14 @@ void beforeSleep(struct aeEventLoop *eventLoop) {
locker.disarm();
if (!fSentReplies)
handleClientsWithPendingWrites(iel, aof_state);
// Scope lock_guard
{
std::lock_guard<std::mutex> lock(time_thread_mutex);
sleeping_threads++;
serverAssert(sleeping_threads <= cserver.cthreads);
}
/* Determine whether the modules are enabled before sleeping, and use that result
both here, and after wakeup to avoid double acquire or release of the GIL */
serverTL->modulesEnabledThisAeLoop = !!moduleCount();
@@ -2643,6 +2653,7 @@ void afterSleep(struct aeEventLoop *eventLoop) {
Otherwise you may double acquire the GIL and cause deadlocks in the module */
if (!ProcessingEventsWhileBlocked) {
if (serverTL->modulesEnabledThisAeLoop) moduleAcquireGIL(TRUE /*fServerThread*/);
wakeTimeThread();
}
}
@@ -2839,7 +2850,7 @@ void initMasterInfo(redisMaster *master)
void initServerConfig(void) {
int j;
updateCachedTime(true);
updateCachedTime();
getRandomHexChars(g_pserver->runid,CONFIG_RUN_ID_SIZE);
g_pserver->runid[CONFIG_RUN_ID_SIZE] = '\0';
changeReplicationId();
@@ -4006,7 +4017,6 @@ void call(client *c, int flags) {
prev_err_count = g_pserver->stat_total_error_replies;
g_pserver->fixed_time_expire++;
updateCachedTime(0);
incrementMvccTstamp();
elapsedStart(&call_timer);
try {
@@ -6211,6 +6221,63 @@ void closeChildUnusedResourceAfterFork() {
cserver.pidfile = NULL;
}
void executeWithoutGlobalLock(std::function<void()> func) {
serverAssert(GlobalLocksAcquired());
std::vector<client*> vecclients;
listIter li;
listNode *ln;
listRewind(g_pserver->clients, &li);
// All client locks must be acquired *after* the global lock is reacquired to prevent deadlocks
// so unlock here, and save them for reacquisition later
while ((ln = listNext(&li)) != nullptr)
{
client *c = (client*)listNodeValue(ln);
if (c->lock.fOwnLock()) {
serverAssert(c->flags & CLIENT_PROTECTED || c->flags & CLIENT_EXECUTING_COMMAND); // If the client is not protected we have no gurantee they won't be free'd in the event loop
c->lock.unlock();
vecclients.push_back(c);
}
}
/* Since we're about to release our lock we need to flush the repl backlog queue */
bool fReplBacklog = g_pserver->repl_batch_offStart >= 0;
if (fReplBacklog) {
flushReplBacklogToClients();
g_pserver->repl_batch_idxStart = -1;
g_pserver->repl_batch_offStart = -1;
}
aeReleaseLock();
serverAssert(!GlobalLocksAcquired());
try {
func();
}
catch (...) {
// Caller expects us to be locked so fix and rethrow
AeLocker locker;
locker.arm(nullptr);
locker.release();
for (client *c : vecclients)
c->lock.lock();
throw;
}
AeLocker locker;
locker.arm(nullptr);
locker.release();
// Restore it so the calling code is not confused
if (fReplBacklog) {
g_pserver->repl_batch_idxStart = g_pserver->repl_backlog_idx;
g_pserver->repl_batch_offStart = g_pserver->master_repl_offset;
}
for (client *c : vecclients)
c->lock.lock();
}
/* purpose is one of CHILD_TYPE_ types */
int redisFork(int purpose) {
int childpid;
@@ -6222,7 +6289,9 @@ int redisFork(int purpose) {
openChildInfoPipe();
}
long long startWriteLock = ustime();
g_forkLock->acquireWrite();
latencyAddSampleIfNeeded("fork-lock",(ustime()-startWriteLock)/1000);
if ((childpid = fork()) == 0) {
/* Child */
g_pserver->in_fork_child = purpose;
@@ -6231,6 +6300,7 @@ int redisFork(int purpose) {
closeChildUnusedResourceAfterFork();
} else {
/* Parent */
g_forkLock->releaseWrite();
g_pserver->stat_total_forks++;
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. */
@@ -6534,6 +6604,47 @@ void OnTerminate()
serverPanic("std::teminate() called");
}
void wakeTimeThread() {
updateCachedTime();
std::lock_guard<std::mutex> lock(time_thread_mutex);
if (sleeping_threads >= cserver.cthreads)
time_thread_cv.notify_one();
sleeping_threads--;
serverAssert(sleeping_threads >= 0);
}
void *timeThreadMain(void*) {
timespec delay;
delay.tv_sec = 0;
delay.tv_nsec = 100;
int cycle_count = 0;
g_forkLock->acquireRead();
while (true) {
{
std::unique_lock<std::mutex> lock(time_thread_mutex);
if (sleeping_threads >= cserver.cthreads) {
g_forkLock->releaseRead();
time_thread_cv.wait(lock);
g_forkLock->acquireRead();
cycle_count = 0;
}
}
updateCachedTime();
if (cycle_count == MAX_CYCLES_TO_HOLD_FORK_LOCK) {
g_forkLock->releaseRead();
g_forkLock->acquireRead();
cycle_count = 0;
}
#if defined(__APPLE__)
nanosleep(&delay, nullptr);
#else
clock_nanosleep(CLOCK_MONOTONIC, 0, &delay, NULL);
#endif
cycle_count++;
}
g_forkLock->releaseRead();
}
void *workerThreadMain(void *parg)
{
int iel = (int)((int64_t)parg);
@@ -6930,6 +7041,8 @@ int main(int argc, char **argv) {
setOOMScoreAdj(-1);
serverAssert(cserver.cthreads > 0 && cserver.cthreads <= MAX_EVENT_LOOPS);
pthread_create(&cserver.time_thread_id, nullptr, timeThreadMain, nullptr);
pthread_attr_t tattr;
pthread_attr_init(&tattr);
pthread_attr_setstacksize(&tattr, 1 << 23); // 8 MB
+8 -1
View File
@@ -96,6 +96,7 @@ typedef long long ustime_t; /* microsecond time type. */
#include "connection.h" /* Connection abstraction */
#include "serverassert.h"
#include "expire.h"
#include "readwritelock.h"
#define REDISMODULE_CORE 1
#include "redismodule.h" /* Redis modules API defines. */
@@ -733,6 +734,9 @@ typedef enum {
#define REDISMODULE_AUX_BEFORE_RDB (1<<0)
#define REDISMODULE_AUX_AFTER_RDB (1<<1)
/* Number of cycles before time thread gives up fork lock */
#define MAX_CYCLES_TO_HOLD_FORK_LOCK 10
struct RedisModule;
struct RedisModuleIO;
struct RedisModuleDigest;
@@ -1536,6 +1540,7 @@ struct redisServerConst {
pid_t pid; /* Main process pid. */
time_t stat_starttime; /* Server start time */
pthread_t main_thread_id; /* Main thread id */
pthread_t time_thread_id;
char *configfile; /* Absolute config file path, or NULL */
char *executable; /* Absolute executable file path. */
char **exec_argv; /* Executable argv vector (copy). */
@@ -2112,6 +2117,7 @@ typedef struct {
//extern struct redisServer server;
extern redisServer *g_pserver;
extern readWriteLock *g_forkLock;
extern struct redisServerConst cserver;
extern __thread struct redisServerThreadVars *serverTL; // thread local server vars
extern struct sharedObjectsStruct shared;
@@ -2495,6 +2501,7 @@ void sendChildInfo(childInfoType info_type, size_t keys, const char *pname);
void receiveChildInfo(void);
/* Fork helpers */
void executeWithoutGlobalLock(std::function<void()> func);
int redisFork(int type);
int hasActiveChildProcess();
void resetChildState();
@@ -2637,7 +2644,7 @@ void resetErrorTableStats(void);
void adjustOpenFilesLimit(void);
void incrementErrorCount(const char *fullerr, size_t namelen);
void closeListeningSockets(int unlink_unix_socket);
void updateCachedTime(int update_daylight_info);
void updateCachedTime(void);
void resetServerStats(void);
void activeDefragCycle(void);
unsigned int getLRUClock(void);
+2 -2
View File
@@ -1,4 +1,4 @@
#define KEYDB_REAL_VERSION "255.255.255"
#define KEYDB_VERSION_NUM 0x00ffffff
#define KEYDB_REAL_VERSION "6.2.2"
#define KEYDB_VERSION_NUM 0x00060202
extern const char *KEYDB_SET_VERSION; // Unlike real version, this can be overriden by the config
+15
View File
@@ -485,6 +485,21 @@ start_server {tags {"scripting"}} {
}
test {EVAL with pipelined command (No crash)} {
r flushall
r config set lua-time-limit 1
set rd [redis_deferring_client]
$rd eval {for i=1,1000000 do redis.call('set', i, 'sdfdsfd') end} 0
$rd set testkey foo
$rd get testkey
after 1200
catch {r echo "foo"} err
assert_match {BUSY*} $err
$rd read
$rd close
}
test {EVAL timeout from AOF} {
# generate a long running script that is propagated to the AOF as script
# make sure that the script times out during loading