Skip to content

Commit 00b8b28

Browse files
Implemented SETEX command (#206)
Co-authored-by: Daniele Salvatore Albano <danielealbano@users.noreply.github.com>
1 parent 827f235 commit 00b8b28

File tree

2 files changed

+185
-0
lines changed

2 files changed

+185
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
/**
2+
* Copyright (C) 2018-2022 Vito Castellano
3+
* All rights reserved.
4+
*
5+
* This software may be modified and distributed under the terms
6+
* of the BSD license. See the LICENSE file for details.
7+
**/
8+
9+
#include <stdint.h>
10+
#include <stdbool.h>
11+
#include <stddef.h>
12+
#include <string.h>
13+
#include <strings.h>
14+
#include <arpa/inet.h>
15+
#include <assert.h>
16+
17+
#include "misc.h"
18+
#include "exttypes.h"
19+
#include "log/log.h"
20+
#include "clock.h"
21+
#include "spinlock.h"
22+
#include "xalloc.h"
23+
#include "data_structures/small_circular_queue/small_circular_queue.h"
24+
#include "data_structures/double_linked_list/double_linked_list.h"
25+
#include "data_structures/queue_mpmc/queue_mpmc.h"
26+
#include "slab_allocator.h"
27+
#include "data_structures/hashtable/mcmp/hashtable.h"
28+
#include "data_structures/hashtable/mcmp/hashtable_op_set.h"
29+
#include "data_structures/hashtable/spsc/hashtable_spsc.h"
30+
#include "protocol/redis/protocol_redis.h"
31+
#include "protocol/redis/protocol_redis_reader.h"
32+
#include "protocol/redis/protocol_redis_writer.h"
33+
#include "module/module.h"
34+
#include "network/io/network_io_common.h"
35+
#include "config.h"
36+
#include "fiber.h"
37+
#include "network/channel/network_channel.h"
38+
#include "storage/io/storage_io_common.h"
39+
#include "storage/channel/storage_channel.h"
40+
#include "storage/db/storage_db.h"
41+
#include "module/redis/module_redis.h"
42+
#include "module/redis/module_redis_connection.h"
43+
#include "module/redis/module_redis_command.h"
44+
#include "network/network.h"
45+
#include "worker/worker_stats.h"
46+
#include "worker/worker_context.h"
47+
48+
#define TAG "module_redis_command_setex"
49+
50+
MODULE_REDIS_COMMAND_FUNCPTR_COMMAND_END(setex) {
51+
bool return_res = false;
52+
bool key_and_value_owned = false;
53+
module_redis_command_setex_context_t *context = connection_context->command.context;
54+
55+
if (context->seconds.value <= 0) {
56+
return_res = module_redis_connection_error_message_printf_noncritical(
57+
connection_context,
58+
"ERR invalid expire time in 'setex' command");
59+
goto end;
60+
}
61+
62+
storage_db_expiry_time_ms_t expiry_time_ms =
63+
clock_realtime_coarse_int64_ms() + ((int64_t)context->seconds.value * 1000);
64+
65+
if (unlikely(!storage_db_op_set(
66+
connection_context->db,
67+
context->key.value.key,
68+
context->key.value.length,
69+
context->value.value.chunk_sequence,
70+
expiry_time_ms))) {
71+
return_res = module_redis_connection_error_message_printf_noncritical(
72+
connection_context,
73+
"ERR setex failed");
74+
goto end;
75+
}
76+
77+
key_and_value_owned = true;
78+
return_res = module_redis_connection_send_ok(connection_context);
79+
80+
end:
81+
if (likely(key_and_value_owned)) {
82+
// Mark both the key and the chunk_sequence as NULL as the storage db now owns them, we don't want them to be
83+
// automatically freed at the end of the execution, especially the key as the hashtable might not need to hold
84+
// a reference to it, it might have already been freed
85+
context->key.value.key = NULL;
86+
context->value.value.chunk_sequence = NULL;
87+
}
88+
89+
return return_res;
90+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
/**
2+
* Copyright (C) 2018-2022 Vito Castellano
3+
* All rights reserved.
4+
*
5+
* This software may be modified and distributed under the terms
6+
* of the BSD license. See the LICENSE file for details.
7+
**/
8+
9+
#include <catch2/catch.hpp>
10+
11+
#include <cstdbool>
12+
#include <cstring>
13+
#include <memory>
14+
#include <string>
15+
16+
#include <unistd.h>
17+
#include <netinet/in.h>
18+
19+
#include "clock.h"
20+
#include "exttypes.h"
21+
#include "spinlock.h"
22+
#include "data_structures/small_circular_queue/small_circular_queue.h"
23+
#include "data_structures/double_linked_list/double_linked_list.h"
24+
#include "data_structures/hashtable/mcmp/hashtable.h"
25+
#include "config.h"
26+
#include "fiber.h"
27+
#include "worker/worker_stats.h"
28+
#include "worker/worker_context.h"
29+
#include "signal_handler_thread.h"
30+
#include "storage/io/storage_io_common.h"
31+
#include "storage/channel/storage_channel.h"
32+
#include "storage/db/storage_db.h"
33+
34+
#include "program.h"
35+
36+
#include "test-modules-redis-command-fixture.hpp"
37+
38+
#pragma GCC diagnostic ignored "-Wwrite-strings"
39+
40+
TEST_CASE_METHOD(TestModulesRedisCommandFixture, "Redis - command - SETEX", "[redis][command][SETEX]") {
41+
SECTION("Missing parameters - key, seconds and value") {
42+
REQUIRE(send_recv_resp_command_text(
43+
client_fd,
44+
std::vector<std::string>{"SETEX"},
45+
"-ERR wrong number of arguments for 'SETEX' command\r\n"));
46+
}
47+
48+
SECTION("Missing parameters - value") {
49+
REQUIRE(send_recv_resp_command_text(
50+
client_fd,
51+
std::vector<std::string>{"SETEX", "a_key", "100"},
52+
"-ERR wrong number of arguments for 'SETEX' command\r\n"));
53+
}
54+
55+
SECTION("Too many parameters - one extra parameter") {
56+
REQUIRE(send_recv_resp_command_text(
57+
client_fd,
58+
std::vector<std::string>{"SETEX", "a_key", "100", "b_value", "extra parameter"},
59+
"-ERR syntax error\r\n"));
60+
}
61+
62+
SECTION("Zero value as expire") {
63+
REQUIRE(send_recv_resp_command_text(
64+
client_fd,
65+
std::vector<std::string>{"SETEX", "a_key", "0", "b_value"},
66+
"-ERR invalid expire time in 'setex' command\r\n"));
67+
}
68+
69+
SECTION("New key - expire in 1s") {
70+
char *key = "a_key";
71+
char *value = "b_value";
72+
config_module_network_timeout.read_ms = 2000;
73+
74+
REQUIRE(send_recv_resp_command_text(
75+
client_fd,
76+
std::vector<std::string>{"SETEX", key, "1", value},
77+
"+OK\r\n"));
78+
79+
REQUIRE(send_recv_resp_command_text(
80+
client_fd,
81+
std::vector<std::string>{"GET", key},
82+
"$7\r\nb_value\r\n"));
83+
84+
// Wait for 1100 ms and try to get the value after the expiration
85+
usleep((1000 + 100) * 1000);
86+
87+
REQUIRE(send_recv_resp_command_text(
88+
client_fd,
89+
std::vector<std::string>{"GET", key},
90+
"$-1\r\n"));
91+
92+
storage_db_entry_index_t *entry_index = storage_db_get_entry_index(db, key, strlen(key));
93+
REQUIRE(entry_index == NULL);
94+
}
95+
}

0 commit comments

Comments
 (0)