Commit 6b8acca1 authored by Georgios Bitzes's avatar Georgios Bitzes
Browse files

Implement helper commands to convert between binary and ascii integers

To be used interactively only. A lot of integers in QDB and EOS are
encoded as binary strings, having an easy way to convert between the
two during debugging an emergency is valuable.
parent 68322818
Pipeline #447389 passed with stages
in 50 minutes and 38 seconds
......@@ -6,6 +6,7 @@ All notable changes to this project will be documented in this file.
### Changed
- Refactoring of transactions, we no longer pack / unpack a transaction into a single request within the same node, saving
CPU cycles.
- Explicitly block zero-sized strings when parsing the redis protocol, print appropriate warning.
### Fixed
- In certain cases, such as when redirecting or reporting unavailability for pipelined writes, fewer
......
......@@ -140,5 +140,8 @@ struct cmdMapInit {
redis_cmd_map["recovery_get"] = {RedisCommand::RECOVERY_GET, CommandType::RECOVERY};
redis_cmd_map["recovery_del"] = {RedisCommand::RECOVERY_DEL, CommandType::RECOVERY};
redis_cmd_map["convert_string_to_int"] = {RedisCommand::CONVERT_STRING_TO_INT, CommandType::CONTROL};
redis_cmd_map["convert_int_to_string"] = {RedisCommand::CONVERT_INT_TO_STRING, CommandType::CONTROL};
}
} cmd_map_init;
......@@ -143,7 +143,10 @@ enum class RedisCommand {
RECOVERY_GET,
RECOVERY_SET,
RECOVERY_DEL,
RECOVERY_INFO
RECOVERY_INFO,
CONVERT_STRING_TO_INT,
CONVERT_INT_TO_STRING,
};
enum class CommandType {
......
......@@ -33,6 +33,47 @@
using namespace quarkdb;
RedisEncodedResponse Dispatcher::handleConversion(RedisRequest &request) {
// Provide simple commands for conversion between binary-string-encoded
// integers and human-readable ASCII representation.
//
// This is strictly for interactive use, to simplify life when debugging and
// dealing with binary-string integers, especially when using the recovery
// tool, or inspecting the contents of the raft journal.
//
// Please don't use this in scripts.. Using QDB as "binary-string conversion"
// service would be really silly.
switch(request.getCommand()) {
case RedisCommand::CONVERT_STRING_TO_INT: {
if(request.size() != 2u) return Formatter::errArgs(request[0]);
if(request[1].size() != 8u) return Formatter::err(SSTR("expected string with 8 characters, was given " << request[1].size() << " instead"));
std::vector<std::string> reply;
reply.emplace_back(SSTR("Interpreted as int64_t: " << binaryStringToInt(request[1].c_str())));
reply.emplace_back(SSTR("Interpreted as uint64_t: " << binaryStringToUnsignedInt(request[1].c_str())));
return Formatter::statusVector(reply);
}
case RedisCommand::CONVERT_INT_TO_STRING: {
if(request.size() != 2u) return Formatter::errArgs(request[0]);
int64_t value;
if(!ParseUtils::parseInt64(request[1], value)) {
return Formatter::err("cannot parse integer");
}
std::vector<std::string> reply;
reply.emplace_back(SSTR("As int64_t: " << intToBinaryString(value)));
reply.emplace_back(SSTR("As uint64_t: " << unsignedIntToBinaryString(value)));
return Formatter::vector(reply);
}
default: {
qdb_throw("internal dispatching error for " << request.toPrintableString());
}
}
}
RedisEncodedResponse Dispatcher::handlePing(RedisRequest &request) {
qdb_assert(request.getCommand() == RedisCommand::PING);
......
......@@ -38,6 +38,7 @@ public:
virtual LinkStatus dispatch(Connection *conn, RedisRequest &req) = 0;
virtual LinkStatus dispatch(Connection *conn, Transaction &multiOp) = 0;
RedisEncodedResponse handlePing(RedisRequest &req);
RedisEncodedResponse handleConversion(RedisRequest &req);
virtual ~Dispatcher() {}
};
......
......@@ -130,6 +130,10 @@ LinkStatus QuarkDBNode::dispatch(Connection *conn, RedisRequest &req) {
case RedisCommand::QUARKDB_INFO: {
return conn->statusVector(this->info().toVector());
}
case RedisCommand::CONVERT_STRING_TO_INT:
case RedisCommand::CONVERT_INT_TO_STRING: {
return conn->raw(handleConversion(req));
}
default: {
return shard->dispatch(conn, req);
}
......
......@@ -23,6 +23,7 @@
#include "RecoveryDispatcher.hh"
#include "../Formatter.hh"
#include "../utils/CommandParsing.hh"
using namespace quarkdb;
RecoveryDispatcher::RecoveryDispatcher(RecoveryEditor &ed) : editor(ed) {
......@@ -37,6 +38,16 @@ LinkStatus RecoveryDispatcher::dispatch(Connection *conn, RedisRequest &req) {
}
RedisEncodedResponse RecoveryDispatcher::dispatch(RedisRequest &request) {
switch(request.getCommand()) {
case RedisCommand::CONVERT_STRING_TO_INT:
case RedisCommand::CONVERT_INT_TO_STRING: {
return handleConversion(request);
}
default: {
// no-op, continue
}
}
if(request.getCommandType() != CommandType::RECOVERY) {
std::string msg = SSTR("unable to dispatch command " << quotes(request[0]) << " - remember we're running in recovery mode, not all operations are available");
qdb_warn(msg);
......
......@@ -38,7 +38,7 @@ struct ScanCommandArguments {
std::string error;
};
ScanCommandArguments parseScanCommand(const RedisRequest::const_iterator &begin, const RedisRequest::const_iterator &end) {
inline ScanCommandArguments parseScanCommand(const RedisRequest::const_iterator &begin, const RedisRequest::const_iterator &end) {
qdb_assert(begin != end);
ScanCommandArguments args;
......
......@@ -638,6 +638,16 @@ TEST_F(Raft_e2e, test_many_redis_commands) {
// Make sure the connection did not hang.
ASSERT_REPLY(tunnel(follower1)->exec("ping", "zxcvbnm"), "zxcvbnm");
// Test integer <-> binary string conversion functions.
redisReplyPtr conv1 = tunnel(follower1)->exec("convert-int-to-string", "999").get();
ASSERT_EQ(qclient::describeRedisReply(conv1), "1) \"As int64_t: \\x00\\x00\\x00\\x00\\x00\\x00\\x03\\xE7\"\n2) \"As uint64_t: \\x00\\x00\\x00\\x00\\x00\\x00\\x03\\xE7\"\n");
ASSERT_REPLY(tunnel(follower1)->exec("convert-int-to-string", "adfs"), "ERR cannot parse integer");
ASSERT_REPLY(tunnel(follower1)->exec("convert-string-to-int", "qqqq"), "ERR expected string with 8 characters, was given 4 instead");
redisReplyPtr conv2 = tunnel(follower1)->exec("convert-string-to-int", unsignedIntToBinaryString(999u)).get();
ASSERT_EQ(qclient::describeRedisReply(conv2), "1) Interpreted as int64_t: 999\n2) Interpreted as uint64_t: 999\n");
}
TEST_F(Raft_e2e, replication_with_trimmed_journal) {
......
......@@ -130,6 +130,16 @@ TEST(Recovery, RemoveJournalEntriesAndChangeClusterID) {
};
ASSERT_REPLY(qcl.exec("recovery-info"), rep);
// Test integer <-> binary string conversion functions.
redisReplyPtr conv1 = qcl.exec("convert-int-to-string", "999").get();
ASSERT_EQ(qclient::describeRedisReply(conv1), "1) \"As int64_t: \\x00\\x00\\x00\\x00\\x00\\x00\\x03\\xE7\"\n2) \"As uint64_t: \\x00\\x00\\x00\\x00\\x00\\x00\\x03\\xE7\"\n");
ASSERT_REPLY(qcl.exec("convert-int-to-string", "adfs"), "ERR cannot parse integer");
ASSERT_REPLY(qcl.exec("convert-string-to-int", "qqqq"), "ERR expected string with 8 characters, was given 4 instead");
redisReplyPtr conv2 = qcl.exec("convert-string-to-int", unsignedIntToBinaryString(999u)).get();
ASSERT_EQ(qclient::describeRedisReply(conv2), "1) Interpreted as int64_t: 999\n2) Interpreted as uint64_t: 999\n");
}
RaftJournal journal("/tmp/quarkdb-recovery-test");
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment