Skip to content

Commit e26645e

Browse files
committed
(string_family): Add support for MC GAT
The memcache protocol commands GAT and GATS should use the same code path as MC GET, but also touch expiry of keys which are searched. The expiry is stored in memcache flag of connection context. The mget code path is changed to expire keys if a combination of mc protocol and the set_expiry flag are found. Signed-off-by: Abhijat Malviya <[email protected]>
1 parent c5c77c0 commit e26645e

File tree

7 files changed

+203
-59
lines changed

7 files changed

+203
-59
lines changed

src/facade/memcache_parser_test.cc

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,20 @@ TEST_F(MCParserTest, Meta) {
149149
EXPECT_TRUE(cmd_.return_hit);
150150
}
151151

152+
TEST_F(MCParserTest, Gat) {
153+
auto res = parser_.Parse("gat 1000 foo bar baz\r\n", &consumed_, &cmd_);
154+
EXPECT_EQ(MemcacheParser::OK, res);
155+
EXPECT_EQ(consumed_, 22);
156+
EXPECT_EQ(cmd_.type, MemcacheParser::GAT);
157+
EXPECT_EQ(cmd_.key, "foo");
158+
EXPECT_THAT(cmd_.keys_ext, UnorderedElementsAre("bar", "baz"));
159+
EXPECT_EQ(cmd_.expire_ts, 1000);
160+
161+
cmd_ = {};
162+
res = parser_.Parse("gat foo bar\r\n", &consumed_, &cmd_);
163+
EXPECT_EQ(MemcacheParser::BAD_INT, res);
164+
}
165+
152166
class MCParserNoreplyTest : public MCParserTest {
153167
protected:
154168
void RunTest(string_view str, bool noreply) {

src/server/acl/acl_family_test.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -329,7 +329,7 @@ TEST_F(AclFamilyTest, TestCat) {
329329
UnorderedElementsAre("GETSET", "GETRANGE", "INCRBYFLOAT", "GETDEL", "DECRBY",
330330
"PREPEND", "SETEX", "MSET", "SET", "PSETEX", "SUBSTR", "DECR",
331331
"STRLEN", "INCR", "INCRBY", "MGET", "GET", "SETNX", "GETEX",
332-
"APPEND", "MSETNX", "SETRANGE"));
332+
"APPEND", "MSETNX", "SETRANGE", "GAT"));
333333
}
334334

