Commit b8c35d85 authored by zhujiashun's avatar zhujiashun

redis_server_protocol: add TransactionHandler

parent e3117cfc
...@@ -61,8 +61,7 @@ struct InputResponse : public InputMessageBase { ...@@ -61,8 +61,7 @@ struct InputResponse : public InputMessageBase {
class RedisConnContext : public Destroyable { class RedisConnContext : public Destroyable {
public: public:
RedisConnContext() RedisConnContext()
: redis_service(NULL) : redis_service(NULL) {}
, handler_continue(NULL) {}
~RedisConnContext(); ~RedisConnContext();
// @Destroyable // @Destroyable
void Destroy() override; void Destroy() override;
...@@ -70,14 +69,14 @@ public: ...@@ -70,14 +69,14 @@ public:
SocketId socket_id; SocketId socket_id;
RedisService* redis_service; RedisService* redis_service;
// If user starts a transaction, handler_continue indicates the // If user starts a transaction, handler_continue indicates the
// first handler pointer that triggers the transaction. // handler pointer that runs the transaction handler.
RedisCommandHandler* handler_continue; std::unique_ptr<RedisCommandHandler> handler_continue;
RedisCommandParser parser; RedisCommandParser parser;
std::vector<std::string> command; std::vector<std::string> command;
}; };
std::string ToLowercase(const std::string& command) { static std::string ToLowercase(const std::string& command) {
std::string res; std::string res;
res.resize(command.size()); res.resize(command.size());
std::transform(command.begin(), command.end(), res.begin(), std::transform(command.begin(), command.end(), res.begin(),
...@@ -108,7 +107,7 @@ int ConsumeTask(RedisConnContext* ctx, ...@@ -108,7 +107,7 @@ int ConsumeTask(RedisConnContext* ctx,
RedisCommandHandler::Result result = RedisCommandHandler::Result result =
ctx->handler_continue->Run(commands[i], &output[i], is_last); ctx->handler_continue->Run(commands[i], &output[i], is_last);
if (result == RedisCommandHandler::OK) { if (result == RedisCommandHandler::OK) {
ctx->handler_continue = NULL; ctx->handler_continue.reset(NULL);
} }
} else { } else {
bool is_last = true; bool is_last = true;
...@@ -133,7 +132,7 @@ int ConsumeTask(RedisConnContext* ctx, ...@@ -133,7 +132,7 @@ int ConsumeTask(RedisConnContext* ctx,
RedisCommandHandler::Result result = RedisCommandHandler::Result result =
ch->Run(commands[i], &output[i], is_last); ch->Run(commands[i], &output[i], is_last);
if (result == RedisCommandHandler::CONTINUE) { if (result == RedisCommandHandler::CONTINUE) {
ctx->handler_continue = ch; ctx->handler_continue.reset(ch->NewTransactionHandler());
} }
} }
} }
......
...@@ -457,4 +457,9 @@ RedisCommandHandler* RedisService::FindCommandHandler(const std::string& name) { ...@@ -457,4 +457,9 @@ RedisCommandHandler* RedisService::FindCommandHandler(const std::string& name) {
return NULL; return NULL;
} }
RedisCommandHandler* RedisCommandHandler::NewTransactionHandler() {
LOG(ERROR) << "NewTransactionHandler is not implemented";
return NULL;
}
} // namespace brpc } // namespace brpc
...@@ -251,17 +251,29 @@ public: ...@@ -251,17 +251,29 @@ public:
// want to do some batch processing, user should buffer the command and output. Once // want to do some batch processing, user should buffer the command and output. Once
// `is_last' is true, then run all the command and set the output of each command. // `is_last' is true, then run all the command and set the output of each command.
// The return value should be RedisCommandHandler::OK for normal cases. If you want // The return value should be RedisCommandHandler::OK for normal cases. If you want
// to implement transaction, return RedisCommandHandler::CONTINUE until server receives // to implement transaction, return RedisCommandHandler::CONTINUE once server receives
// an ending marker. The first handler that return RedisCommandHandler::CONTINUE will // an start marker and brpc will call MultiTransactionHandler() to new a transaction
// continue receiving the following commands until it receives an ending marker and // handler that all the following commands are sent to this tranction handler until
// return RedisCommandHandler::OK to end transaction. For example, the return value // it returns Result::OK. Read the comment below.
// of commands "multi; set k1 v1; set k2 v2; set k3 v3; exec" should be four
// RedisCommandHandler::CONTINUE and one RedisCommandHandler::OK since exec is the
// marker that ends the transaction. User should queue the commands and execute them
// all once the ending marker is received.
virtual RedisCommandHandler::Result Run(const std::vector<std::string>& command, virtual RedisCommandHandler::Result Run(const std::vector<std::string>& command,
brpc::RedisReply* output, brpc::RedisReply* output,
bool is_last) = 0; bool is_last) = 0;
// This function is called to new a transaction handler once Run() returns
// RedisCommandHandler::CONTINUE. All the following commands are sent to this
// handler until it return Result::OK. For example, for command "multi; set k1 v1;
// set k2 v2; set k3 v3; exec":
// 1) In Run(), command is "multi", so return RedisCommandHandler::CONTINUE, and
// brpc calls NewTransactionHandler() to new a handler tran_handler.
// 2) brpc calls tran_handler.Run() with command "set k1 v1", which should return
// RedisCommandHandler::CONTINUE and buffer the command and output.
// 3) brpc calls tran_handler.Run() with command "set k2 v2", which should return
// RedisCommandHandler::CONTINUE and buffer the command and output.
// 4) brpc calls tran_handler.Run() with command "set k3 v3", which should return
// RedisCommandHandler::CONTINUE and buffer the command and output.
// 5) An ending marker(multi) is found in tran_handler.Run(), user exeuctes all
// the command and return RedisCommandHandler::OK. This Transation is done.
virtual RedisCommandHandler* NewTransactionHandler();
}; };
} // namespace brpc } // namespace brpc
......
...@@ -984,44 +984,50 @@ TEST_F(RedisTest, server_concurrency) { ...@@ -984,44 +984,50 @@ TEST_F(RedisTest, server_concurrency) {
class MultiCommandHandler : public brpc::RedisCommandHandler { class MultiCommandHandler : public brpc::RedisCommandHandler {
public: public:
MultiCommandHandler() MultiCommandHandler() {}
: _started(false) {}
RedisCommandHandler::Result Run(const std::vector<std::string>& args, RedisCommandHandler::Result Run(const std::vector<std::string>& args,
brpc::RedisReply* output, brpc::RedisReply* output,
bool is_last) { bool is_last) {
if (strcasecmp(args[0].c_str(), "multi") == 0) { output->SetStatus("OK");
if (!_started) { return brpc::RedisCommandHandler::CONTINUE;
output->SetStatus("OK"); }
_started = true;
} else { RedisCommandHandler* NewTransactionHandler() {
return new MultiTransactionHandler;
}
class MultiTransactionHandler : public brpc::RedisCommandHandler {
public:
RedisCommandHandler::Result Run(const std::vector<std::string>& args,
brpc::RedisReply* output,
bool is_last) {
if (strcasecmp(args[0].c_str(), "multi") == 0) {
output->SetError("ERR duplicate multi"); output->SetError("ERR duplicate multi");
return brpc::RedisCommandHandler::CONTINUE;
} }
return brpc::RedisCommandHandler::CONTINUE; if (strcasecmp(args[0].c_str(), "exec") != 0) {
} _commands.push_back(args);
if (strcasecmp(args[0].c_str(), "exec") != 0) { output->SetStatus("QUEUED");
_commands.push_back(args); return brpc::RedisCommandHandler::CONTINUE;
output->SetStatus("QUEUED"); }
return brpc::RedisCommandHandler::CONTINUE; output->SetArray(_commands.size());
} s_mutex.lock();
output->SetArray(_commands.size()); for (size_t i = 0; i < _commands.size(); ++i) {
s_mutex.lock(); if (_commands[i][0] == "incr") {
for (size_t i = 0; i < _commands.size(); ++i) { int64_t value;
if (_commands[i][0] == "incr") { value = ++int_map[_commands[i][1]];
int64_t value; (*output)[i].SetInteger(value);
value = ++int_map[_commands[i][1]]; } else {
(*output)[i].SetInteger(value); (*output)[i].SetStatus("unknowne command");
}
} }
s_mutex.unlock();
return brpc::RedisCommandHandler::OK;
} }
s_mutex.unlock(); private:
_started = false; std::vector<std::vector<std::string>> _commands;
return brpc::RedisCommandHandler::OK; };
}
RedisCommandHandler* New() { return new MultiCommandHandler; }
private:
std::vector<std::vector<std::string>> _commands;
bool _started;
}; };
TEST_F(RedisTest, server_command_continue) { TEST_F(RedisTest, server_command_continue) {
......
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