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;
|
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
|
/* 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
|
* 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
|
* 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);
|
addReplyLongLong(c,-2);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* The key exists. Return -1 if it has no expire, or the actual
|
/* 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]);
|
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) {
|
if (expire != -1) {
|
||||||
ttl = expire-mstime();
|
ttl = expire-mstime();
|
||||||
@ -574,11 +595,22 @@ void pttlCommand(client *c) {
|
|||||||
/* PERSIST key */
|
/* PERSIST key */
|
||||||
void persistCommand(client *c) {
|
void persistCommand(client *c) {
|
||||||
if (lookupKeyWrite(c->db,c->argv[1])) {
|
if (lookupKeyWrite(c->db,c->argv[1])) {
|
||||||
if (removeExpire(c->db,c->argv[1])) {
|
if (c->argc == 2) {
|
||||||
addReply(c,shared.cone);
|
if (removeExpire(c->db,c->argv[1])) {
|
||||||
g_pserver->dirty++;
|
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 {
|
} else {
|
||||||
addReply(c,shared.czero);
|
addReplyError(c, "Invalid arguments");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
addReply(c,shared.czero);
|
addReply(c,shared.czero);
|
||||||
|
12
src/help.h
12
src/help.h
@ -632,8 +632,8 @@ struct commandHelp {
|
|||||||
0,
|
0,
|
||||||
"2.2.3" },
|
"2.2.3" },
|
||||||
{ "PERSIST",
|
{ "PERSIST",
|
||||||
"key",
|
"key [subkey]",
|
||||||
"Remove the expiration from a key",
|
"Remove the expiration from a key or subkey",
|
||||||
0,
|
0,
|
||||||
"2.2.0" },
|
"2.2.0" },
|
||||||
{ "PEXPIRE",
|
{ "PEXPIRE",
|
||||||
@ -677,8 +677,8 @@ struct commandHelp {
|
|||||||
6,
|
6,
|
||||||
"2.0.0" },
|
"2.0.0" },
|
||||||
{ "PTTL",
|
{ "PTTL",
|
||||||
"key",
|
"key [subkey]",
|
||||||
"Get the time to live for a key in milliseconds",
|
"Get the time to live for a key or subkey in milliseconds",
|
||||||
0,
|
0,
|
||||||
"2.6.0" },
|
"2.6.0" },
|
||||||
{ "PUBLISH",
|
{ "PUBLISH",
|
||||||
@ -952,8 +952,8 @@ struct commandHelp {
|
|||||||
0,
|
0,
|
||||||
"3.2.1" },
|
"3.2.1" },
|
||||||
{ "TTL",
|
{ "TTL",
|
||||||
"key",
|
"key [subkey]",
|
||||||
"Get the time to live for a key",
|
"Get the time to live for a key or subkey",
|
||||||
0,
|
0,
|
||||||
"1.0.0" },
|
"1.0.0" },
|
||||||
{ "TYPE",
|
{ "TYPE",
|
||||||
|
@ -730,7 +730,7 @@ struct redisCommand redisCommandTable[] = {
|
|||||||
"admin no-script",
|
"admin no-script",
|
||||||
0,NULL,0,0,0,0,0,0},
|
0,NULL,0,0,0,0,0,0},
|
||||||
|
|
||||||
{"ttl",ttlCommand,2,
|
{"ttl",ttlCommand,-2,
|
||||||
"read-only fast random @keyspace",
|
"read-only fast random @keyspace",
|
||||||
0,NULL,1,1,1,0,0,0},
|
0,NULL,1,1,1,0,0,0},
|
||||||
|
|
||||||
@ -738,11 +738,11 @@ struct redisCommand redisCommandTable[] = {
|
|||||||
"read-only fast @keyspace",
|
"read-only fast @keyspace",
|
||||||
0,NULL,1,-1,1,0,0,0},
|
0,NULL,1,-1,1,0,0,0},
|
||||||
|
|
||||||
{"pttl",pttlCommand,2,
|
{"pttl",pttlCommand,-2,
|
||||||
"read-only fast random @keyspace",
|
"read-only fast random @keyspace",
|
||||||
0,NULL,1,1,1,0,0,0},
|
0,NULL,1,1,1,0,0,0},
|
||||||
|
|
||||||
{"persist",persistCommand,2,
|
{"persist",persistCommand,-2,
|
||||||
"write fast @keyspace",
|
"write fast @keyspace",
|
||||||
0,NULL,1,1,1,0,0,0},
|
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)
|
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);
|
auto itrInsert = std::lower_bound(m_vecexpireEntries.begin(), m_vecexpireEntries.end(), when);
|
||||||
const char *subkey = (szSubkey) ? sdsdup(szSubkey) : nullptr;
|
const char *subkey = (szSubkey) ? sdsdup(szSubkey) : nullptr;
|
||||||
m_vecexpireEntries.emplace(itrInsert, when, subkey);
|
m_vecexpireEntries.emplace(itrInsert, when, subkey);
|
||||||
@ -823,6 +833,7 @@ class expireEntry {
|
|||||||
public:
|
public:
|
||||||
class iter
|
class iter
|
||||||
{
|
{
|
||||||
|
friend class expireEntry;
|
||||||
expireEntry *m_pentry = nullptr;
|
expireEntry *m_pentry = nullptr;
|
||||||
size_t m_idx = 0;
|
size_t m_idx = 0;
|
||||||
|
|
||||||
@ -958,6 +969,14 @@ public:
|
|||||||
return iter(this, u.m_pfatentry->size());
|
return iter(this, u.m_pfatentry->size());
|
||||||
return iter(this, 1);
|
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)
|
bool FGetPrimaryExpire(long long *pwhen)
|
||||||
{
|
{
|
||||||
@ -2408,6 +2427,7 @@ int rewriteConfig(char *path);
|
|||||||
/* db.c -- Keyspace access API */
|
/* db.c -- Keyspace access API */
|
||||||
int removeExpire(redisDb *db, robj *key);
|
int removeExpire(redisDb *db, robj *key);
|
||||||
int removeExpireCore(redisDb *db, robj *key, dictEntry *de);
|
int removeExpireCore(redisDb *db, robj *key, dictEntry *de);
|
||||||
|
int removeSubkeyExpire(redisDb *db, robj *key, robj *subkey);
|
||||||
void propagateExpire(redisDb *db, robj *key, int lazy);
|
void propagateExpire(redisDb *db, robj *key, int lazy);
|
||||||
int expireIfNeeded(redisDb *db, robj *key);
|
int expireIfNeeded(redisDb *db, robj *key);
|
||||||
expireEntry *getExpire(redisDb *db, robj_roptr key);
|
expireEntry *getExpire(redisDb *db, robj_roptr key);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user