Add support custom commands

Custom commands are from redis module can be added up to 16(currently).
Configuration is like below.

CustomCommand {
  hello {      # hello is command, must be lower case.
               # this command has 2 or 3 parameters and first argument is key
    minArgs 2  # minimum arguments(including command itself)
    maxArgs 3  # maximum arguments(including command itself)
    mode Write,Read # this command is writable and readable
               # mode value can be Admin, Private, NoKey, MultiKey, SMultiKey, MultiKeyVal, KeyAt2, KeyAt3
  }
  newcommand { # default minArgs = 1, maxArgs = 1 and mode = Write
    mode Admin
  }
  ...
}
This commit is contained in:
Yoon 2018-04-05 16:17:47 +09:00
parent ce210cdee3
commit 5d9cd99270
5 changed files with 114 additions and 6 deletions

View File

@ -10,7 +10,7 @@
#include "String.h" #include "String.h"
#include "Command.h" #include "Command.h"
const Command Command::CmdPool[Sentinel] = { Command Command::CmdPool[AvailableCommands] = {
{None, "", 0, MaxArgs, Read}, {None, "", 0, MaxArgs, Read},
{Ping, "ping", 1, 2, Read}, {Ping, "ping", 1, 2, Read},
{PingServ, "ping", 1, 2, Inner}, {PingServ, "ping", 1, 2, Inner},
@ -171,11 +171,14 @@ const Command Command::CmdPool[Sentinel] = {
{SubMsg, "\000SubMsg", 0, 0, Admin} {SubMsg, "\000SubMsg", 0, 0, Admin}
}; };
int Command::Sentinel = Command::MaxCommands;
Command::CommandMap Command::CmdMap; Command::CommandMap Command::CmdMap;
void Command::init() void Command::init()
{ {
int type = 0; int type = 0;
for (auto& i : CmdPool) { for (auto j = 0; j < MaxCommands; j++) {
const auto& i = CmdPool[j];
if (i.type != type) { if (i.type != type) {
Throw(InitFail, "command %s unmatch the index in commands table", i.name); Throw(InitFail, "command %s unmatch the index in commands table", i.name);
} }
@ -187,3 +190,12 @@ void Command::init()
} }
} }
void Command::addCustomCommand(const Command *p) {
if (nullptr != find(p->name)) {
Throw(InitFail, "custom command %s is duplicated", p->name);
}
CmdPool[Sentinel] = *p;
CmdMap[p->name] = &CmdPool[Sentinel];
Sentinel++;
}

View File

@ -8,6 +8,7 @@
#define _PREDIXY_COMMAND_H_ #define _PREDIXY_COMMAND_H_
#include <unordered_map> #include <unordered_map>
#include <vector>
#include "Exception.h" #include "Exception.h"
#include "HashFunc.h" #include "HashFunc.h"
@ -189,7 +190,8 @@ public:
Unsubscribe, Unsubscribe,
SubMsg, SubMsg,
Sentinel MaxCommands,
AvailableCommands = MaxCommands + 128,
}; };
enum Mode enum Mode
{ {
@ -242,6 +244,7 @@ public:
if (cursor < Sentinel) { if (cursor < Sentinel) {
return &CmdPool[cursor++]; return &CmdPool[cursor++];
} }
return nullptr; return nullptr;
} }
static const Command* find(const String& cmd) static const Command* find(const String& cmd)
@ -249,9 +252,11 @@ public:
auto it = CmdMap.find(cmd); auto it = CmdMap.find(cmd);
return it == CmdMap.end() ? nullptr : it->second; return it == CmdMap.end() ? nullptr : it->second;
} }
static void addCustomCommand(const Command *pc);
static int Sentinel;
private: private:
static const int MaxArgs = 100000000; static const int MaxArgs = 100000000;
static const Command CmdPool[Sentinel]; static Command CmdPool[];
class H class H
{ {
public: public:

View File

@ -6,6 +6,7 @@
#include <ctype.h> #include <ctype.h>
#include <iostream> #include <iostream>
#include <sstream>
#include <fstream> #include <fstream>
#include "LogFileSink.h" #include "LogFileSink.h"
#include "ServerPool.h" #include "ServerPool.h"
@ -32,6 +33,15 @@ bool ServerConf::parse(ServerConf& s, const char* str)
return !s.addr.empty(); 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 = 1;
c.cmd.maxArgs = 1;
c.cmd.mode = Command::Write;
}
Conf::Conf(): Conf::Conf():
mBind("0.0.0.0:7617"), mBind("0.0.0.0:7617"),
mWorkerThreads(1), mWorkerThreads(1),
@ -157,6 +167,8 @@ void Conf::setGlobal(const ConfParser::Node* node)
sentinelServerPool = p; sentinelServerPool = p;
} else if (strcasecmp(p->key.c_str(), "DataCenter") == 0) { } else if (strcasecmp(p->key.c_str(), "DataCenter") == 0) {
dataCenter = p; dataCenter = p;
} else if (strcasecmp(p->key.c_str(), "CustomCommand") == 0) {
setCustomCommand(p);
} else { } else {
Throw(UnknownKey, "%s:%d unknown key %s", p->file, p->line, p->key.c_str()); Throw(UnknownKey, "%s:%d unknown key %s", p->file, p->line, p->key.c_str());
} }
@ -358,6 +370,73 @@ 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) {
if (Command::Sentinel >= Command::AvailableCommands) {
Throw(InvalidValue, "%s:%d Too many custom commands", node->file, node->line);
}
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) {
setInt(cc.cmd.minArgs, "minArgs", s, 1);
setInt(cc.cmd.maxArgs, "maxArgs", s, 1, 9999);
setCommandMode(cc.cmd.mode, "mode", s);
}
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(), "Private") == 0)) {
mode |= Command::Private;
} else if ((strcasecmp(mask.c_str(), "NoKey") == 0)) {
mode |= Command::NoKey;
} else if ((strcasecmp(mask.c_str(), "MultiKey") == 0)) {
mode |= Command::MultiKey;
} else if ((strcasecmp(mask.c_str(), "SMultiKey") == 0)) {
mode |= Command::SMultiKey;
} else if ((strcasecmp(mask.c_str(), "MultiKeyVal") == 0)) {
mode |= Command::MultiKeyVal;
} 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) void Conf::setDC(DCConf& dc, const ConfParser::Node* node)
{ {
if (!node->sub) { if (!node->sub) {

View File

@ -19,6 +19,7 @@
#include "Distribution.h" #include "Distribution.h"
#include "ConfParser.h" #include "ConfParser.h"
#include "Auth.h" #include "Auth.h"
#include "Command.h"
struct AuthConf struct AuthConf
{ {
@ -89,10 +90,18 @@ struct DCConf
struct LatencyMonitorConf struct LatencyMonitorConf
{ {
std::string name; std::string name;
std::bitset<Command::Sentinel> cmds; std::bitset<Command::AvailableCommands> cmds;
std::vector<long> timeSpan;//us std::vector<long> timeSpan;//us
}; };
struct CustomCommandConf
{
std::string name;
Command cmd;
static void init(CustomCommandConf &c, const char* name, const int type);
};
class Conf class Conf
{ {
public: public:
@ -201,6 +210,8 @@ private:
void setDC(DCConf& dc, const ConfParser::Node* n); void setDC(DCConf& dc, const ConfParser::Node* n);
void setReadPolicy(ReadPolicyConf& c, const ConfParser::Node* n); void setReadPolicy(ReadPolicyConf& c, const ConfParser::Node* n);
void setLatencyMonitor(LatencyMonitorConf& m, 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: private:
std::string mName; std::string mName;
std::string mBind; std::string mBind;
@ -220,6 +231,7 @@ private:
std::vector<DCConf> mDCConfs; std::vector<DCConf> mDCConfs;
std::string mLocalDC; std::string mLocalDC;
std::vector<LatencyMonitorConf> mLatencyMonitors; std::vector<LatencyMonitorConf> mLatencyMonitors;
std::vector<CustomCommandConf> mCustomCommands;
}; };

View File

@ -98,7 +98,7 @@ public:
Buffer* output(Buffer* buf) const; Buffer* output(Buffer* buf) const;
private: private:
String mName; String mName;
const std::bitset<Command::Sentinel>* mCmds; const std::bitset<Command::AvailableCommands>* mCmds;
std::vector<TimeSpan> mTimeSpan; std::vector<TimeSpan> mTimeSpan;
TimeSpan mLast; TimeSpan mLast;
}; };