Skip to content

Commit fc00f2c

Browse files
authored
feat(memcache): Add support for GETS (#5087)
Support is added for the GETS command. A placeholder CAS token of 0 is always returned. Signed-off-by: Abhijat Malviya <abhijat@dragonflydb.io>
1 parent fe495cd commit fc00f2c

File tree

7 files changed

+34
-11
lines changed

7 files changed

+34
-11
lines changed

src/facade/reply_builder.cc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,7 @@ MCReplyBuilder::MCReplyBuilder(::io::Sink* sink) : SinkReplyBuilder(sink), all_(
212212
}
213213

214214
void MCReplyBuilder::SendValue(std::string_view key, std::string_view value, uint64_t mc_ver,
215-
uint32_t mc_flag) {
215+
uint32_t mc_flag, bool send_cas_token) {
216216
ReplyScope scope(this);
217217
if (flag_.meta) {
218218
string flags;
@@ -227,7 +227,7 @@ void MCReplyBuilder::SendValue(std::string_view key, std::string_view value, uin
227227
}
228228
} else {
229229
WritePieces("VALUE ", key, " ", mc_flag, " ", value.size());
230-
if (mc_ver)
230+
if (send_cas_token)
231231
WritePieces(" ", mc_ver);
232232

233233
if (value.size() <= kMaxInlineSize) {

src/facade/reply_builder.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,8 @@ class MCReplyBuilder : public SinkReplyBuilder {
177177
void SendDeleted();
178178
void SendGetEnd();
179179

180-
void SendValue(std::string_view key, std::string_view value, uint64_t mc_ver, uint32_t mc_flag);
180+
void SendValue(std::string_view key, std::string_view value, uint64_t mc_ver, uint32_t mc_flag,
181+
bool send_cas_token);
181182
void SendSimpleString(std::string_view str) final;
182183
void SendProtocolError(std::string_view str) final;
183184

src/server/dragonfly_test.cc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,9 @@ TEST_F(DflyEngineTest, Memcache) {
341341
auto resp = RunMC(MP::SET, "key", "bar", 1);
342342
EXPECT_THAT(resp, ElementsAre("STORED"));
343343

344+
resp = RunMC(MP::GETS, "key");
345+
EXPECT_THAT(resp, ElementsAre("VALUE key 1 3 0", "bar", "END"));
346+
344347
resp = RunMC(MP::GET, "key");
345348
EXPECT_THAT(resp, ElementsAre("VALUE key 1 3", "bar", "END"));
346349

@@ -366,6 +369,9 @@ TEST_F(DflyEngineTest, Memcache) {
366369

367370
resp = RunMC(MP::GET, "unkn");
368371
EXPECT_THAT(resp, ElementsAre("END"));
372+
373+
resp = GetMC(MP::GETS, {"key", "key2", "unknown"});
374+
EXPECT_THAT(resp, ElementsAre("VALUE key 1 3 0", "bar", "VALUE key2 2 8 0", "bar2val2", "END"));
369375
}
370376

371377
TEST_F(DflyEngineTest, MemcacheFlags) {

src/server/main_service.cc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1536,6 +1536,8 @@ void Service::DispatchMC(const MemcacheParser::Command& cmd, std::string_view va
15361536
strcpy(cmd_name, "PREPEND");
15371537
break;
15381538
case MemcacheParser::GET:
1539+
[[fallthrough]];
1540+
case MemcacheParser::GETS:
15391541
strcpy(cmd_name, "MGET");
15401542
break;
15411543
case MemcacheParser::FLUSHALL:
@@ -1589,6 +1591,9 @@ void Service::DispatchMC(const MemcacheParser::Command& cmd, std::string_view va
15891591
char* key = const_cast<char*>(s.data());
15901592
args.emplace_back(key, s.size());
15911593
}
1594+
if (cmd.type == MemcacheParser::GETS) {
1595+
dfly_cntx->conn_state.memcache_flag |= ConnectionState::FETCH_CAS_VER;
1596+
}
15921597
} else { // write commands.
15931598
if (store_opt[0]) {
15941599
args.emplace_back(store_opt, strlen(store_opt));

src/server/string_family.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1366,7 +1366,7 @@ void StringFamily::MGet(CmdArgList args, const CommandContext& cmnd_cntx) {
13661366
DCHECK(dynamic_cast<CapturingReplyBuilder*>(builder) == nullptr);
13671367
for (const auto& entry : res) {
13681368
if (entry) {
1369-
rb->SendValue(entry->key, entry->value, entry->mc_ver, entry->mc_flag);
1369+
rb->SendValue(entry->key, entry->value, 0, entry->mc_flag, fetch_mask & FETCH_MCVER);
13701370
} else {
13711371
rb->SendMiss();
13721372
}

tests/dragonfly/memcache_meta.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
CacheClient,
77
connection_pool_factory_builder,
88
)
9-
from meta_memcache.protocol import RequestFlags, Miss, Value, Success
9+
from meta_memcache.protocol import RequestFlags, Success
1010

1111
DEFAULT_ARGS = {"memcached_port": 11211, "proactor_threads": 4}
1212

@@ -30,3 +30,12 @@ def test_basic(df_server: DflyInstance):
3030
assert pool.get("key2") is None
3131
assert pool.delete("key1")
3232
assert pool.delete("key1") is False
33+
34+
assert pool.set("cask", "v", 100)
35+
value, cas_token = pool.get_cas("cask")
36+
assert value == "v" and cas_token == 0
37+
38+
k = Key("cask")
39+
response = pool.meta_multiget([k], RequestFlags(return_cas_token=True, return_value=True))
40+
assert k in response
41+
assert response[k].flags.cas_token == 0 and response[k].value == "v"

tests/dragonfly/pymemcached_test.py

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
11
import logging
2-
import pytest
3-
from pymemcache.client.base import Client as MCClient
4-
from redis import Redis
5-
import socket
62
import random
7-
import time
3+
import socket
84
import ssl
5+
import time
6+
7+
from pymemcache.client.base import Client as MCClient
98

109
from . import dfly_args
1110
from .instance import DflyInstance
1211

1312
DEFAULT_ARGS = {"memcached_port": 11211, "proactor_threads": 4}
1413

14+
1515
# Generic basic tests
1616

1717

@@ -32,7 +32,7 @@ def test_basic(memcached_client: MCClient):
3232
# delete
3333
assert memcached_client.delete("key1")
3434
assert not memcached_client.delete("key3")
35-
assert memcached_client.get("key1") == None
35+
assert memcached_client.get("key1") is None
3636

3737
# prepend append
3838
assert memcached_client.set("key4", "B")
@@ -46,6 +46,8 @@ def test_basic(memcached_client: MCClient):
4646
assert memcached_client.incr("key5", 1) == 2
4747
assert memcached_client.decr("key5", 1) == 1
4848

49+
assert memcached_client.gets("key5") == (b"1", b"0")
50+
4951

5052
# Noreply (and pipeline) tests
5153

0 commit comments

Comments
 (0)