Compare commits
19
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3e24768e21 | ||
|
|
2c38667ecf | ||
|
|
38310ec9f6 | ||
|
|
ea4941a3e7 | ||
|
|
5dbf1f6bd6 | ||
|
|
ba1275f653 | ||
|
|
20766a02b8 | ||
|
|
a9d3490f9c | ||
|
|
8405c99017 | ||
|
|
027ad50581 | ||
|
|
e38537202e | ||
|
|
8d8aabb978 | ||
|
|
418356c03a | ||
|
|
3caf6060c4 | ||
|
|
9e138815a1 | ||
|
|
51d3e2cbbc | ||
|
|
242d6aabb2 | ||
|
|
e2e1b4d5d5 | ||
|
|
03287d69be |
No files matched your search
+19
-12
@@ -5,17 +5,14 @@ on: [push, pull_request]
|
||||
jobs:
|
||||
|
||||
test-ubuntu-latest:
|
||||
runs-on: self-hosted
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
with:
|
||||
submodules: true
|
||||
- uses: actions/checkout@v2
|
||||
- name: make
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get -y remove libzstd || true
|
||||
sudo apt-get -y install uuid-dev libcurl4-openssl-dev libbz2-dev zlib1g-dev libsnappy-dev liblz4-dev libzstd-dev libgflags-dev
|
||||
make BUILD_TLS=yes -j2 KEYDB_CFLAGS='-Werror' KEYDB_CXXFLAGS='-Werror'
|
||||
sudo apt-get -y install uuid-dev libcurl4-openssl-dev
|
||||
make KEYDB_CFLAGS='-Werror' KEYDB_CXXFLAGS='-Werror' BUILD_TLS=yes -j2
|
||||
- name: gen-cert
|
||||
run: ./utils/gen-test-certs.sh
|
||||
- name: test-tls
|
||||
@@ -31,9 +28,6 @@ jobs:
|
||||
- name: module tests
|
||||
run: |
|
||||
./runtest-moduleapi
|
||||
- name: rotation test
|
||||
run: |
|
||||
./runtest-rotation
|
||||
|
||||
build-ubuntu-old:
|
||||
runs-on: ubuntu-18.04
|
||||
@@ -44,7 +38,6 @@ jobs:
|
||||
sudo apt-get update
|
||||
sudo apt-get -y install uuid-dev libcurl4-openssl-dev
|
||||
make -j2
|
||||
|
||||
build-macos-latest:
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
@@ -56,4 +49,18 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: make
|
||||
- name: make
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get -y install uuid-dev libcurl4-openssl-dev
|
||||
make KEYDB_CFLAGS='-Werror' KEYDB_CXXFLAGS='-Werror' MALLOC=libc -j2
|
||||
build-ubuntu-32bit:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: make
|
||||
run: |
|
||||
sudo dpkg --add-architecture i386
|
||||
sudo apt-get update
|
||||
sudo apt-get -y install gcc-multilib g++-multilib libc6-dev-i386 lib32z1 uuid-dev:i386 libcurl4-openssl-dev:i386
|
||||
make KEYDB_CFLAGS='-Werror' KEYDB_CXXFLAGS='-Werror' 32bit -j2
|
||||
@@ -0,0 +1,13 @@
|
||||
Copyright (c) 2006-2020, Salvatore Sanfilippo
|
||||
Copyright (C) 2019-2021, John Sully
|
||||
Copyright (C) 2020-2021, EQ Alpha Technology Ltd.
|
||||
Copyright (C) 2022 Snap Inc.
|
||||
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.
|
||||
@@ -99,7 +99,7 @@ Avoid forwarding RREPLAY messages to other masters? WARNING: This setting is dan
|
||||
|
||||
|
||||
```
|
||||
scratch-file-path /path
|
||||
db-s3-object /path/to/bucket
|
||||
```
|
||||
If you would like KeyDB to dump and load directly to AWS S3 this option specifies the bucket. Using this option with the traditional RDB options will result in KeyDB backing up twice to both locations. If both are specified KeyDB will first attempt to load from the local dump file and if that fails load from S3. This requires the AWS CLI tools to be installed and configured which are used under the hood to transfer the data.
|
||||
|
||||
|
||||
@@ -1,3 +1,17 @@
|
||||
keydb (6:6.3.0-1distribution_placeholder) codename_placeholder; urgency=medium
|
||||
|
||||
* This release open sources KeyDB Enterprise features into the open source project along with PSYNC for active replication
|
||||
* Partial synchronization for active replication is introduced
|
||||
* MVCC introduced into codebase from KeyDB Enterprise
|
||||
* Async commands added: GET, MGET. These will see perf improvements
|
||||
* KEYS and SCAN commands will no longer be blocking calls
|
||||
* Async Rehash implemented for additional stability to perf
|
||||
* IStorage interface added
|
||||
* In-process background saving (forkless) to comply with maxmemory setting
|
||||
* See v6.3.0 tagged release notes on github for a detailed explanation of these changes
|
||||
|
||||
-- Ben Schermel <ben@eqalpha.com> Wed, 11 May 2022 20:00:37 +0000
|
||||
|
||||
keydb (6:6.2.2-1distribution_placeholder) codename_placeholder; urgency=medium
|
||||
|
||||
* Acquire lock in module.cpp to fix module test break
|
||||
|
||||
+1
-1
@@ -2782,7 +2782,7 @@ standardConfig configs[] = {
|
||||
createBoolConfig("disable-thp", NULL, MODIFIABLE_CONFIG, g_pserver->disable_thp, 1, NULL, NULL),
|
||||
createBoolConfig("cluster-allow-replica-migration", NULL, MODIFIABLE_CONFIG, g_pserver->cluster_allow_replica_migration, 1, NULL, NULL),
|
||||
createBoolConfig("replica-announced", NULL, MODIFIABLE_CONFIG, g_pserver->replica_announced, 1, NULL, NULL),
|
||||
createBoolConfig("enable-async-commands", NULL, MODIFIABLE_CONFIG, g_pserver->enable_async_commands, 1, NULL, NULL),
|
||||
createBoolConfig("enable-async-commands", NULL, MODIFIABLE_CONFIG, g_pserver->enable_async_commands, 0, NULL, NULL),
|
||||
createBoolConfig("multithread-load-enabled", NULL, MODIFIABLE_CONFIG, g_pserver->multithread_load_enabled, 0, NULL, NULL),
|
||||
createBoolConfig("active-client-balancing", NULL, MODIFIABLE_CONFIG, g_pserver->active_client_balancing, 1, NULL, NULL),
|
||||
|
||||
|
||||
+8
-2
@@ -2669,6 +2669,11 @@ void redisDbPersistentData::prepOverwriteForSnapshot(char *key)
|
||||
auto itr = m_pdbSnapshot->find_cached_threadsafe(key);
|
||||
if (itr.key() != nullptr)
|
||||
{
|
||||
if (itr.val()->FExpires()) {
|
||||
// Note: I'm sure we could handle this, but its too risky at the moment.
|
||||
// There are known bugs doing this with expires
|
||||
return;
|
||||
}
|
||||
sds keyNew = sdsdupshared(itr.key());
|
||||
if (dictAdd(m_pdictTombstone, keyNew, (void*)dictHashKey(m_pdict, key)) != DICT_OK)
|
||||
sdsfree(keyNew);
|
||||
@@ -3263,10 +3268,11 @@ bool redisDbPersistentData::prefetchKeysAsync(client *c, parsed_command &command
|
||||
dictEntry **table;
|
||||
__atomic_load(&c->db->m_pdict->ht[iht].table, &table, __ATOMIC_RELAXED);
|
||||
if (table != nullptr) {
|
||||
dictEntry *de = table[hT];
|
||||
dictEntry *de;
|
||||
__atomic_load(&table[hT], &de, __ATOMIC_ACQUIRE);
|
||||
while (de != nullptr) {
|
||||
_mm_prefetch(dictGetKey(de), _MM_HINT_T2);
|
||||
de = de->next;
|
||||
__atomic_load(&de->next, &de, __ATOMIC_ACQUIRE);
|
||||
}
|
||||
}
|
||||
if (!dictIsRehashing(c->db->m_pdict))
|
||||
|
||||
+29
-3
@@ -128,6 +128,7 @@ int _dictInit(dict *d, dictType *type,
|
||||
d->pauserehash = 0;
|
||||
d->asyncdata = nullptr;
|
||||
d->refcount = 1;
|
||||
d->noshrink = false;
|
||||
return DICT_OK;
|
||||
}
|
||||
|
||||
@@ -204,7 +205,7 @@ int dictMerge(dict *dst, dict *src)
|
||||
|
||||
if (dictSize(dst) == 0)
|
||||
{
|
||||
std::swap(*dst, *src);
|
||||
dict::swap(*dst, *src);
|
||||
std::swap(dst->pauserehash, src->pauserehash);
|
||||
return DICT_OK;
|
||||
}
|
||||
@@ -212,7 +213,7 @@ int dictMerge(dict *dst, dict *src)
|
||||
size_t expectedSize = dictSize(src) + dictSize(dst);
|
||||
if (dictSize(src) > dictSize(dst) && src->asyncdata == nullptr && dst->asyncdata == nullptr)
|
||||
{
|
||||
std::swap(*dst, *src);
|
||||
dict::swap(*dst, *src);
|
||||
std::swap(dst->pauserehash, src->pauserehash);
|
||||
}
|
||||
|
||||
@@ -402,7 +403,7 @@ int dictRehash(dict *d, int n) {
|
||||
|
||||
dictAsyncRehashCtl::dictAsyncRehashCtl(struct dict *d, dictAsyncRehashCtl *next) : dict(d), next(next) {
|
||||
queue.reserve(c_targetQueueSize);
|
||||
__atomic_fetch_add(&d->refcount, 1, __ATOMIC_RELEASE);
|
||||
__atomic_fetch_add(&d->refcount, 1, __ATOMIC_ACQ_REL);
|
||||
this->rehashIdxBase = d->rehashidx;
|
||||
}
|
||||
|
||||
@@ -446,6 +447,9 @@ LDone:
|
||||
}
|
||||
|
||||
void dictRehashAsync(dictAsyncRehashCtl *ctl) {
|
||||
if (ctl->abondon.load(std::memory_order_acquire)) {
|
||||
ctl->hashIdx = ctl->queue.size();
|
||||
}
|
||||
for (size_t idx = ctl->hashIdx; idx < ctl->queue.size(); ++idx) {
|
||||
auto &wi = ctl->queue[idx];
|
||||
wi.hash = dictHashKey(ctl->dict, dictGetKey(wi.de));
|
||||
@@ -455,6 +459,9 @@ void dictRehashAsync(dictAsyncRehashCtl *ctl) {
|
||||
}
|
||||
|
||||
bool dictRehashSomeAsync(dictAsyncRehashCtl *ctl, size_t hashes) {
|
||||
if (ctl->abondon.load(std::memory_order_acquire)) {
|
||||
ctl->hashIdx = ctl->queue.size();
|
||||
}
|
||||
size_t max = std::min(ctl->hashIdx + hashes, ctl->queue.size());
|
||||
for (; ctl->hashIdx < max; ++ctl->hashIdx) {
|
||||
auto &wi = ctl->queue[ctl->hashIdx];
|
||||
@@ -465,6 +472,23 @@ bool dictRehashSomeAsync(dictAsyncRehashCtl *ctl, size_t hashes) {
|
||||
return ctl->hashIdx < ctl->queue.size();
|
||||
}
|
||||
|
||||
|
||||
void discontinueAsyncRehash(dict *d) {
|
||||
// We inform our async rehashers and the completion function the results are to be
|
||||
// abandoned. We keep the asyncdata linked in so that dictEntry's are still added
|
||||
// to the GC list. This is because we can't gurantee when the other threads will
|
||||
// stop looking at them.
|
||||
if (d->asyncdata != nullptr) {
|
||||
auto adata = d->asyncdata;
|
||||
while (adata != nullptr && !adata->abondon.load(std::memory_order_relaxed)) {
|
||||
adata->abondon = true;
|
||||
adata = adata->next;
|
||||
}
|
||||
if (dictIsRehashing(d))
|
||||
d->rehashidx = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void dictCompleteRehashAsync(dictAsyncRehashCtl *ctl, bool fFree) {
|
||||
dict *d = ctl->dict;
|
||||
assert(ctl->done);
|
||||
@@ -786,6 +810,8 @@ int _dictClear(dict *d, dictht *ht, void(callback)(void *)) {
|
||||
if (callback && (i & 65535) == 0) callback(d->privdata);
|
||||
|
||||
if ((he = ht->table[i]) == NULL) continue;
|
||||
dictEntry *deNull = nullptr;
|
||||
__atomic_store(&ht->table[i], &deNull, __ATOMIC_RELEASE);
|
||||
while(he) {
|
||||
nextHe = he->next;
|
||||
if (d->asyncdata && (ssize_t)i < d->rehashidx) {
|
||||
|
||||
+22
@@ -110,12 +110,16 @@ struct dictAsyncRehashCtl {
|
||||
std::atomic<bool> abondon { false };
|
||||
|
||||
dictAsyncRehashCtl(struct dict *d, dictAsyncRehashCtl *next);
|
||||
dictAsyncRehashCtl(const dictAsyncRehashCtl&) = delete;
|
||||
dictAsyncRehashCtl(dictAsyncRehashCtl&&) = delete;
|
||||
~dictAsyncRehashCtl();
|
||||
};
|
||||
#else
|
||||
struct dictAsyncRehashCtl;
|
||||
#endif
|
||||
|
||||
void discontinueAsyncRehash(dict *d);
|
||||
|
||||
typedef struct dict {
|
||||
dictType *type;
|
||||
void *privdata;
|
||||
@@ -125,6 +129,24 @@ typedef struct dict {
|
||||
dictAsyncRehashCtl *asyncdata;
|
||||
int16_t pauserehash; /* If >0 rehashing is paused (<0 indicates coding error) */
|
||||
uint8_t noshrink = false;
|
||||
|
||||
#ifdef __cplusplus
|
||||
dict() = default;
|
||||
dict(dict &) = delete; // No Copy Ctor
|
||||
|
||||
static void swap(dict& a, dict& b) {
|
||||
discontinueAsyncRehash(&a);
|
||||
discontinueAsyncRehash(&b);
|
||||
std::swap(a.type, b.type);
|
||||
std::swap(a.privdata, b.privdata);
|
||||
std::swap(a.ht[0], b.ht[0]);
|
||||
std::swap(a.ht[1], b.ht[1]);
|
||||
std::swap(a.rehashidx, b.rehashidx);
|
||||
// Never swap refcount - they are attached to the specific dict obj
|
||||
std::swap(a.pauserehash, b.pauserehash);
|
||||
std::swap(a.noshrink, b.noshrink);
|
||||
}
|
||||
#endif
|
||||
} dict;
|
||||
|
||||
/* If safe is set to 1 this is a safe iterator, that means, you can call
|
||||
|
||||
+3
-3
@@ -552,7 +552,7 @@ static int evictionTimeProc(
|
||||
UNUSED(clientData);
|
||||
serverAssert(GlobalLocksAcquired());
|
||||
|
||||
if (performEvictions(false) == EVICT_RUNNING) return 0; /* keep evicting */
|
||||
if (performEvictions((bool)clientData) == EVICT_RUNNING) return 0; /* keep evicting */
|
||||
|
||||
/* For EVICT_OK - things are good, no need to keep evicting.
|
||||
* For EVICT_FAIL - there is nothing left to evict. */
|
||||
@@ -832,12 +832,12 @@ int performEvictions(bool fPreSnapshot) {
|
||||
/* After some time, exit the loop early - even if memory limit
|
||||
* hasn't been reached. If we suddenly need to free a lot of
|
||||
* memory, don't want to spend too much time here. */
|
||||
if (g_pserver->m_pstorageFactory == nullptr && elapsedUs(evictionTimer) > eviction_time_limit_us) {
|
||||
if (elapsedUs(evictionTimer) > eviction_time_limit_us) {
|
||||
// We still need to free memory - start eviction timer proc
|
||||
if (!isEvictionProcRunning && serverTL->el != nullptr) {
|
||||
isEvictionProcRunning = 1;
|
||||
aeCreateTimeEvent(serverTL->el, 0,
|
||||
evictionTimeProc, NULL, NULL);
|
||||
evictionTimeProc, (void*)fPreSnapshot, NULL);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include <vector>
|
||||
#include <assert.h>
|
||||
#include <unordered_set>
|
||||
#include <list>
|
||||
|
||||
struct ICollectable
|
||||
{
|
||||
@@ -45,14 +46,14 @@ public:
|
||||
void shutdown()
|
||||
{
|
||||
std::unique_lock<fastlock> lock(m_lock);
|
||||
m_vecepochs.clear();
|
||||
m_listepochs.clear();
|
||||
m_setepochOutstanding.clear();
|
||||
}
|
||||
|
||||
bool empty() const
|
||||
{
|
||||
std::unique_lock<fastlock> lock(m_lock);
|
||||
return m_vecepochs.empty();
|
||||
return m_listepochs.empty();
|
||||
}
|
||||
|
||||
void endEpoch(uint64_t epoch, bool fNoFree = false)
|
||||
@@ -63,12 +64,12 @@ public:
|
||||
m_setepochOutstanding.erase(epoch);
|
||||
if (fNoFree)
|
||||
return;
|
||||
std::vector<EpochHolder> vecclean;
|
||||
std::list<EpochHolder> listclean;
|
||||
|
||||
// No outstanding epochs?
|
||||
if (m_setepochOutstanding.empty())
|
||||
{
|
||||
vecclean = std::move(m_vecepochs); // Everything goes!
|
||||
listclean = std::move(m_listepochs); // Everything goes!
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -77,18 +78,20 @@ public:
|
||||
return; // No available epochs to free
|
||||
|
||||
// Clean any epochs available (after the lock)
|
||||
for (size_t iepoch = 0; iepoch < m_vecepochs.size(); ++iepoch)
|
||||
for (auto itr = m_listepochs.begin(); itr != m_listepochs.end(); /* itr incremented in loop*/)
|
||||
{
|
||||
auto &e = m_vecepochs[iepoch];
|
||||
auto &e = *itr;
|
||||
auto itrNext = itr;
|
||||
++itrNext;
|
||||
if (e < minepoch)
|
||||
{
|
||||
vecclean.emplace_back(std::move(e));
|
||||
m_vecepochs.erase(m_vecepochs.begin() + iepoch);
|
||||
--iepoch;
|
||||
listclean.emplace_back(std::move(e));
|
||||
m_listepochs.erase(itr);
|
||||
}
|
||||
itr = itrNext;
|
||||
}
|
||||
|
||||
assert(vecclean.empty() || fMinElement);
|
||||
assert(listclean.empty() || fMinElement);
|
||||
}
|
||||
|
||||
lock.unlock(); // don't hold it for the potentially long delete of vecclean
|
||||
@@ -100,13 +103,13 @@ public:
|
||||
serverAssert(m_setepochOutstanding.find(epoch) != m_setepochOutstanding.end());
|
||||
serverAssert(sp->FWillFreeChildDebug() == false);
|
||||
|
||||
auto itr = std::find(m_vecepochs.begin(), m_vecepochs.end(), m_epochNext+1);
|
||||
if (itr == m_vecepochs.end())
|
||||
auto itr = std::find(m_listepochs.begin(), m_listepochs.end(), m_epochNext+1);
|
||||
if (itr == m_listepochs.end())
|
||||
{
|
||||
EpochHolder e;
|
||||
e.tstamp = m_epochNext+1;
|
||||
e.m_vecObjs.push_back(std::move(sp));
|
||||
m_vecepochs.emplace_back(std::move(e));
|
||||
m_listepochs.emplace_back(std::move(e));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -117,7 +120,7 @@ public:
|
||||
private:
|
||||
mutable fastlock m_lock { "Garbage Collector"};
|
||||
|
||||
std::vector<EpochHolder> m_vecepochs;
|
||||
std::list<EpochHolder> m_listepochs;
|
||||
std::unordered_set<uint64_t> m_setepochOutstanding;
|
||||
uint64_t m_epochNext = 0;
|
||||
};
|
||||
+6
-4
@@ -1810,6 +1810,8 @@ int writeToClient(client *c, int handler_installed) {
|
||||
is a replica, so only attempt to do so if that's the case. */
|
||||
if (c->flags & CLIENT_SLAVE && !(c->flags & CLIENT_MONITOR) && c->replstate == SLAVE_STATE_ONLINE) {
|
||||
std::unique_lock<fastlock> repl_backlog_lock (g_pserver->repl_backlog_lock);
|
||||
// Ensure all writes to the repl backlog are visible
|
||||
std::atomic_thread_fence(std::memory_order_acquire);
|
||||
|
||||
while (clientHasPendingReplies(c)) {
|
||||
long long repl_end_idx = getReplIndexFromOffset(c->repl_end_off);
|
||||
@@ -2077,8 +2079,6 @@ int handleClientsWithPendingWrites(int iel, int aof_state) {
|
||||
* that may trigger write error or recreate handler. */
|
||||
if ((flags & CLIENT_PROTECTED) && !(flags & CLIENT_SLAVE)) continue;
|
||||
|
||||
//std::unique_lock<decltype(c->lock)> lock(c->lock);
|
||||
|
||||
/* Don't write to clients that are going to be closed anyway. */
|
||||
if (c->flags & CLIENT_CLOSE_ASAP) continue;
|
||||
|
||||
@@ -2096,6 +2096,7 @@ int handleClientsWithPendingWrites(int iel, int aof_state) {
|
||||
|
||||
/* If after the synchronous writes above we still have data to
|
||||
* output to the client, we need to install the writable handler. */
|
||||
std::unique_lock<decltype(c->lock)> lock(c->lock);
|
||||
if (clientHasPendingReplies(c)) {
|
||||
if (connSetWriteHandlerWithBarrier(c->conn, sendReplyToClient, ae_flags, true) == C_ERR) {
|
||||
freeClientAsync(c);
|
||||
@@ -2742,9 +2743,10 @@ void readQueryFromClient(connection *conn) {
|
||||
parseClientCommandBuffer(c);
|
||||
if (g_pserver->enable_async_commands && !serverTL->disable_async_commands && listLength(g_pserver->monitors) == 0 && (aeLockContention() || serverTL->rgdbSnapshot[c->db->id] || g_fTestMode)) {
|
||||
// Frequent writers aren't good candidates for this optimization, they cause us to renew the snapshot too often
|
||||
// so we exclude them unless the snapshot we need already exists
|
||||
// so we exclude them unless the snapshot we need already exists.
|
||||
// Note: In test mode we want to create snapshots as often as possibl to excercise them - we don't care about perf
|
||||
bool fSnapshotExists = c->db->mvccLastSnapshot >= c->mvccCheckpoint;
|
||||
bool fWriteTooRecent = (((getMvccTstamp() - c->mvccCheckpoint) >> MVCC_MS_SHIFT) < static_cast<uint64_t>(g_pserver->snapshot_slip)/2);
|
||||
bool fWriteTooRecent = !g_fTestMode && (((getMvccTstamp() - c->mvccCheckpoint) >> MVCC_MS_SHIFT) < static_cast<uint64_t>(g_pserver->snapshot_slip)/2);
|
||||
|
||||
// The check below avoids running async commands if this is a frequent writer unless a snapshot is already there to service it
|
||||
if (!fWriteTooRecent || fSnapshotExists) {
|
||||
|
||||
+15
-3
@@ -1657,13 +1657,18 @@ int launchRdbSaveThread(pthread_t &child, rdbSaveInfo *rsi)
|
||||
|
||||
g_pserver->rdbThreadVars.tmpfileNum++;
|
||||
g_pserver->rdbThreadVars.fRdbThreadCancel = false;
|
||||
if (pthread_create(&child, NULL, rdbSaveThread, args)) {
|
||||
pthread_attr_t tattr;
|
||||
pthread_attr_init(&tattr);
|
||||
pthread_attr_setstacksize(&tattr, 1 << 23); // 8 MB
|
||||
if (pthread_create(&child, &tattr, rdbSaveThread, args)) {
|
||||
pthread_attr_destroy(&tattr);
|
||||
for (int idb = 0; idb < cserver.dbnum; ++idb)
|
||||
g_pserver->db[idb]->endSnapshot(args->rgpdb[idb]);
|
||||
args->~rdbSaveThreadArgs();
|
||||
zfree(args);
|
||||
return C_ERR;
|
||||
}
|
||||
pthread_attr_destroy(&tattr);
|
||||
g_pserver->child_type = CHILD_TYPE_RDB;
|
||||
}
|
||||
return C_OK;
|
||||
@@ -3049,7 +3054,9 @@ void rdbLoadProgressCallback(rio *r, const void *buf, size_t len) {
|
||||
(r->keys_since_last_callback >= g_pserver->loading_process_events_interval_keys)))
|
||||
{
|
||||
rdbAsyncWorkThread *pwthread = reinterpret_cast<rdbAsyncWorkThread*>(r->chksum_arg);
|
||||
bool fUpdateReplication = (g_pserver->mstime - r->last_update) > 1000;
|
||||
mstime_t mstime;
|
||||
__atomic_load(&g_pserver->mstime, &mstime, __ATOMIC_RELAXED);
|
||||
bool fUpdateReplication = (mstime - r->last_update) > 1000;
|
||||
|
||||
if (fUpdateReplication) {
|
||||
listIter li;
|
||||
@@ -3832,7 +3839,11 @@ int rdbSaveToSlavesSockets(rdbSaveInfo *rsi) {
|
||||
|
||||
g_pserver->rdbThreadVars.tmpfileNum++;
|
||||
g_pserver->rdbThreadVars.fRdbThreadCancel = false;
|
||||
if (pthread_create(&child, nullptr, rdbSaveToSlavesSocketsThread, args)) {
|
||||
pthread_attr_t tattr;
|
||||
pthread_attr_init(&tattr);
|
||||
pthread_attr_setstacksize(&tattr, 1 << 23); // 8 MB
|
||||
if (pthread_create(&child, &tattr, rdbSaveToSlavesSocketsThread, args)) {
|
||||
pthread_attr_destroy(&tattr);
|
||||
serverLog(LL_WARNING,"Can't save in background: fork: %s",
|
||||
strerror(errno));
|
||||
|
||||
@@ -3858,6 +3869,7 @@ int rdbSaveToSlavesSockets(rdbSaveInfo *rsi) {
|
||||
closeChildInfoPipe();
|
||||
return C_ERR;
|
||||
}
|
||||
pthread_attr_destroy(&tattr);
|
||||
g_pserver->child_type = CHILD_TYPE_RDB;
|
||||
|
||||
serverLog(LL_NOTICE,"Background RDB transfer started");
|
||||
|
||||
+7
-3
@@ -264,9 +264,8 @@ void resizeReplicationBacklog(long long newsize) {
|
||||
newsize = CONFIG_REPL_BACKLOG_MIN_SIZE;
|
||||
if (g_pserver->repl_backlog_size == newsize) return;
|
||||
|
||||
std::unique_lock<fastlock> repl_backlog_lock (g_pserver->repl_backlog_lock);
|
||||
|
||||
if (g_pserver->repl_backlog != NULL) {
|
||||
std::unique_lock<fastlock> repl_backlog_lock(g_pserver->repl_backlog_lock);
|
||||
/* What we actually do is to flush the old buffer and realloc a new
|
||||
* empty one. It will refill with new data incrementally.
|
||||
* The reason is that copying a few gigabytes adds latency and even
|
||||
@@ -357,7 +356,7 @@ void freeReplicationBacklog(void) {
|
||||
void feedReplicationBacklog(const void *ptr, size_t len) {
|
||||
serverAssert(GlobalLocksAcquired());
|
||||
const unsigned char *p = (const unsigned char*)ptr;
|
||||
|
||||
std::unique_lock<fastlock> repl_backlog_lock(g_pserver->repl_backlog_lock, std::defer_lock);
|
||||
|
||||
if (g_pserver->repl_batch_idxStart >= 0) {
|
||||
/* We are lower bounded by the lowest replica offset, or the batch offset start if not applicable */
|
||||
@@ -417,6 +416,8 @@ void feedReplicationBacklog(const void *ptr, size_t len) {
|
||||
// We need to update a few variables or later asserts will notice we dropped data
|
||||
g_pserver->repl_batch_offStart = g_pserver->master_repl_offset + len;
|
||||
g_pserver->repl_lowest_off = -1;
|
||||
if (!repl_backlog_lock.owns_lock())
|
||||
repl_backlog_lock.lock(); // we need to acquire the lock if we'll be overwriting data that writeToClient may be reading
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5599,6 +5600,9 @@ void flushReplBacklogToClients()
|
||||
serverAssert(g_pserver->master_repl_offset - g_pserver->repl_batch_offStart <= g_pserver->repl_backlog_size);
|
||||
serverAssert(g_pserver->repl_batch_idxStart != g_pserver->repl_backlog_idx);
|
||||
|
||||
// Repl backlog writes must become visible to all threads at this point
|
||||
std::atomic_thread_fence(std::memory_order_release);
|
||||
|
||||
listIter li;
|
||||
listNode *ln;
|
||||
listRewind(g_pserver->slaves, &li);
|
||||
|
||||
+11
-5
@@ -2109,8 +2109,10 @@ void databasesCron(bool fMainThread) {
|
||||
aeAcquireLock();
|
||||
}
|
||||
|
||||
dictCompleteRehashAsync(serverTL->rehashCtl, true /*fFree*/);
|
||||
serverTL->rehashCtl = nullptr;
|
||||
if (serverTL->rehashCtl->done.load(std::memory_order_relaxed)) {
|
||||
dictCompleteRehashAsync(serverTL->rehashCtl, true /*fFree*/);
|
||||
serverTL->rehashCtl = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
serverAssert(serverTL->rehashCtl == nullptr);
|
||||
@@ -5532,6 +5534,8 @@ sds genRedisInfoString(const char *section) {
|
||||
}
|
||||
|
||||
unsigned int lruclock = g_pserver->lruclock.load();
|
||||
ustime_t ustime;
|
||||
__atomic_load(&g_pserver->ustime, &ustime, __ATOMIC_RELAXED);
|
||||
info = sdscatfmt(info,
|
||||
"# Server\r\n"
|
||||
"redis_version:%s\r\n"
|
||||
@@ -5574,7 +5578,7 @@ sds genRedisInfoString(const char *section) {
|
||||
supervised,
|
||||
g_pserver->runid,
|
||||
g_pserver->port ? g_pserver->port : g_pserver->tls_port,
|
||||
(int64_t)g_pserver->ustime,
|
||||
(int64_t)ustime,
|
||||
(int64_t)uptime,
|
||||
(int64_t)(uptime/(3600*24)),
|
||||
g_pserver->hz.load(),
|
||||
@@ -7604,7 +7608,7 @@ int main(int argc, char **argv) {
|
||||
try {
|
||||
loadDataFromDisk();
|
||||
} catch (ShutdownException) {
|
||||
exit(EXIT_SUCCESS);
|
||||
_Exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
if (g_pserver->cluster_enabled) {
|
||||
@@ -7721,7 +7725,9 @@ int main(int argc, char **argv) {
|
||||
g_pserver->garbageCollector.shutdown();
|
||||
delete g_pserver->m_pstorageFactory;
|
||||
|
||||
return 0;
|
||||
// Don't return because we don't want to run any global dtors
|
||||
_Exit(EXIT_SUCCESS);
|
||||
return 0; // Ensure we're well formed even though this won't get hit
|
||||
}
|
||||
|
||||
/* The End */
|
||||
@@ -26,17 +26,6 @@ public:
|
||||
std::vector<dictEntry*> vecde;
|
||||
};
|
||||
|
||||
void discontinueAsyncRehash(dict *d) {
|
||||
if (d->asyncdata != nullptr) {
|
||||
auto adata = d->asyncdata;
|
||||
while (adata != nullptr) {
|
||||
adata->abondon = true;
|
||||
adata = adata->next;
|
||||
}
|
||||
d->rehashidx = 0;
|
||||
}
|
||||
}
|
||||
|
||||
const redisDbPersistentDataSnapshot *redisDbPersistentData::createSnapshot(uint64_t mvccCheckpoint, bool fOptional)
|
||||
{
|
||||
serverAssert(GlobalLocksAcquired());
|
||||
|
||||
Reference in new issue
Block a user