This is an automated email from the ASF dual-hosted git repository.
twice pushed a commit to branch unstable
in repository https://gitbox.apache.org/repos/asf/kvrocks.git
The following commit(s) were added to refs/heads/unstable by this push:
new c6d9ad29 feat(conn): avoid to check the command name directly (#2668)
c6d9ad29 is described below
commit c6d9ad2916eb0ede75fff5427fe7dd2966093d42
Author: Twice <[email protected]>
AuthorDate: Sun Nov 17 12:22:00 2024 +0800
feat(conn): avoid to check the command name directly (#2668)
---
src/commands/cmd_server.cc | 2 +-
src/commands/cmd_txn.cc | 14 +++++---------
src/commands/commander.h | 9 +++++----
src/common/string_util.cc | 5 +++++
src/common/string_util.h | 1 +
src/server/redis_connection.cc | 6 +++---
tests/gocase/unit/multi/multi_test.go | 2 +-
7 files changed, 21 insertions(+), 18 deletions(-)
diff --git a/src/commands/cmd_server.cc b/src/commands/cmd_server.cc
index 2cc56919..c7f74265 100644
--- a/src/commands/cmd_server.cc
+++ b/src/commands/cmd_server.cc
@@ -1364,7 +1364,7 @@ REDIS_REGISTER_COMMANDS(Server,
MakeCmdAttr<CommandAuth>("auth", 2, "read-only o
MakeCmdAttr<CommandSlaveOf>("slaveof", 3, "read-only
exclusive no-script", NO_KEY),
MakeCmdAttr<CommandStats>("stats", 1, "read-only",
NO_KEY),
MakeCmdAttr<CommandRdb>("rdb", -3, "write exclusive",
NO_KEY),
- MakeCmdAttr<CommandReset>("reset", 1, "ok-loading
multi no-script", NO_KEY),
+ MakeCmdAttr<CommandReset>("reset", 1, "ok-loading
bypass-multi no-script", NO_KEY),
MakeCmdAttr<CommandApplyBatch>("applybatch", -2,
"write no-multi", NO_KEY),
MakeCmdAttr<CommandDump>("dump", 2, "read-only", 1, 1,
1),
MakeCmdAttr<CommandPollUpdates>("pollupdates", -2,
"read-only", NO_KEY), )
diff --git a/src/commands/cmd_txn.cc b/src/commands/cmd_txn.cc
index 3d88d9a2..5f922ddf 100644
--- a/src/commands/cmd_txn.cc
+++ b/src/commands/cmd_txn.cc
@@ -98,10 +98,6 @@ class CommandExec : public Commander {
class CommandWatch : public Commander {
public:
Status Execute([[maybe_unused]] engine::Context &ctx, Server *srv,
Connection *conn, std::string *output) override {
- if (conn->IsFlagEnabled(Connection::kMultiExec)) {
- return {Status::RedisExecErr, "WATCH inside MULTI is not allowed"};
- }
-
// If a conn is already marked as watched_keys_modified, we can skip the
watch.
if (srv->IsWatchedKeysModified(conn)) {
*output = redis::RESP_OK;
@@ -123,10 +119,10 @@ class CommandUnwatch : public Commander {
}
};
-REDIS_REGISTER_COMMANDS(Txn, MakeCmdAttr<CommandMulti>("multi", 1, "multi",
NO_KEY),
- MakeCmdAttr<CommandDiscard>("discard", 1, "multi",
NO_KEY),
- MakeCmdAttr<CommandExec>("exec", 1, "exclusive multi
slow", NO_KEY),
- MakeCmdAttr<CommandWatch>("watch", -2, "multi", 1, -1,
1),
- MakeCmdAttr<CommandUnwatch>("unwatch", 1, "multi",
NO_KEY), )
+REDIS_REGISTER_COMMANDS(Txn, MakeCmdAttr<CommandMulti>("multi", 1,
"bypass-multi", NO_KEY),
+ MakeCmdAttr<CommandDiscard>("discard", 1,
"bypass-multi", NO_KEY),
+ MakeCmdAttr<CommandExec>("exec", 1, "exclusive
bypass-multi slow", NO_KEY),
+ MakeCmdAttr<CommandWatch>("watch", -2, "no-multi", 1,
-1, 1),
+ MakeCmdAttr<CommandUnwatch>("unwatch", 1, "no-multi",
NO_KEY), )
} // namespace redis
diff --git a/src/commands/commander.h b/src/commands/commander.h
index b2a6ae3d..f0589c92 100644
--- a/src/commands/commander.h
+++ b/src/commands/commander.h
@@ -64,8 +64,9 @@ enum CommandFlags : uint64_t {
// "ok-loading" flag, for any command that can be executed while
// the db is in loading phase
kCmdLoading = 1ULL << 5,
- // "multi" flag, for commands that can end a MULTI scope
- kCmdEndMulti = 1ULL << 6,
+ // "bypass-multi" flag, for commands that can be executed in a MULTI scope,
+ // but these commands will NOT be queued and will be executed immediately
+ kCmdBypassMulti = 1ULL << 6,
// "exclusive" flag, for commands that should be executed execlusive globally
kCmdExclusive = 1ULL << 7,
// "no-multi" flag, for commands that cannot be executed in MULTI scope
@@ -320,8 +321,8 @@ inline uint64_t ParseCommandFlags(const std::string
&description, const std::str
flags |= kCmdLoading;
else if (flag == "exclusive")
flags |= kCmdExclusive;
- else if (flag == "multi")
- flags |= kCmdEndMulti;
+ else if (flag == "bypass-multi")
+ flags |= kCmdBypassMulti;
else if (flag == "no-multi")
flags |= kCmdNoMulti;
else if (flag == "no-script")
diff --git a/src/common/string_util.cc b/src/common/string_util.cc
index cce64402..c1e6f7e3 100644
--- a/src/common/string_util.cc
+++ b/src/common/string_util.cc
@@ -42,6 +42,11 @@ std::string ToLower(std::string in) {
return in;
}
+std::string ToUpper(std::string in) {
+ std::transform(in.begin(), in.end(), in.begin(), [](char c) -> char { return
static_cast<char>(std::toupper(c)); });
+ return in;
+}
+
bool EqualICase(std::string_view lhs, std::string_view rhs) {
return lhs.size() == rhs.size() && std::equal(lhs.begin(), lhs.end(),
rhs.begin(),
[](char l, char r) { return
std::tolower(l) == std::tolower(r); });
diff --git a/src/common/string_util.h b/src/common/string_util.h
index f86590ad..619d95d5 100644
--- a/src/common/string_util.h
+++ b/src/common/string_util.h
@@ -32,6 +32,7 @@ namespace util {
std::string Float2String(double d);
std::string ToLower(std::string in);
+std::string ToUpper(std::string in);
bool EqualICase(std::string_view lhs, std::string_view rhs);
std::string BytesToHuman(uint64_t n);
std::string Trim(std::string in, std::string_view chars);
diff --git a/src/server/redis_connection.cc b/src/server/redis_connection.cc
index ca00de16..2e4ed68c 100644
--- a/src/server/redis_connection.cc
+++ b/src/server/redis_connection.cc
@@ -413,7 +413,7 @@ void Connection::ExecuteCommands(std::deque<CommandTokens>
*to_process_cmds) {
// that can guarantee other threads can't come into critical zone, such as
DEBUG,
// CLUSTER subcommand, CONFIG SET, MULTI, LUA (in the immediate future).
// Otherwise, we just use 'ConcurrencyGuard' to allow all workers to
execute commands at the same time.
- if (is_multi_exec && cmd_name != "exec") {
+ if (is_multi_exec && !(cmd_flags & kCmdBypassMulti)) {
// No lock guard, because 'exec' command has acquired
'WorkExclusivityGuard'
} else if (cmd_flags & kCmdExclusive) {
exclusivity = srv_->WorkExclusivityGuard();
@@ -443,7 +443,7 @@ void Connection::ExecuteCommands(std::deque<CommandTokens>
*to_process_cmds) {
}
if (is_multi_exec && (cmd_flags & kCmdNoMulti)) {
- Reply(redis::Error({Status::NotOK, "Can't execute " + cmd_name + " in
MULTI"}));
+ Reply(redis::Error({Status::NotOK, fmt::format("{} inside MULTI is not
allowed", util::ToUpper(cmd_name))}));
multi_error_ = true;
continue;
}
@@ -463,7 +463,7 @@ void Connection::ExecuteCommands(std::deque<CommandTokens>
*to_process_cmds) {
}
// We don't execute commands, but queue them, and then execute in EXEC
command
- if (is_multi_exec && !in_exec_ && !(cmd_flags & kCmdEndMulti)) {
+ if (is_multi_exec && !in_exec_ && !(cmd_flags & kCmdBypassMulti)) {
multi_cmds_.emplace_back(std::move(cmd_tokens));
Reply(redis::SimpleString("QUEUED"));
continue;
diff --git a/tests/gocase/unit/multi/multi_test.go
b/tests/gocase/unit/multi/multi_test.go
index c2a96917..084eb805 100644
--- a/tests/gocase/unit/multi/multi_test.go
+++ b/tests/gocase/unit/multi/multi_test.go
@@ -174,7 +174,7 @@ func TestMulti(t *testing.T) {
t.Run("WATCH inside MULTI is not allowed", func(t *testing.T) {
require.NoError(t, rdb.Do(ctx, "MULTI").Err())
require.EqualError(t, rdb.Do(ctx, "WATCH", "x").Err(), "ERR
WATCH inside MULTI is not allowed")
- require.NoError(t, rdb.Do(ctx, "EXEC").Err())
+ require.NoError(t, rdb.Do(ctx, "DISCARD").Err())
})
t.Run("EXEC without MULTI is not allowed", func(t *testing.T) {