From 76782683127d20f83475ecbf7bdb736f476d92ac Mon Sep 17 00:00:00 2001 From: cheesecrust Date: Fri, 23 Aug 2024 17:14:54 +0900 Subject: [PATCH] FEATURE: Add touch command --- docs/ascii-protocol/ch04-command-key-value.md | 19 +++++ memcached.c | 51 ++++++++++++ t/tlist/engine_default_b.txt | 1 + t/tlist/engine_default_s.txt | 1 + t/touch.t | 81 +++++++++++++++++++ 5 files changed, 153 insertions(+) create mode 100644 t/touch.t diff --git a/docs/ascii-protocol/ch04-command-key-value.md b/docs/ascii-protocol/ch04-command-key-value.md index 07d35a8a4..e3683cd31 100644 --- a/docs/ascii-protocol/ch04-command-key-value.md +++ b/docs/ascii-protocol/ch04-command-key-value.md @@ -110,3 +110,22 @@ decr [ ] [noreply]\r\n | "TYPE_MISMATCH" | 해당 아이템이 key-value 타입이 아님 | "CLIENT_ERROR" | 클라이언트에서 잘못된 질의를 했음을 의미. 이어 나오는 문자열을 통해 오류의 원인을 파악 가능. 예) invalid numeric delta argument, cannot increment or decrement non-numeric value | "SERVER ERROR" | 서버 측의 오류로 연산하지 못했음을 의미. 이어 나오는 문자열을 통해 오류의 원인을 파악 가능. 예) out of memory + +## touch (Item의 expiretime 변경) + +key에 해당하는 item을 fetch 하지 않고 expiretime을 재설정하는 명령은 아래와 같다. 이 명령은 모든 item 타입에 대해 적용이 가능하다. + +``` +touch [noreply]\r\n +``` + +- \ - 대상 item의 key string +- \ - 재설정할 expiretime 값 + +이 명령의 response string과 그 의미는 아래와 같다. + +| Response String | 설명 | +| ------------------------ | ------------------------------- | +| "TOUCHED" | 성공 | +| "NOT_FOUND" | key miss | +| "CLIENT_ERROR bad value" | exptime 값이 유효하지 않은 경우 | diff --git a/memcached.c b/memcached.c index 05043412d..55540410e 100644 --- a/memcached.c +++ b/memcached.c @@ -13085,6 +13085,53 @@ static void process_setattr_command(conn *c, token_t *tokens, const size_t ntoke } } +static void process_touch_command(conn *c, token_t *tokens, const size_t ntokens) +{ + assert(c != NULL); + assert(c->ewouldblock == false); + char *key = tokens[KEY_TOKEN].value; + size_t nkey = tokens[KEY_TOKEN].length; + + if (nkey > KEY_MAX_LENGTH) { + out_string(c, "CLIENT_ERROR bad command line format"); + return; + } + + ENGINE_ERROR_CODE ret; + item_attr attr_data; + ENGINE_ITEM_ATTR attr_id = ATTR_EXPIRETIME; + int64_t exptime; + + set_noreply_maybe(c, tokens, ntokens); + + if (! safe_strtoll(tokens[KEY_TOKEN+1].value, &exptime)) { + ret = ENGINE_EBADVALUE; + } else { + attr_data.exptime = realtime(exptime); + ret = mc_engine.v1->setattr(mc_engine.v0, c, key, nkey, + &attr_id, 1, &attr_data, 0); + CONN_CHECK_AND_SET_EWOULDBLOCK(ret, c); + if (settings.detail_enabled) { + stats_prefix_record_setattr(key, nkey); + } + } + + switch (ret) { + case ENGINE_SUCCESS: + STATS_HITS(c, setattr, key, nkey); + out_string(c, "TOUCHED"); + break; + case ENGINE_KEY_ENOENT: + STATS_MISSES(c, setattr, key, nkey); + out_string(c, "NOT_FOUND"); + break; + default: + STATS_CMD_NOKEY(c, setattr); + if (ret == ENGINE_EBADVALUE) out_string(c, "CLIENT_ERROR bad value"); + else handle_unexpected_errorcode_ascii(c, __func__, ret); + } +} + static void process_command_ascii(conn *c, char *command, int cmdlen) { /* One more token is reserved in tokens strucure @@ -13206,6 +13253,10 @@ static void process_command_ascii(conn *c, char *command, int cmdlen) { process_config_command(c, tokens, ntokens); } + else if ((ntokens >= 4 && ntokens <= 5) && (strcmp(tokens[COMMAND_TOKEN].value, "touch") == 0)) + { + process_touch_command(c, tokens, ntokens); + } #ifdef ENABLE_ZK_INTEGRATION else if ((ntokens >= 3) && (strcmp(tokens[COMMAND_TOKEN].value, "zkensemble") == 0)) { diff --git a/t/tlist/engine_default_b.txt b/t/tlist/engine_default_b.txt index 7e228ecee..ca25edcdf 100644 --- a/t/tlist/engine_default_b.txt +++ b/t/tlist/engine_default_b.txt @@ -102,3 +102,4 @@ ./t/nested_prefix.t ./t/keyscan.t ./t/prefixscan.t +./t/touch.t diff --git a/t/tlist/engine_default_s.txt b/t/tlist/engine_default_s.txt index 31bdd5ee8..fffbc2fe5 100644 --- a/t/tlist/engine_default_s.txt +++ b/t/tlist/engine_default_s.txt @@ -95,3 +95,4 @@ ./t/nested_prefix.t ./t/keyscan.t ./t/prefixscan.t +./t/touch.t diff --git a/t/touch.t b/t/touch.t new file mode 100644 index 000000000..a5cfaa5c7 --- /dev/null +++ b/t/touch.t @@ -0,0 +1,81 @@ +#!/usr/bin/perl + +use strict; +use Test::More tests =>19; +use FindBin qw($Bin); +use lib "$Bin/lib"; +use MemcachedTest; + +my $engine = shift; +my $server = get_memcached($engine); +my $sock = $server->sock; + +my $cmd; +my $val; +my $rst; +my $expire; + +# Initialize +$cmd = "set key 0 0 5"; $val = "datum"; $rst = "STORED"; +mem_cmd_is($sock, $cmd, $val, $rst); +$cmd = "lop create lkey 0 0 1 error"; $rst = "CREATED"; +mem_cmd_is($sock, $cmd, "", $rst); +$cmd = "sop create skey 0 0 1 error"; $rst = "CREATED"; +mem_cmd_is($sock, $cmd, "", $rst); +$cmd = "mop create mkey 0 0 1 error"; $rst = "CREATED"; +mem_cmd_is($sock, $cmd, "", $rst); +$cmd = "bop create bkey 0 0 1 error"; $rst = "CREATED"; +mem_cmd_is($sock, $cmd, "", $rst); + +# Success Cases +# key value +$cmd = "touch key 1"; $rst = "TOUCHED"; +mem_cmd_is($sock, $cmd, "", $rst); +$cmd = "getattr key expiretime"; +$rst = "ATTR expiretime=1\n" + . "END"; +mem_cmd_is($sock, $cmd, "", $rst); +# list +$cmd = "touch lkey 1"; $rst = "TOUCHED"; +mem_cmd_is($sock, $cmd, "", $rst); +$cmd = "getattr lkey expiretime"; +$rst = "ATTR expiretime=1\n" + . "END"; +mem_cmd_is($sock, $cmd, "", $rst); +# set +$cmd = "touch skey 1"; $rst = "TOUCHED"; +mem_cmd_is($sock, $cmd, "", $rst); +$cmd = "getattr skey expiretime"; +$rst = "ATTR expiretime=1\n" + . "END"; +mem_cmd_is($sock, $cmd, "", $rst); +# map +$cmd = "touch mkey 1"; $rst = "TOUCHED"; +mem_cmd_is($sock, $cmd, "", $rst); +$cmd = "getattr mkey expiretime"; +$rst = "ATTR expiretime=1\n" + . "END"; +mem_cmd_is($sock, $cmd, "", $rst); +#btree +$cmd = "touch bkey 1"; $rst = "TOUCHED"; +mem_cmd_is($sock, $cmd, "", $rst); +$cmd = "getattr bkey expiretime"; +$rst = "ATTR expiretime=1\n" + . "END"; +mem_cmd_is($sock, $cmd, "", $rst); + +# Fail Cases +# bad value +$cmd = "set key 0 0 5"; $val = "datum"; $rst = "STORED"; +mem_cmd_is($sock, $cmd, $val, $rst); +$cmd = "touch key str"; $rst = "CLIENT_ERROR bad value"; +mem_cmd_is($sock, $cmd, "", $rst); +# not exist key +$expire = time() - 1; +$cmd = "touch key $expire"; $rst = "TOUCHED"; +mem_cmd_is($sock, $cmd, "", $rst); +$cmd = "touch key 1"; $rst = "NOT_FOUND"; +mem_cmd_is($sock, $cmd, "", $rst); + +# after test +release_memcached($engine, $server);