mirror of
https://github.com/dragonflydb/dragonfly.git
synced 2025-05-10 18:05:44 +02:00
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>
This commit is contained in:
parent
fe495cde3f
commit
fc00f2c972
7 changed files with 34 additions and 11 deletions
|
@ -212,7 +212,7 @@ MCReplyBuilder::MCReplyBuilder(::io::Sink* sink) : SinkReplyBuilder(sink), all_(
|
|||
}
|
||||
|
||||
void MCReplyBuilder::SendValue(std::string_view key, std::string_view value, uint64_t mc_ver,
|
||||
uint32_t mc_flag) {
|
||||
uint32_t mc_flag, bool send_cas_token) {
|
||||
ReplyScope scope(this);
|
||||
if (flag_.meta) {
|
||||
string flags;
|
||||
|
@ -227,7 +227,7 @@ void MCReplyBuilder::SendValue(std::string_view key, std::string_view value, uin
|
|||
}
|
||||
} else {
|
||||
WritePieces("VALUE ", key, " ", mc_flag, " ", value.size());
|
||||
if (mc_ver)
|
||||
if (send_cas_token)
|
||||
WritePieces(" ", mc_ver);
|
||||
|
||||
if (value.size() <= kMaxInlineSize) {
|
||||
|
|
|
@ -177,7 +177,8 @@ class MCReplyBuilder : public SinkReplyBuilder {
|
|||
void SendDeleted();
|
||||
void SendGetEnd();
|
||||
|
||||
void SendValue(std::string_view key, std::string_view value, uint64_t mc_ver, uint32_t mc_flag);
|
||||
void SendValue(std::string_view key, std::string_view value, uint64_t mc_ver, uint32_t mc_flag,
|
||||
bool send_cas_token);
|
||||
void SendSimpleString(std::string_view str) final;
|
||||
void SendProtocolError(std::string_view str) final;
|
||||
|
||||
|
|
|
@ -341,6 +341,9 @@ TEST_F(DflyEngineTest, Memcache) {
|
|||
auto resp = RunMC(MP::SET, "key", "bar", 1);
|
||||
EXPECT_THAT(resp, ElementsAre("STORED"));
|
||||
|
||||
resp = RunMC(MP::GETS, "key");
|
||||
EXPECT_THAT(resp, ElementsAre("VALUE key 1 3 0", "bar", "END"));
|
||||
|
||||
resp = RunMC(MP::GET, "key");
|
||||
EXPECT_THAT(resp, ElementsAre("VALUE key 1 3", "bar", "END"));
|
||||
|
||||
|
@ -366,6 +369,9 @@ TEST_F(DflyEngineTest, Memcache) {
|
|||
|
||||
resp = RunMC(MP::GET, "unkn");
|
||||
EXPECT_THAT(resp, ElementsAre("END"));
|
||||
|
||||
resp = GetMC(MP::GETS, {"key", "key2", "unknown"});
|
||||
EXPECT_THAT(resp, ElementsAre("VALUE key 1 3 0", "bar", "VALUE key2 2 8 0", "bar2val2", "END"));
|
||||
}
|
||||
|
||||
TEST_F(DflyEngineTest, MemcacheFlags) {
|
||||
|
|
|
@ -1536,6 +1536,8 @@ void Service::DispatchMC(const MemcacheParser::Command& cmd, std::string_view va
|
|||
strcpy(cmd_name, "PREPEND");
|
||||
break;
|
||||
case MemcacheParser::GET:
|
||||
[[fallthrough]];
|
||||
case MemcacheParser::GETS:
|
||||
strcpy(cmd_name, "MGET");
|
||||
break;
|
||||
case MemcacheParser::FLUSHALL:
|
||||
|
@ -1589,6 +1591,9 @@ void Service::DispatchMC(const MemcacheParser::Command& cmd, std::string_view va
|
|||
char* key = const_cast<char*>(s.data());
|
||||
args.emplace_back(key, s.size());
|
||||
}
|
||||
if (cmd.type == MemcacheParser::GETS) {
|
||||
dfly_cntx->conn_state.memcache_flag |= ConnectionState::FETCH_CAS_VER;
|
||||
}
|
||||
} else { // write commands.
|
||||
if (store_opt[0]) {
|
||||
args.emplace_back(store_opt, strlen(store_opt));
|
||||
|
|
|
@ -1366,7 +1366,7 @@ void StringFamily::MGet(CmdArgList args, const CommandContext& cmnd_cntx) {
|
|||
DCHECK(dynamic_cast<CapturingReplyBuilder*>(builder) == nullptr);
|
||||
for (const auto& entry : res) {
|
||||
if (entry) {
|
||||
rb->SendValue(entry->key, entry->value, entry->mc_ver, entry->mc_flag);
|
||||
rb->SendValue(entry->key, entry->value, 0, entry->mc_flag, fetch_mask & FETCH_MCVER);
|
||||
} else {
|
||||
rb->SendMiss();
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ from meta_memcache import (
|
|||
CacheClient,
|
||||
connection_pool_factory_builder,
|
||||
)
|
||||
from meta_memcache.protocol import RequestFlags, Miss, Value, Success
|
||||
from meta_memcache.protocol import RequestFlags, Success
|
||||
|
||||
DEFAULT_ARGS = {"memcached_port": 11211, "proactor_threads": 4}
|
||||
|
||||
|
@ -30,3 +30,12 @@ def test_basic(df_server: DflyInstance):
|
|||
assert pool.get("key2") is None
|
||||
assert pool.delete("key1")
|
||||
assert pool.delete("key1") is False
|
||||
|
||||
assert pool.set("cask", "v", 100)
|
||||
value, cas_token = pool.get_cas("cask")
|
||||
assert value == "v" and cas_token == 0
|
||||
|
||||
k = Key("cask")
|
||||
response = pool.meta_multiget([k], RequestFlags(return_cas_token=True, return_value=True))
|
||||
assert k in response
|
||||
assert response[k].flags.cas_token == 0 and response[k].value == "v"
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
import logging
|
||||
import pytest
|
||||
from pymemcache.client.base import Client as MCClient
|
||||
from redis import Redis
|
||||
import socket
|
||||
import random
|
||||
import time
|
||||
import socket
|
||||
import ssl
|
||||
import time
|
||||
|
||||
from pymemcache.client.base import Client as MCClient
|
||||
|
||||
from . import dfly_args
|
||||
from .instance import DflyInstance
|
||||
|
||||
DEFAULT_ARGS = {"memcached_port": 11211, "proactor_threads": 4}
|
||||
|
||||
|
||||
# Generic basic tests
|
||||
|
||||
|
||||
|
@ -32,7 +32,7 @@ def test_basic(memcached_client: MCClient):
|
|||
# delete
|
||||
assert memcached_client.delete("key1")
|
||||
assert not memcached_client.delete("key3")
|
||||
assert memcached_client.get("key1") == None
|
||||
assert memcached_client.get("key1") is None
|
||||
|
||||
# prepend append
|
||||
assert memcached_client.set("key4", "B")
|
||||
|
@ -46,6 +46,8 @@ def test_basic(memcached_client: MCClient):
|
|||
assert memcached_client.incr("key5", 1) == 2
|
||||
assert memcached_client.decr("key5", 1) == 1
|
||||
|
||||
assert memcached_client.gets("key5") == (b"1", b"0")
|
||||
|
||||
|
||||
# Noreply (and pipeline) tests
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue