predixy/src/Response.cpp
joyield ecc63e5586 improve performance
fix scan for sentinel groups
2017-07-30 15:00:51 +08:00

211 lines
5.7 KiB
C++

/*
* predixy - A high performance and full features proxy for redis.
* Copyright (C) 2017 Joyield, Inc. <joyield.com@gmail.com>
* All rights reserved.
*/
#include "Response.h"
#include "Request.h"
struct GenericResponse
{
Response::GenericCode code;
Reply::Type type;
const char* content;
};
static const GenericResponse GenericResponseDefs[] = {
{Response::Pong, Reply::Status, "+PONG\r\n"},
{Response::Ok, Reply::Status, "+OK\r\n"},
{Response::Cmd, Reply::Array, "*0\r\n"},
{Response::UnknownCmd, Reply::Error, "-ERR unknown command\r\n"},
{Response::ArgWrong, Reply::Error, "-ERR argument wrong\r\n"},
{Response::InvalidDb, Reply::Error, "-ERR invalid DB index\r\n"},
{Response::NoPasswordSet, Reply::Error, "-ERR Client sent AUTH, but no password is set\r\n"},
{Response::InvalidPassword, Reply::Error, "-ERR invalid password\r\n"},
{Response::Unauth, Reply::Error, "-NOAUTH Authentication required.\r\n"},
{Response::PermissionDeny, Reply::Error, "-ERR auth permission deny\r\n"},
{Response::NoServer, Reply::Error, "-ERR no server avaliable\r\n"},
{Response::NoServerConnection, Reply::Error, "-ERR no server connection avaliable\r\n"},
{Response::ServerConnectionClose, Reply::Error, "-ERR server connection close\r\n"},
{Response::DeliverRequestFail, Reply::Error, "-ERR deliver request fail\r\n"},
{Response::ForbidTransaction, Reply::Error, "-ERR forbid transaction in current server pool\r\n"},
{Response::ConfigSubCmdUnknown, Reply::Error, "-ERR CONFIG subcommand must be one of GET, SET\r\n"},
{Response::InvalidScanCursor, Reply::Error, "-ERR invalid cursor\r\n"},
{Response::ScanEnd, Reply::Array, "*2\r\n$1\r\n0\r\n*0\r\n"}
};
thread_local static Response* GenericResponses[Response::CodeSentinel];
void Response::init()
{
BufferPtr buf = BufferAlloc::create();
for (auto& r : GenericResponseDefs) {
Response* res = new Response();
res->mType = r.type;
if (buf->room() < (int)strlen(r.content)) {
buf = BufferAlloc::create();
}
buf = res->mRes.set(buf, r.content);
GenericResponses[r.code] = res;
}
}
Response::Response():
mType(Reply::None),
mInteger(0)
{
}
Response::Response(GenericCode code):
mType(Reply::None),
mInteger(0)
{
auto r = GenericResponses[code];
mType = r->mType;
mRes = r->mRes;
}
Response::~Response()
{
}
void Response::set(const ResponseParser& p)
{
mType = p.type();
mInteger = p.integer();
mRes = p.response();
}
void Response::set(int64_t num)
{
mType = Reply::Integer;
mInteger = num;
mRes.fset(nullptr, ":%ld\r\n", num);
}
void Response::setStr(const char* str, int len)
{
mType = Reply::String;
if (len < 0) {
len = strlen(str);
}
mRes.fset(nullptr, "$%d\r\n%.*s\r\n", len, len, str);
}
void Response::setErr(const char* str, int len)
{
mType = Reply::Error;
if (len < 0) {
len = strlen(str);
}
mRes.fset(nullptr, "-ERR %.*s\r\n", len, str);
}
void Response::adjustForLeader(Request* req)
{
if (Request* leader = req->leader()) {
switch (req->type()) {
case Command::Mget:
if (mType == Reply::Array) {
mRes.cut(4);// cut "*1\r\n"
if (leader == req) {
mHead.fset(nullptr, "*%d\r\n", req->followers());
}
} else {
mType = Reply::Array;
if (leader == req) {
mRes.fset(nullptr, "*%d\r\n$-1\r\n", req->followers());
} else {
mRes.set(nullptr, "$-1\r\n");
}
}
break;
case Command::Mset:
break;
case Command::Msetnx:
case Command::Touch:
case Command::Exists:
case Command::Del:
case Command::Unlink:
if (Response* r = leader->getResponse()) {
if (r != this && mType == Reply::Integer && mInteger > 0) {
r->mInteger += mInteger;
}
}
break;
default:
break;
}
}
}
bool Response::send(Socket* s)
{
const char* dat;
int len;
while (mHead.get(dat, len)) {
int n = s->write(dat, len);
if (n > 0) {
mHead.use(n);
} else {
return false;
}
}
while (mRes.get(dat, len)) {
int n = s->write(dat, len);
if (n > 0) {
mRes.use(n);
} else {
return false;
}
}
return true;
}
int Response::fill(IOVec* vecs, int len, Request* req) const
{
bool all = false;
int n = mHead.fill(vecs, len, all);
if (!all) {
return n;
}
n += mRes.fill(vecs + n, len - n, all);
if (n > 0 && all) {
vecs[n - 1].req = req;
}
return n;
}
bool Response::getAddr(int& slot, SString<Const::MaxAddrLen>& addr, const char* token) const
{
SegmentStr<Const::MaxAddrLen + 16> str(mRes);
if (!str.complete() || !str.hasPrefix(token)) {
return false;
}
const char* p = str.data() + strlen(token);
slot = atoi(p);
if (slot < 0 || slot >= Const::RedisClusterSlots) {
return false;
}
p = strchr(p, ' ');
if (!p) {
return false;
}
p++;
const char* e = strchr(p, '\r');
if (!e) {
return false;
}
int len = e - p;
if (len >= Const::MaxAddrLen) {
return false;
}
addr.set(p, len);
return true;
}