简单ttl懒删除支持
This commit is contained in:
@@ -336,12 +336,52 @@ static int expect_argv(const resp_cmd_t *cmd, uint32_t n) {
|
||||
|
||||
|
||||
const char *command[] = {
|
||||
"SET", "GET", "DEL", "MOD", "EXIST",
|
||||
"PING", "AUTH",
|
||||
"SET", "GET", "DEL", "SETEX", "MOD", "EXIST",
|
||||
"RSET", "RGET", "RDEL", "RMOD", "REXIST",
|
||||
"HSET", "HGET", "HDEL", "HMOD", "HEXIST",
|
||||
"SAVE", "SSYNC", "SREADY", "MEMPRINT"
|
||||
};
|
||||
|
||||
typedef struct ttl_visit_ctx_s {
|
||||
uint64_t now_ms;
|
||||
int path_budget;
|
||||
const uint8_t *target_key;
|
||||
uint32_t target_key_len;
|
||||
} ttl_visit_ctx_t;
|
||||
|
||||
static int ttl_schedule_if_expired(rbtree_node *node, void *arg) {
|
||||
ttl_visit_ctx_t *ctx;
|
||||
const uint8_t *key;
|
||||
|
||||
if (!node || !arg) return 0;
|
||||
ctx = (ttl_visit_ctx_t *)arg;
|
||||
if (node->expire_at_ms == 0 || node->expire_at_ms > ctx->now_ms) return 0;
|
||||
|
||||
key = kvs_rbtree_node_key_ptr(node);
|
||||
if (!key || node->key_len == 0) return 0;
|
||||
|
||||
if (ctx->target_key_len == node->key_len &&
|
||||
memcmp(key, ctx->target_key, node->key_len) == 0) {
|
||||
return 0;
|
||||
}
|
||||
if (ctx->path_budget <= 0) return 0;
|
||||
|
||||
if (ttl_delete_schedule(key, node->key_len) == 0) {
|
||||
ctx->path_budget--;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int match_auth_password(const resp_slice_t *password) {
|
||||
size_t expect_len;
|
||||
|
||||
if (!password) return 0;
|
||||
expect_len = strlen(global_cfg.redis_auth_password);
|
||||
if (expect_len != (size_t)password->len) return 0;
|
||||
return memcmp(global_cfg.redis_auth_password, password->ptr, expect_len) == 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 输入:cmd
|
||||
@@ -373,45 +413,89 @@ int resp_dispatch(const resp_cmd_t *cmd, resp_value_t *out_value) {
|
||||
}
|
||||
|
||||
switch (op) {
|
||||
#if ENABLE_ARRAY
|
||||
case KVS_CMD_PING: {
|
||||
if (cmd->argc != 1) { *out_value = resp_error("ERR wrong number of arguments for 'ping'"); return -1; }
|
||||
*out_value = resp_simple("PONG");
|
||||
return 0;
|
||||
}
|
||||
|
||||
case KVS_CMD_AUTH: {
|
||||
if (cmd->argc != 2) { *out_value = resp_error("ERR wrong number of arguments for 'auth'"); return -1; }
|
||||
if (global_cfg.redis_auth_password[0] == '\0' || match_auth_password(&cmd->argv[1])) {
|
||||
*out_value = resp_simple("OK");
|
||||
} else {
|
||||
*out_value = resp_error("ERR invalid password");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if ENABLE_RBTREE
|
||||
case KVS_CMD_SET: {
|
||||
if (cmd->argc != 3) { *out_value = resp_error("ERR wrong number of arguments for 'set'"); return -1; }
|
||||
// <0 error; 0 success; 1 exist
|
||||
int r = kvs_array_set_bin(&global_array,
|
||||
cmd->argv[1].ptr, cmd->argv[1].len,
|
||||
cmd->argv[2].ptr, cmd->argv[2].len);
|
||||
int r = kvs_rbtree_set(&global_rbtree,
|
||||
cmd->argv[1].ptr, cmd->argv[1].len,
|
||||
cmd->argv[2].ptr, cmd->argv[2].len);
|
||||
if (r < 0) { *out_value = resp_error("ERR internal error"); return 0; }
|
||||
else if (r == 1) { *out_value = resp_error("ERR key has exist"); return 0; }
|
||||
*out_value = resp_simple("OK");
|
||||
return 0;
|
||||
}
|
||||
|
||||
case KVS_CMD_GET: {
|
||||
ttl_visit_ctx_t visit_ctx;
|
||||
rbtree_node *node;
|
||||
const uint8_t *v;
|
||||
if (cmd->argc != 2) { *out_value = resp_error("ERR wrong number of arguments for 'get'"); return -1; }
|
||||
uint32_t vlen = 0;
|
||||
// NULL not exist, NOTNULL exist
|
||||
const char *v = kvs_array_get_bin(&global_array, cmd->argv[1].ptr, cmd->argv[1].len, &vlen);
|
||||
if (!v) { *out_value = resp_nil(); return 0; }
|
||||
*out_value = resp_bulk((const uint8_t*)v, vlen);
|
||||
visit_ctx.now_ms = kvs_now_ms();
|
||||
visit_ctx.path_budget = 1;
|
||||
visit_ctx.target_key = cmd->argv[1].ptr;
|
||||
visit_ctx.target_key_len = cmd->argv[1].len;
|
||||
node = rbtree_search_with_visit(&global_rbtree, cmd->argv[1].ptr, cmd->argv[1].len,
|
||||
ttl_schedule_if_expired, &visit_ctx);
|
||||
if (!node || node == global_rbtree.nil) { *out_value = resp_nil(); return 0; }
|
||||
if (node->expire_at_ms != 0 && node->expire_at_ms <= visit_ctx.now_ms) {
|
||||
(void)ttl_delete_schedule(cmd->argv[1].ptr, cmd->argv[1].len);
|
||||
*out_value = resp_nil();
|
||||
return 0;
|
||||
}
|
||||
v = kvs_rbtree_node_value_ptr(node);
|
||||
*out_value = resp_bulk(v, node->value_len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
case KVS_CMD_DEL: {
|
||||
if (cmd->argc != 2) { *out_value = resp_error("ERR wrong number of arguments for 'del'"); return -1; }
|
||||
// <0 error; =0 success; >0 no exist
|
||||
int r = kvs_array_del_bin(&global_array, cmd->argv[1].ptr, cmd->argv[1].len);
|
||||
int r = kvs_rbtree_del(&global_rbtree, cmd->argv[1].ptr, cmd->argv[1].len);
|
||||
if (r < 0) { *out_value = resp_error("ERR internal error"); return 0; }
|
||||
// r == 0, del 1; r > 0, del 0.
|
||||
*out_value = resp_int((r == 0) ? 1 : 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
case KVS_CMD_SETEX: {
|
||||
int64_t ttl_sec = 0;
|
||||
uint64_t now_ms;
|
||||
uint64_t expire_at_ms;
|
||||
int r;
|
||||
if (cmd->argc != 4) { *out_value = resp_error("ERR wrong number of arguments for 'setex'"); return -1; }
|
||||
if (parse_i64(cmd->argv[2].ptr, cmd->argv[2].ptr + cmd->argv[2].len, &ttl_sec) < 0 || ttl_sec <= 0) {
|
||||
*out_value = resp_error("ERR invalid expire time in 'setex'");
|
||||
return -1;
|
||||
}
|
||||
now_ms = kvs_now_ms();
|
||||
expire_at_ms = now_ms + (uint64_t)ttl_sec * 1000ULL;
|
||||
r = kvs_rbtree_set_ex(&global_rbtree,
|
||||
cmd->argv[1].ptr, cmd->argv[1].len,
|
||||
cmd->argv[3].ptr, cmd->argv[3].len,
|
||||
expire_at_ms);
|
||||
if (r < 0) { *out_value = resp_error("ERR internal error"); return 0; }
|
||||
*out_value = resp_simple("OK");
|
||||
return 0;
|
||||
}
|
||||
|
||||
case KVS_CMD_MOD: {
|
||||
if (cmd->argc != 3) { *out_value = resp_error("ERR wrong number of arguments for 'mod'"); return -1; }
|
||||
// <0 error; =0 success; >0 no exist
|
||||
int r = kvs_array_mod_bin(&global_array,
|
||||
cmd->argv[1].ptr, cmd->argv[1].len,
|
||||
cmd->argv[2].ptr, cmd->argv[2].len);
|
||||
int r = kvs_rbtree_mod(&global_rbtree,
|
||||
cmd->argv[1].ptr, cmd->argv[1].len,
|
||||
cmd->argv[2].ptr, cmd->argv[2].len);
|
||||
if (r < 0) { *out_value = resp_error("ERR internal error"); return 0; }
|
||||
if (r == 0) *out_value = resp_simple("OK");
|
||||
else *out_value = resp_error("ERR no such key");
|
||||
@@ -420,34 +504,41 @@ int resp_dispatch(const resp_cmd_t *cmd, resp_value_t *out_value) {
|
||||
|
||||
case KVS_CMD_EXIST: {
|
||||
if (cmd->argc != 2) { *out_value = resp_error("ERR wrong number of arguments for 'exist'"); return -1; }
|
||||
// =0 exist, =1 no exist
|
||||
int r = kvs_array_exist_bin(&global_array, cmd->argv[1].ptr, cmd->argv[1].len);
|
||||
int r = kvs_rbtree_exist(&global_rbtree, cmd->argv[1].ptr, cmd->argv[1].len);
|
||||
if (r < 0) { *out_value = resp_error("ERR internal error"); return 0; }
|
||||
*out_value = resp_int((r == 0) ? 1 : 0);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ENABLE_RBTREE
|
||||
case KVS_CMD_RSET: {
|
||||
if (cmd->argc != 3) { *out_value = resp_error("ERR wrong number of arguments for 'rset'"); return 0; }
|
||||
// <0 error; 0 success; 1 exist
|
||||
int r = kvs_rbtree_set(&global_rbtree,
|
||||
cmd->argv[1].ptr, cmd->argv[1].len,
|
||||
cmd->argv[2].ptr, cmd->argv[2].len);
|
||||
if (r < 0) { *out_value = resp_error("ERR internal error"); return 0; }
|
||||
else if (r == 1) { *out_value = resp_error("ERR key has exist"); return 0; }
|
||||
*out_value = resp_simple("OK");
|
||||
return 0;
|
||||
}
|
||||
|
||||
case KVS_CMD_RGET: {
|
||||
ttl_visit_ctx_t visit_ctx;
|
||||
rbtree_node *node;
|
||||
const uint8_t *v;
|
||||
if (cmd->argc != 2) { *out_value = resp_error("ERR wrong number of arguments for 'rget'"); return 0; }
|
||||
uint32_t vlen = 0;
|
||||
// NULL notexist, NOTNULL exist。out_value_len 是长度。
|
||||
const char *v = kvs_rbtree_get(&global_rbtree, cmd->argv[1].ptr, cmd->argv[1].len, &vlen);
|
||||
if (!v) { *out_value = resp_nil(); return 0; }
|
||||
*out_value = resp_bulk((const uint8_t*)v, vlen);
|
||||
visit_ctx.now_ms = kvs_now_ms();
|
||||
visit_ctx.path_budget = 1;
|
||||
visit_ctx.target_key = cmd->argv[1].ptr;
|
||||
visit_ctx.target_key_len = cmd->argv[1].len;
|
||||
node = rbtree_search_with_visit(&global_rbtree, cmd->argv[1].ptr, cmd->argv[1].len,
|
||||
ttl_schedule_if_expired, &visit_ctx);
|
||||
if (!node || node == global_rbtree.nil) { *out_value = resp_nil(); return 0; }
|
||||
if (node->expire_at_ms != 0 && node->expire_at_ms <= visit_ctx.now_ms) {
|
||||
(void)ttl_delete_schedule(cmd->argv[1].ptr, cmd->argv[1].len);
|
||||
*out_value = resp_nil();
|
||||
return 0;
|
||||
}
|
||||
v = kvs_rbtree_node_value_ptr(node);
|
||||
*out_value = resp_bulk(v, node->value_len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user