Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implemented PSETEX command #207

Merged
merged 3 commits into from
Aug 31, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 83 additions & 0 deletions src/module/redis/command/module_redis_command_psetex.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/**
* Copyright (C) 2018-2022 Vito Castellano
* All rights reserved.
*
* This software may be modified and distributed under the terms
* of the BSD license. See the LICENSE file for details.
**/

#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#include <string.h>
#include <strings.h>
#include <arpa/inet.h>
#include <assert.h>

#include "misc.h"
#include "exttypes.h"
#include "log/log.h"
#include "clock.h"
#include "spinlock.h"
#include "xalloc.h"
#include "data_structures/small_circular_queue/small_circular_queue.h"
#include "data_structures/double_linked_list/double_linked_list.h"
#include "data_structures/queue_mpmc/queue_mpmc.h"
#include "slab_allocator.h"
#include "data_structures/hashtable/mcmp/hashtable.h"
#include "data_structures/hashtable/mcmp/hashtable_op_set.h"
#include "data_structures/hashtable/spsc/hashtable_spsc.h"
#include "protocol/redis/protocol_redis.h"
#include "protocol/redis/protocol_redis_reader.h"
#include "protocol/redis/protocol_redis_writer.h"
#include "module/module.h"
#include "network/io/network_io_common.h"
#include "config.h"
#include "fiber.h"
#include "network/channel/network_channel.h"
#include "storage/io/storage_io_common.h"
#include "storage/channel/storage_channel.h"
#include "storage/db/storage_db.h"
#include "module/redis/module_redis.h"
#include "module/redis/module_redis_connection.h"
#include "module/redis/module_redis_command.h"
#include "network/network.h"
#include "worker/worker_stats.h"
#include "worker/worker_context.h"

#define TAG "module_redis_command_psetex"

MODULE_REDIS_COMMAND_FUNCPTR_COMMAND_END(psetex) {
bool return_res = false;
bool key_and_value_owned = false;
module_redis_command_psetex_context_t *context = connection_context->command.context;

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a check to valiate that the value passed in greater than 0, if not the following error has to be returned

ERR invalid expire time in 'psetex' command

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done 👍

storage_db_expiry_time_ms_t expiry_time_ms =
clock_realtime_coarse_int64_ms() + (int64_t)context->milliseconds.value;

if (unlikely(!storage_db_op_set(
connection_context->db,
context->key.value.key,
context->key.value.length,
context->value.value.chunk_sequence,
expiry_time_ms))) {
return_res = module_redis_connection_error_message_printf_noncritical(
connection_context,
"ERR psetex failed");
goto end;
}

key_and_value_owned = true;
return_res = module_redis_connection_send_ok(connection_context);

end:
if (likely(key_and_value_owned)) {
// Mark both the key and the chunk_sequence as NULL as the storage db now owns them, we don't want them to be
// automatically freed at the end of the execution, especially the key as the hashtable might not need to hold
// a reference to it, it might have already been freed
context->key.value.key = NULL;
context->value.value.chunk_sequence = NULL;
}

return return_res;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
/**
* Copyright (C) 2018-2022 Vito Castellano
* All rights reserved.
*
* This software may be modified and distributed under the terms
* of the BSD license. See the LICENSE file for details.
**/

#include <catch2/catch.hpp>

#include <cstdbool>
#include <cstring>
#include <memory>
#include <string>

#include <unistd.h>
#include <netinet/in.h>

#include "clock.h"
#include "exttypes.h"
#include "spinlock.h"
#include "data_structures/small_circular_queue/small_circular_queue.h"
#include "data_structures/double_linked_list/double_linked_list.h"
#include "data_structures/hashtable/mcmp/hashtable.h"
#include "config.h"
#include "fiber.h"
#include "worker/worker_stats.h"
#include "worker/worker_context.h"
#include "signal_handler_thread.h"
#include "storage/io/storage_io_common.h"
#include "storage/channel/storage_channel.h"
#include "storage/db/storage_db.h"

#include "program.h"

#include "test-modules-redis-command-fixture.hpp"

#pragma GCC diagnostic ignored "-Wwrite-strings"

TEST_CASE_METHOD(TestModulesRedisCommandFixture, "Redis - command - PSETEX", "[redis][command][PSETEX]") {
SECTION("Missing parameters - key, milliseconds and value") {
REQUIRE(send_recv_resp_command_text(
client_fd,
std::vector<std::string>{"PSETEX"},
"-ERR wrong number of arguments for 'PSETEX' command\r\n"));
}

SECTION("Missing parameters - value") {
REQUIRE(send_recv_resp_command_text(
client_fd,
std::vector<std::string>{"PSETEX", "a_key", "100"},
"-ERR wrong number of arguments for 'PSETEX' command\r\n"));
}

SECTION("Too many parameters - one extra parameter") {
REQUIRE(send_recv_resp_command_text(
client_fd,
std::vector<std::string>{"PSETEX", "a_key", "100", "b_value", "extra parameter"},
"-ERR syntax error\r\n"));
}

SECTION("New key - expire in 500ms") {
char *key = "a_key";
char *value = "b_value";
config_module_network_timeout.read_ms = 1000;

REQUIRE(send_recv_resp_command_text(
client_fd,
std::vector<std::string>{"PSETEX", key, "500", value},
"+OK\r\n"));

REQUIRE(send_recv_resp_command_text(
client_fd,
std::vector<std::string>{"GET", key},
"$7\r\nb_value\r\n"));

// Wait for 600 ms and try to get the value after the expiration
usleep((500 + 100) * 1000);

REQUIRE(send_recv_resp_command_text(
client_fd,
std::vector<std::string>{"GET", key},
"$-1\r\n"));

storage_db_entry_index_t *entry_index = storage_db_get_entry_index(db, key, strlen(key));
REQUIRE(entry_index == NULL);
}

SECTION("New key - expire in 1s") {
char *key = "a_key";
char *value = "b_value";
config_module_network_timeout.read_ms = 2000;

REQUIRE(send_recv_resp_command_text(
client_fd,
std::vector<std::string>{"PSETEX", key, "1000", value},
"+OK\r\n"));

REQUIRE(send_recv_resp_command_text(
client_fd,
std::vector<std::string>{"GET", key},
"$7\r\nb_value\r\n"));

// Wait for 1100 ms and try to get the value after the expiration
usleep((1000 + 100) * 1000);

REQUIRE(send_recv_resp_command_text(
client_fd,
std::vector<std::string>{"GET", key},
"$-1\r\n"));

storage_db_entry_index_t *entry_index = storage_db_get_entry_index(db, key, strlen(key));
REQUIRE(entry_index == NULL);
}
}