Skip to content

Commit f74c376

Browse files
committed
FEATURE: Add gat command
add get and touch command
1 parent 447601d commit f74c376

File tree

3 files changed

+157
-6
lines changed

3 files changed

+157
-6
lines changed

docs/ascii-protocol/ch04-command-key-value.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,34 @@ END\r\n
7373

7474
mget 명령에서 메모리 부족으로 일부 key에 대해서만 정상 조회한 후 실패한 경우, 전체 연산을 서버 에러 처리한다.
7575

76+
## gat/gats (get and touch)
77+
78+
gat, gats 명령은 item을 가져옴과 동시에 exptime을 재설정한다.
79+
위 두 명령은 item이 collection 이 아닌 경우에만 동작합니다.
80+
81+
```
82+
gat <exptime> <key>*\r\n
83+
gats <exptime> <key>*\r\n
84+
```
85+
86+
- \<key\>* - 대상 item의 key string 복수개의 key를 공백을 두고 지정할 수 있다.
87+
- \<exptime\> - 재설정할 expiretime 값 이다.
88+
89+
gat 명령이 정상 수행되었을 경우, Response string 은 아래와 같이 구성된다.
90+
91+
- key hit된 아이템 정보를 모두 출력
92+
- key miss된 아이템은 별도 response 없이 생략
93+
- 응답의 끝에 "END\r\n" 출력
94+
95+
gats 명령의 경우 cas value 도 같이 출력된다.
96+
97+
실패시 string은 아래와 같다.
98+
99+
| Response String | 설명 |
100+
|----------------------|------------------------ |
101+
| "CLIENT_ERROR" | 클라이언트에서 잘못된 질의를 했음을 의미. 이어 나오는 문자열을 통해 오류의 원인을 파악 가능. 예) bad command line format
102+
| "SERVER ERROR" | 서버 측의 오류로 조회하지 못했음을 의미. 이어 나오는 문자열을 통해 오류의 원인을 파악 가능. 예) out of memory writing get response
103+
76104
## deletion 명령
77105

78106
delete 명령이 있으며 syntax는 다음과 같다.

memcached.c

Lines changed: 59 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
#include "config.h"
3434
#include "memcached.h"
3535
#include "memcached/extension_loggers.h"
36+
#include "engines/default/item_base.h"
3637
#ifdef ENABLE_ZK_INTEGRATION
3738
#include "arcus_zk.h"
3839
#include "arcus_hb.h"
@@ -2977,7 +2978,7 @@ static char *get_suffix_buffer(conn *c)
29772978
}
29782979

