Hide the database dict
Former-commit-id: 065846a9c32aeb55901c194d824828fb70805350
This commit is contained in:
parent
4669d4d0d0
commit
ceaaf39c96
51
src/aof.cpp
51
src/aof.cpp
@ -1300,29 +1300,23 @@ ssize_t aofReadDiffFromParent(void) {
|
|||||||
|
|
||||||
int rewriteAppendOnlyFileRio(rio *aof) {
|
int rewriteAppendOnlyFileRio(rio *aof) {
|
||||||
dictIterator *di = NULL;
|
dictIterator *di = NULL;
|
||||||
dictEntry *de;
|
|
||||||
size_t processed = 0;
|
size_t processed = 0;
|
||||||
int j;
|
int j;
|
||||||
|
|
||||||
for (j = 0; j < cserver.dbnum; j++) {
|
for (j = 0; j < cserver.dbnum; j++) {
|
||||||
char selectcmd[] = "*2\r\n$6\r\nSELECT\r\n";
|
char selectcmd[] = "*2\r\n$6\r\nSELECT\r\n";
|
||||||
redisDb *db = g_pserver->db+j;
|
redisDb *db = g_pserver->db+j;
|
||||||
dict *d = db->pdict;
|
if (db->size() == 0) continue;
|
||||||
if (dictSize(d) == 0) continue;
|
|
||||||
di = dictGetSafeIterator(d);
|
|
||||||
|
|
||||||
/* SELECT the new DB */
|
/* SELECT the new DB */
|
||||||
if (rioWrite(aof,selectcmd,sizeof(selectcmd)-1) == 0) goto werr;
|
if (rioWrite(aof,selectcmd,sizeof(selectcmd)-1) == 0) goto werr;
|
||||||
if (rioWriteBulkLongLong(aof,j) == 0) goto werr;
|
if (rioWriteBulkLongLong(aof,j) == 0) goto werr;
|
||||||
|
|
||||||
/* Iterate this DB writing every entry */
|
/* Iterate this DB writing every entry */
|
||||||
while((de = dictNext(di)) != NULL) {
|
bool fComplete = db->iterate([&](const char *keystr, robj *o)->bool{
|
||||||
sds keystr;
|
robj key;
|
||||||
robj key, *o;
|
|
||||||
|
|
||||||
keystr = (sds)dictGetKey(de);
|
initStaticStringObject(key,(sds)keystr);
|
||||||
o = (robj*)dictGetVal(de);
|
|
||||||
initStaticStringObject(key,keystr);
|
|
||||||
|
|
||||||
expireEntry *pexpire = getExpire(db,&key);
|
expireEntry *pexpire = getExpire(db,&key);
|
||||||
|
|
||||||
@ -1330,22 +1324,22 @@ int rewriteAppendOnlyFileRio(rio *aof) {
|
|||||||
if (o->type == OBJ_STRING) {
|
if (o->type == OBJ_STRING) {
|
||||||
/* Emit a SET command */
|
/* Emit a SET command */
|
||||||
char cmd[]="*3\r\n$3\r\nSET\r\n";
|
char cmd[]="*3\r\n$3\r\nSET\r\n";
|
||||||
if (rioWrite(aof,cmd,sizeof(cmd)-1) == 0) goto werr;
|
if (rioWrite(aof,cmd,sizeof(cmd)-1) == 0) return false;
|
||||||
/* Key and value */
|
/* Key and value */
|
||||||
if (rioWriteBulkObject(aof,&key) == 0) goto werr;
|
if (rioWriteBulkObject(aof,&key) == 0) return false;
|
||||||
if (rioWriteBulkObject(aof,o) == 0) goto werr;
|
if (rioWriteBulkObject(aof,o) == 0) return false;
|
||||||
} else if (o->type == OBJ_LIST) {
|
} else if (o->type == OBJ_LIST) {
|
||||||
if (rewriteListObject(aof,&key,o) == 0) goto werr;
|
if (rewriteListObject(aof,&key,o) == 0) return false;
|
||||||
} else if (o->type == OBJ_SET) {
|
} else if (o->type == OBJ_SET) {
|
||||||
if (rewriteSetObject(aof,&key,o) == 0) goto werr;
|
if (rewriteSetObject(aof,&key,o) == 0) return false;
|
||||||
} else if (o->type == OBJ_ZSET) {
|
} else if (o->type == OBJ_ZSET) {
|
||||||
if (rewriteSortedSetObject(aof,&key,o) == 0) goto werr;
|
if (rewriteSortedSetObject(aof,&key,o) == 0) return false;
|
||||||
} else if (o->type == OBJ_HASH) {
|
} else if (o->type == OBJ_HASH) {
|
||||||
if (rewriteHashObject(aof,&key,o) == 0) goto werr;
|
if (rewriteHashObject(aof,&key,o) == 0) return false;
|
||||||
} else if (o->type == OBJ_STREAM) {
|
} else if (o->type == OBJ_STREAM) {
|
||||||
if (rewriteStreamObject(aof,&key,o) == 0) goto werr;
|
if (rewriteStreamObject(aof,&key,o) == 0) return false;
|
||||||
} else if (o->type == OBJ_MODULE) {
|
} else if (o->type == OBJ_MODULE) {
|
||||||
if (rewriteModuleObject(aof,&key,o) == 0) goto werr;
|
if (rewriteModuleObject(aof,&key,o) == 0) return false;
|
||||||
} else {
|
} else {
|
||||||
serverPanic("Unknown object type");
|
serverPanic("Unknown object type");
|
||||||
}
|
}
|
||||||
@ -1355,17 +1349,17 @@ int rewriteAppendOnlyFileRio(rio *aof) {
|
|||||||
if (subExpire.subkey() == nullptr)
|
if (subExpire.subkey() == nullptr)
|
||||||
{
|
{
|
||||||
char cmd[]="*3\r\n$9\r\nPEXPIREAT\r\n";
|
char cmd[]="*3\r\n$9\r\nPEXPIREAT\r\n";
|
||||||
if (rioWrite(aof,cmd,sizeof(cmd)-1) == 0) goto werr;
|
if (rioWrite(aof,cmd,sizeof(cmd)-1) == 0) return false;
|
||||||
if (rioWriteBulkObject(aof,&key) == 0) goto werr;
|
if (rioWriteBulkObject(aof,&key) == 0) return false;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
char cmd[]="*4\r\n$12\r\nEXPIREMEMBER\r\n";
|
char cmd[]="*4\r\n$12\r\nEXPIREMEMBER\r\n";
|
||||||
if (rioWrite(aof,cmd,sizeof(cmd)-1) == 0) goto werr;
|
if (rioWrite(aof,cmd,sizeof(cmd)-1) == 0) return false;
|
||||||
if (rioWriteBulkObject(aof,&key) == 0) goto werr;
|
if (rioWriteBulkObject(aof,&key) == 0) return false;
|
||||||
if (rioWrite(aof,subExpire.subkey(),sdslen(subExpire.subkey())) == 0) goto werr;
|
if (rioWrite(aof,subExpire.subkey(),sdslen(subExpire.subkey())) == 0) return false;
|
||||||
}
|
}
|
||||||
if (rioWriteBulkLongLong(aof,subExpire.when()) == 0) goto werr; // common
|
if (rioWriteBulkLongLong(aof,subExpire.when()) == 0) return false; // common
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* Read some diff from the parent process from time to time. */
|
/* Read some diff from the parent process from time to time. */
|
||||||
@ -1373,9 +1367,10 @@ int rewriteAppendOnlyFileRio(rio *aof) {
|
|||||||
processed = aof->processed_bytes;
|
processed = aof->processed_bytes;
|
||||||
aofReadDiffFromParent();
|
aofReadDiffFromParent();
|
||||||
}
|
}
|
||||||
}
|
return true;
|
||||||
dictReleaseIterator(di);
|
});
|
||||||
di = NULL;
|
if (!fComplete)
|
||||||
|
goto werr;
|
||||||
}
|
}
|
||||||
return C_OK;
|
return C_OK;
|
||||||
|
|
||||||
|
@ -3910,7 +3910,7 @@ int verifyClusterConfigWithData(void) {
|
|||||||
|
|
||||||
/* Make sure we only have keys in DB0. */
|
/* Make sure we only have keys in DB0. */
|
||||||
for (j = 1; j < cserver.dbnum; j++) {
|
for (j = 1; j < cserver.dbnum; j++) {
|
||||||
if (dictSize(g_pserver->db[j].pdict)) return C_ERR;
|
if (g_pserver->db[j].size()) return C_ERR;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check that all the slots we see populated memory have a corresponding
|
/* Check that all the slots we see populated memory have a corresponding
|
||||||
@ -4286,7 +4286,7 @@ NULL
|
|||||||
clusterReplyMultiBulkSlots(c);
|
clusterReplyMultiBulkSlots(c);
|
||||||
} else if (!strcasecmp(szFromObj(c->argv[1]),"flushslots") && c->argc == 2) {
|
} else if (!strcasecmp(szFromObj(c->argv[1]),"flushslots") && c->argc == 2) {
|
||||||
/* CLUSTER FLUSHSLOTS */
|
/* CLUSTER FLUSHSLOTS */
|
||||||
if (dictSize(g_pserver->db[0].pdict) != 0) {
|
if (g_pserver->db[0].size() != 0) {
|
||||||
addReplyError(c,"DB must be empty to perform CLUSTER FLUSHSLOTS.");
|
addReplyError(c,"DB must be empty to perform CLUSTER FLUSHSLOTS.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -4621,7 +4621,7 @@ NULL
|
|||||||
* slots nor keys to accept to replicate some other node.
|
* slots nor keys to accept to replicate some other node.
|
||||||
* Slaves can switch to another master without issues. */
|
* Slaves can switch to another master without issues. */
|
||||||
if (nodeIsMaster(myself) &&
|
if (nodeIsMaster(myself) &&
|
||||||
(myself->numslots != 0 || dictSize(g_pserver->db[0].pdict) != 0)) {
|
(myself->numslots != 0 || g_pserver->db[0].size() != 0)) {
|
||||||
addReplyError(c,
|
addReplyError(c,
|
||||||
"To set a master the node must be empty and "
|
"To set a master the node must be empty and "
|
||||||
"without assigned slots.");
|
"without assigned slots.");
|
||||||
@ -4778,7 +4778,7 @@ NULL
|
|||||||
|
|
||||||
/* Slaves can be reset while containing data, but not master nodes
|
/* Slaves can be reset while containing data, but not master nodes
|
||||||
* that must be empty. */
|
* that must be empty. */
|
||||||
if (nodeIsMaster(myself) && dictSize(c->db->pdict) != 0) {
|
if (nodeIsMaster(myself) && c->db->size() != 0) {
|
||||||
addReplyError(c,"CLUSTER RESET can't be called with "
|
addReplyError(c,"CLUSTER RESET can't be called with "
|
||||||
"master nodes containing keys");
|
"master nodes containing keys");
|
||||||
return;
|
return;
|
||||||
|
78
src/db.cpp
78
src/db.cpp
@ -70,10 +70,8 @@ void updateExpire(redisDb *db, sds key, robj *valOld, robj *valNew)
|
|||||||
* implementations that should instead rely on lookupKeyRead(),
|
* implementations that should instead rely on lookupKeyRead(),
|
||||||
* lookupKeyWrite() and lookupKeyReadWithFlags(). */
|
* lookupKeyWrite() and lookupKeyReadWithFlags(). */
|
||||||
static robj *lookupKey(redisDb *db, robj *key, int flags) {
|
static robj *lookupKey(redisDb *db, robj *key, int flags) {
|
||||||
dictEntry *de = dictFind(db->pdict,ptrFromObj(key));
|
robj *val = db->find(key);
|
||||||
if (de) {
|
if (val) {
|
||||||
robj *val = (robj*)dictGetVal(de);
|
|
||||||
|
|
||||||
/* Update the access time for the ageing algorithm.
|
/* Update the access time for the ageing algorithm.
|
||||||
* Don't do it if we have a saving child, as this will trigger
|
* Don't do it if we have a saving child, as this will trigger
|
||||||
* a copy on write madness. */
|
* a copy on write madness. */
|
||||||
@ -317,7 +315,7 @@ void setKey(redisDb *db, robj *key, robj *val) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int dbExists(redisDb *db, robj *key) {
|
int dbExists(redisDb *db, robj *key) {
|
||||||
return dictFind(db->pdict,ptrFromObj(key)) != NULL;
|
return (db->find(key) != nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Return a random key, in form of a Redis object.
|
/* Return a random key, in form of a Redis object.
|
||||||
@ -325,21 +323,20 @@ int dbExists(redisDb *db, robj *key) {
|
|||||||
*
|
*
|
||||||
* The function makes sure to return keys not already expired. */
|
* The function makes sure to return keys not already expired. */
|
||||||
robj *dbRandomKey(redisDb *db) {
|
robj *dbRandomKey(redisDb *db) {
|
||||||
dictEntry *de;
|
|
||||||
int maxtries = 100;
|
int maxtries = 100;
|
||||||
int allvolatile = dictSize(db->pdict) == db->setexpire->size();
|
bool allvolatile = db->size() == db->setexpire->size();
|
||||||
|
|
||||||
while(1) {
|
while(1) {
|
||||||
sds key;
|
sds key;
|
||||||
robj *keyobj;
|
robj *keyobj;
|
||||||
|
|
||||||
de = dictGetRandomKey(db->pdict);
|
auto pair = db->random();
|
||||||
if (de == NULL) return NULL;
|
if (pair.first == NULL) return NULL;
|
||||||
|
|
||||||
key = (sds)dictGetKey(de);
|
key = (sds)pair.first;
|
||||||
keyobj = createStringObject(key,sdslen(key));
|
keyobj = createStringObject(key,sdslen(key));
|
||||||
|
|
||||||
if (((robj*)dictGetVal(de))->FExpires())
|
if (pair.second->FExpires())
|
||||||
{
|
{
|
||||||
if (allvolatile && listLength(g_pserver->masters) && --maxtries == 0) {
|
if (allvolatile && listLength(g_pserver->masters) && --maxtries == 0) {
|
||||||
/* If the DB is composed only of keys with an expire set,
|
/* If the DB is composed only of keys with an expire set,
|
||||||
@ -354,7 +351,7 @@ robj *dbRandomKey(redisDb *db) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (((robj*)dictGetVal(de))->FExpires())
|
if (pair.second->FExpires())
|
||||||
{
|
{
|
||||||
if (expireIfNeeded(db,keyobj)) {
|
if (expireIfNeeded(db,keyobj)) {
|
||||||
decrRefCount(keyobj);
|
decrRefCount(keyobj);
|
||||||
@ -427,7 +424,7 @@ robj *dbUnshareStringValue(redisDb *db, robj *key, robj *o) {
|
|||||||
return o;
|
return o;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Remove all keys from all the databases in a Redis g_pserver->
|
/* Remove all keys from all the databases in a Redis DB
|
||||||
* If callback is given the function is called from time to time to
|
* If callback is given the function is called from time to time to
|
||||||
* signal that work is in progress.
|
* signal that work is in progress.
|
||||||
*
|
*
|
||||||
@ -635,9 +632,25 @@ void randomkeyCommand(client *c) {
|
|||||||
decrRefCount(key);
|
decrRefCount(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool redisDb::iterate(std::function<bool(const char*, robj*)> fn)
|
||||||
|
{
|
||||||
|
dictIterator *di = dictGetSafeIterator(pdict);
|
||||||
|
dictEntry *de = nullptr;
|
||||||
|
bool fResult = true;
|
||||||
|
while((de = dictNext(di)) != nullptr)
|
||||||
|
{
|
||||||
|
if (!fn((const char*)dictGetKey(de), (robj*)dictGetVal(de)))
|
||||||
|
{
|
||||||
|
fResult = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dictReleaseIterator(di);
|
||||||
|
return fResult;
|
||||||
|
}
|
||||||
|
|
||||||
void keysCommand(client *c) {
|
void keysCommand(client *c) {
|
||||||
dictIterator *di;
|
|
||||||
dictEntry *de;
|
|
||||||
sds pattern = szFromObj(c->argv[1]);
|
sds pattern = szFromObj(c->argv[1]);
|
||||||
int plen = sdslen(pattern), allkeys;
|
int plen = sdslen(pattern), allkeys;
|
||||||
unsigned long numkeys = 0;
|
unsigned long numkeys = 0;
|
||||||
@ -645,10 +658,8 @@ void keysCommand(client *c) {
|
|||||||
|
|
||||||
aeReleaseLock();
|
aeReleaseLock();
|
||||||
|
|
||||||
di = dictGetSafeIterator(c->db->pdict);
|
|
||||||
allkeys = (pattern[0] == '*' && pattern[1] == '\0');
|
allkeys = (pattern[0] == '*' && pattern[1] == '\0');
|
||||||
while((de = dictNext(di)) != NULL) {
|
c->db->iterate([&](const char *key, robj *)->bool {
|
||||||
sds key = (sds)dictGetKey(de);
|
|
||||||
robj *keyobj;
|
robj *keyobj;
|
||||||
|
|
||||||
if (allkeys || stringmatchlen(pattern,plen,key,sdslen(key),0)) {
|
if (allkeys || stringmatchlen(pattern,plen,key,sdslen(key),0)) {
|
||||||
@ -659,8 +670,8 @@ void keysCommand(client *c) {
|
|||||||
}
|
}
|
||||||
decrRefCount(keyobj);
|
decrRefCount(keyobj);
|
||||||
}
|
}
|
||||||
}
|
return true;
|
||||||
dictReleaseIterator(di);
|
});
|
||||||
setDeferredArrayLen(c,replylen,numkeys);
|
setDeferredArrayLen(c,replylen,numkeys);
|
||||||
|
|
||||||
fastlock_unlock(&c->db->lock); // we must release the DB lock before acquiring the AE lock to prevent deadlocks
|
fastlock_unlock(&c->db->lock); // we must release the DB lock before acquiring the AE lock to prevent deadlocks
|
||||||
@ -928,7 +939,7 @@ void scanCommand(client *c) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void dbsizeCommand(client *c) {
|
void dbsizeCommand(client *c) {
|
||||||
addReplyLongLong(c,dictSize(c->db->pdict));
|
addReplyLongLong(c,c->db->size());
|
||||||
}
|
}
|
||||||
|
|
||||||
void lastsaveCommand(client *c) {
|
void lastsaveCommand(client *c) {
|
||||||
@ -1313,20 +1324,17 @@ void setExpire(client *c, redisDb *db, robj *key, expireEntry &&e)
|
|||||||
/* Return the expire time of the specified key, or null if no expire
|
/* Return the expire time of the specified key, or null if no expire
|
||||||
* is associated with this key (i.e. the key is non volatile) */
|
* is associated with this key (i.e. the key is non volatile) */
|
||||||
expireEntry *getExpire(redisDb *db, robj_roptr key) {
|
expireEntry *getExpire(redisDb *db, robj_roptr key) {
|
||||||
dictEntry *de;
|
|
||||||
|
|
||||||
/* No expire? return ASAP */
|
/* No expire? return ASAP */
|
||||||
if (db->setexpire->size() == 0)
|
if (db->setexpire->size() == 0)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
de = dictFind(db->pdict, ptrFromObj(key));
|
auto pair = db->lookup_tuple(key);
|
||||||
if (de == NULL)
|
if (pair.first == nullptr)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
robj *obj = (robj*)dictGetVal(de);
|
if (!pair.second->FExpires())
|
||||||
if (!obj->FExpires())
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
auto itr = db->setexpire->find((sds)dictGetKey(de));
|
auto itr = db->setexpire->find(pair.first);
|
||||||
return itr.operator->();
|
return itr.operator->();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1789,3 +1797,17 @@ unsigned int delKeysInSlot(unsigned int hashslot) {
|
|||||||
unsigned int countKeysInSlot(unsigned int hashslot) {
|
unsigned int countKeysInSlot(unsigned int hashslot) {
|
||||||
return g_pserver->cluster->slots_keys_count[hashslot];
|
return g_pserver->cluster->slots_keys_count[hashslot];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void redisDb::initialize(int id)
|
||||||
|
{
|
||||||
|
this->pdict = dictCreate(&dbDictType,NULL);
|
||||||
|
this->setexpire = new(MALLOC_LOCAL) expireset();
|
||||||
|
this->expireitr = this->setexpire->end();
|
||||||
|
this->blocking_keys = dictCreate(&keylistDictType,NULL);
|
||||||
|
this->ready_keys = dictCreate(&objectKeyPointerValueDictType,NULL);
|
||||||
|
this->watched_keys = dictCreate(&keylistDictType,NULL);
|
||||||
|
this->id = id;
|
||||||
|
this->avg_ttl = 0;
|
||||||
|
this->last_expire_set = 0;
|
||||||
|
this->defrag_later = listCreate();
|
||||||
|
}
|
||||||
|
@ -266,8 +266,6 @@ void xorObjectDigest(redisDb *db, robj_roptr keyobj, unsigned char *digest, robj
|
|||||||
* a different digest. */
|
* a different digest. */
|
||||||
void computeDatasetDigest(unsigned char *final) {
|
void computeDatasetDigest(unsigned char *final) {
|
||||||
unsigned char digest[20];
|
unsigned char digest[20];
|
||||||
dictIterator *di = NULL;
|
|
||||||
dictEntry *de;
|
|
||||||
int j;
|
int j;
|
||||||
uint32_t aux;
|
uint32_t aux;
|
||||||
|
|
||||||
@ -276,8 +274,7 @@ void computeDatasetDigest(unsigned char *final) {
|
|||||||
for (j = 0; j < cserver.dbnum; j++) {
|
for (j = 0; j < cserver.dbnum; j++) {
|
||||||
redisDb *db = g_pserver->db+j;
|
redisDb *db = g_pserver->db+j;
|
||||||
|
|
||||||
if (dictSize(db->pdict) == 0) continue;
|
if (db->size() == 0) continue;
|
||||||
di = dictGetSafeIterator(db->pdict);
|
|
||||||
|
|
||||||
/* hash the DB id, so the same dataset moved in a different
|
/* hash the DB id, so the same dataset moved in a different
|
||||||
* DB will lead to a different digest */
|
* DB will lead to a different digest */
|
||||||
@ -285,24 +282,21 @@ void computeDatasetDigest(unsigned char *final) {
|
|||||||
mixDigest(final,&aux,sizeof(aux));
|
mixDigest(final,&aux,sizeof(aux));
|
||||||
|
|
||||||
/* Iterate this DB writing every entry */
|
/* Iterate this DB writing every entry */
|
||||||
while((de = dictNext(di)) != NULL) {
|
db->iterate([&](const char *key, robj *o)->bool {
|
||||||
sds key;
|
robj *keyobj;
|
||||||
robj *keyobj, *o;
|
|
||||||
|
|
||||||
memset(digest,0,20); /* This key-val digest */
|
memset(digest,0,20); /* This key-val digest */
|
||||||
key = (sds)dictGetKey(de);
|
|
||||||
keyobj = createStringObject(key,sdslen(key));
|
keyobj = createStringObject(key,sdslen(key));
|
||||||
|
|
||||||
mixDigest(digest,key,sdslen(key));
|
mixDigest(digest,key,sdslen(key));
|
||||||
|
|
||||||
o = (robj*)dictGetVal(de);
|
|
||||||
xorObjectDigest(db,keyobj,digest,o);
|
xorObjectDigest(db,keyobj,digest,o);
|
||||||
|
|
||||||
/* We can finally xor the key-val digest to the final digest */
|
/* We can finally xor the key-val digest to the final digest */
|
||||||
xorDigest(final,digest,20);
|
xorDigest(final,digest,20);
|
||||||
decrRefCount(keyobj);
|
decrRefCount(keyobj);
|
||||||
}
|
return true;
|
||||||
dictReleaseIterator(di);
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -394,15 +388,14 @@ NULL
|
|||||||
serverLog(LL_WARNING,"Append Only File loaded by DEBUG LOADAOF");
|
serverLog(LL_WARNING,"Append Only File loaded by DEBUG LOADAOF");
|
||||||
addReply(c,shared.ok);
|
addReply(c,shared.ok);
|
||||||
} else if (!strcasecmp(szFromObj(c->argv[1]),"object") && c->argc == 3) {
|
} else if (!strcasecmp(szFromObj(c->argv[1]),"object") && c->argc == 3) {
|
||||||
dictEntry *de;
|
|
||||||
robj *val;
|
robj *val;
|
||||||
const char *strenc;
|
const char *strenc;
|
||||||
|
|
||||||
if ((de = dictFind(c->db->pdict,ptrFromObj(c->argv[2]))) == NULL) {
|
val = c->db->find(c->argv[2]);
|
||||||
|
if (val == NULL) {
|
||||||
addReply(c,shared.nokeyerr);
|
addReply(c,shared.nokeyerr);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
val = (robj*)dictGetVal(de);
|
|
||||||
strenc = strEncoding(val->encoding);
|
strenc = strEncoding(val->encoding);
|
||||||
|
|
||||||
char extra[138] = {0};
|
char extra[138] = {0};
|
||||||
@ -446,16 +439,14 @@ NULL
|
|||||||
strenc, rdbSavedObjectLen(val),
|
strenc, rdbSavedObjectLen(val),
|
||||||
val->lru, estimateObjectIdleTime(val)/1000, extra);
|
val->lru, estimateObjectIdleTime(val)/1000, extra);
|
||||||
} else if (!strcasecmp(szFromObj(c->argv[1]),"sdslen") && c->argc == 3) {
|
} else if (!strcasecmp(szFromObj(c->argv[1]),"sdslen") && c->argc == 3) {
|
||||||
dictEntry *de;
|
auto pair = c->db->lookup_tuple(c->argv[2]);
|
||||||
robj *val;
|
robj *val = pair.second;
|
||||||
sds key;
|
const char *key = pair.first;
|
||||||
|
|
||||||
if ((de = dictFind(c->db->pdict,ptrFromObj(c->argv[2]))) == NULL) {
|
if (val == NULL) {
|
||||||
addReply(c,shared.nokeyerr);
|
addReply(c,shared.nokeyerr);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
val = (robj*)dictGetVal(de);
|
|
||||||
key = (sds)dictGetKey(de);
|
|
||||||
|
|
||||||
if (val->type != OBJ_STRING || !sdsEncodedObject(val)) {
|
if (val->type != OBJ_STRING || !sdsEncodedObject(val)) {
|
||||||
addReplyError(c,"Not an sds encoded string.");
|
addReplyError(c,"Not an sds encoded string.");
|
||||||
@ -465,16 +456,16 @@ NULL
|
|||||||
"val_sds_len:%lld, val_sds_avail:%lld, val_zmalloc: %lld",
|
"val_sds_len:%lld, val_sds_avail:%lld, val_zmalloc: %lld",
|
||||||
(long long) sdslen(key),
|
(long long) sdslen(key),
|
||||||
(long long) sdsavail(key),
|
(long long) sdsavail(key),
|
||||||
(long long) sdsZmallocSize(key),
|
(long long) sdsZmallocSize((sds)key),
|
||||||
(long long) sdslen(szFromObj(val)),
|
(long long) sdslen(szFromObj(val)),
|
||||||
(long long) sdsavail(szFromObj(val)),
|
(long long) sdsavail(szFromObj(val)),
|
||||||
(long long) getStringObjectSdsUsedMemory(val));
|
(long long) getStringObjectSdsUsedMemory(val));
|
||||||
}
|
}
|
||||||
} else if (!strcasecmp(szFromObj(c->argv[1]),"ziplist") && c->argc == 3) {
|
} else if (!strcasecmp(szFromObj(c->argv[1]),"ziplist") && c->argc == 3) {
|
||||||
robj *o;
|
robj_roptr o;
|
||||||
|
|
||||||
if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.nokeyerr))
|
if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.nokeyerr))
|
||||||
== NULL) return;
|
== nullptr) return;
|
||||||
|
|
||||||
if (o->encoding != OBJ_ENCODING_ZIPLIST) {
|
if (o->encoding != OBJ_ENCODING_ZIPLIST) {
|
||||||
addReplyError(c,"Not an sds encoded string.");
|
addReplyError(c,"Not an sds encoded string.");
|
||||||
@ -490,7 +481,7 @@ NULL
|
|||||||
|
|
||||||
if (getLongFromObjectOrReply(c, c->argv[2], &keys, NULL) != C_OK)
|
if (getLongFromObjectOrReply(c, c->argv[2], &keys, NULL) != C_OK)
|
||||||
return;
|
return;
|
||||||
dictExpand(c->db->pdict,keys);
|
c->db->expand(keys);
|
||||||
for (j = 0; j < keys; j++) {
|
for (j = 0; j < keys; j++) {
|
||||||
long valsize = 0;
|
long valsize = 0;
|
||||||
snprintf(buf,sizeof(buf),"%s:%lu",
|
snprintf(buf,sizeof(buf),"%s:%lu",
|
||||||
@ -641,7 +632,7 @@ NULL
|
|||||||
}
|
}
|
||||||
|
|
||||||
stats = sdscatprintf(stats,"[Dictionary HT]\n");
|
stats = sdscatprintf(stats,"[Dictionary HT]\n");
|
||||||
dictGetStats(buf,sizeof(buf),g_pserver->db[dbid].pdict);
|
g_pserver->db[dbid].getStats(buf,sizeof(buf));
|
||||||
stats = sdscat(stats,buf);
|
stats = sdscat(stats,buf);
|
||||||
|
|
||||||
stats = sdscatprintf(stats,"[Expires set]\n");
|
stats = sdscatprintf(stats,"[Expires set]\n");
|
||||||
@ -650,11 +641,11 @@ NULL
|
|||||||
|
|
||||||
addReplyBulkSds(c,stats);
|
addReplyBulkSds(c,stats);
|
||||||
} else if (!strcasecmp(szFromObj(c->argv[1]),"htstats-key") && c->argc == 3) {
|
} else if (!strcasecmp(szFromObj(c->argv[1]),"htstats-key") && c->argc == 3) {
|
||||||
robj *o;
|
robj_roptr o;
|
||||||
dict *ht = NULL;
|
dict *ht = NULL;
|
||||||
|
|
||||||
if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.nokeyerr))
|
if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.nokeyerr))
|
||||||
== NULL) return;
|
== nullptr) return;
|
||||||
|
|
||||||
/* Get the hash table reference from the object, if possible. */
|
/* Get the hash table reference from the object, if possible. */
|
||||||
switch (o->encoding) {
|
switch (o->encoding) {
|
||||||
@ -1254,12 +1245,10 @@ void logCurrentClient(void) {
|
|||||||
* selected DB, and if so print info about the associated object. */
|
* selected DB, and if so print info about the associated object. */
|
||||||
if (cc->argc >= 1) {
|
if (cc->argc >= 1) {
|
||||||
robj *val, *key;
|
robj *val, *key;
|
||||||
dictEntry *de;
|
|
||||||
|
|
||||||
key = getDecodedObject(cc->argv[1]);
|
key = getDecodedObject(cc->argv[1]);
|
||||||
de = dictFind(cc->db->pdict, ptrFromObj(key));
|
val = cc->db->find(key);
|
||||||
if (de) {
|
if (val) {
|
||||||
val = (robj*)dictGetVal(de);
|
|
||||||
serverLog(LL_WARNING,"key '%s' found in DB containing the following object:", (char*)ptrFromObj(key));
|
serverLog(LL_WARNING,"key '%s' found in DB containing the following object:", (char*)ptrFromObj(key));
|
||||||
serverLogObjectDebugInfo(val);
|
serverLogObjectDebugInfo(val);
|
||||||
}
|
}
|
||||||
|
@ -902,9 +902,8 @@ long defragOtherGlobals() {
|
|||||||
|
|
||||||
/* returns 0 more work may or may not be needed (see non-zero cursor),
|
/* returns 0 more work may or may not be needed (see non-zero cursor),
|
||||||
* and 1 if time is up and more work is needed. */
|
* and 1 if time is up and more work is needed. */
|
||||||
int defragLaterItem(dictEntry *de, unsigned long *cursor, long long endtime) {
|
int defragLaterItem(robj *ob, unsigned long *cursor, long long endtime) {
|
||||||
if (de) {
|
if (ob) {
|
||||||
robj *ob = (robj*)dictGetVal(de);
|
|
||||||
if (ob->type == OBJ_LIST) {
|
if (ob->type == OBJ_LIST) {
|
||||||
g_pserver->stat_active_defrag_hits += scanLaterList(ob);
|
g_pserver->stat_active_defrag_hits += scanLaterList(ob);
|
||||||
*cursor = 0; /* list has no scan, we must finish it in one go */
|
*cursor = 0; /* list has no scan, we must finish it in one go */
|
||||||
@ -959,11 +958,11 @@ int defragLaterStep(redisDb *db, long long endtime) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* each time we enter this function we need to fetch the key from the dict again (if it still exists) */
|
/* each time we enter this function we need to fetch the key from the dict again (if it still exists) */
|
||||||
dictEntry *de = dictFind(db->pdict, current_key);
|
robj *o = db->find(current_key);
|
||||||
key_defragged = g_pserver->stat_active_defrag_hits;
|
key_defragged = g_pserver->stat_active_defrag_hits;
|
||||||
do {
|
do {
|
||||||
int quit = 0;
|
int quit = 0;
|
||||||
if (defragLaterItem(de, &cursor, endtime))
|
if (defragLaterItem(o, &cursor, endtime))
|
||||||
quit = 1; /* time is up, we didn't finish all the work */
|
quit = 1; /* time is up, we didn't finish all the work */
|
||||||
|
|
||||||
/* Don't start a new BIG key in this loop, this is because the
|
/* Don't start a new BIG key in this loop, this is because the
|
||||||
|
@ -87,7 +87,7 @@ unsigned int LRU_CLOCK(void) {
|
|||||||
|
|
||||||
/* Given an object returns the min number of milliseconds the object was never
|
/* Given an object returns the min number of milliseconds the object was never
|
||||||
* requested, using an approximated LRU algorithm. */
|
* requested, using an approximated LRU algorithm. */
|
||||||
unsigned long long estimateObjectIdleTime(robj *o) {
|
unsigned long long estimateObjectIdleTime(robj_roptr o) {
|
||||||
unsigned long long lruclock = LRU_CLOCK();
|
unsigned long long lruclock = LRU_CLOCK();
|
||||||
if (lruclock >= o->lru) {
|
if (lruclock >= o->lru) {
|
||||||
return (lruclock - o->lru) * LRU_CLOCK_RESOLUTION;
|
return (lruclock - o->lru) * LRU_CLOCK_RESOLUTION;
|
||||||
@ -252,17 +252,17 @@ struct visitFunctor
|
|||||||
return count < g_pserver->maxmemory_samples;
|
return count < g_pserver->maxmemory_samples;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
void evictionPoolPopulate(int dbid, dict *dbdict, expireset *setexpire, struct evictionPoolEntry *pool)
|
void evictionPoolPopulate(int dbid, redisDb *db, expireset *setexpire, struct evictionPoolEntry *pool)
|
||||||
{
|
{
|
||||||
if (setexpire != nullptr)
|
if (setexpire != nullptr)
|
||||||
{
|
{
|
||||||
visitFunctor visitor { dbid, dbdict, pool, 0 };
|
visitFunctor visitor { dbid, db->pdict, pool, 0 };
|
||||||
setexpire->random_visit(visitor);
|
setexpire->random_visit(visitor);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
dictEntry **samples = (dictEntry**)alloca(g_pserver->maxmemory_samples * sizeof(dictEntry*));
|
dictEntry **samples = (dictEntry**)alloca(g_pserver->maxmemory_samples * sizeof(dictEntry*));
|
||||||
int count = dictGetSomeKeys(dbdict,samples,g_pserver->maxmemory_samples);
|
int count = dictGetSomeKeys(db->pdict,samples,g_pserver->maxmemory_samples);
|
||||||
for (int j = 0; j < count; j++) {
|
for (int j = 0; j < count; j++) {
|
||||||
robj *o = (robj*)dictGetVal(samples[j]);
|
robj *o = (robj*)dictGetVal(samples[j]);
|
||||||
processEvictionCandidate(dbid, (sds)dictGetKey(samples[j]), o, nullptr, pool);
|
processEvictionCandidate(dbid, (sds)dictGetKey(samples[j]), o, nullptr, pool);
|
||||||
@ -504,8 +504,8 @@ int freeMemoryIfNeeded(void) {
|
|||||||
db = g_pserver->db+i;
|
db = g_pserver->db+i;
|
||||||
if (g_pserver->maxmemory_policy & MAXMEMORY_FLAG_ALLKEYS)
|
if (g_pserver->maxmemory_policy & MAXMEMORY_FLAG_ALLKEYS)
|
||||||
{
|
{
|
||||||
if ((keys = dictSize(db->pdict)) != 0) {
|
if ((keys = db->size()) != 0) {
|
||||||
evictionPoolPopulate(i, db->pdict, nullptr, pool);
|
evictionPoolPopulate(i, db, nullptr, pool);
|
||||||
total_keys += keys;
|
total_keys += keys;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -513,7 +513,7 @@ int freeMemoryIfNeeded(void) {
|
|||||||
{
|
{
|
||||||
keys = db->setexpire->size();
|
keys = db->setexpire->size();
|
||||||
if (keys != 0)
|
if (keys != 0)
|
||||||
evictionPoolPopulate(i, db->pdict, db->setexpire, pool);
|
evictionPoolPopulate(i, db, db->setexpire, pool);
|
||||||
total_keys += keys;
|
total_keys += keys;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -525,9 +525,9 @@ int freeMemoryIfNeeded(void) {
|
|||||||
bestdbid = pool[k].dbid;
|
bestdbid = pool[k].dbid;
|
||||||
sds key = nullptr;
|
sds key = nullptr;
|
||||||
|
|
||||||
dictEntry *de = dictFind(g_pserver->db[pool[k].dbid].pdict,pool[k].key);
|
auto pair = g_pserver->db[pool[k].dbid].lookup_tuple(pool[k].key);
|
||||||
if (de != nullptr && (g_pserver->maxmemory_policy & MAXMEMORY_FLAG_ALLKEYS || ((robj*)dictGetVal(de))->FExpires()))
|
if (pair.first != nullptr && (g_pserver->maxmemory_policy & MAXMEMORY_FLAG_ALLKEYS || pair.second->FExpires()))
|
||||||
key = (sds)dictGetKey(de);
|
key = (sds)pair.first;
|
||||||
|
|
||||||
/* Remove the entry from the pool. */
|
/* Remove the entry from the pool. */
|
||||||
if (pool[k].key != pool[k].cached)
|
if (pool[k].key != pool[k].cached)
|
||||||
@ -559,9 +559,9 @@ int freeMemoryIfNeeded(void) {
|
|||||||
db = g_pserver->db+j;
|
db = g_pserver->db+j;
|
||||||
if (g_pserver->maxmemory_policy == MAXMEMORY_ALLKEYS_RANDOM)
|
if (g_pserver->maxmemory_policy == MAXMEMORY_ALLKEYS_RANDOM)
|
||||||
{
|
{
|
||||||
if (dictSize(db->pdict) != 0) {
|
if (db->size() != 0) {
|
||||||
dictEntry *de = dictGetRandomKey(db->pdict);
|
auto pair = db->random();
|
||||||
bestkey = (sds)dictGetKey(de);
|
bestkey = (sds)pair.first;
|
||||||
bestdbid = j;
|
bestdbid = j;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -581,16 +581,17 @@ int freeMemoryIfNeeded(void) {
|
|||||||
/* Finally remove the selected key. */
|
/* Finally remove the selected key. */
|
||||||
if (bestkey) {
|
if (bestkey) {
|
||||||
db = g_pserver->db+bestdbid;
|
db = g_pserver->db+bestdbid;
|
||||||
|
|
||||||
robj *keyobj = createStringObject(bestkey,sdslen(bestkey));
|
robj *keyobj = createStringObject(bestkey,sdslen(bestkey));
|
||||||
propagateExpire(db,keyobj,g_pserver->lazyfree_lazy_eviction);
|
propagateExpire(db,keyobj,g_pserver->lazyfree_lazy_eviction);
|
||||||
/* We compute the amount of memory freed by db*Delete() alone.
|
/* We compute the amount of memory freed by db*Delete() alone.
|
||||||
* It is possible that actually the memory needed to propagate
|
* It is possible that actually the memory needed to propagate
|
||||||
* the DEL in AOF and replication link is greater than the one
|
* the DEL in AOF and replication link is greater than the one
|
||||||
* we are freeing removing the key, but we can't account for
|
* we are freeing removing the key, but we can't account for
|
||||||
* that otherwise we would never exit the loop.
|
* that otherwise we would never exit the loop.
|
||||||
*
|
*
|
||||||
* AOF and Output buffer memory will be freed eventually so
|
* AOF and Output buffer memory will be freed eventually so
|
||||||
* we only care about memory used by the key space. */
|
* we only care about memory used by the key space. */
|
||||||
delta = (long long) zmalloc_used_memory();
|
delta = (long long) zmalloc_used_memory();
|
||||||
latencyStartMonitor(eviction_latency);
|
latencyStartMonitor(eviction_latency);
|
||||||
if (g_pserver->lazyfree_lazy_eviction)
|
if (g_pserver->lazyfree_lazy_eviction)
|
||||||
@ -609,18 +610,18 @@ int freeMemoryIfNeeded(void) {
|
|||||||
keys_freed++;
|
keys_freed++;
|
||||||
|
|
||||||
/* When the memory to free starts to be big enough, we may
|
/* When the memory to free starts to be big enough, we may
|
||||||
* start spending so much time here that is impossible to
|
* start spending so much time here that is impossible to
|
||||||
* deliver data to the slaves fast enough, so we force the
|
* deliver data to the slaves fast enough, so we force the
|
||||||
* transmission here inside the loop. */
|
* transmission here inside the loop. */
|
||||||
if (slaves) flushSlavesOutputBuffers();
|
if (slaves) flushSlavesOutputBuffers();
|
||||||
|
|
||||||
/* Normally our stop condition is the ability to release
|
/* Normally our stop condition is the ability to release
|
||||||
* a fixed, pre-computed amount of memory. However when we
|
* a fixed, pre-computed amount of memory. However when we
|
||||||
* are deleting objects in another thread, it's better to
|
* are deleting objects in another thread, it's better to
|
||||||
* check, from time to time, if we already reached our target
|
* check, from time to time, if we already reached our target
|
||||||
* memory, since the "mem_freed" amount is computed only
|
* memory, since the "mem_freed" amount is computed only
|
||||||
* across the dbAsyncDelete() call, while the thread can
|
* across the dbAsyncDelete() call, while the thread can
|
||||||
* release the memory all the time. */
|
* release the memory all the time. */
|
||||||
if (g_pserver->lazyfree_lazy_eviction && !(keys_freed % 16)) {
|
if (g_pserver->lazyfree_lazy_eviction && !(keys_freed % 16)) {
|
||||||
if (getMaxmemoryState(NULL,NULL,NULL,NULL) == C_OK) {
|
if (getMaxmemoryState(NULL,NULL,NULL,NULL) == C_OK) {
|
||||||
/* Let's satisfy our stop condition. */
|
/* Let's satisfy our stop condition. */
|
||||||
|
@ -74,8 +74,7 @@ void activeExpireCycleExpire(redisDb *db, expireEntry &e, long long now) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
expireEntryFat *pfat = e.pfatentry();
|
expireEntryFat *pfat = e.pfatentry();
|
||||||
dictEntry *de = dictFind(db->pdict, e.key());
|
robj *val = db->find(e.key());
|
||||||
robj *val = (robj*)dictGetVal(de);
|
|
||||||
int deleted = 0;
|
int deleted = 0;
|
||||||
while (!pfat->FEmpty())
|
while (!pfat->FEmpty())
|
||||||
{
|
{
|
||||||
@ -140,14 +139,12 @@ void expireMemberCommand(client *c)
|
|||||||
when += mstime();
|
when += mstime();
|
||||||
|
|
||||||
/* No key, return zero. */
|
/* No key, return zero. */
|
||||||
dictEntry *de = dictFind(c->db->pdict, szFromObj(c->argv[1]));
|
robj *val = c->db->find(c->argv[1]);
|
||||||
if (de == NULL) {
|
if (val == nullptr) {
|
||||||
addReply(c,shared.czero);
|
addReply(c,shared.czero);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
robj *val = (robj*)dictGetVal(de);
|
|
||||||
|
|
||||||
switch (val->type)
|
switch (val->type)
|
||||||
{
|
{
|
||||||
case OBJ_SET:
|
case OBJ_SET:
|
||||||
@ -361,10 +358,10 @@ void expireSlaveKeys(void) {
|
|||||||
redisDb *db = g_pserver->db+dbid;
|
redisDb *db = g_pserver->db+dbid;
|
||||||
|
|
||||||
// the expire is hashed based on the key pointer, so we need the point in the main db
|
// the expire is hashed based on the key pointer, so we need the point in the main db
|
||||||
dictEntry *deMain = dictFind(db->pdict, keyname);
|
auto pairMain = db->lookup_tuple(keyname);
|
||||||
auto itr = db->setexpire->end();
|
auto itr = db->setexpire->end();
|
||||||
if (deMain != nullptr)
|
if (pairMain.first != nullptr)
|
||||||
itr = db->setexpire->find((sds)dictGetKey(deMain));
|
itr = db->setexpire->find((sds)pairMain.first);
|
||||||
int expired = 0;
|
int expired = 0;
|
||||||
|
|
||||||
if (itr != db->setexpire->end())
|
if (itr != db->setexpire->end())
|
||||||
|
@ -333,7 +333,7 @@ void touchWatchedKeysOnFlush(int dbid) {
|
|||||||
* key exists, mark the client as dirty, as the key will be
|
* key exists, mark the client as dirty, as the key will be
|
||||||
* removed. */
|
* removed. */
|
||||||
if (dbid == -1 || wk->db->id == dbid) {
|
if (dbid == -1 || wk->db->id == dbid) {
|
||||||
if (dictFind(wk->db->pdict, ptrFromObj(wk->key)) != NULL)
|
if (wk->db->find(wk->key) != NULL)
|
||||||
c->flags |= CLIENT_DIRTY_CAS;
|
c->flags |= CLIENT_DIRTY_CAS;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -789,7 +789,7 @@ size_t streamRadixTreeMemoryUsage(rax *rax) {
|
|||||||
* case of aggregated data types where only "sample_size" elements
|
* case of aggregated data types where only "sample_size" elements
|
||||||
* are checked and averaged to estimate the total size. */
|
* are checked and averaged to estimate the total size. */
|
||||||
#define OBJ_COMPUTE_SIZE_DEF_SAMPLES 5 /* Default sample size. */
|
#define OBJ_COMPUTE_SIZE_DEF_SAMPLES 5 /* Default sample size. */
|
||||||
size_t objectComputeSize(robj *o, size_t sample_size) {
|
size_t objectComputeSize(robj_roptr o, size_t sample_size) {
|
||||||
sds ele, ele2;
|
sds ele, ele2;
|
||||||
dict *d;
|
dict *d;
|
||||||
dictIterator *di;
|
dictIterator *di;
|
||||||
@ -800,7 +800,7 @@ size_t objectComputeSize(robj *o, size_t sample_size) {
|
|||||||
if(o->encoding == OBJ_ENCODING_INT) {
|
if(o->encoding == OBJ_ENCODING_INT) {
|
||||||
asize = sizeof(*o);
|
asize = sizeof(*o);
|
||||||
} else if(o->encoding == OBJ_ENCODING_RAW) {
|
} else if(o->encoding == OBJ_ENCODING_RAW) {
|
||||||
asize = sdsAllocSize(szFromObj(o))+sizeof(*o);
|
asize = sdsAllocSize((sds)szFromObj(o))+sizeof(*o);
|
||||||
} else if(o->encoding == OBJ_ENCODING_EMBSTR) {
|
} else if(o->encoding == OBJ_ENCODING_EMBSTR) {
|
||||||
asize = sdslen(szFromObj(o))+2+sizeof(*o);
|
asize = sdslen(szFromObj(o))+2+sizeof(*o);
|
||||||
} else {
|
} else {
|
||||||
@ -1054,16 +1054,16 @@ struct redisMemOverhead *getMemoryOverheadData(void) {
|
|||||||
|
|
||||||
for (j = 0; j < cserver.dbnum; j++) {
|
for (j = 0; j < cserver.dbnum; j++) {
|
||||||
redisDb *db = g_pserver->db+j;
|
redisDb *db = g_pserver->db+j;
|
||||||
long long keyscount = dictSize(db->pdict);
|
long long keyscount = db->size();
|
||||||
if (keyscount==0) continue;
|
if (keyscount==0) continue;
|
||||||
|
|
||||||
mh->total_keys += keyscount;
|
mh->total_keys += keyscount;
|
||||||
mh->db = (decltype(mh->db))zrealloc(mh->db,sizeof(mh->db[0])*(mh->num_dbs+1), MALLOC_LOCAL);
|
mh->db = (decltype(mh->db))zrealloc(mh->db,sizeof(mh->db[0])*(mh->num_dbs+1), MALLOC_LOCAL);
|
||||||
mh->db[mh->num_dbs].dbid = j;
|
mh->db[mh->num_dbs].dbid = j;
|
||||||
|
|
||||||
mem = dictSize(db->pdict) * sizeof(dictEntry) +
|
mem = db->size() * sizeof(dictEntry) +
|
||||||
dictSlots(db->pdict) * sizeof(dictEntry*) +
|
db->slots() * sizeof(dictEntry*) +
|
||||||
dictSize(db->pdict) * sizeof(robj);
|
db->size() * sizeof(robj);
|
||||||
mh->db[mh->num_dbs].overhead_ht_main = mem;
|
mh->db[mh->num_dbs].overhead_ht_main = mem;
|
||||||
mem_total+=mem;
|
mem_total+=mem;
|
||||||
|
|
||||||
@ -1246,15 +1246,12 @@ void objectSetLRUOrLFU(robj *val, long long lfu_freq, long long lru_idle,
|
|||||||
|
|
||||||
/* This is a helper function for the OBJECT command. We need to lookup keys
|
/* This is a helper function for the OBJECT command. We need to lookup keys
|
||||||
* without any modification of LRU or other parameters. */
|
* without any modification of LRU or other parameters. */
|
||||||
robj *objectCommandLookup(client *c, robj *key) {
|
robj_roptr objectCommandLookup(client *c, robj *key) {
|
||||||
dictEntry *de;
|
return c->db->find(key);
|
||||||
|
|
||||||
if ((de = dictFind(c->db->pdict,ptrFromObj(key))) == NULL) return NULL;
|
|
||||||
return (robj*) dictGetVal(de);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
robj *objectCommandLookupOrReply(client *c, robj *key, robj *reply) {
|
robj_roptr objectCommandLookupOrReply(client *c, robj *key, robj *reply) {
|
||||||
robj *o = objectCommandLookup(c,key);
|
robj_roptr o = objectCommandLookup(c,key);
|
||||||
|
|
||||||
if (!o) addReply(c, reply);
|
if (!o) addReply(c, reply);
|
||||||
return o;
|
return o;
|
||||||
@ -1263,7 +1260,7 @@ robj *objectCommandLookupOrReply(client *c, robj *key, robj *reply) {
|
|||||||
/* Object command allows to inspect the internals of an Redis Object.
|
/* Object command allows to inspect the internals of an Redis Object.
|
||||||
* Usage: OBJECT <refcount|encoding|idletime|freq> <key> */
|
* Usage: OBJECT <refcount|encoding|idletime|freq> <key> */
|
||||||
void objectCommand(client *c) {
|
void objectCommand(client *c) {
|
||||||
robj *o;
|
robj_roptr o;
|
||||||
|
|
||||||
if (c->argc == 2 && !strcasecmp(szFromObj(c->argv[1]),"help")) {
|
if (c->argc == 2 && !strcasecmp(szFromObj(c->argv[1]),"help")) {
|
||||||
const char *help[] = {
|
const char *help[] = {
|
||||||
@ -1276,15 +1273,15 @@ NULL
|
|||||||
addReplyHelp(c, help);
|
addReplyHelp(c, help);
|
||||||
} else if (!strcasecmp(szFromObj(c->argv[1]),"refcount") && c->argc == 3) {
|
} else if (!strcasecmp(szFromObj(c->argv[1]),"refcount") && c->argc == 3) {
|
||||||
if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.null[c->resp]))
|
if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.null[c->resp]))
|
||||||
== NULL) return;
|
== nullptr) return;
|
||||||
addReplyLongLong(c,o->getrefcount(std::memory_order_relaxed));
|
addReplyLongLong(c,o->getrefcount(std::memory_order_relaxed));
|
||||||
} else if (!strcasecmp(szFromObj(c->argv[1]),"encoding") && c->argc == 3) {
|
} else if (!strcasecmp(szFromObj(c->argv[1]),"encoding") && c->argc == 3) {
|
||||||
if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.null[c->resp]))
|
if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.null[c->resp]))
|
||||||
== NULL) return;
|
== nullptr) return;
|
||||||
addReplyBulkCString(c,strEncoding(o->encoding));
|
addReplyBulkCString(c,strEncoding(o->encoding));
|
||||||
} else if (!strcasecmp(szFromObj(c->argv[1]),"idletime") && c->argc == 3) {
|
} else if (!strcasecmp(szFromObj(c->argv[1]),"idletime") && c->argc == 3) {
|
||||||
if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.null[c->resp]))
|
if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.null[c->resp]))
|
||||||
== NULL) return;
|
== nullptr) return;
|
||||||
if (g_pserver->maxmemory_policy & MAXMEMORY_FLAG_LFU) {
|
if (g_pserver->maxmemory_policy & MAXMEMORY_FLAG_LFU) {
|
||||||
addReplyError(c,"An LFU maxmemory policy is selected, idle time not tracked. Please note that when switching between policies at runtime LRU and LFU data will take some time to adjust.");
|
addReplyError(c,"An LFU maxmemory policy is selected, idle time not tracked. Please note that when switching between policies at runtime LRU and LFU data will take some time to adjust.");
|
||||||
return;
|
return;
|
||||||
@ -1292,7 +1289,7 @@ NULL
|
|||||||
addReplyLongLong(c,estimateObjectIdleTime(o)/1000);
|
addReplyLongLong(c,estimateObjectIdleTime(o)/1000);
|
||||||
} else if (!strcasecmp(szFromObj(c->argv[1]),"freq") && c->argc == 3) {
|
} else if (!strcasecmp(szFromObj(c->argv[1]),"freq") && c->argc == 3) {
|
||||||
if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.null[c->resp]))
|
if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.null[c->resp]))
|
||||||
== NULL) return;
|
== nullptr) return;
|
||||||
if (!(g_pserver->maxmemory_policy & MAXMEMORY_FLAG_LFU)) {
|
if (!(g_pserver->maxmemory_policy & MAXMEMORY_FLAG_LFU)) {
|
||||||
addReplyError(c,"An LFU maxmemory policy is not selected, access frequency not tracked. Please note that when switching between policies at runtime LRU and LFU data will take some time to adjust.");
|
addReplyError(c,"An LFU maxmemory policy is not selected, access frequency not tracked. Please note that when switching between policies at runtime LRU and LFU data will take some time to adjust.");
|
||||||
return;
|
return;
|
||||||
@ -1301,7 +1298,7 @@ NULL
|
|||||||
* in case of the key has not been accessed for a long time,
|
* in case of the key has not been accessed for a long time,
|
||||||
* because we update the access time only
|
* because we update the access time only
|
||||||
* when the key is read or overwritten. */
|
* when the key is read or overwritten. */
|
||||||
addReplyLongLong(c,LFUDecrAndReturn(o));
|
addReplyLongLong(c,LFUDecrAndReturn(o.unsafe_robjcast()));
|
||||||
} else {
|
} else {
|
||||||
addReplySubcommandSyntaxError(c);
|
addReplySubcommandSyntaxError(c);
|
||||||
}
|
}
|
||||||
@ -1323,7 +1320,6 @@ NULL
|
|||||||
};
|
};
|
||||||
addReplyHelp(c, help);
|
addReplyHelp(c, help);
|
||||||
} else if (!strcasecmp(szFromObj(c->argv[1]),"usage") && c->argc >= 3) {
|
} else if (!strcasecmp(szFromObj(c->argv[1]),"usage") && c->argc >= 3) {
|
||||||
dictEntry *de;
|
|
||||||
long long samples = OBJ_COMPUTE_SIZE_DEF_SAMPLES;
|
long long samples = OBJ_COMPUTE_SIZE_DEF_SAMPLES;
|
||||||
for (int j = 3; j < c->argc; j++) {
|
for (int j = 3; j < c->argc; j++) {
|
||||||
if (!strcasecmp(szFromObj(c->argv[j]),"samples") &&
|
if (!strcasecmp(szFromObj(c->argv[j]),"samples") &&
|
||||||
@ -1342,12 +1338,14 @@ NULL
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ((de = dictFind(c->db->pdict,ptrFromObj(c->argv[2]))) == NULL) {
|
|
||||||
|
auto pair = c->db->lookup_tuple(c->argv[2]);
|
||||||
|
if (pair.first == NULL) {
|
||||||
addReplyNull(c, shared.nullbulk);
|
addReplyNull(c, shared.nullbulk);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
size_t usage = objectComputeSize((robj*)dictGetVal(de),samples);
|
size_t usage = objectComputeSize(pair.second,samples);
|
||||||
usage += sdsAllocSize((sds)dictGetKey(de));
|
usage += sdsAllocSize((sds)pair.first);
|
||||||
usage += sizeof(dictEntry);
|
usage += sizeof(dictEntry);
|
||||||
addReplyLongLong(c,usage);
|
addReplyLongLong(c,usage);
|
||||||
} else if (!strcasecmp(szFromObj(c->argv[1]),"stats") && c->argc == 2) {
|
} else if (!strcasecmp(szFromObj(c->argv[1]),"stats") && c->argc == 2) {
|
||||||
|
24
src/rdb.cpp
24
src/rdb.cpp
@ -1143,8 +1143,8 @@ int saveKey(rio *rdb, redisDb *db, int flags, size_t *processed, const char *key
|
|||||||
* integer pointed by 'error' is set to the value of errno just after the I/O
|
* integer pointed by 'error' is set to the value of errno just after the I/O
|
||||||
* error. */
|
* error. */
|
||||||
int rdbSaveRio(rio *rdb, int *error, int flags, rdbSaveInfo *rsi) {
|
int rdbSaveRio(rio *rdb, int *error, int flags, rdbSaveInfo *rsi) {
|
||||||
dictIterator *di = NULL;
|
|
||||||
dictEntry *de;
|
dictEntry *de;
|
||||||
|
dictIterator *di = NULL;
|
||||||
char magic[10];
|
char magic[10];
|
||||||
int j;
|
int j;
|
||||||
uint64_t cksum;
|
uint64_t cksum;
|
||||||
@ -1158,9 +1158,7 @@ int rdbSaveRio(rio *rdb, int *error, int flags, rdbSaveInfo *rsi) {
|
|||||||
|
|
||||||
for (j = 0; j < cserver.dbnum; j++) {
|
for (j = 0; j < cserver.dbnum; j++) {
|
||||||
redisDb *db = g_pserver->db+j;
|
redisDb *db = g_pserver->db+j;
|
||||||
dict *d = db->pdict;
|
if (db->size() == 0) continue;
|
||||||
if (dictSize(d) == 0) continue;
|
|
||||||
di = dictGetSafeIterator(d);
|
|
||||||
|
|
||||||
/* Write the SELECT DB opcode */
|
/* Write the SELECT DB opcode */
|
||||||
if (rdbSaveType(rdb,RDB_OPCODE_SELECTDB) == -1) goto werr;
|
if (rdbSaveType(rdb,RDB_OPCODE_SELECTDB) == -1) goto werr;
|
||||||
@ -1171,7 +1169,7 @@ int rdbSaveRio(rio *rdb, int *error, int flags, rdbSaveInfo *rsi) {
|
|||||||
* However this does not limit the actual size of the DB to load since
|
* However this does not limit the actual size of the DB to load since
|
||||||
* these sizes are just hints to resize the hash tables. */
|
* these sizes are just hints to resize the hash tables. */
|
||||||
uint64_t db_size, expires_size;
|
uint64_t db_size, expires_size;
|
||||||
db_size = dictSize(db->pdict);
|
db_size = db->size();
|
||||||
expires_size = db->setexpire->size();
|
expires_size = db->setexpire->size();
|
||||||
if (rdbSaveType(rdb,RDB_OPCODE_RESIZEDB) == -1) goto werr;
|
if (rdbSaveType(rdb,RDB_OPCODE_RESIZEDB) == -1) goto werr;
|
||||||
if (rdbSaveLen(rdb,db_size) == -1) goto werr;
|
if (rdbSaveLen(rdb,db_size) == -1) goto werr;
|
||||||
@ -1179,19 +1177,17 @@ int rdbSaveRio(rio *rdb, int *error, int flags, rdbSaveInfo *rsi) {
|
|||||||
|
|
||||||
/* Iterate this DB writing every entry */
|
/* Iterate this DB writing every entry */
|
||||||
size_t ckeysExpired = 0;
|
size_t ckeysExpired = 0;
|
||||||
while((de = dictNext(di)) != NULL) {
|
bool fSavedAll = db->iterate([&](const char *keystr, robj *o)->bool{
|
||||||
sds keystr = (sds)dictGetKey(de);
|
|
||||||
robj *o = (robj*)dictGetVal(de);
|
|
||||||
|
|
||||||
if (o->FExpires())
|
if (o->FExpires())
|
||||||
++ckeysExpired;
|
++ckeysExpired;
|
||||||
|
|
||||||
if (!saveKey(rdb, db, flags, &processed, keystr, o))
|
if (!saveKey(rdb, db, flags, &processed, keystr, o))
|
||||||
goto werr;
|
return false;
|
||||||
}
|
return true;
|
||||||
|
});
|
||||||
|
if (!fSavedAll)
|
||||||
|
goto werr;
|
||||||
serverAssert(ckeysExpired == db->setexpire->size());
|
serverAssert(ckeysExpired == db->setexpire->size());
|
||||||
dictReleaseIterator(di);
|
|
||||||
di = NULL; /* So that we don't release it again on error. */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If we are storing the replication information on disk, persist
|
/* If we are storing the replication information on disk, persist
|
||||||
@ -1998,7 +1994,7 @@ int rdbLoadRio(rio *rdb, rdbSaveInfo *rsi, int loading_aof) {
|
|||||||
goto eoferr;
|
goto eoferr;
|
||||||
if ((expires_size = rdbLoadLen(rdb,NULL)) == RDB_LENERR)
|
if ((expires_size = rdbLoadLen(rdb,NULL)) == RDB_LENERR)
|
||||||
goto eoferr;
|
goto eoferr;
|
||||||
dictExpand(db->pdict,db_size);
|
db->expand(db_size);
|
||||||
continue; /* Read next opcode. */
|
continue; /* Read next opcode. */
|
||||||
} else if (type == RDB_OPCODE_AUX) {
|
} else if (type == RDB_OPCODE_AUX) {
|
||||||
/* AUX: generic string-string fields. Use to add state to RDB
|
/* AUX: generic string-string fields. Use to add state to RDB
|
||||||
|
@ -1885,8 +1885,8 @@ int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) {
|
|||||||
for (j = 0; j < cserver.dbnum; j++) {
|
for (j = 0; j < cserver.dbnum; j++) {
|
||||||
long long size, used, vkeys;
|
long long size, used, vkeys;
|
||||||
|
|
||||||
size = dictSlots(g_pserver->db[j].pdict);
|
size = g_pserver->db[j].slots();
|
||||||
used = dictSize(g_pserver->db[j].pdict);
|
used = g_pserver->db[j].size();
|
||||||
vkeys = g_pserver->db[j].setexpire->size();
|
vkeys = g_pserver->db[j].setexpire->size();
|
||||||
if (used || vkeys) {
|
if (used || vkeys) {
|
||||||
serverLog(LL_VERBOSE,"DB %d: %lld keys (%lld volatile) in %lld slots HT.",j,used,vkeys,size);
|
serverLog(LL_VERBOSE,"DB %d: %lld keys (%lld volatile) in %lld slots HT.",j,used,vkeys,size);
|
||||||
@ -2924,16 +2924,7 @@ void initServer(void) {
|
|||||||
/* Create the Redis databases, and initialize other internal state. */
|
/* Create the Redis databases, and initialize other internal state. */
|
||||||
for (int j = 0; j < cserver.dbnum; j++) {
|
for (int j = 0; j < cserver.dbnum; j++) {
|
||||||
new (&g_pserver->db[j]) redisDb;
|
new (&g_pserver->db[j]) redisDb;
|
||||||
g_pserver->db[j].pdict = dictCreate(&dbDictType,NULL);
|
g_pserver->db[j].initialize(j);
|
||||||
g_pserver->db[j].setexpire = new(MALLOC_LOCAL) expireset();
|
|
||||||
g_pserver->db[j].expireitr = g_pserver->db[j].setexpire->end();
|
|
||||||
g_pserver->db[j].blocking_keys = dictCreate(&keylistDictType,NULL);
|
|
||||||
g_pserver->db[j].ready_keys = dictCreate(&objectKeyPointerValueDictType,NULL);
|
|
||||||
g_pserver->db[j].watched_keys = dictCreate(&keylistDictType,NULL);
|
|
||||||
g_pserver->db[j].id = j;
|
|
||||||
g_pserver->db[j].avg_ttl = 0;
|
|
||||||
g_pserver->db[j].last_expire_set = 0;
|
|
||||||
g_pserver->db[j].defrag_later = listCreate();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Fixup Master Client Database */
|
/* Fixup Master Client Database */
|
||||||
@ -4575,7 +4566,7 @@ sds genRedisInfoString(const char *section) {
|
|||||||
for (j = 0; j < cserver.dbnum; j++) {
|
for (j = 0; j < cserver.dbnum; j++) {
|
||||||
long long keys, vkeys;
|
long long keys, vkeys;
|
||||||
|
|
||||||
keys = dictSize(g_pserver->db[j].pdict);
|
keys = g_pserver->db[j].size();
|
||||||
vkeys = g_pserver->db[j].setexpire->size();
|
vkeys = g_pserver->db[j].setexpire->size();
|
||||||
|
|
||||||
// Adjust TTL by the current time
|
// Adjust TTL by the current time
|
||||||
|
76
src/server.h
76
src/server.h
@ -129,6 +129,11 @@ public:
|
|||||||
return m_ptr;
|
return m_ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const redisObject& operator*() const
|
||||||
|
{
|
||||||
|
return *m_ptr;
|
||||||
|
}
|
||||||
|
|
||||||
bool operator!() const
|
bool operator!() const
|
||||||
{
|
{
|
||||||
return !m_ptr;
|
return !m_ptr;
|
||||||
@ -1014,10 +1019,74 @@ typedef struct clientReplyBlock {
|
|||||||
* by integers from 0 (the default database) up to the max configured
|
* by integers from 0 (the default database) up to the max configured
|
||||||
* database. The database number is the 'id' field in the structure. */
|
* database. The database number is the 'id' field in the structure. */
|
||||||
typedef struct redisDb {
|
typedef struct redisDb {
|
||||||
|
// Legacy C API, Do not add more
|
||||||
|
friend void tryResizeHashTables(int);
|
||||||
|
friend int incrementallyRehash(int);
|
||||||
|
friend int dbAddCore(redisDb *db, robj *key, robj *val);
|
||||||
|
friend void dbOverwriteCore(redisDb *db, dictEntry *de, robj *key, robj *val, bool fUpdateMvcc, bool fRemoveExpire);
|
||||||
|
friend void dbOverwrite(redisDb *db, robj *key, robj *val);
|
||||||
|
friend int dbMerge(redisDb *db, robj *key, robj *val, int fReplace);
|
||||||
|
friend void setKey(redisDb *db, robj *key, robj *val);
|
||||||
|
friend int dbSyncDelete(redisDb *db, robj *key);
|
||||||
|
friend int dbAsyncDelete(redisDb *db, robj *key);
|
||||||
|
friend long long emptyDb(int dbnum, int flags, void(callback)(void*));
|
||||||
|
friend void emptyDbAsync(redisDb *db);
|
||||||
|
friend void scanGenericCommand(struct client *c, robj_roptr o, unsigned long cursor);
|
||||||
|
friend int dbSwapDatabases(int id1, int id2);
|
||||||
|
friend int removeExpire(redisDb *db, robj *key);
|
||||||
|
friend void setExpire(struct client *c, redisDb *db, robj *key, robj *subkey, long long when);
|
||||||
|
friend void setExpire(client *c, redisDb *db, robj *key, expireEntry &&e);
|
||||||
|
friend void evictionPoolPopulate(int dbid, redisDb *db, expireset *setexpire, struct evictionPoolEntry *pool);
|
||||||
|
friend void activeDefragCycle(void);
|
||||||
|
|
||||||
redisDb()
|
redisDb()
|
||||||
: expireitr(nullptr)
|
: expireitr(nullptr)
|
||||||
{};
|
{};
|
||||||
|
|
||||||
|
void initialize(int id);
|
||||||
|
|
||||||
|
size_t slots() const { return dictSlots(pdict); }
|
||||||
|
size_t size() const { return dictSize(pdict); }
|
||||||
|
void expand(uint64_t slots) { dictExpand(pdict, slots); }
|
||||||
|
|
||||||
|
robj *find(robj_roptr key)
|
||||||
|
{
|
||||||
|
return find(szFromObj(key));
|
||||||
|
}
|
||||||
|
robj *find(const char *key)
|
||||||
|
{
|
||||||
|
dictEntry *de = dictFind(pdict, key);
|
||||||
|
if (de != nullptr)
|
||||||
|
return (robj*)dictGetVal(de);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<const char*,robj*> lookup_tuple(robj_roptr key)
|
||||||
|
{
|
||||||
|
return lookup_tuple(szFromObj(key));
|
||||||
|
}
|
||||||
|
std::pair<const char*,robj*> lookup_tuple(const char *key)
|
||||||
|
{
|
||||||
|
dictEntry *de = dictFind(pdict, key);
|
||||||
|
if (de != nullptr)
|
||||||
|
return std::make_pair<const char*,robj*>((const char*)dictGetKey(de), (robj*)dictGetVal(de));
|
||||||
|
return std::make_pair<const char*,robj*>(nullptr, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<const char*,robj*> random()
|
||||||
|
{
|
||||||
|
dictEntry *de = dictGetRandomKey(pdict);
|
||||||
|
if (de != nullptr)
|
||||||
|
return std::make_pair<const char*,robj*>((const char*)dictGetKey(de), (robj*)dictGetVal(de));
|
||||||
|
return std::make_pair<const char*,robj*>(nullptr, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool iterate(std::function<bool(const char*, robj*)> fn);
|
||||||
|
void getStats(char *buf, size_t bufsize) { dictGetStats(buf, bufsize, pdict); }
|
||||||
|
|
||||||
|
private:
|
||||||
dict *pdict; /* The keyspace for this DB */
|
dict *pdict; /* The keyspace for this DB */
|
||||||
|
public:
|
||||||
expireset *setexpire;
|
expireset *setexpire;
|
||||||
expireset::setiter expireitr;
|
expireset::setiter expireitr;
|
||||||
|
|
||||||
@ -1907,6 +1976,7 @@ extern dictType dbDictType;
|
|||||||
extern dictType shaScriptObjectDictType;
|
extern dictType shaScriptObjectDictType;
|
||||||
extern double R_Zero, R_PosInf, R_NegInf, R_Nan;
|
extern double R_Zero, R_PosInf, R_NegInf, R_Nan;
|
||||||
extern dictType hashDictType;
|
extern dictType hashDictType;
|
||||||
|
extern dictType keylistDictType;
|
||||||
extern dictType replScriptCacheDictType;
|
extern dictType replScriptCacheDictType;
|
||||||
extern dictType keyptrDictType;
|
extern dictType keyptrDictType;
|
||||||
extern dictType modulesDictType;
|
extern dictType modulesDictType;
|
||||||
@ -2135,7 +2205,7 @@ const char *strEncoding(int encoding);
|
|||||||
int compareStringObjects(robj *a, robj *b);
|
int compareStringObjects(robj *a, robj *b);
|
||||||
int collateStringObjects(robj *a, robj *b);
|
int collateStringObjects(robj *a, robj *b);
|
||||||
int equalStringObjects(robj *a, robj *b);
|
int equalStringObjects(robj *a, robj *b);
|
||||||
unsigned long long estimateObjectIdleTime(robj *o);
|
unsigned long long estimateObjectIdleTime(robj_roptr o);
|
||||||
void trimStringObjectIfNeeded(robj *o);
|
void trimStringObjectIfNeeded(robj *o);
|
||||||
#define sdsEncodedObject(objptr) (objptr->encoding == OBJ_ENCODING_RAW || objptr->encoding == OBJ_ENCODING_EMBSTR)
|
#define sdsEncodedObject(objptr) (objptr->encoding == OBJ_ENCODING_RAW || objptr->encoding == OBJ_ENCODING_EMBSTR)
|
||||||
|
|
||||||
@ -2417,8 +2487,8 @@ robj *lookupKeyWrite(redisDb *db, robj *key);
|
|||||||
robj_roptr lookupKeyReadOrReply(client *c, robj *key, robj *reply);
|
robj_roptr lookupKeyReadOrReply(client *c, robj *key, robj *reply);
|
||||||
robj *lookupKeyWriteOrReply(client *c, robj *key, robj *reply);
|
robj *lookupKeyWriteOrReply(client *c, robj *key, robj *reply);
|
||||||
robj_roptr lookupKeyReadWithFlags(redisDb *db, robj *key, int flags);
|
robj_roptr lookupKeyReadWithFlags(redisDb *db, robj *key, int flags);
|
||||||
robj *objectCommandLookup(client *c, robj *key);
|
robj_roptr objectCommandLookup(client *c, robj *key);
|
||||||
robj *objectCommandLookupOrReply(client *c, robj *key, robj *reply);
|
robj_roptr objectCommandLookupOrReply(client *c, robj *key, robj *reply);
|
||||||
void objectSetLRUOrLFU(robj *val, long long lfu_freq, long long lru_idle,
|
void objectSetLRUOrLFU(robj *val, long long lfu_freq, long long lru_idle,
|
||||||
long long lru_clock);
|
long long lru_clock);
|
||||||
#define LOOKUP_NONE 0
|
#define LOOKUP_NONE 0
|
||||||
|
Loading…
x
Reference in New Issue
Block a user