335335
TEST_F(AclFamilyTest, TestGetUser) {

src/server/conn_context.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,7 @@ struct ConnectionState {
149149

150150
enum MCGetMask {
151151
FETCH_CAS_VER = 1,
152+
SET_EXPIRY = 2,
152153
};
153154

154155
size_t UsedMemory() const;
@@ -257,8 +258,10 @@ struct ConnectionState {
257258

258259
// used for memcache set/get commands.
259260
// For set op - it's the flag value we are storing along with the value.
260-
// For get op - we use it as a mask of MCGetMask values.
261-
uint32_t memcache_flag = 0;
261+
// For get op - we use it as a mask of MCGetMask values stored in the highest two bits
262+
// For GAT and GATS - the expiry in seconds supplied is stored in the top 62 bits along with the
263+
// MCGetMask values in the lower two bits
264+
uint64_t memcache_flag = 0;
262265

263266
ExecInfo exec_info;
264267
ReplicationInfo replication_info;

src/server/dragonfly_test.cc

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -374,6 +374,25 @@ TEST_F(DflyEngineTest, Memcache) {
374374
EXPECT_THAT(resp, ElementsAre("VALUE key 1 3 0", "bar", "VALUE key2 2 8 0", "bar2val2", "END"));
375375
}
376376

377+
TEST_F(DflyEngineTest, MemcacheGat) {
378+
using m = MemcacheParser;
379+
380+
EXPECT_THAT(GetMC(m::GAT, {"1000", "foo", "bar"}), ElementsAre("END"));
381+
EXPECT_THAT(RunMC(m::SET, "exp-key", "exp-val"), ElementsAre("STORED"));
382+
EXPECT_THAT(GetMC(m::GAT, {"1", "exp-key"}), ElementsAre("VALUE exp-key 0 7", "exp-val", "END"));
383+
384+
AdvanceTime(2 * 1000);
385+
EXPECT_THAT(RunMC(m::GET, "exp-key"), ElementsAre("END"));
386+
387+
EXPECT_THAT(RunMC(m::SET, "a", "a-val"), ElementsAre("STORED"));
388+
EXPECT_THAT(RunMC(m::SET, "x", "x-val"), ElementsAre("STORED"));
389+
EXPECT_THAT(GetMC(m::GAT, {"3", "a", "x", "z", "foo"}),
390+
ElementsAre("VALUE a 0 5", "a-val", "VALUE x 0 5", "x-val", "END"));
391+
392+
AdvanceTime(2 * 1000);
393+
EXPECT_THAT(GetMC(m::GAT, {"3", "a", "x", "z", "foo"}), ElementsAre("END"));
394+
}
395+
377396
TEST_F(DflyEngineTest, MemcacheFlags) {
378397
using MP = MemcacheParser;
379398

src/server/main_service.cc

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1596,6 +1596,9 @@ void Service::DispatchMC(const MemcacheParser::Command& cmd, std::string_view va
15961596
case MemcacheParser::QUIT:
15971597
strcpy(cmd_name, "QUIT");
15981598
break;
1599+
case MemcacheParser::GAT:
1600+
strcpy(cmd_name, "GAT");
1601+
break;
15991602
case MemcacheParser::STATS:
16001603
server_family_.StatsMC(cmd.key, mc_builder);
16011604
return;
@@ -1616,6 +1619,13 @@ void Service::DispatchMC(const MemcacheParser::Command& cmd, std::string_view va
16161619

16171620
ConnectionContext* dfly_cntx = static_cast<ConnectionContext*>(cntx);
16181621

1622+
// if expire_ts is greater than month it's a unix timestamp
1623+
// https://github.com/memcached/memcached/blob/master/doc/protocol.txt#L139
1624+
constexpr uint32_t kExpireLimit = 60 * 60 * 24 * 30;
1625+
const uint64_t expire_ts = cmd.expire_ts && cmd.expire_ts <= kExpireLimit
1626+
? cmd.expire_ts + time(nullptr)
1627+
: cmd.expire_ts;
1628+
16191629
if (MemcacheParser::IsStoreCmd(cmd.type)) {
16201630
char* v = const_cast<char*>(value.data());
16211631
args.emplace_back(v, value.size());
@@ -1624,12 +1634,6 @@ void Service::DispatchMC(const MemcacheParser::Command& cmd, std::string_view va
16241634
args.emplace_back(store_opt, strlen(store_opt));
16251635
}
16261636

1627-
// if expire_ts is greater than month it's a unix timestamp
1628-
// https://github.com/memcached/memcached/blob/master/doc/protocol.txt#L139
1629-
constexpr uint32_t kExpireLimit = 60 * 60 * 24 * 30;
1630-
const uint64_t expire_ts = cmd.expire_ts && cmd.expire_ts <= kExpireLimit
1631-
? cmd.expire_ts + time(nullptr)
1632-
: cmd.expire_ts;
16331637
if (expire_ts && memcmp(cmd_name, "SET", 3) == 0) {
16341638
char* next = absl::numbers_internal::FastIntToBuffer(expire_ts, ttl);
16351639
args.emplace_back(ttl_op, 4);
@@ -1641,6 +1645,11 @@ void Service::DispatchMC(const MemcacheParser::Command& cmd, std::string_view va
16411645
char* key = const_cast<char*>(s.data());
16421646
args.emplace_back(key, s.size());
16431647
}
1648+
1649+
if (cmd.type == MemcacheParser::GAT) {
1650+
dfly_cntx->conn_state.memcache_flag |= ConnectionState::SET_EXPIRY;
1651+
dfly_cntx->conn_state.memcache_flag |= expire_ts << 2;
1652+
}
16441653
if (cmd.type == MemcacheParser::GETS) {
16451654
dfly_cntx->conn_state.memcache_flag |= ConnectionState::FETCH_CAS_VER;
16461655
}

0 commit comments

Comments
 (0)