More work on memcached API

1. Fix memcached ttl semantics.
2. For incr/decr commands if a key does not exist - return NOT_FOUND.
This commit is contained in:
Roman Gershman 2022-02-24 17:14:03 +02:00
parent 737c5fed71
commit 7445b20548
6 changed files with 28 additions and 4 deletions

View File

@ -82,7 +82,7 @@ struct Connection::Shutdown {
struct Connection::Request {
absl::FixedArray<MutableSlice> args;
// I do not use m_heap_t explicitly but mi_stl_allocator at the end does the same job
// I do not use mi_heap_t explicitly but mi_stl_allocator at the end does the same job
// of using the thread's heap.
absl::FixedArray<char, 256, mi_stl_allocator<char>> storage;

View File

@ -471,9 +471,12 @@ void Service::DispatchMC(const MemcacheParser::Command& cmd, std::string_view va
ConnectionContext* cntx) {
absl::InlinedVector<MutableSlice, 8> args;
char cmd_name[16];
char ttl[16];
char store_opt[32] = {0};
char ttl_op[] = "EX";
MCReplyBuilder* mc_builder = static_cast<MCReplyBuilder*>(cntx->reply_builder());
switch (cmd.type) {
case MemcacheParser::REPLACE:
strcpy(cmd_name, "SET");
@ -529,6 +532,12 @@ void Service::DispatchMC(const MemcacheParser::Command& cmd, std::string_view va
if (store_opt[0]) {
args.emplace_back(store_opt, strlen(store_opt));
}
if (cmd.expire_ts && memcmp(cmd_name, "SET", 3) == 0) {
char* next = absl::numbers_internal::FastIntToBuffer(cmd.expire_ts, ttl);
args.emplace_back(ttl_op, 2);
args.emplace_back(ttl, next - ttl);
}
cntx->conn_state.memcache_flag = cmd.flags;
} else if (cmd.type < MemcacheParser::QUIT) { // read commands
for (auto s : cmd.keys_ext) {

View File

@ -125,6 +125,10 @@ void MCReplyBuilder::SendSetSkipped() {
SendDirect("NOT_STORED\r\n");
}
void MCReplyBuilder::SendNotFound() {
SendDirect("NOT_FOUND\r\n");
}
RedisReplyBuilder::RedisReplyBuilder(::io::Sink* sink) : SinkReplyBuilder(sink) {
}

View File

@ -99,6 +99,7 @@ class MCReplyBuilder : public SinkReplyBuilder {
void SendSetSkipped() final;
void SendClientError(std::string_view str);
void SendNotFound();
};
class RedisReplyBuilder : public SinkReplyBuilder {

View File

@ -263,8 +263,10 @@ void StringFamily::Prepend(CmdArgList args, ConnectionContext* cntx) {
}
void StringFamily::IncrByGeneric(std::string_view key, int64_t val, ConnectionContext* cntx) {
bool skip_on_missing = cntx->protocol() == Protocol::MEMCACHE;
auto cb = [&](Transaction* t, EngineShard* shard) {
OpResult<int64_t> res = OpIncrBy(OpArgs{shard, t->db_index()}, key, val);
OpResult<int64_t> res = OpIncrBy(OpArgs{shard, t->db_index()}, key, val, skip_on_missing);
return res;
};
@ -281,6 +283,8 @@ void StringFamily::IncrByGeneric(std::string_view key, int64_t val, ConnectionCo
return builder->SendError("increment or decrement would overflow");
case OpStatus::WRONG_TYPE:
return builder->SendError(kWrongTypeErr);
case OpStatus::KEY_NOTFOUND: // Relevant only for MC
return reinterpret_cast<MCReplyBuilder*>(builder)->SendNotFound();
default:;
}
__builtin_unreachable();
@ -432,11 +436,14 @@ OpStatus StringFamily::OpMSet(const Transaction* t, EngineShard* es) {
}
OpResult<int64_t> StringFamily::OpIncrBy(const OpArgs& op_args, std::string_view key,
int64_t incr) {
int64_t incr, bool skip_on_missing) {
auto& db_slice = op_args.shard->db_slice();
auto [it, expire_it] = db_slice.FindExt(op_args.db_ind, key);
if (!IsValid(it)) {
if (skip_on_missing)
return OpStatus::KEY_NOTFOUND;
CompactObj cobj;
cobj.SetInt(incr);

View File

@ -73,7 +73,10 @@ class StringFamily {
EngineShard* shard);
static OpStatus OpMSet(const Transaction* t, EngineShard* es);
static OpResult<int64_t> OpIncrBy(const OpArgs& op_args, std::string_view key, int64_t val);
// if skip_on_missing - returns KEY_NOTFOUND.
static OpResult<int64_t> OpIncrBy(const OpArgs& op_args, std::string_view key, int64_t val,
bool skip_on_missing);
// Returns the length of the extended string. if prepend is false - appends the val.
static OpResult<uint32_t> ExtendOrSet(const OpArgs& op_args, std::string_view key,