Implement TTL and PERSIST commands for subkeys. Also ensure expiremember replaces any old subkey expiries
Former-commit-id: 16f96efbe7c6c27f2a79f5b472447407f905df15
This commit is contained in:
parent
3670376457
commit
0a00341a80
33
src/db.cpp
33
src/db.cpp
@ -1227,6 +1227,39 @@ int removeExpireCore(redisDb *db, robj *key, dictEntry *de) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
int removeSubkeyExpire(redisDb *db, robj *key, robj *subkey) {
|
||||
dictEntry *de = dictFind(db->pdict,ptrFromObj(key));
|
||||
serverAssertWithInfo(NULL,key,de != NULL);
|
||||
|
||||
robj *val = (robj*)dictGetVal(de);
|
||||
if (!val->FExpires())
|
||||
return 0;
|
||||
|
||||
auto itr = db->setexpire->find((sds)dictGetKey(de));
|
||||
serverAssert(itr != db->setexpire->end());
|
||||
serverAssert(itr->key() == (sds)dictGetKey(de));
|
||||
if (!itr->FFat())
|
||||
return 0;
|
||||
|
||||
int found = 0;
|
||||
for (auto subitr : *itr)
|
||||
{
|
||||
if (subitr.subkey() == nullptr)
|
||||
continue;
|
||||
if (sdscmp((sds)subitr.subkey(), szFromObj(subkey)) == 0)
|
||||
{
|
||||
itr->erase(subitr);
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (itr->pfatentry()->size() == 0)
|
||||
removeExpireCore(db, key, de);
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
/* Set an expire to the specified key. If the expire is set in the context
|
||||
* of an user calling a command 'c' is the client, otherwise 'c' is set
|
||||
* to NULL. The 'when' parameter is the absolute unix time in milliseconds
|
||||
|
@ -544,11 +544,32 @@ void ttlGenericCommand(client *c, int output_ms) {
|
||||
addReplyLongLong(c,-2);
|
||||
return;
|
||||
}
|
||||
|
||||
/* The key exists. Return -1 if it has no expire, or the actual
|
||||
* TTL value otherwise. */
|
||||
* TTL value otherwise. */
|
||||
expireEntry *pexpire = getExpire(c->db,c->argv[1]);
|
||||
if (pexpire != nullptr)
|
||||
pexpire->FGetPrimaryExpire(&expire);
|
||||
|
||||
if (c->argc == 2) {
|
||||
// primary expire
|
||||
if (pexpire != nullptr)
|
||||
pexpire->FGetPrimaryExpire(&expire);
|
||||
} else if (c->argc == 3) {
|
||||
// We want a subkey expire
|
||||
if (pexpire && pexpire->FFat()) {
|
||||
for (auto itr : *pexpire) {
|
||||
if (itr.subkey() == nullptr)
|
||||
continue;
|
||||
if (sdscmp((sds)itr.subkey(), szFromObj(c->argv[2])) == 0) {
|
||||
expire = itr.when();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
addReplyError(c, "Invalid arguments");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (expire != -1) {
|
||||
ttl = expire-mstime();
|
||||
@ -574,11 +595,22 @@ void pttlCommand(client *c) {
|
||||
/* PERSIST key */
|
||||
void persistCommand(client *c) {
|
||||
if (lookupKeyWrite(c->db,c->argv[1])) {
|
||||
if (removeExpire(c->db,c->argv[1])) {
|
||||
addReply(c,shared.cone);
|
||||
g_pserver->dirty++;
|
||||
if (c->argc == 2) {
|
||||
if (removeExpire(c->db,c->argv[1])) {
|
||||
addReply(c,shared.cone);
|
||||
g_pserver->dirty++;
|
||||
} else {
|
||||
addReply(c,shared.czero);
|
||||
}
|
||||
} else if (c->argc == 3) {
|
||||
if (removeSubkeyExpire(c->db, c->argv[1], c->argv[2])) {
|
||||
addReply(c,shared.cone);
|
||||
g_pserver->dirty++;
|
||||
} else {
|
||||
addReply(c,shared.czero);
|
||||
}
|
||||
} else {
|
||||
addReply(c,shared.czero);
|
||||
addReplyError(c, "Invalid arguments");
|
||||
}
|
||||
} else {
|
||||
addReply(c,shared.czero);
|
||||
|
12
src/help.h
12
src/help.h
@ -632,8 +632,8 @@ struct commandHelp {
|
||||
0,
|
||||
"2.2.3" },
|
||||
{ "PERSIST",
|
||||
"key",
|
||||
"Remove the expiration from a key",
|
||||
"key [subkey]",
|
||||
"Remove the expiration from a key or subkey",
|
||||
0,
|
||||
"2.2.0" },
|
||||
{ "PEXPIRE",
|
||||
@ -677,8 +677,8 @@ struct commandHelp {
|
||||
6,
|
||||
"2.0.0" },
|
||||
{ "PTTL",
|
||||
"key",
|
||||
"Get the time to live for a key in milliseconds",
|
||||
"key [subkey]",
|
||||
"Get the time to live for a key or subkey in milliseconds",
|
||||
0,
|
||||
"2.6.0" },
|
||||
{ "PUBLISH",
|
||||
@ -952,8 +952,8 @@ struct commandHelp {
|
||||
0,
|
||||
"3.2.1" },
|
||||
{ "TTL",
|
||||
"key",
|
||||
"Get the time to live for a key",
|
||||
"key [subkey]",
|
||||
"Get the time to live for a key or subkey",
|
||||
0,
|
||||
"1.0.0" },
|
||||
{ "TYPE",
|
||||
|
@ -730,7 +730,7 @@ struct redisCommand redisCommandTable[] = {
|
||||
"admin no-script",
|
||||
0,NULL,0,0,0,0,0,0},
|
||||
|
||||
{"ttl",ttlCommand,2,
|
||||
{"ttl",ttlCommand,-2,
|
||||
"read-only fast random @keyspace",
|
||||
0,NULL,1,1,1,0,0,0},
|
||||
|
||||
@ -738,11 +738,11 @@ struct redisCommand redisCommandTable[] = {
|
||||
"read-only fast @keyspace",
|
||||
0,NULL,1,-1,1,0,0,0},
|
||||
|
||||
{"pttl",pttlCommand,2,
|
||||
{"pttl",pttlCommand,-2,
|
||||
"read-only fast random @keyspace",
|
||||
0,NULL,1,1,1,0,0,0},
|
||||
|
||||
{"persist",persistCommand,2,
|
||||
{"persist",persistCommand,-2,
|
||||
"write fast @keyspace",
|
||||
0,NULL,1,1,1,0,0,0},
|
||||
|
||||
|
20
src/server.h
20
src/server.h
@ -800,6 +800,16 @@ public:
|
||||
|
||||
void expireSubKey(const char *szSubkey, long long when)
|
||||
{
|
||||
// First check if the subkey already has an expiration
|
||||
for (auto &entry : m_vecexpireEntries)
|
||||
{
|
||||
if (entry.spsubkey == nullptr)
|
||||
continue;
|
||||
if (sdscmp((sds)entry.spsubkey.get(), (sds)szSubkey) == 0) {
|
||||
m_vecexpireEntries.erase(m_vecexpireEntries.begin() + (&entry - m_vecexpireEntries.data()));
|
||||
break;
|
||||
}
|
||||
}
|
||||
auto itrInsert = std::lower_bound(m_vecexpireEntries.begin(), m_vecexpireEntries.end(), when);
|
||||
const char *subkey = (szSubkey) ? sdsdup(szSubkey) : nullptr;
|
||||
m_vecexpireEntries.emplace(itrInsert, when, subkey);
|
||||
@ -823,6 +833,7 @@ class expireEntry {
|
||||
public:
|
||||
class iter
|
||||
{
|
||||
friend class expireEntry;
|
||||
expireEntry *m_pentry = nullptr;
|
||||
size_t m_idx = 0;
|
||||
|
||||
@ -958,6 +969,14 @@ public:
|
||||
return iter(this, u.m_pfatentry->size());
|
||||
return iter(this, 1);
|
||||
}
|
||||
|
||||
void erase(iter &itr)
|
||||
{
|
||||
if (!FFat())
|
||||
throw -1; // assert
|
||||
pfatentry()->m_vecexpireEntries.erase(
|
||||
pfatentry()->m_vecexpireEntries.begin() + itr.m_idx);
|
||||
}
|
||||
|
||||
bool FGetPrimaryExpire(long long *pwhen)
|
||||
{
|
||||
@ -2408,6 +2427,7 @@ int rewriteConfig(char *path);
|
||||
/* db.c -- Keyspace access API */
|
||||
int removeExpire(redisDb *db, robj *key);
|
||||
int removeExpireCore(redisDb *db, robj *key, dictEntry *de);
|
||||
int removeSubkeyExpire(redisDb *db, robj *key, robj *subkey);
|
||||
void propagateExpire(redisDb *db, robj *key, int lazy);
|
||||
int expireIfNeeded(redisDb *db, robj *key);
|
||||
expireEntry *getExpire(redisDb *db, robj_roptr key);
|
||||
|
Loading…
x
Reference in New Issue
Block a user