diff --git a/conf/command.conf b/conf/command.conf new file mode 100644 index 0000000..22ddd4f --- /dev/null +++ b/conf/command.conf @@ -0,0 +1,66 @@ +# CustomCommand section must be defined before Latency monitor to support all commands +# +# NOTE: only support maximum 16 custom commands + +CustomCommand { + # from redis source src/modules/hello.c + # hello.push.native key value + hello.push.native { # name of command, must be lowercase. + # key parameter is located at first argument. i.e. command key ... + # Currently support only 1 key with first argument. + minArgs 3 # minimum arguments(including command itself), default is 2(key only) + maxArgs 3 # maximum arguments(including command itself), default is 2(key only) + mode Read,Write # a command mode Read/Write/Admin is exclusive, default is Write + } + # hello.repl2 + # just skipped since minArgs = 2, maxArgs = 2, mode = Read,Write are default values + hello.repl2 { + mode Read,Write + } + # hello.toggle.case key + hello.toggle.case { + mode Read,Write + } + # hello.more.expire key milliseconds + hello.more.expire { + minArgs 3 + maxArgs 3 + mode Read,Write + } + # hello.zsumrange key startscore endscore + hello.zsumrange { + minArgs 4 + maxArgs 4 + mode Read,Write + } + # hello.lexrange key min_lex max_lex min_age max_age + hello.lexrange { + minArgs 6 + maxArgs 6 + mode Read,Write + } + # hello.hcopy key srcfield dstfield + hello.hcopy { + minArgs 4 + maxArgs 4 + mode Read,Write + } + + # from redis source src/modules/hellotype.c + # hellotype.insert key value + hello.insert { + minArgs 3 + maxArgs 3 + mode Read,Write + } + # hellotype.range key first count + hellotype.range { + minArgs 4 + maxArgs 4 + mode Read,Write + } + # hellotype.len key + hello.len { + mode Read,Write + } +} diff --git a/conf/predixy.conf b/conf/predixy.conf index aa76713..a9f3ae1 100644 --- a/conf/predixy.conf +++ b/conf/predixy.conf @@ -93,6 +93,11 @@ Include try.conf # Include dc.conf +################################### COMMAND #################################### +## Custom command define, see command.conf +## must be defined before Latency monitor +#Include command.conf + ################################### LATENCY #################################### ## Latency monitor define, see latency.conf Include latency.conf diff --git a/src/Command.cpp b/src/Command.cpp index 0b5df05..7f3b9f5 100644 --- a/src/Command.cpp +++ b/src/Command.cpp @@ -10,7 +10,7 @@ #include "String.h" #include "Command.h" -const Command Command::CmdPool[Sentinel] = { +Command Command::CmdPool[AvailableCommands] = { {None, "", 0, MaxArgs, Read}, {Ping, "ping", 1, 2, Read}, {PingServ, "ping", 1, 2, Inner}, @@ -171,11 +171,14 @@ const Command Command::CmdPool[Sentinel] = { {SubMsg, "\000SubMsg", 0, 0, Admin} }; +int Command::Sentinel = Command::MaxCommands; Command::CommandMap Command::CmdMap; + void Command::init() { int type = 0; - for (auto& i : CmdPool) { + for (auto j = 0; j < MaxCommands; j++) { + const auto& i = CmdPool[j]; if (i.type != type) { Throw(InitFail, "command %s unmatch the index in commands table", i.name); } @@ -187,3 +190,15 @@ void Command::init() } } +void Command::addCustomCommand(const Command *p) { + if (Sentinel >= AvailableCommands) { + Throw(InitFail, "too many custom commands(>%d)", MaxCustomCommands); + } + if (nullptr != find(p->name)) { + Throw(InitFail, "custom command %s is duplicated", p->name); + } + CmdPool[Sentinel] = *p; + CmdMap[p->name] = &CmdPool[Sentinel]; + Sentinel++; +} + diff --git a/src/Command.h b/src/Command.h index eb15709..1b43e92 100644 --- a/src/Command.h +++ b/src/Command.h @@ -8,6 +8,7 @@ #define _PREDIXY_COMMAND_H_ #include +#include #include "Exception.h" #include "HashFunc.h" @@ -189,7 +190,9 @@ public: Unsubscribe, SubMsg, - Sentinel + MaxCommands, + MaxCustomCommands = 16, + AvailableCommands = MaxCommands + MaxCustomCommands, }; enum Mode { @@ -242,6 +245,7 @@ public: if (cursor < Sentinel) { return &CmdPool[cursor++]; } + return nullptr; } static const Command* find(const String& cmd) @@ -249,9 +253,11 @@ public: auto it = CmdMap.find(cmd); return it == CmdMap.end() ? nullptr : it->second; } + static void addCustomCommand(const Command *pc); + static int Sentinel; private: static const int MaxArgs = 100000000; - static const Command CmdPool[Sentinel]; + static Command CmdPool[]; class H { public: diff --git a/src/Conf.cpp b/src/Conf.cpp index f5325fa..4547b21 100644 --- a/src/Conf.cpp +++ b/src/Conf.cpp @@ -6,6 +6,7 @@ #include #include +#include #include #include "LogFileSink.h" #include "ServerPool.h" @@ -32,6 +33,15 @@ bool ServerConf::parse(ServerConf& s, const char* str) return !s.addr.empty(); } +void CustomCommandConf::init(CustomCommandConf&c, const char* name, const int type) { + c.name = name; + c.cmd.type = (Command::Type)type; + c.cmd.name = c.name.c_str(); + c.cmd.minArgs = 2; + c.cmd.maxArgs = 2; + c.cmd.mode = Command::Write; +} + Conf::Conf(): mBind("0.0.0.0:7617"), mWorkerThreads(1), @@ -157,6 +167,8 @@ void Conf::setGlobal(const ConfParser::Node* node) sentinelServerPool = p; } else if (strcasecmp(p->key.c_str(), "DataCenter") == 0) { dataCenter = p; + } else if (strcasecmp(p->key.c_str(), "CustomCommand") == 0) { + setCustomCommand(p); } else { Throw(UnknownKey, "%s:%d unknown key %s", p->file, p->line, p->key.c_str()); } @@ -358,6 +370,64 @@ void Conf::setDataCenter(const ConfParser::Node* node) } } +void Conf::setCustomCommand(const ConfParser::Node* node) +{ + if (!node->sub) { + Throw(InvalidValue, "%s:%d CustomCommand require scope value", node->file, node->line); + } + for (auto p = node->sub; p; p = p->next) { + mCustomCommands.push_back(CustomCommandConf{}); + auto& cc = mCustomCommands.back(); + CustomCommandConf::init(cc, p->key.c_str(), Command::Sentinel); + auto s = p->sub; + if (!s) { + Throw(InvalidValue, "%s:%d CustomCommand.Command require scope value", + node->file, node->line); + } + for (;s ; s = s->next) { + if (setInt(cc.cmd.minArgs, "minArgs", s, 2)) { + } else if (setInt(cc.cmd.maxArgs, "maxArgs", s, 2, 9999)) { + } else if (setCommandMode(cc.cmd.mode, "mode", s)) { + } else { + Throw(UnknownKey, "%s:%d unknown key %s", + s->file, s->line, s->key.c_str()); + } + } + Command::addCustomCommand(&cc.cmd); + } +} + +bool Conf::setCommandMode(int& mode, const char* name, const ConfParser::Node* n, const int defaultMode) +{ + if (strcasecmp(name, n->key.c_str()) == 0) { + if (n->val.size() == 0) { + mode = defaultMode; + } else { + mode = Command::Unknown; + } + std::string mask; + std::istringstream is(n->val); + while (std::getline(is, mask, ',')) { + if ((strcasecmp(mask.c_str(), "Write") == 0)) { + mode |= Command::Write; + } else if ((strcasecmp(mask.c_str(), "Read") == 0)) { + mode |= Command::Read; + } else if ((strcasecmp(mask.c_str(), "Admin") == 0)) { + mode |= Command::Admin; + } else if ((strcasecmp(mask.c_str(), "KeyAt2") == 0)) { + mode |= Command::KeyAt2; + } else if ((strcasecmp(mask.c_str(), "KeyAt3") == 0)) { + mode |= Command::KeyAt3; + } else { + Throw(InvalidValue, "%s:%d %s %s is not an mode", + n->file, n->line, name, n->val.c_str()); + } + } + return true; + } + return false; +} + void Conf::setDC(DCConf& dc, const ConfParser::Node* node) { if (!node->sub) { diff --git a/src/Conf.h b/src/Conf.h index 04e9a17..1b9e002 100644 --- a/src/Conf.h +++ b/src/Conf.h @@ -19,6 +19,7 @@ #include "Distribution.h" #include "ConfParser.h" #include "Auth.h" +#include "Command.h" struct AuthConf { @@ -89,10 +90,18 @@ struct DCConf struct LatencyMonitorConf { std::string name; - std::bitset cmds; + std::bitset cmds; std::vector timeSpan;//us }; +struct CustomCommandConf +{ + std::string name; + Command cmd; + + static void init(CustomCommandConf &c, const char* name, const int type); +}; + class Conf { public: @@ -201,6 +210,8 @@ private: void setDC(DCConf& dc, const ConfParser::Node* n); void setReadPolicy(ReadPolicyConf& c, const ConfParser::Node* n); void setLatencyMonitor(LatencyMonitorConf& m, const ConfParser::Node* n); + void setCustomCommand(const ConfParser::Node* n); + bool setCommandMode(int& mode, const char* name, const ConfParser::Node* n, const int defaultMode = Command::Write); private: std::string mName; std::string mBind; @@ -220,6 +231,7 @@ private: std::vector mDCConfs; std::string mLocalDC; std::vector mLatencyMonitors; + std::vector mCustomCommands; }; diff --git a/src/LatencyMonitor.h b/src/LatencyMonitor.h index 866a400..55dc3a3 100644 --- a/src/LatencyMonitor.h +++ b/src/LatencyMonitor.h @@ -98,7 +98,7 @@ public: Buffer* output(Buffer* buf) const; private: String mName; - const std::bitset* mCmds; + const std::bitset* mCmds; std::vector mTimeSpan; TimeSpan mLast; };