29792980
static ENGINE_ERROR_CODE
2980-
process_get_single(conn *c, char *key, size_t nkey, bool return_cas)
2981+
process_get_single(conn *c, char *key, size_t nkey, bool return_cas, bool* coll)
29812982
{
29822983
item *it;
29832984
char *cas_val = NULL;
@@ -2988,6 +2989,9 @@ process_get_single(conn *c, char *key, size_t nkey, bool return_cas)
29882989
if (ret != ENGINE_SUCCESS) {
29892990
it = NULL;
29902991
}
2992+
if (ret == ENGINE_EBADTYPE) {
2993+
*coll = true;
2994+
}
29912995
if (settings.detail_enabled) {
29922996
stats_prefix_record_get(key, nkey, (it != NULL));
29932997
}
@@ -3070,6 +3074,7 @@ static void process_mget_complete(conn *c, bool return_cas)
30703074

30713075
ENGINE_ERROR_CODE ret = ENGINE_SUCCESS;
30723076
token_t *key_tokens;
3077+
bool coll = false;
30733078

30743079
do {
30753080
key_tokens = (token_t*)token_buff_get(&c->thread->token_buff, c->coll_numkeys);
@@ -3087,7 +3092,7 @@ static void process_mget_complete(conn *c, bool return_cas)
30873092
/* do get operation for each key */
30883093
for (int k = 0; k < c->coll_numkeys; k++) {
30893094
ret = process_get_single(c, key_tokens[k].value, key_tokens[k].length,
3090-
return_cas);
3095+
return_cas, &coll);
30913096
if (ret != ENGINE_SUCCESS) {
30923097
break; /* ret == ENGINE_ENOMEM*/
30933098
}
@@ -8345,11 +8350,23 @@ static void process_stats_command(conn *c, token_t *tokens, const size_t ntokens
83458350
}
83468351

83478352
/* ntokens is overwritten here... shrug.. */
8348-
static inline void process_get_command(conn *c, token_t *tokens, size_t ntokens, bool return_cas)
8353+
static inline void process_get_command(conn *c, token_t *tokens, size_t ntokens, bool return_cas, bool should_touch)
83498354
{
83508355
assert(c != NULL);
8356+
83518357
token_t *key_token = &tokens[KEY_TOKEN];
83528358
ENGINE_ERROR_CODE ret = ENGINE_SUCCESS;
8359+
int64_t exptime = 0;
8360+
bool coll = false;
8361+
8362+
if (should_touch) {
8363+
// For get and touch commands, use first token as exptime
8364+
if (!safe_strtoll(tokens[1].value, &exptime)) {
8365+
out_string(c, "CLIENT_ERROR invalid exptime argument");
8366+
return;
8367+
}
8368+
key_token++;
8369+
}
83538370

83548371
do {
83558372
while (key_token->length != 0) {
@@ -8358,10 +8375,38 @@ static inline void process_get_command(conn *c, token_t *tokens, size_t ntokens,
83588375
}
83598376
/* do get operation for each key */
83608377
ret = process_get_single(c, key_token->value, key_token->length,
8361-
return_cas);
8378+
return_cas, &coll);
83628379
if (ret != ENGINE_SUCCESS) {
83638380
break; /* ret == ENGINE_ENOMEM */
83648381
}
8382+
8383+
if (should_touch && !coll) {
8384+
ENGINE_ITEM_ATTR attr_id = ATTR_EXPIRETIME;
8385+
ENGINE_ERROR_CODE gat_ret = ENGINE_SUCCESS;
8386+
item_attr attr_data;
8387+
8388+
attr_data.exptime = realtime(exptime);
8389+
gat_ret = mc_engine.v1->setattr(mc_engine.v0, c, key_token->value, key_token->length,
8390+
&attr_id, 1, &attr_data, 0);
8391+
CONN_CHECK_AND_SET_EWOULDBLOCK(gat_ret, c);
8392+
if (settings.detail_enabled) {
8393+
stats_prefix_record_setattr(key_token->value, key_token->length);
8394+
}
8395+
8396+
if (gat_ret == ENGINE_KEY_ENOENT) {
8397+
STATS_MISSES(c, setattr, key_token->value, key_token->length);
8398+
out_string(c, "CLIENT_ERROR key not found");
8399+
return;
8400+
} else if (gat_ret == ENGINE_EBADVALUE) {
8401+
STATS_CMD_NOKEY(c, setattr);
8402+
out_string(c, "CLIENT_ERROR bad value");
8403+
return;
8404+
} else if (gat_ret != ENGINE_SUCCESS) {
8405+
STATS_CMD_NOKEY(c, setattr);
8406+
handle_unexpected_errorcode_ascii(c, __func__, gat_ret);
8407+
return;
8408+
}
8409+
}
83658410
key_token++;
83668411
}
83678412
if (ret != ENGINE_SUCCESS) break;
@@ -13124,11 +13169,19 @@ static void process_command_ascii(conn *c, char *command, int cmdlen)
1312413169

1312513170
if ((ntokens >= 3) && (strcmp(tokens[COMMAND_TOKEN].value, "get") == 0))
1312613171
{
13127-
process_get_command(c, tokens, ntokens, false);
13172+
process_get_command(c, tokens, ntokens, false, false);
1312813173
}
1312913174
else if ((ntokens >= 3) && (strcmp(tokens[COMMAND_TOKEN].value, "gets") == 0))
1313013175
{
13131-
process_get_command(c, tokens, ntokens, true);
13176+
process_get_command(c, tokens, ntokens, true, false);
13177+
}
13178+
else if ((ntokens >= 3) && (strcmp(tokens[COMMAND_TOKEN].value, "gat") == 0))
13179+
{
13180+
process_get_command(c, tokens, ntokens, false, true);
13181+
}
13182+
else if ((ntokens >= 3) && (strcmp(tokens[COMMAND_TOKEN].value, "gats") == 0))
13183+
{
13184+
process_get_command(c, tokens, ntokens, true, true);
1313213185
}
1313313186
else if ((ntokens == 4) && (strcmp(tokens[COMMAND_TOKEN].value, "mget") == 0))
1313413187
{

t/gat.t

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
#!/usr/bin/perl
2+
3+
use strict;
4+
use Test::More tests =>12;
5+
use FindBin qw($Bin);
6+
use lib "$Bin/lib";
7+
use MemcachedTest;
8+
9+
my $engine = shift;
10+
my $server = get_memcached($engine);
11+
my $sock = $server->sock;
12+
13+
my $cmd;
14+
my $val;
15+
my $rst;
16+
my $expire;
17+
18+
# Initialize
19+
$cmd = "set key 0 0 5"; $val = "datum"; $rst = "STORED";
20+
mem_cmd_is($sock, $cmd, $val, $rst);
21+
22+
# Success Cases
23+
# key value
24+
$cmd = "gat 1 key";
25+
$rst = "VALUE key 0 5\n"
26+
. "datum\n"
27+
. "END";
28+
mem_cmd_is($sock, $cmd, "", $rst);
29+
$cmd = "getattr key expiretime";
30+
$rst = "ATTR expiretime=1\n"
31+
. "END";
32+
mem_cmd_is($sock, $cmd, "", $rst);
33+
# gats command
34+
$cmd = "gats 1 key";
35+
$rst = "VALUE key 0 5 1\n"
36+
. "datum\n"
37+
. "END";
38+
mem_cmd_is($sock, $cmd, "", $rst);
39+
$cmd = "getattr key expiretime";
40+
$rst = "ATTR expiretime=1\n"
41+
. "END";
42+
mem_cmd_is($sock, $cmd, "", $rst);
43+
44+
# Fail Cases
45+
# bad value
46+
$cmd = "set key 0 0 5"; $val = "datum"; $rst = "STORED";
47+
mem_cmd_is($sock, $cmd, $val, $rst);
48+
$cmd = "gat str key"; $rst = "CLIENT_ERROR invalid exptime argument";
49+
mem_cmd_is($sock, $cmd, "", $rst);
50+
# not exist key
51+
$expire = time() - 1;
52+
$cmd = "gat $expire key";
53+
$rst = "VALUE key 0 5\n"
54+
. "datum\n"
55+
. "END";
56+
mem_cmd_is($sock, $cmd, "", $rst);
57+
$cmd = "gat 1 key"; $rst = "CLIENT_ERROR key not found";
58+
mem_cmd_is($sock, $cmd, "", $rst);
59+
# collection type
60+
$cmd = "lop create lkey 0 0 5"; $rst = "CREATED";
61+
mem_cmd_is($sock, $cmd, "", $rst);
62+
$cmd = "gat 1 lkey"; $rst = "END";
63+
mem_cmd_is($sock, $cmd, "", $rst);
64+
$cmd = "getattr lkey expiretime";
65+
$rst = "ATTR expiretime=0\n"
66+
. "END";
67+
mem_cmd_is($sock, $cmd, "", $rst);
68+
69+
# after test
70+
release_memcached($engine, $server);

0 commit comments

Comments
 (0)