Skip to content

Commit

Permalink
feat(generic_family): Implement EXPIRETIME and PEXPIRETIME commands (#…
Browse files Browse the repository at this point in the history
…3524)

Introduce EXPIRETIME and PEXPIRETIME commands
  • Loading branch information
Eunoia1729 authored Aug 26, 2024
1 parent 839b1be commit cfb9fda
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 3 deletions.
55 changes: 52 additions & 3 deletions src/server/generic_family.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down Expand Up @@ -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))
Expand All @@ -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();
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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
5 changes: 5 additions & 0 deletions src/server/generic_family.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand All @@ -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);
Expand Down
15 changes: 15 additions & 0 deletions src/server/generic_family_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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

0 comments on commit cfb9fda

Please sign in to comment.