Skip to content

Commit f4c94b2

Browse files
committed
feat(server): SCAN command add ATTR options
1 parent 8d6a184 commit f4c94b2

File tree

4 files changed

+76
-1
lines changed

4 files changed

+76
-1
lines changed

src/server/common.cc

+13
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,19 @@ OpResult<ScanOpts> ScanOpts::TryFrom(CmdArgList args) {
308308
if (!absl::SimpleAtoi(ArgS(args, i + 1), &scan_opts.bucket_id)) {
309309
return facade::OpStatus::INVALID_INT;
310310
}
311+
} else if (opt == "ATTR") {
312+
string_view mask = ArgS(args, i + 1);
313+
if (mask == "v") {
314+
scan_opts.mask = ScanOpts::Mask::Volatile;
315+
} else if (mask == "p") {
316+
scan_opts.mask = ScanOpts::Mask::Permanent;
317+
} else if (mask == "a") {
318+
scan_opts.mask = ScanOpts::Mask::Accessed;
319+
} else if (mask == "u") {
320+
scan_opts.mask = ScanOpts::Mask::Untouched;
321+
} else {
322+
return facade::OpStatus::SYNTAX_ERR;
323+
}
311324
} else {
312325
return facade::OpStatus::SYNTAX_ERR;
313326
}

src/server/common.h

+8
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,14 @@ struct ScanOpts {
312312
size_t limit = 10;
313313
std::optional<CompactObjType> type_filter;
314314
unsigned bucket_id = UINT_MAX;
315+
enum class Mask {
316+
Volatile, // volatile, keys that have ttl
317+
Permanent, // permanent, keys that do not have ttl
318+
Accessed, // accessed, the key has been accessed since the last load/flush event, or the last
319+
// time a flag was reset.
320+
Untouched, // untouched, the key has not been accessed/touched.
321+
};
322+
std::optional<Mask> mask;
315323

316324
bool Matches(std::string_view val_name) const;
317325
static OpResult<ScanOpts> TryFrom(CmdArgList args);

src/server/generic_family.cc

+12-1
Original file line numberDiff line numberDiff line change
@@ -587,7 +587,17 @@ bool ScanCb(const OpArgs& op_args, PrimeIterator prime_it, const ScanOpts& opts,
587587
}
588588

589589
bool matches = !opts.type_filter || it->second.ObjType() == opts.type_filter;
590-
590+
if (opts.mask.has_value()) {
591+
if (opts.mask == ScanOpts::Mask::Volatile) {
592+
matches &= it->second.HasExpire();
593+
} else if (opts.mask == ScanOpts::Mask::Permanent) {
594+
matches &= !it->second.HasExpire();
595+
} else if (opts.mask == ScanOpts::Mask::Accessed) {
596+
matches &= it->first.WasTouched();
597+
} else if (opts.mask == ScanOpts::Mask::Untouched) {
598+
matches &= !it->first.WasTouched();
599+
}
600+
}
591601
if (!matches)
592602
return false;
593603

@@ -1735,6 +1745,7 @@ void GenericFamily::Echo(CmdArgList args, const CommandContext& cmd_cntx) {
17351745
}
17361746

17371747
// SCAN cursor [MATCH <glob>] [TYPE <type>] [COUNT <count>] [BUCKET <bucket_id>]
1748+
// [ATTR <mask>]
17381749
void GenericFamily::Scan(CmdArgList args, const CommandContext& cmd_cntx) {
17391750
string_view token = ArgS(args, 0);
17401751
uint64_t cursor = 0;

src/server/generic_family_test.cc

+43
Original file line numberDiff line numberDiff line change
@@ -412,6 +412,49 @@ TEST_F(GenericFamilyTest, Scan) {
412412
EXPECT_EQ(resp, "");
413413
}
414414

415+
TEST_F(GenericFamilyTest, ScanWithAttr) {
416+
Run({"flushdb"});
417+
418+
Run({"set", "hello", "world"});
419+
Run({"set", "foo", "bar"});
420+
421+
Run({"expire", "hello", "1000"});
422+
423+
auto resp = Run({"scan", "0", "attr", "v"});
424+
auto vec = StrArray(resp.GetVec()[1]);
425+
ASSERT_EQ(1, vec.size());
426+
EXPECT_EQ(vec[0], "hello");
427+
428+
resp = Run({"scan", "0", "attr", "p"});
429+
vec = StrArray(resp.GetVec()[1]);
430+
ASSERT_EQ(1, vec.size());
431+
EXPECT_EQ(vec[0], "foo");
432+
433+
// before run get "foo", scan with a attr should return "hello", because set "hello" expire before
434+
resp = Run({"scan", "0", "attr", "a"});
435+
vec = StrArray(resp.GetVec()[1]);
436+
ASSERT_EQ(1, vec.size());
437+
EXPECT_EQ(vec[0], "hello");
438+
439+
// before run get "foo", scan with a attr should return "foo"
440+
resp = Run({"scan", "0", "attr", "u"});
441+
vec = StrArray(resp.GetVec()[1]);
442+
ASSERT_EQ(1, vec.size());
443+
EXPECT_EQ(vec[0], "foo");
444+
445+
ASSERT_THAT(Run({"get", "foo"}), "bar");
446+
447+
// after run get "foo", scan with a attr should return "foo" and "hello"
448+
resp = Run({"scan", "0", "attr", "a"});
449+
vec = StrArray(resp.GetVec()[1]);
450+
ASSERT_EQ(2, vec.size());
451+
452+
// after run get "foo", scan with a attr should return empty set
453+
resp = Run({"scan", "0", "attr", "u"});
454+
vec = StrArray(resp.GetVec()[1]);
455+
ASSERT_EQ(0, vec.size());
456+
}
457+
415458
TEST_F(GenericFamilyTest, Sort) {
416459
// Test list sort with params
417460
Run({"del", "list-1"});

0 commit comments

Comments
 (0)