mirror of
https://github.com/dragonflydb/dragonfly.git
synced 2025-05-11 18:35:46 +02:00
feat(generic_family): Implement EXPIRETIME and PEXPIRETIME commands (#3524)
Introduce EXPIRETIME and PEXPIRETIME commands
This commit is contained in:
parent
839b1be82d
commit
cfb9fdab34
3 changed files with 72 additions and 3 deletions
|
@ -1325,6 +1325,38 @@ void GenericFamily::RenameNx(CmdArgList args, ConnectionContext* cntx) {
|
|||
}
|
||||
}
|
||||
|
||||
void GenericFamily::ExpireTime(CmdArgList args, ConnectionContext* cntx) {
|
||||
ExpireTimeGeneric(args, cntx, TimeUnit::SEC);
|
||||
}
|
||||
|
||||
void GenericFamily::PExpireTime(CmdArgList args, ConnectionContext* cntx) {
|
||||
ExpireTimeGeneric(args, cntx, TimeUnit::MSEC);
|
||||
}
|
||||
|
||||
void GenericFamily::ExpireTimeGeneric(CmdArgList args, ConnectionContext* cntx, TimeUnit unit) {
|
||||
string_view key = ArgS(args, 0);
|
||||
|
||||
auto cb = [&](Transaction* t, EngineShard* shard) { return OpExpireTime(t, shard, key); };
|
||||
OpResult<uint64_t> result = cntx->transaction->ScheduleSingleHopT(std::move(cb));
|
||||
|
||||
if (result) {
|
||||
long ttl = (unit == TimeUnit::SEC) ? (result.value() + 500) / 1000 : result.value();
|
||||
cntx->SendLong(ttl);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (result.status()) {
|
||||
case OpStatus::KEY_NOTFOUND:
|
||||
cntx->SendLong(-2);
|
||||
break;
|
||||
default:
|
||||
LOG_IF(ERROR, result.status() != OpStatus::SKIPPED)
|
||||
<< "Unexpected status " << result.status();
|
||||
cntx->SendLong(-1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void GenericFamily::Ttl(CmdArgList args, ConnectionContext* cntx) {
|
||||
TtlGeneric(args, cntx, TimeUnit::SEC);
|
||||
}
|
||||
|
@ -1487,7 +1519,8 @@ void GenericFamily::Scan(CmdArgList args, ConnectionContext* cntx) {
|
|||
}
|
||||
}
|
||||
|
||||
OpResult<uint64_t> GenericFamily::OpTtl(Transaction* t, EngineShard* shard, string_view key) {
|
||||
OpResult<uint64_t> GenericFamily::OpExpireTime(Transaction* t, EngineShard* shard,
|
||||
string_view key) {
|
||||
auto& db_slice = t->GetDbSlice(shard->shard_id());
|
||||
auto [it, expire_it] = db_slice.FindReadOnly(t->GetDbContext(), key);
|
||||
if (!IsValid(it))
|
||||
|
@ -1496,11 +1529,23 @@ OpResult<uint64_t> GenericFamily::OpTtl(Transaction* t, EngineShard* shard, stri
|
|||
if (!IsValid(expire_it))
|
||||
return OpStatus::SKIPPED;
|
||||
|
||||
int64_t ttl_ms = db_slice.ExpireTime(expire_it) - t->GetDbContext().time_now_ms;
|
||||
int64_t ttl_ms = db_slice.ExpireTime(expire_it);
|
||||
DCHECK_GT(ttl_ms, 0); // Otherwise FindReadOnly would return null.
|
||||
return ttl_ms;
|
||||
}
|
||||
|
||||
OpResult<uint64_t> GenericFamily::OpTtl(Transaction* t, EngineShard* shard, string_view key) {
|
||||
auto opExpireTimeResult = OpExpireTime(t, shard, key);
|
||||
|
||||
if (opExpireTimeResult) {
|
||||
int64_t ttl_ms = opExpireTimeResult.value() - t->GetDbContext().time_now_ms;
|
||||
DCHECK_GT(ttl_ms, 0); // Otherwise FindReadOnly would return null.
|
||||
return ttl_ms;
|
||||
} else {
|
||||
return opExpireTimeResult;
|
||||
}
|
||||
}
|
||||
|
||||
OpResult<uint32_t> GenericFamily::OpExists(const OpArgs& op_args, const ShardArgs& keys) {
|
||||
DVLOG(1) << "Exists: " << keys.Front();
|
||||
auto& db_slice = op_args.GetDbSlice();
|
||||
|
@ -1697,6 +1742,8 @@ constexpr uint32_t kStick = KEYSPACE | WRITE | FAST;
|
|||
constexpr uint32_t kSort = WRITE | SET | SORTEDSET | LIST | SLOW | DANGEROUS;
|
||||
constexpr uint32_t kMove = KEYSPACE | WRITE | FAST;
|
||||
constexpr uint32_t kRestore = KEYSPACE | WRITE | SLOW | DANGEROUS;
|
||||
constexpr uint32_t kExpireTime = KEYSPACE | READ | FAST;
|
||||
constexpr uint32_t kPExpireTime = KEYSPACE | READ | FAST;
|
||||
} // namespace acl
|
||||
|
||||
void GenericFamily::Register(CommandRegistry* registry) {
|
||||
|
@ -1738,7 +1785,9 @@ void GenericFamily::Register(CommandRegistry* registry) {
|
|||
<< CI{"MOVE", CO::WRITE | CO::GLOBAL_TRANS | CO::NO_AUTOJOURNAL, 3, 1, 1, acl::kMove}.HFUNC(
|
||||
Move)
|
||||
<< CI{"RESTORE", CO::WRITE, -4, 1, 1, acl::kRestore}.HFUNC(Restore)
|
||||
<< CI{"RANDOMKEY", CO::READONLY, 1, 0, 0, 0}.HFUNC(RandomKey);
|
||||
<< CI{"RANDOMKEY", CO::READONLY, 1, 0, 0, 0}.HFUNC(RandomKey)
|
||||
<< CI{"EXPIRETIME", CO::READONLY | CO::FAST, 2, 1, 1, acl::kExpireTime}.HFUNC(ExpireTime)
|
||||
<< CI{"PEXPIRETIME", CO::READONLY | CO::FAST, 2, 1, 1, acl::kPExpireTime}.HFUNC(PExpireTime);
|
||||
}
|
||||
|
||||
} // namespace dfly
|
||||
|
|
|
@ -56,6 +56,8 @@ class GenericFamily {
|
|||
|
||||
static void Rename(CmdArgList args, ConnectionContext* cntx);
|
||||
static void RenameNx(CmdArgList args, ConnectionContext* cntx);
|
||||
static void ExpireTime(CmdArgList args, ConnectionContext* cntx);
|
||||
static void PExpireTime(CmdArgList args, ConnectionContext* cntx);
|
||||
static void Ttl(CmdArgList args, ConnectionContext* cntx);
|
||||
static void Pttl(CmdArgList args, ConnectionContext* cntx);
|
||||
|
||||
|
@ -71,8 +73,11 @@ class GenericFamily {
|
|||
|
||||
static ErrorReply RenameGeneric(CmdArgList args, bool destination_should_not_exist,
|
||||
ConnectionContext* cntx);
|
||||
|
||||
static void ExpireTimeGeneric(CmdArgList args, ConnectionContext* cntx, TimeUnit unit);
|
||||
static void TtlGeneric(CmdArgList args, ConnectionContext* cntx, TimeUnit unit);
|
||||
|
||||
static OpResult<uint64_t> OpExpireTime(Transaction* t, EngineShard* shard, std::string_view key);
|
||||
static OpResult<uint64_t> OpTtl(Transaction* t, EngineShard* shard, std::string_view key);
|
||||
static OpResult<void> OpRen(const OpArgs& op_args, std::string_view from, std::string_view to,
|
||||
bool destination_should_not_exist);
|
||||
|
|
|
@ -775,4 +775,19 @@ TEST_F(GenericFamilyTest, JsonType) {
|
|||
ASSERT_THAT(vec, ElementsAre("json"));
|
||||
}
|
||||
|
||||
TEST_F(GenericFamilyTest, ExpireTime) {
|
||||
EXPECT_EQ(-2, CheckedInt({"EXPIRETIME", "foo"}));
|
||||
EXPECT_EQ(-2, CheckedInt({"PEXPIRETIME", "foo"}));
|
||||
Run({"set", "foo", "bar"});
|
||||
EXPECT_EQ(-1, CheckedInt({"EXPIRETIME", "foo"}));
|
||||
EXPECT_EQ(-1, CheckedInt({"PEXPIRETIME", "foo"}));
|
||||
|
||||
// set expiry
|
||||
uint64_t expire_time_in_ms = TEST_current_time_ms + 5000;
|
||||
uint64_t expire_time_in_seconds = (expire_time_in_ms + 500) / 1000;
|
||||
Run({"pexpireat", "foo", absl::StrCat(expire_time_in_ms)});
|
||||
EXPECT_EQ(expire_time_in_seconds, CheckedInt({"EXPIRETIME", "foo"}));
|
||||
EXPECT_EQ(expire_time_in_ms, CheckedInt({"PEXPIRETIME", "foo"}));
|
||||
}
|
||||
|
||||
} // namespace dfly
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue