简单ttl懒删除支持

This commit is contained in:
1iaan
2026-04-04 22:31:00 +08:00
parent 6ede44bd80
commit 78519fbfe5
11 changed files with 375 additions and 98 deletions

View File

@@ -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;
}