mirror of
https://github.com/Snapchat/KeyDB.git
synced 2026-11-04 08:42:14 -05:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fb45a32e0c |
No files matched your search
@@ -1,20 +0,0 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
** Log Files **
|
||||
These should be KeyDB logs, not syslogs or logs from your container manager. If you are reporting a crash there will be a line in your log stating:
|
||||
"=== KEYDB BUG REPORT START: Cut & paste starting from here ==="
|
||||
|
||||
Please copy everything after this line.
|
||||
|
||||
**To Reproduce**
|
||||
Do you know how to reproduce this? If so please provide repro steps.
|
||||
@@ -1,20 +0,0 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
||||
Vendored
+58
@@ -0,0 +1,58 @@
|
||||
{
|
||||
"files.associations": {
|
||||
"zmalloc.h": "c",
|
||||
"stat.h": "c",
|
||||
"array": "cpp",
|
||||
"atomic": "cpp",
|
||||
"*.tcc": "cpp",
|
||||
"cctype": "cpp",
|
||||
"chrono": "cpp",
|
||||
"clocale": "cpp",
|
||||
"cmath": "cpp",
|
||||
"condition_variable": "cpp",
|
||||
"cstdarg": "cpp",
|
||||
"cstddef": "cpp",
|
||||
"cstdint": "cpp",
|
||||
"cstdio": "cpp",
|
||||
"cstdlib": "cpp",
|
||||
"cstring": "cpp",
|
||||
"ctime": "cpp",
|
||||
"cwchar": "cpp",
|
||||
"cwctype": "cpp",
|
||||
"deque": "cpp",
|
||||
"list": "cpp",
|
||||
"unordered_map": "cpp",
|
||||
"vector": "cpp",
|
||||
"exception": "cpp",
|
||||
"fstream": "cpp",
|
||||
"functional": "cpp",
|
||||
"future": "cpp",
|
||||
"initializer_list": "cpp",
|
||||
"iomanip": "cpp",
|
||||
"iosfwd": "cpp",
|
||||
"iostream": "cpp",
|
||||
"istream": "cpp",
|
||||
"limits": "cpp",
|
||||
"memory": "cpp",
|
||||
"mutex": "cpp",
|
||||
"new": "cpp",
|
||||
"numeric": "cpp",
|
||||
"optional": "cpp",
|
||||
"ostream": "cpp",
|
||||
"ratio": "cpp",
|
||||
"scoped_allocator": "cpp",
|
||||
"sstream": "cpp",
|
||||
"stdexcept": "cpp",
|
||||
"streambuf": "cpp",
|
||||
"string_view": "cpp",
|
||||
"system_error": "cpp",
|
||||
"thread": "cpp",
|
||||
"cinttypes": "cpp",
|
||||
"tuple": "cpp",
|
||||
"type_traits": "cpp",
|
||||
"typeinfo": "cpp",
|
||||
"utility": "cpp",
|
||||
"set": "cpp",
|
||||
"algorithm": "cpp"
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -4,7 +4,7 @@ FROM ubuntu:18.04
|
||||
|
||||
RUN apt-get update \
|
||||
&& DEBIAN_FRONTEND=noninteractive apt-get install -qqy \
|
||||
build-essential nasm autotools-dev autoconf libcurl4-openssl-dev libjemalloc-dev tcl tcl-dev uuid-dev libcurl4-openssl-dev \
|
||||
build-essential nasm autotools-dev autoconf libcurl4-openssl-dev libjemalloc-dev tcl tcl-dev uuid-dev \
|
||||
&& apt-get clean
|
||||
|
||||
CMD make
|
||||
+2
-4
@@ -1374,9 +1374,7 @@ void propagateExpire(redisDb *db, robj *key, int lazy) {
|
||||
|
||||
if (g_pserver->aof_state != AOF_OFF)
|
||||
feedAppendOnlyFile(cserver.delCommand,db->id,argv,2);
|
||||
// Active replicas do their own expiries, do not propogate
|
||||
if (!g_pserver->fActiveReplica)
|
||||
replicationFeedSlaves(g_pserver->slaves,db->id,argv,2);
|
||||
replicationFeedSlaves(g_pserver->slaves,db->id,argv,2);
|
||||
|
||||
decrRefCount(argv[0]);
|
||||
decrRefCount(argv[1]);
|
||||
@@ -1444,7 +1442,7 @@ int expireIfNeeded(redisDb *db, robj *key) {
|
||||
* Still we try to return the right information to the caller,
|
||||
* that is, 0 if we think the key should be still valid, 1 if
|
||||
* we think the key is expired at this time. */
|
||||
if (listLength(g_pserver->masters) && !g_pserver->fActiveReplica) return 1;
|
||||
if (listLength(g_pserver->masters)) return 1;
|
||||
|
||||
/* Delete the key */
|
||||
g_pserver->stat_expiredkeys++;
|
||||
|
||||
+1
-1
@@ -535,7 +535,7 @@ void expireGenericCommand(client *c, long long basetime, int unit) {
|
||||
*
|
||||
* Instead we take the other branch of the IF statement setting an expire
|
||||
* (possibly in the past) and wait for an explicit DEL from the master. */
|
||||
if (when <= mstime() && !g_pserver->loading && (!listLength(g_pserver->masters) || g_pserver->fActiveReplica)) {
|
||||
if (when <= mstime() && !g_pserver->loading && !listLength(g_pserver->masters)) {
|
||||
robj *aux;
|
||||
|
||||
int deleted = g_pserver->lazyfree_lazy_expire ? dbAsyncDelete(c->db,key) :
|
||||
|
||||
+78
-9
@@ -1,18 +1,90 @@
|
||||
.intel_syntax noprefix
|
||||
.text
|
||||
.section .note.GNU-stack,"",@progbits
|
||||
|
||||
.extern gettid
|
||||
.extern fastlock_sleep
|
||||
|
||||
.data
|
||||
.align 8
|
||||
fastlock_unlock_vec:
|
||||
.quad fastlock_unlock_vanilla
|
||||
fastlock_lock_vec:
|
||||
.quad cpuid_check_tsxrtm_polyphill
|
||||
fastlock_trylock_vec:
|
||||
.quad fastlock_trylock_vanilla
|
||||
|
||||
.text
|
||||
.global fastlock_unlock
|
||||
fastlock_unlock:
|
||||
mov rax, [rip + fastlock_unlock_vec]
|
||||
jmp rax
|
||||
.global fastlock_lock
|
||||
.type fastlock_lock,@function
|
||||
fastlock_lock:
|
||||
mov rax, [rip + fastlock_lock_vec]
|
||||
jmp rax
|
||||
.global fastlock_trylock
|
||||
.type fastlock_trylock,@function
|
||||
fastlock_trylock:
|
||||
mov rax, [rip + fastlock_trylock_vec]
|
||||
jmp rax
|
||||
|
||||
# This is the first use of assembly in this codebase, a valid question is WHY?
|
||||
# The spinlock we implement here is performance critical, and simply put GCC
|
||||
# emits awful code. The original C code is left in fastlock.cpp for reference
|
||||
# and x-plat.
|
||||
|
||||
cpuid_check_tsxrtm_polyphill:
|
||||
mov eax, 7
|
||||
xor ecx, ecx
|
||||
cpuid
|
||||
and ebx, 0x800
|
||||
jnz .LHasTSX
|
||||
# CPU does not have TSX RTM support
|
||||
lea rax, [rip+fastlock_lock_vanilla]
|
||||
mov [rip + fastlock_lock_vec], rax
|
||||
jmp rax
|
||||
.LHasTSX:
|
||||
lea rax, [rip+fastlock_lock_tsxrtm]
|
||||
mov [rip + fastlock_lock_vec], rax
|
||||
lea rax, [rip+fastlock_trylock_tsxrtm]
|
||||
mov [rip + fastlock_trylock_vec], rax
|
||||
lea rax, [rip+fastlock_unlock_tsxrtm]
|
||||
mov [rip + fastlock_unlock_vec], rax
|
||||
jmp fastlock_lock_tsxrtm
|
||||
|
||||
.ALIGN 16
|
||||
.global fastlock_lock
|
||||
.type fastlock_lock,@function
|
||||
fastlock_lock:
|
||||
fastlock_lock_tsxrtm:
|
||||
xbegin fastlock_lock_vanilla
|
||||
fastlock_lock_tsxrtmcore:
|
||||
# make sure the lock is not taken (do this inside the TX)
|
||||
mov eax, [rdi+64]
|
||||
mov ecx, eax # duplicate in ecx
|
||||
ror ecx, 16 # swap upper and lower 16-bits
|
||||
cmp eax, ecx # are the upper and lower 16-bits the same?
|
||||
jne .LContested
|
||||
mov eax, 1 # this is not needed for the regular lock, but is for try lock which shares this body
|
||||
ret
|
||||
.LContested:
|
||||
xabort 0
|
||||
|
||||
.ALIGN 16
|
||||
fastlock_trylock_tsxrtm:
|
||||
xbegin fastlock_trylock_vanilla
|
||||
jmp fastlock_lock_tsxrtmcore
|
||||
|
||||
.ALIGN 16
|
||||
fastlock_unlock_tsxrtm:
|
||||
mov eax, [rdi+64]
|
||||
mov ecx, eax # duplicate in ecx
|
||||
ror ecx, 16 # swap upper and lower 16-bits
|
||||
cmp eax, ecx # are the upper and lower 16-bits the same?
|
||||
jne fastlock_unlock_vanilla
|
||||
xend
|
||||
ret
|
||||
|
||||
.ALIGN 16
|
||||
fastlock_lock_vanilla:
|
||||
.cfi_startproc
|
||||
.cfi_def_cfa rsp, 8
|
||||
# RDI points to the struct:
|
||||
@@ -79,9 +151,7 @@ fastlock_lock:
|
||||
.cfi_endproc
|
||||
|
||||
.ALIGN 16
|
||||
.global fastlock_trylock
|
||||
.type fastlock_trylock,@function
|
||||
fastlock_trylock:
|
||||
fastlock_trylock_vanilla:
|
||||
# RDI points to the struct:
|
||||
# int32_t m_pidOwner
|
||||
# int32_t m_depth
|
||||
@@ -125,8 +195,7 @@ fastlock_trylock:
|
||||
ret
|
||||
|
||||
.ALIGN 16
|
||||
.global fastlock_unlock
|
||||
fastlock_unlock:
|
||||
fastlock_unlock_vanilla:
|
||||
# RDI points to the struct:
|
||||
# int32_t m_pidOwner
|
||||
# int32_t m_depth
|
||||
|
||||
@@ -99,7 +99,6 @@ client *createClient(int fd, int iel) {
|
||||
* in the context of a client. When commands are executed in other
|
||||
* contexts (for instance a Lua script) we need a non connected client. */
|
||||
if (fd != -1) {
|
||||
serverAssert(iel == (serverTL - g_pserver->rgthreadvar));
|
||||
anetNonBlock(NULL,fd);
|
||||
anetEnableTcpNoDelay(NULL,fd);
|
||||
if (cserver.tcpkeepalive)
|
||||
|
||||
@@ -714,34 +714,6 @@ int getLongLongFromObject(robj *o, long long *target) {
|
||||
return C_OK;
|
||||
}
|
||||
|
||||
int getUnsignedLongLongFromObject(robj *o, uint64_t *target) {
|
||||
uint64_t value;
|
||||
|
||||
if (o == NULL) {
|
||||
value = 0;
|
||||
} else {
|
||||
serverAssertWithInfo(NULL,o,o->type == OBJ_STRING);
|
||||
if (sdsEncodedObject(o)) {
|
||||
char *pchEnd = nullptr;
|
||||
errno = 0;
|
||||
value = strtoull(szFromObj(o), &pchEnd, 10);
|
||||
if (value == 0) {
|
||||
// potential error
|
||||
if (errno != 0)
|
||||
return C_ERR;
|
||||
if (pchEnd == szFromObj(o))
|
||||
return C_ERR;
|
||||
}
|
||||
} else if (o->encoding == OBJ_ENCODING_INT) {
|
||||
value = (long)ptrFromObj(o);
|
||||
} else {
|
||||
serverPanic("Unknown string encoding");
|
||||
}
|
||||
}
|
||||
if (target) *target = value;
|
||||
return C_OK;
|
||||
}
|
||||
|
||||
int getLongLongFromObjectOrReply(client *c, robj *o, long long *target, const char *msg) {
|
||||
long long value;
|
||||
if (getLongLongFromObject(o, &value) != C_OK) {
|
||||
|
||||
+1
-8
@@ -2115,19 +2115,12 @@ int rdbLoadRio(rio *rdb, rdbSaveInfo *rsi, int loading_aof) {
|
||||
if ((key = rdbLoadStringObject(rdb)) == NULL) goto eoferr;
|
||||
/* Read value */
|
||||
if ((val = rdbLoadObject(type,rdb,key, mvcc_tstamp)) == NULL) goto eoferr;
|
||||
bool fStaleMvccKey = val->mvcc_tstamp < rsi->mvccMinThreshold;
|
||||
/* Check if the key already expired. This function is used when loading
|
||||
* an RDB file from disk, either at startup, or when an RDB was
|
||||
* received from the master. In the latter case, the master is
|
||||
* responsible for key expiry. If we would expire keys here, the
|
||||
* snapshot taken by the master may not be reflected on the replica. */
|
||||
bool fExpiredKey = (listLength(g_pserver->masters) == 0 || g_pserver->fActiveReplica) && !loading_aof && expiretime != -1 && expiretime < now;
|
||||
if (fStaleMvccKey || fExpiredKey) {
|
||||
if (fStaleMvccKey && !fExpiredKey && rsi->mi != nullptr && rsi->mi->staleKeyMap != nullptr && lookupKeyRead(db, key) == nullptr) {
|
||||
// We have a key that we've already deleted and is not back in our database.
|
||||
// We'll need to inform the sending master of the delete if it is also a replica of us
|
||||
rsi->mi->staleKeyMap->operator[](db - g_pserver->db).push_back(key);
|
||||
}
|
||||
if (listLength(g_pserver->masters) == 0 && !loading_aof && expiretime != -1 && expiretime < now) {
|
||||
decrRefCount(key);
|
||||
key = nullptr;
|
||||
decrRefCount(val);
|
||||
|
||||
+4
-119
@@ -48,7 +48,6 @@ void replicationResurrectCachedMaster(redisMaster *mi, int newfd);
|
||||
void replicationSendAck(redisMaster *mi);
|
||||
void putSlaveOnline(client *replica);
|
||||
int cancelReplicationHandshake(redisMaster *mi);
|
||||
static void propagateMasterStaleKeys();
|
||||
|
||||
/* --------------------------- Utility functions ---------------------------- */
|
||||
|
||||
@@ -130,23 +129,6 @@ static bool FAnyDisconnectedMasters()
|
||||
return false;
|
||||
}
|
||||
|
||||
client *replicaFromMaster(redisMaster *mi)
|
||||
{
|
||||
if (mi->master == nullptr)
|
||||
return nullptr;
|
||||
|
||||
listIter liReplica;
|
||||
listNode *lnReplica;
|
||||
listRewind(g_pserver->slaves, &liReplica);
|
||||
while ((lnReplica = listNext(&liReplica)) != nullptr)
|
||||
{
|
||||
client *replica = (client*)listNodeValue(lnReplica);
|
||||
if (FSameHost(mi->master, replica))
|
||||
return replica;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/* ---------------------------------- MASTER -------------------------------- */
|
||||
|
||||
void createReplicationBacklog(void) {
|
||||
@@ -343,20 +325,12 @@ void replicationFeedSlaves(list *slaves, int dictid, robj **argv, int argc) {
|
||||
char uuid[40] = {'\0'};
|
||||
uuid_unparse(cserver.uuid, uuid);
|
||||
char proto[1024];
|
||||
int cchProto = snprintf(proto, sizeof(proto), "*5\r\n$7\r\nRREPLAY\r\n$%d\r\n%s\r\n$%lld\r\n", (int)strlen(uuid), uuid, cchbuf);
|
||||
int cchProto = snprintf(proto, sizeof(proto), "*4\r\n$7\r\nRREPLAY\r\n$%d\r\n%s\r\n$%lld\r\n", (int)strlen(uuid), uuid, cchbuf);
|
||||
cchProto = std::min((int)sizeof(proto), cchProto);
|
||||
long long master_repl_offset_start = g_pserver->master_repl_offset;
|
||||
|
||||
char szDbNum[128];
|
||||
int cchDictIdNum = snprintf(szDbNum, sizeof(szDbNum), "%d", dictid);
|
||||
int cchDbNum = snprintf(szDbNum, sizeof(szDbNum), "$%d\r\n%d\r\n", cchDictIdNum, dictid);
|
||||
cchDbNum = std::min<int>(cchDbNum, sizeof(szDbNum)); // snprintf is tricky like that
|
||||
|
||||
char szMvcc[128];
|
||||
uint64_t mvccTstamp = getMvccTstamp();
|
||||
int cchMvccNum = snprintf(szMvcc, sizeof(szMvcc), "%lu", mvccTstamp);
|
||||
int cchMvcc = snprintf(szMvcc, sizeof(szMvcc), "$%d\r\n%lu\r\n", cchMvccNum, mvccTstamp);
|
||||
cchMvcc = std::min<int>(cchMvcc, sizeof(szMvcc)); // tricky snprintf
|
||||
int cchDbNum = snprintf(szDbNum, sizeof(szDbNum), "$%d\r\n%d\r\n", (dictid/10)+1, dictid);
|
||||
|
||||
/* Write the command to the replication backlog if any. */
|
||||
if (g_pserver->repl_backlog)
|
||||
@@ -400,7 +374,6 @@ void replicationFeedSlaves(list *slaves, int dictid, robj **argv, int argc) {
|
||||
const char *crlf = "\r\n";
|
||||
feedReplicationBacklog(crlf, 2);
|
||||
feedReplicationBacklog(szDbNum, cchDbNum);
|
||||
feedReplicationBacklog(szMvcc, cchMvcc);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -436,7 +409,6 @@ void replicationFeedSlaves(list *slaves, int dictid, robj **argv, int argc) {
|
||||
{
|
||||
addReplyAsync(replica,shared.crlf);
|
||||
addReplyProtoAsync(replica, szDbNum, cchDbNum);
|
||||
addReplyProtoAsync(replica, szMvcc, cchMvcc);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -697,7 +669,6 @@ int masterTryPartialResynchronization(client *c) {
|
||||
c->repl_ack_time = g_pserver->unixtime;
|
||||
c->repl_put_online_on_ack = 0;
|
||||
listAddNodeTail(g_pserver->slaves,c);
|
||||
|
||||
/* We can't use the connection buffers since they are used to accumulate
|
||||
* new commands at this stage. But we are sure the socket send buffer is
|
||||
* empty so this write will never fail actually. */
|
||||
@@ -1031,8 +1002,6 @@ void replconfCommand(client *c) {
|
||||
c->slave_capa |= SLAVE_CAPA_EOF;
|
||||
else if (!strcasecmp((const char*)ptrFromObj(c->argv[j+1]),"psync2"))
|
||||
c->slave_capa |= SLAVE_CAPA_PSYNC2;
|
||||
else if (!strcasecmp((const char*)ptrFromObj(c->argv[j+1]), "activeExpire"))
|
||||
c->slave_capa |= SLAVE_CAPA_ACTIVE_EXPIRE;
|
||||
} else if (!strcasecmp((const char*)ptrFromObj(c->argv[j]),"ack")) {
|
||||
/* REPLCONF ACK is used by replica to inform the master the amount
|
||||
* of replication stream that it processed so far. It is an
|
||||
@@ -1102,14 +1071,6 @@ void putSlaveOnline(client *replica) {
|
||||
refreshGoodSlavesCount();
|
||||
serverLog(LL_NOTICE,"Synchronization with replica %s succeeded",
|
||||
replicationGetSlaveName(replica));
|
||||
|
||||
if (!(replica->slave_capa & SLAVE_CAPA_ACTIVE_EXPIRE) && g_pserver->fActiveReplica)
|
||||
{
|
||||
serverLog(LL_WARNING, "Warning: replica %s does not support active expiration. This client may not correctly process key expirations."
|
||||
"\n\tThis is OK if you are in the process of an active upgrade.", replicationGetSlaveName(replica));
|
||||
serverLog(LL_WARNING, "Connections between active replicas and traditional replicas is deprecated. This will be refused in future versions."
|
||||
"\n\tPlease fix your replica topology");
|
||||
}
|
||||
}
|
||||
|
||||
void sendBulkToSlave(aeEventLoop *el, int fd, void *privdata, int mask) {
|
||||
@@ -1615,15 +1576,6 @@ void readSyncBulkPayload(aeEventLoop *el, int fd, void *privdata, int mask) {
|
||||
aeDeleteFileEvent(el,mi->repl_transfer_s,AE_READABLE);
|
||||
serverLog(LL_NOTICE, "MASTER <-> REPLICA sync: Loading DB in memory");
|
||||
rdbSaveInfo rsi = RDB_SAVE_INFO_INIT;
|
||||
if (g_pserver->fActiveReplica)
|
||||
{
|
||||
rsi.mvccMinThreshold = mi->mvccLastSync;
|
||||
if (mi->staleKeyMap != nullptr)
|
||||
mi->staleKeyMap->clear();
|
||||
else
|
||||
mi->staleKeyMap = new (MALLOC_LOCAL) std::map<int, std::vector<robj_sharedptr>>();
|
||||
rsi.mi = mi;
|
||||
}
|
||||
if (rdbLoadFile(rdb_filename, &rsi) != C_OK) {
|
||||
serverLog(LL_WARNING,"Failed trying to load the MASTER synchronization DB from disk");
|
||||
cancelReplicationHandshake(mi);
|
||||
@@ -2142,16 +2094,8 @@ void syncWithMaster(aeEventLoop *el, int fd, void *privdata, int mask) {
|
||||
*
|
||||
* The master will ignore capabilities it does not understand. */
|
||||
if (mi->repl_state == REPL_STATE_SEND_CAPA) {
|
||||
if (g_pserver->fActiveReplica)
|
||||
{
|
||||
err = sendSynchronousCommand(mi, SYNC_CMD_WRITE,fd,"REPLCONF",
|
||||
"capa","eof","capa","psync2","capa","activeExpire",NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
err = sendSynchronousCommand(mi, SYNC_CMD_WRITE,fd,"REPLCONF",
|
||||
"capa","eof","capa","psync2",NULL);
|
||||
}
|
||||
err = sendSynchronousCommand(mi, SYNC_CMD_WRITE,fd,"REPLCONF",
|
||||
"capa","eof","capa","psync2",NULL);
|
||||
if (err) goto write_error;
|
||||
sdsfree(err);
|
||||
mi->repl_state = REPL_STATE_RECEIVE_CAPA;
|
||||
@@ -2419,7 +2363,6 @@ void freeMasterInfo(redisMaster *mi)
|
||||
{
|
||||
zfree(mi->masterauth);
|
||||
zfree(mi->masteruser);
|
||||
delete mi->staleKeyMap;
|
||||
zfree(mi);
|
||||
}
|
||||
|
||||
@@ -3253,8 +3196,6 @@ void replicationCron(void) {
|
||||
}
|
||||
}
|
||||
|
||||
propagateMasterStaleKeys();
|
||||
|
||||
/* Refresh the number of slaves with lag <= min-slaves-max-lag. */
|
||||
refreshGoodSlavesCount();
|
||||
replication_cron_loops++; /* Incremented with frequency 1 HZ. */
|
||||
@@ -3401,17 +3342,6 @@ void replicaReplayCommand(client *c)
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t mvcc = 0;
|
||||
if (c->argc >= 5)
|
||||
{
|
||||
if (getUnsignedLongLongFromObject(c->argv[4], &mvcc) != C_OK)
|
||||
{
|
||||
addReplyError(c, "Invalid MVCC Timestamp");
|
||||
s_pstate->Cancel();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (FSameUuidNoNil(uuid, cserver.uuid))
|
||||
{
|
||||
addReply(c, shared.ok);
|
||||
@@ -3438,11 +3368,6 @@ void replicaReplayCommand(client *c)
|
||||
{
|
||||
addReply(c, shared.ok);
|
||||
selectDb(c, cFake->db->id);
|
||||
redisMaster *mi = MasterInfoFromClient(c);
|
||||
if (mi != nullptr) // this should never be null but I'd prefer not to crash
|
||||
{
|
||||
mi->mvccLastSync = mvcc;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -3477,43 +3402,3 @@ void updateMasterAuth()
|
||||
mi->masteruser = zstrdup(cserver.default_masteruser);
|
||||
}
|
||||
}
|
||||
|
||||
static void propagateMasterStaleKeys()
|
||||
{
|
||||
listIter li;
|
||||
listNode *ln;
|
||||
listRewind(g_pserver->masters, &li);
|
||||
robj *rgobj[2];
|
||||
|
||||
rgobj[0] = createEmbeddedStringObject("DEL", 3);
|
||||
|
||||
while ((ln = listNext(&li)) != nullptr)
|
||||
{
|
||||
redisMaster *mi = (redisMaster*)listNodeValue(ln);
|
||||
if (mi->staleKeyMap != nullptr)
|
||||
{
|
||||
if (mi->master != nullptr)
|
||||
{
|
||||
for (auto &pair : *mi->staleKeyMap)
|
||||
{
|
||||
if (pair.second.empty())
|
||||
continue;
|
||||
|
||||
client *replica = replicaFromMaster(mi);
|
||||
if (replica == nullptr)
|
||||
continue;
|
||||
|
||||
for (auto &spkey : pair.second)
|
||||
{
|
||||
rgobj[1] = spkey.get();
|
||||
replicationFeedSlave(replica, pair.first, rgobj, 2, false);
|
||||
}
|
||||
}
|
||||
delete mi->staleKeyMap;
|
||||
mi->staleKeyMap = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
decrRefCount(rgobj[0]);
|
||||
}
|
||||
+3
-4
@@ -1703,9 +1703,9 @@ void clientsCron(int iel) {
|
||||
void databasesCron(void) {
|
||||
/* Expire keys by random sampling. Not required for slaves
|
||||
* as master will synthesize DELs for us. */
|
||||
if (g_pserver->active_expire_enabled && (listLength(g_pserver->masters) == 0 || g_pserver->fActiveReplica)) {
|
||||
if (g_pserver->active_expire_enabled && listLength(g_pserver->masters) == 0) {
|
||||
activeExpireCycle(ACTIVE_EXPIRE_CYCLE_SLOW);
|
||||
} else if (listLength(g_pserver->masters) && !g_pserver->fActiveReplica) {
|
||||
} else if (listLength(g_pserver->masters)) {
|
||||
expireSlaveKeys();
|
||||
}
|
||||
|
||||
@@ -2105,7 +2105,7 @@ void beforeSleep(struct aeEventLoop *eventLoop) {
|
||||
|
||||
/* Run a fast expire cycle (the called function will return
|
||||
* ASAP if a fast cycle is not needed). */
|
||||
if (g_pserver->active_expire_enabled && (listLength(g_pserver->masters) == 0 || g_pserver->fActiveReplica))
|
||||
if (g_pserver->active_expire_enabled && listLength(g_pserver->masters) == 0)
|
||||
activeExpireCycle(ACTIVE_EXPIRE_CYCLE_FAST);
|
||||
|
||||
/* Send all the slaves an ACK request if at least one client blocked
|
||||
@@ -2319,7 +2319,6 @@ void initMasterInfo(redisMaster *master)
|
||||
|
||||
master->repl_state = REPL_STATE_NONE;
|
||||
master->repl_down_since = 0; /* Never connected, repl is down since EVER. */
|
||||
master->mvccLastSync = 0;
|
||||
}
|
||||
|
||||
void initServerConfig(void) {
|
||||
|
||||
+1
-90
@@ -54,7 +54,6 @@
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <map>
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#include <lua.h>
|
||||
@@ -145,87 +144,6 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
void decrRefCount(robj_roptr o);
|
||||
void incrRefCount(robj_roptr o);
|
||||
class robj_sharedptr
|
||||
{
|
||||
redisObject *m_ptr;
|
||||
|
||||
public:
|
||||
robj_sharedptr()
|
||||
: m_ptr(nullptr)
|
||||
{}
|
||||
robj_sharedptr(redisObject *ptr)
|
||||
: m_ptr(ptr)
|
||||
{
|
||||
incrRefCount(ptr);
|
||||
}
|
||||
~robj_sharedptr()
|
||||
{
|
||||
if (m_ptr)
|
||||
decrRefCount(m_ptr);
|
||||
}
|
||||
robj_sharedptr(const robj_sharedptr& other)
|
||||
{
|
||||
m_ptr = other.m_ptr;
|
||||
incrRefCount(m_ptr);
|
||||
}
|
||||
|
||||
robj_sharedptr(robj_sharedptr&& other)
|
||||
{
|
||||
m_ptr = other.m_ptr;
|
||||
other.m_ptr = nullptr;
|
||||
}
|
||||
|
||||
robj_sharedptr &operator=(const robj_sharedptr& other)
|
||||
{
|
||||
if (m_ptr)
|
||||
decrRefCount(m_ptr);
|
||||
m_ptr = other.m_ptr;
|
||||
incrRefCount(m_ptr);
|
||||
return *this;
|
||||
}
|
||||
robj_sharedptr &operator=(redisObject *ptr)
|
||||
{
|
||||
if (m_ptr)
|
||||
decrRefCount(m_ptr);
|
||||
m_ptr = ptr;
|
||||
incrRefCount(m_ptr);
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator==(const robj_sharedptr &other) const
|
||||
{
|
||||
return m_ptr == other.m_ptr;
|
||||
}
|
||||
|
||||
bool operator!=(const robj_sharedptr &other) const
|
||||
{
|
||||
return m_ptr != other.m_ptr;
|
||||
}
|
||||
|
||||
redisObject* operator->() const
|
||||
{
|
||||
return m_ptr;
|
||||
}
|
||||
|
||||
bool operator!() const
|
||||
{
|
||||
return !m_ptr;
|
||||
}
|
||||
|
||||
operator bool() const{
|
||||
return !!m_ptr;
|
||||
}
|
||||
|
||||
operator redisObject *()
|
||||
{
|
||||
return (redisObject*)m_ptr;
|
||||
}
|
||||
|
||||
redisObject *get() { return m_ptr; }
|
||||
};
|
||||
|
||||
/* Error codes */
|
||||
#define C_OK 0
|
||||
#define C_ERR -1
|
||||
@@ -515,7 +433,6 @@ public:
|
||||
#define SLAVE_CAPA_NONE 0
|
||||
#define SLAVE_CAPA_EOF (1<<0) /* Can parse the RDB EOF streaming format. */
|
||||
#define SLAVE_CAPA_PSYNC2 (1<<1) /* Supports PSYNC2 protocol. */
|
||||
#define SLAVE_CAPA_ACTIVE_EXPIRE (1<<2) /* Will the slave perform its own expirations? (Don't send delete) */
|
||||
|
||||
/* Synchronous read timeout - replica side */
|
||||
#define CONFIG_REPL_SYNCIO_TIMEOUT 5
|
||||
@@ -1473,11 +1390,9 @@ typedef struct rdbSaveInfo {
|
||||
char repl_id[CONFIG_RUN_ID_SIZE+1]; /* Replication ID. */
|
||||
long long repl_offset; /* Replication offset. */
|
||||
int fForceSetKey;
|
||||
uint64_t mvccMinThreshold;
|
||||
struct redisMaster *mi;
|
||||
} rdbSaveInfo;
|
||||
|
||||
#define RDB_SAVE_INFO_INIT {-1,0,"000000000000000000000000000000",-1, TRUE, 0, nullptr}
|
||||
#define RDB_SAVE_INFO_INIT {-1,0,"000000000000000000000000000000",-1, TRUE}
|
||||
|
||||
struct malloc_stats {
|
||||
size_t zmalloc_used;
|
||||
@@ -1551,9 +1466,6 @@ struct redisMaster {
|
||||
|
||||
unsigned char master_uuid[UUID_BINARY_LEN]; /* Used during sync with master, this is our master's UUID */
|
||||
/* After we've connected with our master use the UUID in g_pserver->master */
|
||||
uint64_t mvccLastSync;
|
||||
/* During a handshake the server may have stale keys, we track these here to share once a reciprocal connection is made */
|
||||
std::map<int, std::vector<robj_sharedptr>> *staleKeyMap;
|
||||
};
|
||||
|
||||
// Const vars are not changed after worker threads are launched
|
||||
@@ -2243,7 +2155,6 @@ int getLongLongFromObjectOrReply(client *c, robj *o, long long *target, const ch
|
||||
int getDoubleFromObjectOrReply(client *c, robj *o, double *target, const char *msg);
|
||||
int getDoubleFromObject(const robj *o, double *target);
|
||||
int getLongLongFromObject(robj *o, long long *target);
|
||||
int getUnsignedLongLongFromObject(robj *o, uint64_t *target);
|
||||
int getLongDoubleFromObject(robj *o, long double *target);
|
||||
int getLongDoubleFromObjectOrReply(client *c, robj *o, long double *target, const char *msg);
|
||||
const char *strEncoding(int encoding);
|
||||
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
#define KEYDB_REAL_VERSION "5.2.0"
|
||||
#define KEYDB_REAL_VERSION "0.0.0"
|
||||
extern const char *KEYDB_SET_VERSION; // Unlike real version, this can be overriden by the config
|
||||
|
||||
@@ -9,7 +9,6 @@ start_server {tags {"active-repl"} overrides {active-replica yes}} {
|
||||
set master [srv 0 client]
|
||||
set master_host [srv 0 host]
|
||||
set master_port [srv 0 port]
|
||||
set master_pid [s process_id]
|
||||
|
||||
# Use a short replication timeout on the slave, so that if there
|
||||
# are no bugs the timeout is triggered in a reasonable amount
|
||||
@@ -95,26 +94,6 @@ start_server {tags {"active-repl"} overrides {active-replica yes}} {
|
||||
assert_equal {0} [$slave del testkey1]
|
||||
}
|
||||
|
||||
test {Active replica expire propogates when source is down} {
|
||||
$slave flushall
|
||||
$slave set testkey2 foo
|
||||
$slave set testkey1 foo
|
||||
wait_for_condition 50 1000 {
|
||||
[string match *foo* [$master get testkey1]]
|
||||
} else {
|
||||
fail "Replication failed to propogate"
|
||||
}
|
||||
$slave expire testkey1 2
|
||||
assert_equal {1} [$slave wait 1 500] { "value should propogate
|
||||
within 0.5 seconds" }
|
||||
exec kill -SIGSTOP $slave_pid
|
||||
after 3000
|
||||
# Ensure testkey1 is gone. Note, we can't do this directly as the normal commands lie to us
|
||||
# about what is actually in the dict. The only way to know is with a count from info
|
||||
assert_equal {1} [expr [string first {keys=1} [$master info keyspace]] >= 0] {"slave expired"}
|
||||
}
|
||||
exec kill -SIGCONT $slave_pid
|
||||
|
||||
test {Active replica different databases} {
|
||||
$master select 3
|
||||
$master set testkey abcd
|
||||
|
||||
Reference in new issue
Block a user