mirror of
https://github.com/zerotier/ZeroTierOne.git
synced 2025-06-06 20:43:44 +02:00
Merge branch 'edge' of http://git.int.zerotier.com/zerotier/ZeroTierOne into edge
This commit is contained in:
commit
bf33368add
85 changed files with 23336 additions and 352 deletions
|
@ -27,17 +27,22 @@ if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
|
|||
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo")
|
||||
endif()
|
||||
|
||||
#option(BUILD_CENTRAL_CONTROLLER "Build ZeroTier Central Controller" OFF)
|
||||
#if (BUILD_CENTRAL_CONTROLLER)
|
||||
# find_package(PostgreSQL REQUIRED)
|
||||
# set(ENABLE_SSL_SUPPORT OFF)
|
||||
# set(BUILD_SHARED_LIBS OFF)
|
||||
# set(BUILD_EXAMPLES OFF)
|
||||
# set(BUILD_TOOLS OFF)
|
||||
# set(BUILD_TESTS OFF)
|
||||
# set(BUILD_API_DOCS OFF)
|
||||
# add_subdirectory("ext/librabbitmq")
|
||||
#endif(BUILD_CENTRAL_CONTROLLER)
|
||||
option(BUILD_CENTRAL_CONTROLLER "Build ZeroTier Central Controller" OFF)
|
||||
if(BUILD_CENTRAL_CONTROLLER)
|
||||
find_package(PkgConfig REQUIRED)
|
||||
if(APPLE)
|
||||
set(CMAKE_PREFIX_PATH
|
||||
${CMAKE_PREFIX_PATH}
|
||||
/usr/local/opt/libpq
|
||||
/usr/local/lib
|
||||
)
|
||||
endif(APPLE)
|
||||
find_package(PostgreSQL REQUIRED)
|
||||
|
||||
pkg_check_modules(hiredis REQUIRED IMPORTED_TARGET hiredis)
|
||||
|
||||
add_subdirectory(controller/thirdparty/redis-plus-plus-1.1.1)
|
||||
endif(BUILD_CENTRAL_CONTROLLER)
|
||||
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
add_definitions(-DZT_DEBUG)
|
||||
|
@ -95,15 +100,15 @@ if (
|
|||
add_compile_options(-maes -mrdrnd -mpclmul -msse -msse2 -mssse3)
|
||||
endif()
|
||||
|
||||
if(BUILD_CENTRAL_CONTROLLER)
|
||||
add_definitions(-DZT_CONTROLLER_USE_LIBPQ=1)
|
||||
endif(BUILD_CENTRAL_CONTROLLER)
|
||||
|
||||
add_subdirectory(core)
|
||||
add_subdirectory(controller)
|
||||
add_subdirectory(osdep)
|
||||
add_subdirectory(serviceiocore)
|
||||
|
||||
#if(BUILD_CENTRAL_CONTROLLER)
|
||||
# set(libs ${libs} rabbitmq-static ${PostgreSQL_LIBRARIES})
|
||||
#endif(BUILD_CENTRAL_CONTROLLER)
|
||||
|
||||
set(
|
||||
zt_core
|
||||
zt_osdep
|
||||
|
@ -117,4 +122,5 @@ add_custom_target(zerotier ALL
|
|||
BYPRODUCTS zerotier
|
||||
)
|
||||
add_dependencies(zerotier zt_osdep zt_core zt_controller zt_service_io_core)
|
||||
|
||||
set(ADDITIONAL_MAKE_CLEAN_FILES zerotier)
|
||||
|
|
6
Makefile
6
Makefile
|
@ -6,13 +6,13 @@ all: setup
|
|||
cd ${BUILDDIR} && $(MAKE) -j$(shell getconf _NPROCESSORS_ONLN)
|
||||
|
||||
setup:
|
||||
mkdir -p ${BUILDDIR} && cd ${BUILDDIR} && cmake .. -DCMAKE_BUILD_TYPE=Release
|
||||
mkdir -p ${BUILDDIR} && cd ${BUILDDIR} && cmake .. -DCMAKE_BUILD_TYPE=Release ${CMAKE_ARGS}
|
||||
|
||||
setup-debug:
|
||||
mkdir -p ${BUILDDIR} && cd ${BUILDDIR} && cmake .. -DCMAKE_BUILD_TYPE=Debug
|
||||
mkdir -p ${BUILDDIR} && cd ${BUILDDIR} && cmake .. -DCMAKE_BUILD_TYPE=Debug ${CMAKE_ARGS}
|
||||
|
||||
debug:
|
||||
mkdir -p ${BUILDDIR} && cd ${BUILDDIR} && cmake .. -DCMAKE_BUILD_TYPE=Debug && $(MAKE)
|
||||
mkdir -p ${BUILDDIR} && cd ${BUILDDIR} && cmake .. -DCMAKE_BUILD_TYPE=Debug ${CMAKE_ARGS} && $(MAKE)
|
||||
|
||||
clean:
|
||||
rm -rf ${BUILDDIR} cmake-build-*
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
cmake_minimum_required(VERSION 2.8)
|
||||
project(zt_controller)
|
||||
|
||||
|
||||
if(WIN32)
|
||||
add_definitions(-DNOMINMAX)
|
||||
endif(WIN32)
|
||||
|
@ -11,7 +12,6 @@ set(ctl_src
|
|||
EmbeddedNetworkController.cpp
|
||||
FileDB.cpp
|
||||
LFDB.cpp
|
||||
RabbitMQ.cpp
|
||||
)
|
||||
|
||||
set(ctl_hdr
|
||||
|
@ -20,17 +20,17 @@ set(ctl_hdr
|
|||
EmbeddedNetworkController.hpp
|
||||
FileDB.hpp
|
||||
LFDB.hpp
|
||||
RabbitMQ.hpp
|
||||
Redis.hpp
|
||||
)
|
||||
|
||||
if(BUILD_CENTRAL_CONTROLLER)
|
||||
add_definitions(-DZT_CONTROLLER_USE_LIBPQ)
|
||||
include_directories("../ext/librabbitmq/librabbitmq" ${PostgreSQL_INCLUDE_DIRS})
|
||||
|
||||
set(ctl_src ${ctl_src} PostgreSQL.cpp)
|
||||
set(ctl_hdr ${ctl_hdr} PostgreSQL.hpp)
|
||||
endif(BUILD_CENTRAL_CONTROLLER)
|
||||
|
||||
add_library(${PROJECT_NAME} STATIC ${ctl_src} ${ctl_hdr})
|
||||
add_library(${PROJECT_NAME} STATIC ${ctl_src} ${ctl_hdr} ${PostgreSQL_LIBRARIES} ${STATIC_LIB} ${hiredis_libraries})
|
||||
target_include_directories(${PROJECT_NAME}
|
||||
PUBLIC ${hiredis_INCLUDE_DIRS}/../ ${CMAKE_CURRENT_LIST_DIR}/thirdparty/redis-plus-plus-1.1.1/src/sw ${PostgreSQL_INCLUDE_DIRS}
|
||||
PRIVATE ${CMAKE_BINARY_DIR}/core)
|
||||
target_compile_features(${PROJECT_NAME} PUBLIC cxx_std_11)
|
||||
|
||||
|
|
|
@ -446,7 +446,7 @@ static bool _parseRule(json &r,ZT_VirtualNetworkRule &rule)
|
|||
|
||||
} // anonymous namespace
|
||||
|
||||
EmbeddedNetworkController::EmbeddedNetworkController(Node *node,const char *ztPath,const char *dbPath, int listenPort, MQConfig *mqc) :
|
||||
EmbeddedNetworkController::EmbeddedNetworkController(Node *node,const char *ztPath,const char *dbPath, int listenPort, RedisConfig *rc) :
|
||||
_startTime(OSUtils::now()),
|
||||
_listenPort(listenPort),
|
||||
_node(node),
|
||||
|
@ -454,7 +454,7 @@ EmbeddedNetworkController::EmbeddedNetworkController(Node *node,const char *ztPa
|
|||
_path(dbPath),
|
||||
_sender((NetworkController::Sender *)0),
|
||||
_db(this),
|
||||
_mqc(mqc)
|
||||
_rc(rc)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -476,7 +476,7 @@ void EmbeddedNetworkController::init(const Identity &signingId,Sender *sender)
|
|||
|
||||
#ifdef ZT_CONTROLLER_USE_LIBPQ
|
||||
if ((_path.length() > 9)&&(_path.substr(0,9) == "postgres:")) {
|
||||
_db.addDB(std::shared_ptr<DB>(new PostgreSQL(_signingId,_path.substr(9).c_str(), _listenPort, _mqc)));
|
||||
_db.addDB(std::shared_ptr<DB>(new PostgreSQL(_signingId,_path.substr(9).c_str(), _listenPort, _rc)));
|
||||
} else {
|
||||
#endif
|
||||
_db.addDB(std::shared_ptr<DB>(new FileDB(_path.c_str())));
|
||||
|
|
|
@ -44,7 +44,7 @@ namespace ZeroTier {
|
|||
|
||||
class Node;
|
||||
|
||||
struct MQConfig;
|
||||
struct RedisConfig;
|
||||
|
||||
class EmbeddedNetworkController : public NetworkController,public DB::ChangeListener
|
||||
{
|
||||
|
@ -54,7 +54,7 @@ public:
|
|||
* @param ztPath ZeroTier base path
|
||||
* @param dbPath Database path (file path or database credentials)
|
||||
*/
|
||||
EmbeddedNetworkController(Node *node,const char *ztPath,const char *dbPath, int listenPort, MQConfig *mqc = NULL);
|
||||
EmbeddedNetworkController(Node *node,const char *ztPath,const char *dbPath, int listenPort, RedisConfig *rc = NULL);
|
||||
virtual ~EmbeddedNetworkController();
|
||||
|
||||
virtual void init(const Identity &signingId,Sender *sender);
|
||||
|
@ -147,7 +147,7 @@ private:
|
|||
std::unordered_map< _MemberStatusKey,_MemberStatus,_MemberStatusHash > _memberStatus;
|
||||
std::mutex _memberStatus_l;
|
||||
|
||||
MQConfig *_mqc;
|
||||
RedisConfig *_rc;
|
||||
};
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
/*
|
||||
* Copyright (c)2013-2020 ZeroTier, Inc.
|
||||
* Copyright (c)2019 ZeroTier, Inc.
|
||||
*
|
||||
* Use of this software is governed by the Business Source License included
|
||||
* in the LICENSE.TXT file in the project's root directory.
|
||||
*
|
||||
* Change Date: 2024-01-01
|
||||
* Change Date: 2023-01-01
|
||||
*
|
||||
* On the date above, in accordance with the Business Source License, use
|
||||
* of this software will be governed by version 2.0 of the Apache License.
|
||||
|
@ -17,13 +17,13 @@
|
|||
|
||||
#include "../core/Constants.hpp"
|
||||
#include "EmbeddedNetworkController.hpp"
|
||||
#include "RabbitMQ.hpp"
|
||||
#include "../version.h"
|
||||
#include "version.h"
|
||||
#include "Redis.hpp"
|
||||
|
||||
#include <libpq-fe.h>
|
||||
#include <sstream>
|
||||
#include <amqp.h>
|
||||
#include <amqp_tcp_socket.h>
|
||||
#include <climits>
|
||||
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
|
@ -69,7 +69,11 @@ std::string join(const std::vector<std::string> &elements, const char * const se
|
|||
|
||||
using namespace ZeroTier;
|
||||
|
||||
PostgreSQL::PostgreSQL(const Identity &myId, const char *path, int listenPort, MQConfig *mqc)
|
||||
using Attrs = std::vector<std::pair<std::string, std::string>>;
|
||||
using Item = std::pair<std::string, Attrs>;
|
||||
using ItemStream = std::vector<Item>;
|
||||
|
||||
PostgreSQL::PostgreSQL(const Identity &myId, const char *path, int listenPort, RedisConfig *rc)
|
||||
: DB()
|
||||
, _myId(myId)
|
||||
, _myAddress(myId.address())
|
||||
|
@ -78,7 +82,9 @@ PostgreSQL::PostgreSQL(const Identity &myId, const char *path, int listenPort, M
|
|||
, _run(1)
|
||||
, _waitNoticePrinted(false)
|
||||
, _listenPort(listenPort)
|
||||
, _mqc(mqc)
|
||||
, _rc(rc)
|
||||
, _redis(NULL)
|
||||
, _cluster(NULL)
|
||||
{
|
||||
char myAddress[64];
|
||||
_myAddressStr = myId.address().toString(myAddress);
|
||||
|
@ -108,13 +114,37 @@ PostgreSQL::PostgreSQL(const Identity &myId, const char *path, int listenPort, M
|
|||
fprintf(stderr, "Central database schema version too low. This controller version requires a minimum schema version of %d. Please upgrade your Central instance", DB_MINIMUM_VERSION);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
PQclear(res);
|
||||
res = NULL;
|
||||
|
||||
if (_rc != NULL) {
|
||||
sw::redis::ConnectionOptions opts;
|
||||
sw::redis::ConnectionPoolOptions poolOpts;
|
||||
opts.host = _rc->hostname;
|
||||
opts.port = _rc->port;
|
||||
opts.password = _rc->password;
|
||||
opts.db = 0;
|
||||
poolOpts.size = 10;
|
||||
if (_rc->clusterMode) {
|
||||
fprintf(stderr, "Using Redis in Cluster Mode\n");
|
||||
_cluster = std::make_shared<sw::redis::RedisCluster>(opts, poolOpts);
|
||||
} else {
|
||||
fprintf(stderr, "Using Redis in Standalone Mode\n");
|
||||
_redis = std::make_shared<sw::redis::Redis>(opts, poolOpts);
|
||||
}
|
||||
}
|
||||
|
||||
_readyLock.lock();
|
||||
|
||||
fprintf(stderr, "[%s] NOTICE: %.10llx controller PostgreSQL waiting for initial data download..." ZT_EOL_S, ::_timestr(), (unsigned long long)_myAddress.toInt());
|
||||
_waitNoticePrinted = true;
|
||||
|
||||
initializeNetworks(conn);
|
||||
initializeMembers(conn);
|
||||
|
||||
PQfinish(conn);
|
||||
conn = NULL;
|
||||
|
||||
_readyLock.lock();
|
||||
_heartbeatThread = std::thread(&PostgreSQL::heartbeat, this);
|
||||
_membersDbWatcher = std::thread(&PostgreSQL::membersDbWatcher, this);
|
||||
_networksDbWatcher = std::thread(&PostgreSQL::networksDbWatcher, this);
|
||||
|
@ -132,21 +162,17 @@ PostgreSQL::~PostgreSQL()
|
|||
_heartbeatThread.join();
|
||||
_membersDbWatcher.join();
|
||||
_networksDbWatcher.join();
|
||||
_commitQueue.stop();
|
||||
for (int i = 0; i < ZT_CENTRAL_CONTROLLER_COMMIT_THREADS; ++i) {
|
||||
_commitThread[i].join();
|
||||
}
|
||||
_onlineNotificationThread.join();
|
||||
|
||||
}
|
||||
|
||||
|
||||
bool PostgreSQL::waitForReady()
|
||||
{
|
||||
while (_ready < 2) {
|
||||
if (!_waitNoticePrinted) {
|
||||
_waitNoticePrinted = true;
|
||||
fprintf(stderr, "[%s] NOTICE: %.10llx controller PostgreSQL waiting for initial data download..." ZT_EOL_S, ::_timestr(), (unsigned long long)_myAddress.toInt());
|
||||
}
|
||||
_readyLock.lock();
|
||||
_readyLock.unlock();
|
||||
}
|
||||
|
@ -166,24 +192,24 @@ bool PostgreSQL::save(nlohmann::json &record,bool notifyListeners)
|
|||
return false;
|
||||
const std::string objtype = record["objtype"];
|
||||
if (objtype == "network") {
|
||||
const uint64_t nwid = DB::jsonIntHex(record["id"],0ULL);
|
||||
const uint64_t nwid = jsonIntHex(record["id"],0ULL);
|
||||
if (nwid) {
|
||||
nlohmann::json old;
|
||||
get(nwid,old);
|
||||
if ((!old.is_object())||(!_compareRecords(old,record))) {
|
||||
record["revision"] = DB::jsonInt(record["revision"],0ULL) + 1ULL;
|
||||
record["revision"] = jsonInt(record["revision"],0ULL) + 1ULL;
|
||||
_commitQueue.post(std::pair<nlohmann::json,bool>(record,notifyListeners));
|
||||
modified = true;
|
||||
}
|
||||
}
|
||||
} else if (objtype == "member") {
|
||||
const uint64_t nwid = DB::jsonIntHex(record["nwid"],0ULL);
|
||||
const uint64_t id = DB::jsonIntHex(record["id"],0ULL);
|
||||
const uint64_t nwid = jsonIntHex(record["nwid"],0ULL);
|
||||
const uint64_t id = jsonIntHex(record["id"],0ULL);
|
||||
if ((id)&&(nwid)) {
|
||||
nlohmann::json network,old;
|
||||
get(nwid,network,id,old);
|
||||
if ((!old.is_object())||(!_compareRecords(old,record))) {
|
||||
record["revision"] = DB::jsonInt(record["revision"],0ULL) + 1ULL;
|
||||
record["revision"] = jsonInt(record["revision"],0ULL) + 1ULL;
|
||||
_commitQueue.post(std::pair<nlohmann::json,bool>(record,notifyListeners));
|
||||
modified = true;
|
||||
}
|
||||
|
@ -207,12 +233,15 @@ void PostgreSQL::eraseNetwork(const uint64_t networkId)
|
|||
tmp.first["objtype"] = "_delete_network";
|
||||
tmp.second = true;
|
||||
_commitQueue.post(tmp);
|
||||
nlohmann::json nullJson;
|
||||
_networkChanged(tmp.first, nullJson, true);
|
||||
}
|
||||
|
||||
void PostgreSQL::eraseMember(const uint64_t networkId, const uint64_t memberId)
|
||||
{
|
||||
char tmp2[24];
|
||||
std::pair<nlohmann::json,bool> tmp;
|
||||
waitForReady();
|
||||
std::pair<nlohmann::json,bool> tmp, nw;
|
||||
Utils::hex(networkId, tmp2);
|
||||
tmp.first["nwid"] = tmp2;
|
||||
Utils::hex(memberId, tmp2);
|
||||
|
@ -220,6 +249,8 @@ void PostgreSQL::eraseMember(const uint64_t networkId, const uint64_t memberId)
|
|||
tmp.first["objtype"] = "_delete_member";
|
||||
tmp.second = true;
|
||||
_commitQueue.post(tmp);
|
||||
nlohmann::json nullJson;
|
||||
_memberChanged(tmp.first, nullJson, true);
|
||||
}
|
||||
|
||||
void PostgreSQL::nodeIsOnline(const uint64_t networkId, const uint64_t memberId, const InetAddress &physicalAddress)
|
||||
|
@ -240,10 +271,29 @@ void PostgreSQL::initializeNetworks(PGconn *conn)
|
|||
exit(1);
|
||||
}
|
||||
|
||||
std::string setKey = "networks:{" + _myAddressStr + "}";
|
||||
|
||||
if (_rc != NULL) {
|
||||
try {
|
||||
if (_rc->clusterMode) {
|
||||
_cluster->del(setKey);
|
||||
} else {
|
||||
_redis->del(setKey);
|
||||
}
|
||||
} catch (sw::redis::Error &e) {
|
||||
// del can throw an error if the key doesn't exist
|
||||
// swallow it and move along
|
||||
}
|
||||
}
|
||||
|
||||
std::unordered_set<std::string> networkSet;
|
||||
|
||||
const char *params[1] = {
|
||||
_myAddressStr.c_str()
|
||||
};
|
||||
|
||||
fprintf(stderr, "Initializing Networks...\n");
|
||||
|
||||
PGresult *res = PQexecParams(conn, "SELECT id, EXTRACT(EPOCH FROM creation_time AT TIME ZONE 'UTC')*1000, capabilities, "
|
||||
"enable_broadcast, EXTRACT(EPOCH FROM last_modified AT TIME ZONE 'UTC')*1000, mtu, multicast_limit, name, private, remote_trace_level, "
|
||||
"remote_trace_target, revision, rules, tags, v4_assign_mode, v6_assign_mode FROM ztc_network "
|
||||
|
@ -269,9 +319,12 @@ void PostgreSQL::initializeNetworks(PGconn *conn)
|
|||
const char *nwidparam[1] = {
|
||||
PQgetvalue(res, i, 0)
|
||||
};
|
||||
std::string nwid = PQgetvalue(res, i, 0);
|
||||
|
||||
config["id"] = PQgetvalue(res, i, 0);
|
||||
config["nwid"] = PQgetvalue(res, i, 0);
|
||||
networkSet.insert(nwid);
|
||||
|
||||
config["id"] = nwid;
|
||||
config["nwid"] = nwid;
|
||||
try {
|
||||
config["creationTime"] = std::stoull(PQgetvalue(res, i, 1));
|
||||
} catch (std::exception &e) {
|
||||
|
@ -384,14 +437,29 @@ void PostgreSQL::initializeNetworks(PGconn *conn)
|
|||
|
||||
PQclear(res);
|
||||
|
||||
if(!networkSet.empty()) {
|
||||
if (_rc && _rc->clusterMode) {
|
||||
auto tx = _cluster->transaction(_myAddressStr, true);
|
||||
tx.sadd(setKey, networkSet.begin(), networkSet.end());
|
||||
tx.exec();
|
||||
} else if (_rc && !_rc->clusterMode) {
|
||||
auto tx = _redis->transaction(true);
|
||||
tx.sadd(setKey, networkSet.begin(), networkSet.end());
|
||||
tx.exec();
|
||||
}
|
||||
}
|
||||
|
||||
if (++this->_ready == 2) {
|
||||
if (_waitNoticePrinted) {
|
||||
fprintf(stderr,"[%s] NOTICE: %.10llx controller PostgreSQL data download complete." ZT_EOL_S,_timestr(),(unsigned long long)_myAddress.toInt());
|
||||
}
|
||||
_readyLock.unlock();
|
||||
}
|
||||
} catch (sw::redis::Error &e) {
|
||||
fprintf(stderr, "ERROR: Error initializing networks in Redis: %s\n", e.what());
|
||||
exit(-1);
|
||||
} catch (std::exception &e) {
|
||||
fprintf(stderr, "ERROR: Error initializing networks: %s", e.what());
|
||||
fprintf(stderr, "ERROR: Error initializing networks: %s\n", e.what());
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
|
@ -403,11 +471,44 @@ void PostgreSQL::initializeMembers(PGconn *conn)
|
|||
fprintf(stderr, "Bad Database Connection: %s", PQerrorMessage(conn));
|
||||
exit(1);
|
||||
}
|
||||
std::string setKeyBase = "network-nodes-all:{" + _myAddressStr + "}:";
|
||||
|
||||
if (_rc != NULL) {
|
||||
std::lock_guard<std::mutex> l(_networks_l);
|
||||
std::unordered_set<std::string> deletes;
|
||||
for ( auto it : _networks) {
|
||||
uint64_t nwid_i = it.first;
|
||||
char nwidTmp[64] = {0};
|
||||
OSUtils::ztsnprintf(nwidTmp, sizeof(nwidTmp), "%.16llx", nwid_i);
|
||||
std::string nwid(nwidTmp);
|
||||
std::string key = setKeyBase + nwid;
|
||||
deletes.insert(key);
|
||||
}
|
||||
|
||||
if (!deletes.empty()) {
|
||||
if (_rc->clusterMode) {
|
||||
auto tx = _cluster->transaction(_myAddressStr, true);
|
||||
for (std::string k : deletes) {
|
||||
tx.del(k);
|
||||
}
|
||||
tx.exec();
|
||||
} else {
|
||||
auto tx = _redis->transaction(true);
|
||||
for (std::string k : deletes) {
|
||||
tx.del(k);
|
||||
}
|
||||
tx.exec();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const char *params[1] = {
|
||||
_myAddressStr.c_str()
|
||||
};
|
||||
|
||||
std::unordered_map<std::string, std::string> networkMembers;
|
||||
|
||||
fprintf(stderr, "Initializing Members...\n");
|
||||
PGresult *res = PQexecParams(conn,
|
||||
"SELECT m.id, m.network_id, m.active_bridge, m.authorized, m.capabilities, EXTRACT(EPOCH FROM m.creation_time AT TIME ZONE 'UTC')*1000, m.identity, "
|
||||
" EXTRACT(EPOCH FROM m.last_authorized_time AT TIME ZONE 'UTC')*1000, "
|
||||
|
@ -438,6 +539,9 @@ void PostgreSQL::initializeMembers(PGconn *conn)
|
|||
|
||||
std::string memberId(PQgetvalue(res, i, 0));
|
||||
std::string networkId(PQgetvalue(res, i, 1));
|
||||
|
||||
networkMembers.insert(std::pair<std::string, std::string>(setKeyBase+networkId, memberId));
|
||||
|
||||
std::string ctime = PQgetvalue(res, i, 5);
|
||||
config["id"] = memberId;
|
||||
config["nwid"] = networkId;
|
||||
|
@ -530,7 +634,12 @@ void PostgreSQL::initializeMembers(PGconn *conn)
|
|||
|
||||
int n = PQntuples(r2);
|
||||
for (int j = 0; j < n; ++j) {
|
||||
config["ipAssignments"].push_back(PQgetvalue(r2, j, 0));
|
||||
std::string ipaddr = PQgetvalue(r2, j, 0);
|
||||
std::size_t pos = ipaddr.find('/');
|
||||
if (pos != std::string::npos) {
|
||||
ipaddr = ipaddr.substr(0, pos);
|
||||
}
|
||||
config["ipAssignments"].push_back(ipaddr);
|
||||
}
|
||||
|
||||
_memberChanged(empty, config, false);
|
||||
|
@ -538,12 +647,31 @@ void PostgreSQL::initializeMembers(PGconn *conn)
|
|||
|
||||
PQclear(res);
|
||||
|
||||
if (!networkMembers.empty()) {
|
||||
if (_rc != NULL) {
|
||||
if (_rc->clusterMode) {
|
||||
auto tx = _cluster->transaction(_myAddressStr, true);
|
||||
for (auto it : networkMembers) {
|
||||
tx.sadd(it.first, it.second);
|
||||
}
|
||||
tx.exec();
|
||||
} else {
|
||||
auto tx = _redis->transaction(true);
|
||||
for (auto it : networkMembers) {
|
||||
tx.sadd(it.first, it.second);
|
||||
}
|
||||
tx.exec();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (++this->_ready == 2) {
|
||||
if (_waitNoticePrinted) {
|
||||
fprintf(stderr,"[%s] NOTICE: %.10llx controller PostgreSQL data download complete." ZT_EOL_S,_timestr(),(unsigned long long)_myAddress.toInt());
|
||||
}
|
||||
_readyLock.unlock();
|
||||
}
|
||||
} catch (sw::redis::Error &e) {
|
||||
fprintf(stderr, "ERROR: Error initializing members (redis): %s\n", e.what());
|
||||
} catch (std::exception &e) {
|
||||
fprintf(stderr, "ERROR: Error initializing members: %s\n", e.what());
|
||||
exit(-1);
|
||||
|
@ -581,14 +709,15 @@ void PostgreSQL::heartbeat()
|
|||
PQfinish(conn);
|
||||
exit(6);
|
||||
}
|
||||
int64_t ts = OSUtils::now();
|
||||
if (conn) {
|
||||
std::string major = std::to_string(ZEROTIER_ONE_VERSION_MAJOR);
|
||||
std::string minor = std::to_string(ZEROTIER_ONE_VERSION_MINOR);
|
||||
std::string rev = std::to_string(ZEROTIER_ONE_VERSION_REVISION);
|
||||
std::string build = std::to_string(ZEROTIER_ONE_VERSION_BUILD);
|
||||
std::string now = std::to_string(OSUtils::now());
|
||||
std::string major = std::to_string(ZEROTIER_VERSION_MAJOR);
|
||||
std::string minor = std::to_string(ZEROTIER_VERSION_MINOR);
|
||||
std::string rev = std::to_string(ZEROTIER_VERSION_REVISION);
|
||||
std::string build = std::to_string(ZEROTIER_VERSION_BUILD);
|
||||
std::string now = std::to_string(ts);
|
||||
std::string host_port = std::to_string(_listenPort);
|
||||
std::string use_rabbitmq = (_mqc != NULL) ? "true" : "false";
|
||||
std::string use_redis = (_rc != NULL) ? "true" : "false";
|
||||
const char *values[10] = {
|
||||
controllerId,
|
||||
hostname,
|
||||
|
@ -599,16 +728,16 @@ void PostgreSQL::heartbeat()
|
|||
rev.c_str(),
|
||||
build.c_str(),
|
||||
host_port.c_str(),
|
||||
use_rabbitmq.c_str()
|
||||
use_redis.c_str()
|
||||
};
|
||||
|
||||
PGresult *res = PQexecParams(conn,
|
||||
"INSERT INTO ztc_controller (id, cluster_host, last_alive, public_identity, v_major, v_minor, v_rev, v_build, host_port, use_rabbitmq) "
|
||||
"INSERT INTO ztc_controller (id, cluster_host, last_alive, public_identity, v_major, v_minor, v_rev, v_build, host_port, use_redis) "
|
||||
"VALUES ($1, $2, TO_TIMESTAMP($3::double precision/1000), $4, $5, $6, $7, $8, $9, $10) "
|
||||
"ON CONFLICT (id) DO UPDATE SET cluster_host = EXCLUDED.cluster_host, last_alive = EXCLUDED.last_alive, "
|
||||
"public_identity = EXCLUDED.public_identity, v_major = EXCLUDED.v_major, v_minor = EXCLUDED.v_minor, "
|
||||
"v_rev = EXCLUDED.v_rev, v_build = EXCLUDED.v_rev, host_port = EXCLUDED.host_port, "
|
||||
"use_rabbitmq = EXCLUDED.use_rabbitmq",
|
||||
"use_redis = EXCLUDED.use_redis",
|
||||
10, // number of parameters
|
||||
NULL, // oid field. ignore
|
||||
values, // values for substitution
|
||||
|
@ -621,12 +750,20 @@ void PostgreSQL::heartbeat()
|
|||
}
|
||||
PQclear(res);
|
||||
}
|
||||
if (_rc != NULL) {
|
||||
if (_rc->clusterMode) {
|
||||
_cluster->zadd("controllers", controllerId, ts);
|
||||
} else {
|
||||
_redis->zadd("controllers", controllerId, ts);
|
||||
}
|
||||
}
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
|
||||
}
|
||||
|
||||
PQfinish(conn);
|
||||
conn = NULL;
|
||||
fprintf(stderr, "Exited heartbeat thread\n");
|
||||
}
|
||||
|
||||
void PostgreSQL::membersDbWatcher()
|
||||
|
@ -638,12 +775,10 @@ void PostgreSQL::membersDbWatcher()
|
|||
exit(1);
|
||||
}
|
||||
|
||||
initializeMembers(conn);
|
||||
|
||||
if (this->_mqc != NULL) {
|
||||
if (_rc) {
|
||||
PQfinish(conn);
|
||||
conn = NULL;
|
||||
_membersWatcher_RabbitMQ();
|
||||
_membersWatcher_Redis();
|
||||
} else {
|
||||
_membersWatcher_Postgres(conn);
|
||||
PQfinish(conn);
|
||||
|
@ -698,41 +833,62 @@ void PostgreSQL::_membersWatcher_Postgres(PGconn *conn) {
|
|||
}
|
||||
}
|
||||
|
||||
void PostgreSQL::_membersWatcher_RabbitMQ() {
|
||||
void PostgreSQL::_membersWatcher_Redis() {
|
||||
char buf[11] = {0};
|
||||
std::string qname = "member_"+ std::string(_myAddress.toString(buf));
|
||||
RabbitMQ rmq(_mqc, qname.c_str());
|
||||
try {
|
||||
rmq.init();
|
||||
} catch (std::runtime_error &e) {
|
||||
fprintf(stderr, "RABBITMQ ERROR: %s\n", e.what());
|
||||
exit(11);
|
||||
}
|
||||
std::string key = "member-stream:{" + std::string(_myAddress.toString(buf)) + "}";
|
||||
|
||||
while (_run == 1) {
|
||||
try {
|
||||
std::string msg = rmq.consume();
|
||||
// fprintf(stderr, "Got Member Update: %s\n", msg.c_str());
|
||||
if (msg.empty()) {
|
||||
continue;
|
||||
json tmp;
|
||||
std::unordered_map<std::string, ItemStream> result;
|
||||
if (_rc->clusterMode) {
|
||||
_cluster->xread(key, "$", std::chrono::seconds(1), 0, std::inserter(result, result.end()));
|
||||
} else {
|
||||
_redis->xread(key, "$", std::chrono::seconds(1), 0, std::inserter(result, result.end()));
|
||||
}
|
||||
json tmp(json::parse(msg));
|
||||
if (!result.empty()) {
|
||||
for (auto element : result) {
|
||||
#ifdef ZT_TRACE
|
||||
fprintf(stdout, "Received notification from: %s\n", element.first.c_str());
|
||||
#endif
|
||||
for (auto rec : element.second) {
|
||||
std::string id = rec.first;
|
||||
auto attrs = rec.second;
|
||||
#ifdef ZT_TRACE
|
||||
fprintf(stdout, "Record ID: %s\n", id.c_str());
|
||||
fprintf(stdout, "attrs len: %lu\n", attrs.size());
|
||||
#endif
|
||||
for (auto a : attrs) {
|
||||
#ifdef ZT_TRACE
|
||||
fprintf(stdout, "key: %s\nvalue: %s\n", a.first.c_str(), a.second.c_str());
|
||||
#endif
|
||||
try {
|
||||
tmp = json::parse(a.second);
|
||||
json &ov = tmp["old_val"];
|
||||
json &nv = tmp["new_val"];
|
||||
json oldConfig, newConfig;
|
||||
if (ov.is_object()) oldConfig = ov;
|
||||
if (nv.is_object()) newConfig = nv;
|
||||
if (oldConfig.is_object() || newConfig.is_object()) {
|
||||
_memberChanged(oldConfig,newConfig,(this->_ready>=2));
|
||||
if (oldConfig.is_object()||newConfig.is_object()) {
|
||||
_memberChanged(oldConfig,newConfig,(this->_ready >= 2));
|
||||
}
|
||||
} catch (std::runtime_error &e) {
|
||||
fprintf(stderr, "RABBITMQ ERROR member change: %s\n", e.what());
|
||||
break;
|
||||
} catch(std::exception &e ) {
|
||||
fprintf(stderr, "RABBITMQ ERROR member change: %s\n", e.what());
|
||||
} catch(...) {
|
||||
fprintf(stderr, "RABBITMQ ERROR member change: unknown error\n");
|
||||
} catch (...) {
|
||||
fprintf(stderr, "json parse error in networkWatcher_Redis\n");
|
||||
}
|
||||
}
|
||||
if (_rc->clusterMode) {
|
||||
_cluster->xdel(key, id);
|
||||
} else {
|
||||
_redis->xdel(key, id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (sw::redis::Error &e) {
|
||||
fprintf(stderr, "Error in Redis members watcher: %s\n", e.what());
|
||||
}
|
||||
}
|
||||
fprintf(stderr, "membersWatcher ended\n");
|
||||
}
|
||||
|
||||
void PostgreSQL::networksDbWatcher()
|
||||
|
@ -744,12 +900,10 @@ void PostgreSQL::networksDbWatcher()
|
|||
exit(1);
|
||||
}
|
||||
|
||||
initializeNetworks(conn);
|
||||
|
||||
if (this->_mqc != NULL) {
|
||||
if (_rc) {
|
||||
PQfinish(conn);
|
||||
conn = NULL;
|
||||
_networksWatcher_RabbitMQ();
|
||||
_networksWatcher_Redis();
|
||||
} else {
|
||||
_networksWatcher_Postgres(conn);
|
||||
PQfinish(conn);
|
||||
|
@ -802,24 +956,38 @@ void PostgreSQL::_networksWatcher_Postgres(PGconn *conn) {
|
|||
}
|
||||
}
|
||||
|
||||
void PostgreSQL::_networksWatcher_RabbitMQ() {
|
||||
void PostgreSQL::_networksWatcher_Redis() {
|
||||
char buf[11] = {0};
|
||||
std::string qname = "network_"+ std::string(_myAddress.toString(buf));
|
||||
RabbitMQ rmq(_mqc, qname.c_str());
|
||||
try {
|
||||
rmq.init();
|
||||
} catch (std::runtime_error &e) {
|
||||
fprintf(stderr, "RABBITMQ ERROR: %s\n", e.what());
|
||||
exit(11);
|
||||
}
|
||||
std::string key = "network-stream:{" + std::string(_myAddress.toString(buf)) + "}";
|
||||
|
||||
while (_run == 1) {
|
||||
try {
|
||||
std::string msg = rmq.consume();
|
||||
if (msg.empty()) {
|
||||
continue;
|
||||
json tmp;
|
||||
std::unordered_map<std::string, ItemStream> result;
|
||||
if (_rc->clusterMode) {
|
||||
_cluster->xread(key, "$", std::chrono::seconds(1), 0, std::inserter(result, result.end()));
|
||||
} else {
|
||||
_redis->xread(key, "$", std::chrono::seconds(1), 0, std::inserter(result, result.end()));
|
||||
}
|
||||
// fprintf(stderr, "Got network update: %s\n", msg.c_str());
|
||||
json tmp(json::parse(msg));
|
||||
|
||||
if (!result.empty()) {
|
||||
for (auto element : result) {
|
||||
#ifdef ZT_TRACE
|
||||
fprintf(stdout, "Received notification from: %s\n", element.first.c_str());
|
||||
#endif
|
||||
for (auto rec : element.second) {
|
||||
std::string id = rec.first;
|
||||
auto attrs = rec.second;
|
||||
#ifdef ZT_TRACE
|
||||
fprintf(stdout, "Record ID: %s\n", id.c_str());
|
||||
fprintf(stdout, "attrs len: %lu\n", attrs.size());
|
||||
#endif
|
||||
for (auto a : attrs) {
|
||||
#ifdef ZT_TRACE
|
||||
fprintf(stdout, "key: %s\nvalue: %s\n", a.first.c_str(), a.second.c_str());
|
||||
#endif
|
||||
try {
|
||||
tmp = json::parse(a.second);
|
||||
json &ov = tmp["old_val"];
|
||||
json &nv = tmp["new_val"];
|
||||
json oldConfig, newConfig;
|
||||
|
@ -828,15 +996,23 @@ void PostgreSQL::_networksWatcher_RabbitMQ() {
|
|||
if (oldConfig.is_object()||newConfig.is_object()) {
|
||||
_networkChanged(oldConfig,newConfig,(this->_ready >= 2));
|
||||
}
|
||||
} catch (std::runtime_error &e) {
|
||||
fprintf(stderr, "RABBITMQ ERROR: %s\n", e.what());
|
||||
break;
|
||||
} catch (std::exception &e) {
|
||||
fprintf(stderr, "RABBITMQ ERROR network watcher: %s\n", e.what());
|
||||
} catch(...) {
|
||||
fprintf(stderr, "RABBITMQ ERROR network watcher: unknown error\n");
|
||||
} catch (...) {
|
||||
fprintf(stderr, "json parse error in networkWatcher_Redis\n");
|
||||
}
|
||||
}
|
||||
if (_rc->clusterMode) {
|
||||
_cluster->xdel(key, id);
|
||||
} else {
|
||||
_redis->xdel(key, id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (sw::redis::Error &e) {
|
||||
fprintf(stderr, "Error in Redis networks watcher: %s\n", e.what());
|
||||
}
|
||||
}
|
||||
fprintf(stderr, "networksWatcher ended\n");
|
||||
}
|
||||
|
||||
void PostgreSQL::commitThread()
|
||||
|
@ -872,12 +1048,12 @@ void PostgreSQL::commitThread()
|
|||
target = (*config)["remoteTraceTarget"];
|
||||
}
|
||||
|
||||
std::string caps = DB::jsonDump((*config)["capabilities"], -1);
|
||||
std::string caps = jsonDump((*config)["capabilities"], -1);
|
||||
std::string lastAuthTime = std::to_string((long long)(*config)["lastAuthorizedTime"]);
|
||||
std::string lastDeauthTime = std::to_string((long long)(*config)["lastDeauthorizedTime"]);
|
||||
std::string rtraceLevel = std::to_string((int)(*config)["remoteTraceLevel"]);
|
||||
std::string rev = std::to_string((unsigned long long)(*config)["revision"]);
|
||||
std::string tags = DB::jsonDump((*config)["tags"], -1);
|
||||
std::string tags = jsonDump((*config)["tags"], -1);
|
||||
std::string vmajor = std::to_string((int)(*config)["vMajor"]);
|
||||
std::string vminor = std::to_string((int)(*config)["vMinor"]);
|
||||
std::string vrev = std::to_string((int)(*config)["vRev"]);
|
||||
|
@ -924,7 +1100,7 @@ void PostgreSQL::commitThread()
|
|||
|
||||
if (PQresultStatus(res) != PGRES_COMMAND_OK) {
|
||||
fprintf(stderr, "ERROR: Error updating member: %s\n", PQresultErrorMessage(res));
|
||||
fprintf(stderr, "%s", DB::jsonDump(*config, 2).c_str());
|
||||
fprintf(stderr, "%s", jsonDump(*config, 2).c_str());
|
||||
PQclear(res);
|
||||
delete config;
|
||||
config = nullptr;
|
||||
|
@ -984,7 +1160,7 @@ void PostgreSQL::commitThread()
|
|||
};
|
||||
|
||||
res = PQexecParams(conn,
|
||||
"INSERT INTO ztc_member_ip_assignment (member_id, network_id, address) VALUES ($1, $2, $3)",
|
||||
"INSERT INTO ztc_member_ip_assignment (member_id, network_id, address) VALUES ($1, $2, $3) ON CONFLICT (network_id, member_id, address) DO NOTHING",
|
||||
3,
|
||||
NULL,
|
||||
v3,
|
||||
|
@ -998,6 +1174,7 @@ void PostgreSQL::commitThread()
|
|||
PQclear(PQexec(conn, "ROLLBACK"));
|
||||
break;;
|
||||
}
|
||||
assignments.push_back(addr);
|
||||
}
|
||||
|
||||
res = PQexec(conn, "COMMIT");
|
||||
|
@ -1007,8 +1184,8 @@ void PostgreSQL::commitThread()
|
|||
|
||||
PQclear(res);
|
||||
|
||||
const uint64_t nwidInt = DB::jsonIntHex((*config)["nwid"], 0ULL);
|
||||
const uint64_t memberidInt = DB::jsonIntHex((*config)["id"], 0ULL);
|
||||
const uint64_t nwidInt = jsonIntHex((*config)["nwid"], 0ULL);
|
||||
const uint64_t memberidInt = jsonIntHex((*config)["id"], 0ULL);
|
||||
if (nwidInt && memberidInt) {
|
||||
nlohmann::json nwOrig;
|
||||
nlohmann::json memOrig;
|
||||
|
@ -1038,15 +1215,15 @@ void PostgreSQL::commitThread()
|
|||
if ((*config)["rulesSource"].is_string()) {
|
||||
rulesSource = (*config)["rulesSource"];
|
||||
}
|
||||
std::string caps = DB::jsonDump((*config)["capabilitles"], -1);
|
||||
std::string caps = jsonDump((*config)["capabilitles"], -1);
|
||||
std::string now = std::to_string(OSUtils::now());
|
||||
std::string mtu = std::to_string((int)(*config)["mtu"]);
|
||||
std::string mcastLimit = std::to_string((int)(*config)["multicastLimit"]);
|
||||
std::string rtraceLevel = std::to_string((int)(*config)["remoteTraceLevel"]);
|
||||
std::string rules = DB::jsonDump((*config)["rules"], -1);
|
||||
std::string tags = DB::jsonDump((*config)["tags"], -1);
|
||||
std::string v4mode = DB::jsonDump((*config)["v4AssignMode"],-1);
|
||||
std::string v6mode = DB::jsonDump((*config)["v6AssignMode"], -1);
|
||||
std::string rules = jsonDump((*config)["rules"], -1);
|
||||
std::string tags = jsonDump((*config)["tags"], -1);
|
||||
std::string v4mode = jsonDump((*config)["v4AssignMode"],-1);
|
||||
std::string v6mode = jsonDump((*config)["v6AssignMode"], -1);
|
||||
bool enableBroadcast = (*config)["enableBroadcast"];
|
||||
bool isPrivate = (*config)["private"];
|
||||
|
||||
|
@ -1253,7 +1430,7 @@ void PostgreSQL::commitThread()
|
|||
}
|
||||
PQclear(res);
|
||||
|
||||
const uint64_t nwidInt = DB::jsonIntHex((*config)["nwid"], 0ULL);
|
||||
const uint64_t nwidInt = jsonIntHex((*config)["nwid"], 0ULL);
|
||||
if (nwidInt) {
|
||||
nlohmann::json nwOrig;
|
||||
nlohmann::json nwNew(*config);
|
||||
|
@ -1268,6 +1445,20 @@ void PostgreSQL::commitThread()
|
|||
} catch (std::exception &e) {
|
||||
fprintf(stderr, "ERROR: Error updating member: %s\n", e.what());
|
||||
}
|
||||
if (_rc != NULL) {
|
||||
try {
|
||||
std::string id = (*config)["id"];
|
||||
std::string controllerId = _myAddressStr.c_str();
|
||||
std::string key = "networks:{" + controllerId + "}";
|
||||
if (_rc->clusterMode) {
|
||||
_cluster->sadd(key, id);
|
||||
} else {
|
||||
_redis->sadd(key, id);
|
||||
}
|
||||
} catch (sw::redis::Error &e) {
|
||||
fprintf(stderr, "ERROR: Error adding network to Redis: %s\n", e.what());
|
||||
}
|
||||
}
|
||||
} else if (objtype == "_delete_network") {
|
||||
try {
|
||||
std::string networkId = (*config)["nwid"];
|
||||
|
@ -1291,6 +1482,22 @@ void PostgreSQL::commitThread()
|
|||
} catch (std::exception &e) {
|
||||
fprintf(stderr, "ERROR: Error deleting network: %s\n", e.what());
|
||||
}
|
||||
if (_rc != NULL) {
|
||||
try {
|
||||
std::string id = (*config)["id"];
|
||||
std::string controllerId = _myAddressStr.c_str();
|
||||
std::string key = "networks:{" + controllerId + "}";
|
||||
if (_rc->clusterMode) {
|
||||
_cluster->srem(key, id);
|
||||
_cluster->del("network-nodes-online:{"+controllerId+"}:"+id);
|
||||
} else {
|
||||
_redis->srem(key, id);
|
||||
_redis->del("network-nodes-online:{"+controllerId+"}:"+id);
|
||||
}
|
||||
} catch (sw::redis::Error &e) {
|
||||
fprintf(stderr, "ERROR: Error adding network to Redis: %s\n", e.what());
|
||||
}
|
||||
}
|
||||
} else if (objtype == "_delete_member") {
|
||||
try {
|
||||
std::string memberId = (*config)["id"];
|
||||
|
@ -1318,6 +1525,23 @@ void PostgreSQL::commitThread()
|
|||
} catch (std::exception &e) {
|
||||
fprintf(stderr, "ERROR: Error deleting member: %s\n", e.what());
|
||||
}
|
||||
if (_rc != NULL) {
|
||||
try {
|
||||
std::string memberId = (*config)["id"];
|
||||
std::string networkId = (*config)["nwid"];
|
||||
std::string controllerId = _myAddressStr.c_str();
|
||||
std::string key = "network-nodes-all:{" + controllerId + "}:" + networkId;
|
||||
if (_rc->clusterMode) {
|
||||
_cluster->srem(key, memberId);
|
||||
_cluster->del("member:{"+controllerId+"}:"+networkId+":"+memberId);
|
||||
} else {
|
||||
_redis->srem(key, memberId);
|
||||
_redis->del("member:{"+controllerId+"}:"+networkId+":"+memberId);
|
||||
}
|
||||
} catch (sw::redis::Error &e) {
|
||||
fprintf(stderr, "ERROR: Error deleting member from Redis: %s\n", e.what());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
fprintf(stderr, "ERROR: unknown objtype");
|
||||
}
|
||||
|
@ -1333,9 +1557,21 @@ void PostgreSQL::commitThread()
|
|||
fprintf(stderr, "ERROR: %s commitThread should still be running! Exiting Controller.\n", _myAddressStr.c_str());
|
||||
exit(7);
|
||||
}
|
||||
fprintf(stderr, "commitThread finished\n");
|
||||
}
|
||||
|
||||
void PostgreSQL::onlineNotificationThread()
|
||||
{
|
||||
waitForReady();
|
||||
|
||||
if (_rc != NULL) {
|
||||
onlineNotification_Redis();
|
||||
} else {
|
||||
onlineNotification_Postgres();
|
||||
}
|
||||
}
|
||||
|
||||
void PostgreSQL::onlineNotification_Postgres()
|
||||
{
|
||||
PGconn *conn = getPgConn();
|
||||
if (PQstatus(conn) == CONNECTION_BAD) {
|
||||
|
@ -1345,9 +1581,7 @@ void PostgreSQL::onlineNotificationThread()
|
|||
}
|
||||
_connected = 1;
|
||||
|
||||
//int64_t lastUpdatedNetworkStatus = 0;
|
||||
std::unordered_map< std::pair<uint64_t,uint64_t>,int64_t,_PairHasher > lastOnlineCumulative;
|
||||
|
||||
nlohmann::json jtmp1, jtmp2;
|
||||
while (_run == 1) {
|
||||
if (PQstatus(conn) != CONNECTION_OK) {
|
||||
fprintf(stderr, "ERROR: Online Notification thread lost connection to Postgres.");
|
||||
|
@ -1355,9 +1589,6 @@ void PostgreSQL::onlineNotificationThread()
|
|||
exit(5);
|
||||
}
|
||||
|
||||
// map used to send notifications to front end
|
||||
std::unordered_map<std::string, std::vector<std::string>> updateMap;
|
||||
|
||||
std::unordered_map< std::pair<uint64_t,uint64_t>,std::pair<int64_t,InetAddress>,_PairHasher > lastOnline;
|
||||
{
|
||||
std::lock_guard<std::mutex> l(_lastOnline_l);
|
||||
|
@ -1378,20 +1609,13 @@ void PostgreSQL::onlineNotificationThread()
|
|||
OSUtils::ztsnprintf(nwidTmp,sizeof(nwidTmp), "%.16llx", nwid_i);
|
||||
OSUtils::ztsnprintf(memTmp,sizeof(memTmp), "%.10llx", i->first.second);
|
||||
|
||||
auto found = _networks.find(nwid_i);
|
||||
if (found == _networks.end()) {
|
||||
continue; // skip members trying to join non-existant networks
|
||||
if(!get(nwid_i, jtmp1, i->first.second, jtmp2)) {
|
||||
continue; // skip non existent networks/members
|
||||
}
|
||||
|
||||
std::string networkId(nwidTmp);
|
||||
std::string memberId(memTmp);
|
||||
|
||||
std::vector<std::string> &members = updateMap[networkId];
|
||||
members.push_back(memberId);
|
||||
|
||||
lastOnlineCumulative[i->first] = i->second.first;
|
||||
|
||||
|
||||
const char *qvals[2] = {
|
||||
networkId.c_str(),
|
||||
memberId.c_str()
|
||||
|
@ -1451,7 +1675,7 @@ void PostgreSQL::onlineNotificationThread()
|
|||
PQclear(res);
|
||||
}
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
}
|
||||
fprintf(stderr, "%s: Fell out of run loop in onlineNotificationThread\n", _myAddressStr.c_str());
|
||||
PQfinish(conn);
|
||||
|
@ -1461,6 +1685,95 @@ void PostgreSQL::onlineNotificationThread()
|
|||
}
|
||||
}
|
||||
|
||||
void PostgreSQL::onlineNotification_Redis()
|
||||
{
|
||||
_connected = 1;
|
||||
|
||||
char buf[11] = {0};
|
||||
std::string controllerId = std::string(_myAddress.toString(buf));
|
||||
|
||||
while (_run == 1) {
|
||||
std::unordered_map< std::pair<uint64_t,uint64_t>,std::pair<int64_t,InetAddress>,_PairHasher > lastOnline;
|
||||
{
|
||||
std::lock_guard<std::mutex> l(_lastOnline_l);
|
||||
lastOnline.swap(_lastOnline);
|
||||
}
|
||||
try {
|
||||
if (!lastOnline.empty()) {
|
||||
if (_rc->clusterMode) {
|
||||
auto tx = _cluster->transaction(controllerId, true);
|
||||
_doRedisUpdate(tx, controllerId, lastOnline);
|
||||
} else {
|
||||
auto tx = _redis->transaction(true);
|
||||
_doRedisUpdate(tx, controllerId, lastOnline);
|
||||
}
|
||||
}
|
||||
} catch (sw::redis::Error &e) {
|
||||
#ifdef ZT_TRACE
|
||||
fprintf(stderr, "Error in online notification thread (redis): %s\n", e.what());
|
||||
#endif
|
||||
}
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
}
|
||||
}
|
||||
|
||||
void PostgreSQL::_doRedisUpdate(sw::redis::Transaction &tx, std::string &controllerId,
|
||||
std::unordered_map< std::pair<uint64_t,uint64_t>,std::pair<int64_t,InetAddress>,_PairHasher > &lastOnline)
|
||||
|
||||
{
|
||||
nlohmann::json jtmp1, jtmp2;
|
||||
for (auto i=lastOnline.begin(); i != lastOnline.end(); ++i) {
|
||||
uint64_t nwid_i = i->first.first;
|
||||
uint64_t memberid_i = i->first.second;
|
||||
char nwidTmp[64];
|
||||
char memTmp[64];
|
||||
char ipTmp[64];
|
||||
OSUtils::ztsnprintf(nwidTmp,sizeof(nwidTmp), "%.16llx", nwid_i);
|
||||
OSUtils::ztsnprintf(memTmp,sizeof(memTmp), "%.10llx", memberid_i);
|
||||
|
||||
if (!get(nwid_i, jtmp1, memberid_i, jtmp2)){
|
||||
continue; // skip non existent members/networks
|
||||
}
|
||||
|
||||
std::string networkId(nwidTmp);
|
||||
std::string memberId(memTmp);
|
||||
|
||||
int64_t ts = i->second.first;
|
||||
std::string ipAddr = i->second.second.toIpString(ipTmp);
|
||||
std::string timestamp = std::to_string(ts);
|
||||
|
||||
std::unordered_map<std::string, std::string> record = {
|
||||
{"id", memberId},
|
||||
{"address", ipAddr},
|
||||
{"last_updated", std::to_string(ts)}
|
||||
};
|
||||
tx.zadd("nodes-online:{"+controllerId+"}", memberId, ts)
|
||||
.zadd("nodes-online2:{"+controllerId+"}", networkId+"-"+memberId, ts)
|
||||
.zadd("network-nodes-online:{"+controllerId+"}:"+networkId, memberId, ts)
|
||||
.zadd("active-networks:{"+controllerId+"}", networkId, ts)
|
||||
.sadd("network-nodes-all:{"+controllerId+"}:"+networkId, memberId)
|
||||
.hmset("member:{"+controllerId+"}:"+networkId+":"+memberId, record.begin(), record.end());
|
||||
}
|
||||
|
||||
// expire records from all-nodes and network-nodes member list
|
||||
uint64_t expireOld = OSUtils::now() - 300000;
|
||||
|
||||
tx.zremrangebyscore("nodes-online:{"+controllerId+"}", sw::redis::RightBoundedInterval<double>(expireOld, sw::redis::BoundType::LEFT_OPEN));
|
||||
tx.zremrangebyscore("nodes-online2:{"+controllerId+"}", sw::redis::RightBoundedInterval<double>(expireOld, sw::redis::BoundType::LEFT_OPEN));
|
||||
tx.zremrangebyscore("active-networks:{"+controllerId+"}", sw::redis::RightBoundedInterval<double>(expireOld, sw::redis::BoundType::LEFT_OPEN));
|
||||
{
|
||||
std::lock_guard<std::mutex> l(_networks_l);
|
||||
for (const auto &it : _networks) {
|
||||
uint64_t nwid_i = it.first;
|
||||
char nwidTmp[64];
|
||||
OSUtils::ztsnprintf(nwidTmp,sizeof(nwidTmp), "%.16llx", nwid_i);
|
||||
tx.zremrangebyscore("network-nodes-online:{"+controllerId+"}:"+nwidTmp,
|
||||
sw::redis::RightBoundedInterval<double>(expireOld, sw::redis::BoundType::LEFT_OPEN));
|
||||
}
|
||||
}
|
||||
tx.exec();
|
||||
}
|
||||
|
||||
PGconn *PostgreSQL::getPgConn(OverrideMode m)
|
||||
{
|
||||
if (m == ALLOW_PGBOUNCER_OVERRIDE) {
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
/*
|
||||
* Copyright (c)2013-2020 ZeroTier, Inc.
|
||||
* Copyright (c)2019 ZeroTier, Inc.
|
||||
*
|
||||
* Use of this software is governed by the Business Source License included
|
||||
* in the LICENSE.TXT file in the project's root directory.
|
||||
*
|
||||
* Change Date: 2024-01-01
|
||||
* Change Date: 2023-01-01
|
||||
*
|
||||
* On the date above, in accordance with the Business Source License, use
|
||||
* of this software will be governed by version 2.0 of the Apache License.
|
||||
|
@ -20,13 +20,16 @@
|
|||
|
||||
#define ZT_CENTRAL_CONTROLLER_COMMIT_THREADS 4
|
||||
|
||||
#include <memory>
|
||||
#include <redis++/redis++.h>
|
||||
|
||||
extern "C" {
|
||||
typedef struct pg_conn PGconn;
|
||||
}
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
struct MQConfig;
|
||||
struct RedisConfig;
|
||||
|
||||
/**
|
||||
* A controller database driver that talks to PostgreSQL
|
||||
|
@ -37,7 +40,7 @@ struct MQConfig;
|
|||
class PostgreSQL : public DB
|
||||
{
|
||||
public:
|
||||
PostgreSQL(const Identity &myId, const char *path, int listenPort, MQConfig *mqc = NULL);
|
||||
PostgreSQL(const Identity &myId, const char *path, int listenPort, RedisConfig *rc);
|
||||
virtual ~PostgreSQL();
|
||||
|
||||
virtual bool waitForReady();
|
||||
|
@ -59,13 +62,18 @@ private:
|
|||
void heartbeat();
|
||||
void membersDbWatcher();
|
||||
void _membersWatcher_Postgres(PGconn *conn);
|
||||
void _membersWatcher_RabbitMQ();
|
||||
void networksDbWatcher();
|
||||
void _networksWatcher_Postgres(PGconn *conn);
|
||||
void _networksWatcher_RabbitMQ();
|
||||
|
||||
void _membersWatcher_Redis();
|
||||
void _networksWatcher_Redis();
|
||||
|
||||
void commitThread();
|
||||
void onlineNotificationThread();
|
||||
void onlineNotification_Postgres();
|
||||
void onlineNotification_Redis();
|
||||
void _doRedisUpdate(sw::redis::Transaction &tx, std::string &controllerId,
|
||||
std::unordered_map< std::pair<uint64_t,uint64_t>,std::pair<int64_t,InetAddress>,_PairHasher > &lastOnline);
|
||||
|
||||
enum OverrideMode {
|
||||
ALLOW_PGBOUNCER_OVERRIDE = 0,
|
||||
|
@ -96,7 +104,9 @@ private:
|
|||
|
||||
int _listenPort;
|
||||
|
||||
MQConfig *_mqc;
|
||||
RedisConfig *_rc;
|
||||
std::shared_ptr<sw::redis::Redis> _redis;
|
||||
std::shared_ptr<sw::redis::RedisCluster> _cluster;
|
||||
};
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
|
|
@ -1,120 +0,0 @@
|
|||
/*
|
||||
* Copyright (c)2013-2020 ZeroTier, Inc.
|
||||
*
|
||||
* Use of this software is governed by the Business Source License included
|
||||
* in the LICENSE.TXT file in the project's root directory.
|
||||
*
|
||||
* Change Date: 2024-01-01
|
||||
*
|
||||
* On the date above, in accordance with the Business Source License, use
|
||||
* of this software will be governed by version 2.0 of the Apache License.
|
||||
*/
|
||||
/****/
|
||||
|
||||
#include "RabbitMQ.hpp"
|
||||
|
||||
#ifdef ZT_CONTROLLER_USE_LIBPQ
|
||||
|
||||
#include <amqp.h>
|
||||
#include <amqp_tcp_socket.h>
|
||||
#include <stdexcept>
|
||||
#include <cstring>
|
||||
|
||||
namespace ZeroTier
|
||||
{
|
||||
|
||||
RabbitMQ::RabbitMQ(MQConfig *cfg, const char *queueName)
|
||||
: _mqc(cfg)
|
||||
, _qName(queueName)
|
||||
, _socket(NULL)
|
||||
, _status(0)
|
||||
{
|
||||
}
|
||||
|
||||
RabbitMQ::~RabbitMQ()
|
||||
{
|
||||
amqp_channel_close(_conn, _channel, AMQP_REPLY_SUCCESS);
|
||||
amqp_connection_close(_conn, AMQP_REPLY_SUCCESS);
|
||||
amqp_destroy_connection(_conn);
|
||||
}
|
||||
|
||||
void RabbitMQ::init()
|
||||
{
|
||||
struct timeval tval;
|
||||
memset(&tval, 0, sizeof(struct timeval));
|
||||
tval.tv_sec = 5;
|
||||
|
||||
fprintf(stderr, "Initializing RabbitMQ %s\n", _qName);
|
||||
_conn = amqp_new_connection();
|
||||
_socket = amqp_tcp_socket_new(_conn);
|
||||
if (!_socket) {
|
||||
throw std::runtime_error("Can't create socket for RabbitMQ");
|
||||
}
|
||||
|
||||
_status = amqp_socket_open_noblock(_socket, _mqc->host.c_str(), _mqc->port, &tval);
|
||||
if (_status) {
|
||||
throw std::runtime_error("Can't connect to RabbitMQ");
|
||||
}
|
||||
|
||||
amqp_rpc_reply_t r = amqp_login(_conn, "/", 0, 131072, 0, AMQP_SASL_METHOD_PLAIN,
|
||||
_mqc->username.c_str(), _mqc->password.c_str());
|
||||
if (r.reply_type != AMQP_RESPONSE_NORMAL) {
|
||||
throw std::runtime_error("RabbitMQ Login Error");
|
||||
}
|
||||
|
||||
static int chan = 0;
|
||||
{
|
||||
Mutex::Lock l(_chan_m);
|
||||
_channel = ++chan;
|
||||
}
|
||||
amqp_channel_open(_conn, _channel);
|
||||
r = amqp_get_rpc_reply(_conn);
|
||||
if(r.reply_type != AMQP_RESPONSE_NORMAL) {
|
||||
throw std::runtime_error("Error opening communication channel");
|
||||
}
|
||||
|
||||
_q = amqp_queue_declare(_conn, _channel, amqp_cstring_bytes(_qName), 0, 0, 0, 0, amqp_empty_table);
|
||||
r = amqp_get_rpc_reply(_conn);
|
||||
if (r.reply_type != AMQP_RESPONSE_NORMAL) {
|
||||
throw std::runtime_error("Error declaring queue " + std::string(_qName));
|
||||
}
|
||||
|
||||
amqp_basic_consume(_conn, _channel, amqp_cstring_bytes(_qName), amqp_empty_bytes, 0, 1, 0, amqp_empty_table);
|
||||
r = amqp_get_rpc_reply(_conn);
|
||||
if (r.reply_type != AMQP_RESPONSE_NORMAL) {
|
||||
throw std::runtime_error("Error consuming queue " + std::string(_qName));
|
||||
}
|
||||
fprintf(stderr, "RabbitMQ Init OK %s\n", _qName);
|
||||
}
|
||||
|
||||
std::string RabbitMQ::consume()
|
||||
{
|
||||
amqp_rpc_reply_t res;
|
||||
amqp_envelope_t envelope;
|
||||
amqp_maybe_release_buffers(_conn);
|
||||
|
||||
struct timeval timeout;
|
||||
timeout.tv_sec = 1;
|
||||
timeout.tv_usec = 0;
|
||||
|
||||
res = amqp_consume_message(_conn, &envelope, &timeout, 0);
|
||||
if (res.reply_type != AMQP_RESPONSE_NORMAL) {
|
||||
if (res.reply_type == AMQP_RESPONSE_LIBRARY_EXCEPTION && res.library_error == AMQP_STATUS_TIMEOUT) {
|
||||
// timeout waiting for message. Return empty string
|
||||
return "";
|
||||
} else {
|
||||
throw std::runtime_error("Error getting message");
|
||||
}
|
||||
}
|
||||
|
||||
std::string msg(
|
||||
(const char*)envelope.message.body.bytes,
|
||||
envelope.message.body.len
|
||||
);
|
||||
amqp_destroy_envelope(&envelope);
|
||||
return msg;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // ZT_CONTROLLER_USE_LIBPQ
|
|
@ -1,69 +0,0 @@
|
|||
/*
|
||||
* Copyright (c)2013-2020 ZeroTier, Inc.
|
||||
*
|
||||
* Use of this software is governed by the Business Source License included
|
||||
* in the LICENSE.TXT file in the project's root directory.
|
||||
*
|
||||
* Change Date: 2024-01-01
|
||||
*
|
||||
* On the date above, in accordance with the Business Source License, use
|
||||
* of this software will be governed by version 2.0 of the Apache License.
|
||||
*/
|
||||
/****/
|
||||
|
||||
#ifndef ZT_CONTROLLER_RABBITMQ_HPP
|
||||
#define ZT_CONTROLLER_RABBITMQ_HPP
|
||||
|
||||
#include "DB.hpp"
|
||||
#include <string>
|
||||
|
||||
namespace ZeroTier
|
||||
{
|
||||
struct MQConfig {
|
||||
std::string host;
|
||||
int port;
|
||||
std::string username;
|
||||
std::string password;
|
||||
};
|
||||
}
|
||||
|
||||
#ifdef ZT_CONTROLLER_USE_LIBPQ
|
||||
|
||||
#include "../core/Mutex.hpp"
|
||||
|
||||
#include <amqp.h>
|
||||
#include <amqp_tcp_socket.h>
|
||||
|
||||
|
||||
namespace ZeroTier
|
||||
{
|
||||
|
||||
class RabbitMQ {
|
||||
public:
|
||||
RabbitMQ(MQConfig *cfg, const char *queueName);
|
||||
~RabbitMQ();
|
||||
|
||||
void init();
|
||||
|
||||
std::string consume();
|
||||
|
||||
private:
|
||||
MQConfig *_mqc;
|
||||
const char *_qName;
|
||||
|
||||
amqp_socket_t *_socket;
|
||||
amqp_connection_state_t _conn;
|
||||
amqp_queue_declare_ok_t *_q;
|
||||
int _status;
|
||||
|
||||
int _channel;
|
||||
|
||||
Mutex _chan_m;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // ZT_CONTROLLER_USE_LIBPQ
|
||||
|
||||
#endif // ZT_CONTROLLER_RABBITMQ_HPP
|
||||
|
15
controller/Redis.hpp
Normal file
15
controller/Redis.hpp
Normal file
|
@ -0,0 +1,15 @@
|
|||
#ifndef ZT_CONTROLLER_REDIS_HPP
|
||||
#define ZT_CONTROLLER_REDIS_HPP
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace ZeroTier {
|
||||
struct RedisConfig {
|
||||
std::string hostname;
|
||||
int port;
|
||||
std::string password;
|
||||
bool clusterMode;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
32
controller/thirdparty/redis-plus-plus-1.1.1/.gitignore
vendored
Normal file
32
controller/thirdparty/redis-plus-plus-1.1.1/.gitignore
vendored
Normal file
|
@ -0,0 +1,32 @@
|
|||
# Prerequisites
|
||||
*.d
|
||||
|
||||
# Compiled Object files
|
||||
*.slo
|
||||
*.lo
|
||||
*.o
|
||||
*.obj
|
||||
|
||||
# Precompiled Headers
|
||||
*.gch
|
||||
*.pch
|
||||
|
||||
# Compiled Dynamic libraries
|
||||
*.so
|
||||
*.dylib
|
||||
*.dll
|
||||
|
||||
# Fortran module files
|
||||
*.mod
|
||||
*.smod
|
||||
|
||||
# Compiled Static libraries
|
||||
*.lai
|
||||
*.la
|
||||
*.a
|
||||
*.lib
|
||||
|
||||
# Executables
|
||||
*.exe
|
||||
*.out
|
||||
*.app
|
48
controller/thirdparty/redis-plus-plus-1.1.1/CMakeLists.txt
vendored
Normal file
48
controller/thirdparty/redis-plus-plus-1.1.1/CMakeLists.txt
vendored
Normal file
|
@ -0,0 +1,48 @@
|
|||
project(redis++)
|
||||
|
||||
if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
|
||||
cmake_minimum_required(VERSION 3.0.0)
|
||||
else()
|
||||
cmake_minimum_required(VERSION 2.8.0)
|
||||
endif()
|
||||
|
||||
set(CMAKE_CXX_FLAGS "-std=c++11 -Wall -W -Werror -fPIC")
|
||||
|
||||
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
|
||||
|
||||
set(PROJECT_SOURCE_DIR ${PROJECT_SOURCE_DIR}/src/sw/redis++)
|
||||
|
||||
file(GLOB PROJECT_SOURCE_FILES "${PROJECT_SOURCE_DIR}/*.cpp")
|
||||
|
||||
set(STATIC_LIB static)
|
||||
set(SHARED_LIB shared)
|
||||
|
||||
add_library(${STATIC_LIB} STATIC ${PROJECT_SOURCE_FILES})
|
||||
add_library(${SHARED_LIB} SHARED ${PROJECT_SOURCE_FILES})
|
||||
|
||||
# hiredis dependency
|
||||
find_path(HIREDIS_HEADER hiredis)
|
||||
target_include_directories(${STATIC_LIB} PUBLIC ${HIREDIS_HEADER})
|
||||
target_include_directories(${SHARED_LIB} PUBLIC ${HIREDIS_HEADER})
|
||||
|
||||
find_library(HIREDIS_LIB hiredis)
|
||||
target_link_libraries(${SHARED_LIB} ${HIREDIS_LIB})
|
||||
|
||||
set_target_properties(${STATIC_LIB} PROPERTIES OUTPUT_NAME ${PROJECT_NAME})
|
||||
set_target_properties(${SHARED_LIB} PROPERTIES OUTPUT_NAME ${PROJECT_NAME})
|
||||
|
||||
set_target_properties(${STATIC_LIB} PROPERTIES CLEAN_DIRECT_OUTPUT 1)
|
||||
set_target_properties(${SHARED_LIB} PROPERTIES CLEAN_DIRECT_OUTPUT 1)
|
||||
|
||||
# Install static lib.
|
||||
install(TARGETS ${STATIC_LIB}
|
||||
ARCHIVE DESTINATION lib)
|
||||
|
||||
# Install shared lib.
|
||||
install(TARGETS ${SHARED_LIB}
|
||||
LIBRARY DESTINATION lib)
|
||||
|
||||
#Install headers.
|
||||
set(HEADER_PATH "sw/redis++")
|
||||
file(GLOB HEADERS "${PROJECT_SOURCE_DIR}/*.h*")
|
||||
install(FILES ${HEADERS} DESTINATION ${CMAKE_INSTALL_PREFIX}/include/${HEADER_PATH})
|
201
controller/thirdparty/redis-plus-plus-1.1.1/LICENSE
vendored
Normal file
201
controller/thirdparty/redis-plus-plus-1.1.1/LICENSE
vendored
Normal file
|
@ -0,0 +1,201 @@
|
|||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
1776
controller/thirdparty/redis-plus-plus-1.1.1/README.md
vendored
Normal file
1776
controller/thirdparty/redis-plus-plus-1.1.1/README.md
vendored
Normal file
File diff suppressed because it is too large
Load diff
376
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/command.cpp
vendored
Normal file
376
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/command.cpp
vendored
Normal file
|
@ -0,0 +1,376 @@
|
|||
/**************************************************************************
|
||||
Copyright (c) 2017 sewenew
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*************************************************************************/
|
||||
|
||||
#include "command.h"
|
||||
#include <cassert>
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
namespace cmd {
|
||||
|
||||
// KEY commands.
|
||||
|
||||
void restore(Connection &connection,
|
||||
const StringView &key,
|
||||
const StringView &val,
|
||||
long long ttl,
|
||||
bool replace) {
|
||||
CmdArgs args;
|
||||
args << "RESTORE" << key << ttl << val;
|
||||
|
||||
if (replace) {
|
||||
args << "REPLACE";
|
||||
}
|
||||
|
||||
connection.send(args);
|
||||
}
|
||||
|
||||
// STRING commands.
|
||||
|
||||
void bitop(Connection &connection,
|
||||
BitOp op,
|
||||
const StringView &destination,
|
||||
const StringView &key) {
|
||||
CmdArgs args;
|
||||
|
||||
detail::set_bitop(args, op);
|
||||
|
||||
args << destination << key;
|
||||
|
||||
connection.send(args);
|
||||
}
|
||||
|
||||
void set(Connection &connection,
|
||||
const StringView &key,
|
||||
const StringView &val,
|
||||
long long ttl,
|
||||
UpdateType type) {
|
||||
CmdArgs args;
|
||||
args << "SET" << key << val;
|
||||
|
||||
if (ttl > 0) {
|
||||
args << "PX" << ttl;
|
||||
}
|
||||
|
||||
detail::set_update_type(args, type);
|
||||
|
||||
connection.send(args);
|
||||
}
|
||||
|
||||
// LIST commands.
|
||||
|
||||
void linsert(Connection &connection,
|
||||
const StringView &key,
|
||||
InsertPosition position,
|
||||
const StringView &pivot,
|
||||
const StringView &val) {
|
||||
std::string pos;
|
||||
switch (position) {
|
||||
case InsertPosition::BEFORE:
|
||||
pos = "BEFORE";
|
||||
break;
|
||||
|
||||
case InsertPosition::AFTER:
|
||||
pos = "AFTER";
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
|
||||
connection.send("LINSERT %b %s %b %b",
|
||||
key.data(), key.size(),
|
||||
pos.c_str(),
|
||||
pivot.data(), pivot.size(),
|
||||
val.data(), val.size());
|
||||
}
|
||||
|
||||
// GEO commands.
|
||||
|
||||
void geodist(Connection &connection,
|
||||
const StringView &key,
|
||||
const StringView &member1,
|
||||
const StringView &member2,
|
||||
GeoUnit unit) {
|
||||
CmdArgs args;
|
||||
args << "GEODIST" << key << member1 << member2;
|
||||
|
||||
detail::set_geo_unit(args, unit);
|
||||
|
||||
connection.send(args);
|
||||
}
|
||||
|
||||
void georadius_store(Connection &connection,
|
||||
const StringView &key,
|
||||
const std::pair<double, double> &loc,
|
||||
double radius,
|
||||
GeoUnit unit,
|
||||
const StringView &destination,
|
||||
bool store_dist,
|
||||
long long count) {
|
||||
CmdArgs args;
|
||||
args << "GEORADIUS" << key << loc.first << loc.second;
|
||||
|
||||
detail::set_georadius_store_parameters(args,
|
||||
radius,
|
||||
unit,
|
||||
destination,
|
||||
store_dist,
|
||||
count);
|
||||
|
||||
connection.send(args);
|
||||
}
|
||||
|
||||
void georadius(Connection &connection,
|
||||
const StringView &key,
|
||||
const std::pair<double, double> &loc,
|
||||
double radius,
|
||||
GeoUnit unit,
|
||||
long long count,
|
||||
bool asc,
|
||||
bool with_coord,
|
||||
bool with_dist,
|
||||
bool with_hash) {
|
||||
CmdArgs args;
|
||||
args << "GEORADIUS" << key << loc.first << loc.second;
|
||||
|
||||
detail::set_georadius_parameters(args,
|
||||
radius,
|
||||
unit,
|
||||
count,
|
||||
asc,
|
||||
with_coord,
|
||||
with_dist,
|
||||
with_hash);
|
||||
|
||||
connection.send(args);
|
||||
}
|
||||
|
||||
void georadiusbymember(Connection &connection,
|
||||
const StringView &key,
|
||||
const StringView &member,
|
||||
double radius,
|
||||
GeoUnit unit,
|
||||
long long count,
|
||||
bool asc,
|
||||
bool with_coord,
|
||||
bool with_dist,
|
||||
bool with_hash) {
|
||||
CmdArgs args;
|
||||
args << "GEORADIUSBYMEMBER" << key << member;
|
||||
|
||||
detail::set_georadius_parameters(args,
|
||||
radius,
|
||||
unit,
|
||||
count,
|
||||
asc,
|
||||
with_coord,
|
||||
with_dist,
|
||||
with_hash);
|
||||
|
||||
connection.send(args);
|
||||
}
|
||||
|
||||
void georadiusbymember_store(Connection &connection,
|
||||
const StringView &key,
|
||||
const StringView &member,
|
||||
double radius,
|
||||
GeoUnit unit,
|
||||
const StringView &destination,
|
||||
bool store_dist,
|
||||
long long count) {
|
||||
CmdArgs args;
|
||||
args << "GEORADIUSBYMEMBER" << key << member;
|
||||
|
||||
detail::set_georadius_store_parameters(args,
|
||||
radius,
|
||||
unit,
|
||||
destination,
|
||||
store_dist,
|
||||
count);
|
||||
|
||||
connection.send(args);
|
||||
}
|
||||
|
||||
// Stream commands.
|
||||
|
||||
void xtrim(Connection &connection, const StringView &key, long long count, bool approx) {
|
||||
CmdArgs args;
|
||||
args << "XTRIM" << key << "MAXLEN";
|
||||
|
||||
if (approx) {
|
||||
args << "~";
|
||||
}
|
||||
|
||||
args << count;
|
||||
|
||||
connection.send(args);
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
|
||||
void set_bitop(CmdArgs &args, BitOp op) {
|
||||
args << "BITOP";
|
||||
|
||||
switch (op) {
|
||||
case BitOp::AND:
|
||||
args << "AND";
|
||||
break;
|
||||
|
||||
case BitOp::OR:
|
||||
args << "OR";
|
||||
break;
|
||||
|
||||
case BitOp::XOR:
|
||||
args << "XOR";
|
||||
break;
|
||||
|
||||
case BitOp::NOT:
|
||||
args << "NOT";
|
||||
break;
|
||||
|
||||
default:
|
||||
throw Error("Unknown bit operations");
|
||||
}
|
||||
}
|
||||
|
||||
void set_update_type(CmdArgs &args, UpdateType type) {
|
||||
switch (type) {
|
||||
case UpdateType::EXIST:
|
||||
args << "XX";
|
||||
break;
|
||||
|
||||
case UpdateType::NOT_EXIST:
|
||||
args << "NX";
|
||||
break;
|
||||
|
||||
case UpdateType::ALWAYS:
|
||||
// Do nothing.
|
||||
break;
|
||||
|
||||
default:
|
||||
throw Error("Unknown update type");
|
||||
}
|
||||
}
|
||||
|
||||
void set_aggregation_type(CmdArgs &args, Aggregation aggr) {
|
||||
args << "AGGREGATE";
|
||||
|
||||
switch (aggr) {
|
||||
case Aggregation::SUM:
|
||||
args << "SUM";
|
||||
break;
|
||||
|
||||
case Aggregation::MIN:
|
||||
args << "MIN";
|
||||
break;
|
||||
|
||||
case Aggregation::MAX:
|
||||
args << "MAX";
|
||||
break;
|
||||
|
||||
default:
|
||||
throw Error("Unknown aggregation type");
|
||||
}
|
||||
}
|
||||
|
||||
void set_geo_unit(CmdArgs &args, GeoUnit unit) {
|
||||
switch (unit) {
|
||||
case GeoUnit::M:
|
||||
args << "m";
|
||||
break;
|
||||
|
||||
case GeoUnit::KM:
|
||||
args << "km";
|
||||
break;
|
||||
|
||||
case GeoUnit::MI:
|
||||
args << "mi";
|
||||
break;
|
||||
|
||||
case GeoUnit::FT:
|
||||
args << "ft";
|
||||
break;
|
||||
|
||||
default:
|
||||
throw Error("Unknown geo unit type");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void set_georadius_store_parameters(CmdArgs &args,
|
||||
double radius,
|
||||
GeoUnit unit,
|
||||
const StringView &destination,
|
||||
bool store_dist,
|
||||
long long count) {
|
||||
args << radius;
|
||||
|
||||
detail::set_geo_unit(args, unit);
|
||||
|
||||
args << "COUNT" << count;
|
||||
|
||||
if (store_dist) {
|
||||
args << "STOREDIST";
|
||||
} else {
|
||||
args << "STORE";
|
||||
}
|
||||
|
||||
args << destination;
|
||||
}
|
||||
|
||||
void set_georadius_parameters(CmdArgs &args,
|
||||
double radius,
|
||||
GeoUnit unit,
|
||||
long long count,
|
||||
bool asc,
|
||||
bool with_coord,
|
||||
bool with_dist,
|
||||
bool with_hash) {
|
||||
args << radius;
|
||||
|
||||
detail::set_geo_unit(args, unit);
|
||||
|
||||
if (with_coord) {
|
||||
args << "WITHCOORD";
|
||||
}
|
||||
|
||||
if (with_dist) {
|
||||
args << "WITHDIST";
|
||||
}
|
||||
|
||||
if (with_hash) {
|
||||
args << "WITHHASH";
|
||||
}
|
||||
|
||||
args << "COUNT" << count;
|
||||
|
||||
if (asc) {
|
||||
args << "ASC";
|
||||
} else {
|
||||
args << "DESC";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
2233
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/command.h
vendored
Normal file
2233
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/command.h
vendored
Normal file
File diff suppressed because it is too large
Load diff
180
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/command_args.h
vendored
Normal file
180
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/command_args.h
vendored
Normal file
|
@ -0,0 +1,180 @@
|
|||
/**************************************************************************
|
||||
Copyright (c) 2017 sewenew
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*************************************************************************/
|
||||
|
||||
#ifndef SEWENEW_REDISPLUSPLUS_COMMAND_ARGS_H
|
||||
#define SEWENEW_REDISPLUSPLUS_COMMAND_ARGS_H
|
||||
|
||||
#include <vector>
|
||||
#include <list>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include "utils.h"
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
class CmdArgs {
|
||||
public:
|
||||
template <typename Arg>
|
||||
CmdArgs& append(Arg &&arg);
|
||||
|
||||
template <typename Arg, typename ...Args>
|
||||
CmdArgs& append(Arg &&arg, Args &&...args);
|
||||
|
||||
// All overloads of operator<< are for internal use only.
|
||||
CmdArgs& operator<<(const StringView &arg);
|
||||
|
||||
template <typename T,
|
||||
typename std::enable_if<std::is_arithmetic<typename std::decay<T>::type>::value,
|
||||
int>::type = 0>
|
||||
CmdArgs& operator<<(T &&arg);
|
||||
|
||||
template <typename Iter>
|
||||
CmdArgs& operator<<(const std::pair<Iter, Iter> &range);
|
||||
|
||||
template <std::size_t N, typename ...Args>
|
||||
auto operator<<(const std::tuple<Args...> &) ->
|
||||
typename std::enable_if<N == sizeof...(Args), CmdArgs&>::type {
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <std::size_t N = 0, typename ...Args>
|
||||
auto operator<<(const std::tuple<Args...> &arg) ->
|
||||
typename std::enable_if<N < sizeof...(Args), CmdArgs&>::type;
|
||||
|
||||
const char** argv() {
|
||||
return _argv.data();
|
||||
}
|
||||
|
||||
const std::size_t* argv_len() {
|
||||
return _argv_len.data();
|
||||
}
|
||||
|
||||
std::size_t size() const {
|
||||
return _argv.size();
|
||||
}
|
||||
|
||||
private:
|
||||
// Deep copy.
|
||||
CmdArgs& _append(std::string arg);
|
||||
|
||||
// Shallow copy.
|
||||
CmdArgs& _append(const StringView &arg);
|
||||
|
||||
// Shallow copy.
|
||||
CmdArgs& _append(const char *arg);
|
||||
|
||||
template <typename T,
|
||||
typename std::enable_if<std::is_arithmetic<typename std::decay<T>::type>::value,
|
||||
int>::type = 0>
|
||||
CmdArgs& _append(T &&arg) {
|
||||
return operator<<(std::forward<T>(arg));
|
||||
}
|
||||
|
||||
template <typename Iter>
|
||||
CmdArgs& _append(std::true_type, const std::pair<Iter, Iter> &range);
|
||||
|
||||
template <typename Iter>
|
||||
CmdArgs& _append(std::false_type, const std::pair<Iter, Iter> &range);
|
||||
|
||||
std::vector<const char *> _argv;
|
||||
std::vector<std::size_t> _argv_len;
|
||||
|
||||
std::list<std::string> _args;
|
||||
};
|
||||
|
||||
template <typename Arg>
|
||||
inline CmdArgs& CmdArgs::append(Arg &&arg) {
|
||||
return _append(std::forward<Arg>(arg));
|
||||
}
|
||||
|
||||
template <typename Arg, typename ...Args>
|
||||
inline CmdArgs& CmdArgs::append(Arg &&arg, Args &&...args) {
|
||||
_append(std::forward<Arg>(arg));
|
||||
|
||||
return append(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
inline CmdArgs& CmdArgs::operator<<(const StringView &arg) {
|
||||
_argv.push_back(arg.data());
|
||||
_argv_len.push_back(arg.size());
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename Iter>
|
||||
inline CmdArgs& CmdArgs::operator<<(const std::pair<Iter, Iter> &range) {
|
||||
return _append(IsKvPair<typename std::decay<decltype(*std::declval<Iter>())>::type>(), range);
|
||||
}
|
||||
|
||||
template <typename T,
|
||||
typename std::enable_if<std::is_arithmetic<typename std::decay<T>::type>::value,
|
||||
int>::type>
|
||||
inline CmdArgs& CmdArgs::operator<<(T &&arg) {
|
||||
return _append(std::to_string(std::forward<T>(arg)));
|
||||
}
|
||||
|
||||
template <std::size_t N, typename ...Args>
|
||||
auto CmdArgs::operator<<(const std::tuple<Args...> &arg) ->
|
||||
typename std::enable_if<N < sizeof...(Args), CmdArgs&>::type {
|
||||
operator<<(std::get<N>(arg));
|
||||
|
||||
return operator<<<N + 1, Args...>(arg);
|
||||
}
|
||||
|
||||
inline CmdArgs& CmdArgs::_append(std::string arg) {
|
||||
_args.push_back(std::move(arg));
|
||||
return operator<<(_args.back());
|
||||
}
|
||||
|
||||
inline CmdArgs& CmdArgs::_append(const StringView &arg) {
|
||||
return operator<<(arg);
|
||||
}
|
||||
|
||||
inline CmdArgs& CmdArgs::_append(const char *arg) {
|
||||
return operator<<(arg);
|
||||
}
|
||||
|
||||
template <typename Iter>
|
||||
CmdArgs& CmdArgs::_append(std::false_type, const std::pair<Iter, Iter> &range) {
|
||||
auto first = range.first;
|
||||
auto last = range.second;
|
||||
while (first != last) {
|
||||
*this << *first;
|
||||
++first;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename Iter>
|
||||
CmdArgs& CmdArgs::_append(std::true_type, const std::pair<Iter, Iter> &range) {
|
||||
auto first = range.first;
|
||||
auto last = range.second;
|
||||
while (first != last) {
|
||||
*this << first->first << first->second;
|
||||
++first;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // end SEWENEW_REDISPLUSPLUS_COMMAND_ARGS_H
|
201
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/command_options.cpp
vendored
Normal file
201
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/command_options.cpp
vendored
Normal file
|
@ -0,0 +1,201 @@
|
|||
/**************************************************************************
|
||||
Copyright (c) 2017 sewenew
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*************************************************************************/
|
||||
|
||||
#include "command_options.h"
|
||||
#include "errors.h"
|
||||
|
||||
namespace {
|
||||
|
||||
const std::string NEGATIVE_INFINITY_NUMERIC = "-inf";
|
||||
const std::string POSITIVE_INFINITY_NUMERIC = "+inf";
|
||||
|
||||
const std::string NEGATIVE_INFINITY_STRING = "-";
|
||||
const std::string POSITIVE_INFINITY_STRING = "+";
|
||||
|
||||
std::string unbound(const std::string &bnd);
|
||||
|
||||
std::string bound(const std::string &bnd);
|
||||
|
||||
}
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
const std::string& UnboundedInterval<double>::min() const {
|
||||
return NEGATIVE_INFINITY_NUMERIC;
|
||||
}
|
||||
|
||||
const std::string& UnboundedInterval<double>::max() const {
|
||||
return POSITIVE_INFINITY_NUMERIC;
|
||||
}
|
||||
|
||||
BoundedInterval<double>::BoundedInterval(double min, double max, BoundType type) :
|
||||
_min(std::to_string(min)),
|
||||
_max(std::to_string(max)) {
|
||||
switch (type) {
|
||||
case BoundType::CLOSED:
|
||||
// Do nothing
|
||||
break;
|
||||
|
||||
case BoundType::OPEN:
|
||||
_min = unbound(_min);
|
||||
_max = unbound(_max);
|
||||
break;
|
||||
|
||||
case BoundType::LEFT_OPEN:
|
||||
_min = unbound(_min);
|
||||
break;
|
||||
|
||||
case BoundType::RIGHT_OPEN:
|
||||
_max = unbound(_max);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw Error("Unknow BoundType");
|
||||
}
|
||||
}
|
||||
|
||||
LeftBoundedInterval<double>::LeftBoundedInterval(double min, BoundType type) :
|
||||
_min(std::to_string(min)) {
|
||||
switch (type) {
|
||||
case BoundType::OPEN:
|
||||
_min = unbound(_min);
|
||||
break;
|
||||
|
||||
case BoundType::RIGHT_OPEN:
|
||||
// Do nothing.
|
||||
break;
|
||||
|
||||
default:
|
||||
throw Error("Bound type can only be OPEN or RIGHT_OPEN");
|
||||
}
|
||||
}
|
||||
|
||||
const std::string& LeftBoundedInterval<double>::max() const {
|
||||
return POSITIVE_INFINITY_NUMERIC;
|
||||
}
|
||||
|
||||
RightBoundedInterval<double>::RightBoundedInterval(double max, BoundType type) :
|
||||
_max(std::to_string(max)) {
|
||||
switch (type) {
|
||||
case BoundType::OPEN:
|
||||
_max = unbound(_max);
|
||||
break;
|
||||
|
||||
case BoundType::LEFT_OPEN:
|
||||
// Do nothing.
|
||||
break;
|
||||
|
||||
default:
|
||||
throw Error("Bound type can only be OPEN or LEFT_OPEN");
|
||||
}
|
||||
}
|
||||
|
||||
const std::string& RightBoundedInterval<double>::min() const {
|
||||
return NEGATIVE_INFINITY_NUMERIC;
|
||||
}
|
||||
|
||||
const std::string& UnboundedInterval<std::string>::min() const {
|
||||
return NEGATIVE_INFINITY_STRING;
|
||||
}
|
||||
|
||||
const std::string& UnboundedInterval<std::string>::max() const {
|
||||
return POSITIVE_INFINITY_STRING;
|
||||
}
|
||||
|
||||
BoundedInterval<std::string>::BoundedInterval(const std::string &min,
|
||||
const std::string &max,
|
||||
BoundType type) {
|
||||
switch (type) {
|
||||
case BoundType::CLOSED:
|
||||
_min = bound(min);
|
||||
_max = bound(max);
|
||||
break;
|
||||
|
||||
case BoundType::OPEN:
|
||||
_min = unbound(min);
|
||||
_max = unbound(max);
|
||||
break;
|
||||
|
||||
case BoundType::LEFT_OPEN:
|
||||
_min = unbound(min);
|
||||
_max = bound(max);
|
||||
break;
|
||||
|
||||
case BoundType::RIGHT_OPEN:
|
||||
_min = bound(min);
|
||||
_max = unbound(max);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw Error("Unknow BoundType");
|
||||
}
|
||||
}
|
||||
|
||||
LeftBoundedInterval<std::string>::LeftBoundedInterval(const std::string &min, BoundType type) {
|
||||
switch (type) {
|
||||
case BoundType::OPEN:
|
||||
_min = unbound(min);
|
||||
break;
|
||||
|
||||
case BoundType::RIGHT_OPEN:
|
||||
_min = bound(min);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw Error("Bound type can only be OPEN or RIGHT_OPEN");
|
||||
}
|
||||
}
|
||||
|
||||
const std::string& LeftBoundedInterval<std::string>::max() const {
|
||||
return POSITIVE_INFINITY_STRING;
|
||||
}
|
||||
|
||||
RightBoundedInterval<std::string>::RightBoundedInterval(const std::string &max, BoundType type) {
|
||||
switch (type) {
|
||||
case BoundType::OPEN:
|
||||
_max = unbound(max);
|
||||
break;
|
||||
|
||||
case BoundType::LEFT_OPEN:
|
||||
_max = bound(max);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw Error("Bound type can only be OPEN or LEFT_OPEN");
|
||||
}
|
||||
}
|
||||
|
||||
const std::string& RightBoundedInterval<std::string>::min() const {
|
||||
return NEGATIVE_INFINITY_STRING;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
std::string unbound(const std::string &bnd) {
|
||||
return "(" + bnd;
|
||||
}
|
||||
|
||||
std::string bound(const std::string &bnd) {
|
||||
return "[" + bnd;
|
||||
}
|
||||
|
||||
}
|
211
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/command_options.h
vendored
Normal file
211
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/command_options.h
vendored
Normal file
|
@ -0,0 +1,211 @@
|
|||
/**************************************************************************
|
||||
Copyright (c) 2017 sewenew
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*************************************************************************/
|
||||
|
||||
#ifndef SEWENEW_REDISPLUSPLUS_COMMAND_OPTIONS_H
|
||||
#define SEWENEW_REDISPLUSPLUS_COMMAND_OPTIONS_H
|
||||
|
||||
#include <string>
|
||||
#include "utils.h"
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
enum class UpdateType {
|
||||
EXIST,
|
||||
NOT_EXIST,
|
||||
ALWAYS
|
||||
};
|
||||
|
||||
enum class InsertPosition {
|
||||
BEFORE,
|
||||
AFTER
|
||||
};
|
||||
|
||||
enum class BoundType {
|
||||
CLOSED,
|
||||
OPEN,
|
||||
LEFT_OPEN,
|
||||
RIGHT_OPEN
|
||||
};
|
||||
|
||||
// (-inf, +inf)
|
||||
template <typename T>
|
||||
class UnboundedInterval;
|
||||
|
||||
// [min, max], (min, max), (min, max], [min, max)
|
||||
template <typename T>
|
||||
class BoundedInterval;
|
||||
|
||||
// [min, +inf), (min, +inf)
|
||||
template <typename T>
|
||||
class LeftBoundedInterval;
|
||||
|
||||
// (-inf, max], (-inf, max)
|
||||
template <typename T>
|
||||
class RightBoundedInterval;
|
||||
|
||||
template <>
|
||||
class UnboundedInterval<double> {
|
||||
public:
|
||||
const std::string& min() const;
|
||||
|
||||
const std::string& max() const;
|
||||
};
|
||||
|
||||
template <>
|
||||
class BoundedInterval<double> {
|
||||
public:
|
||||
BoundedInterval(double min, double max, BoundType type);
|
||||
|
||||
const std::string& min() const {
|
||||
return _min;
|
||||
}
|
||||
|
||||
const std::string& max() const {
|
||||
return _max;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string _min;
|
||||
std::string _max;
|
||||
};
|
||||
|
||||
template <>
|
||||
class LeftBoundedInterval<double> {
|
||||
public:
|
||||
LeftBoundedInterval(double min, BoundType type);
|
||||
|
||||
const std::string& min() const {
|
||||
return _min;
|
||||
}
|
||||
|
||||
const std::string& max() const;
|
||||
|
||||
private:
|
||||
std::string _min;
|
||||
};
|
||||
|
||||
template <>
|
||||
class RightBoundedInterval<double> {
|
||||
public:
|
||||
RightBoundedInterval(double max, BoundType type);
|
||||
|
||||
const std::string& min() const;
|
||||
|
||||
const std::string& max() const {
|
||||
return _max;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string _max;
|
||||
};
|
||||
|
||||
template <>
|
||||
class UnboundedInterval<std::string> {
|
||||
public:
|
||||
const std::string& min() const;
|
||||
|
||||
const std::string& max() const;
|
||||
};
|
||||
|
||||
template <>
|
||||
class BoundedInterval<std::string> {
|
||||
public:
|
||||
BoundedInterval(const std::string &min, const std::string &max, BoundType type);
|
||||
|
||||
const std::string& min() const {
|
||||
return _min;
|
||||
}
|
||||
|
||||
const std::string& max() const {
|
||||
return _max;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string _min;
|
||||
std::string _max;
|
||||
};
|
||||
|
||||
template <>
|
||||
class LeftBoundedInterval<std::string> {
|
||||
public:
|
||||
LeftBoundedInterval(const std::string &min, BoundType type);
|
||||
|
||||
const std::string& min() const {
|
||||
return _min;
|
||||
}
|
||||
|
||||
const std::string& max() const;
|
||||
|
||||
private:
|
||||
std::string _min;
|
||||
};
|
||||
|
||||
template <>
|
||||
class RightBoundedInterval<std::string> {
|
||||
public:
|
||||
RightBoundedInterval(const std::string &max, BoundType type);
|
||||
|
||||
const std::string& min() const;
|
||||
|
||||
const std::string& max() const {
|
||||
return _max;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string _max;
|
||||
};
|
||||
|
||||
struct LimitOptions {
|
||||
long long offset = 0;
|
||||
long long count = -1;
|
||||
};
|
||||
|
||||
enum class Aggregation {
|
||||
SUM,
|
||||
MIN,
|
||||
MAX
|
||||
};
|
||||
|
||||
enum class BitOp {
|
||||
AND,
|
||||
OR,
|
||||
XOR,
|
||||
NOT
|
||||
};
|
||||
|
||||
enum class GeoUnit {
|
||||
M,
|
||||
KM,
|
||||
MI,
|
||||
FT
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct WithCoord : TupleWithType<std::pair<double, double>, T> {};
|
||||
|
||||
template <typename T>
|
||||
struct WithDist : TupleWithType<double, T> {};
|
||||
|
||||
template <typename T>
|
||||
struct WithHash : TupleWithType<long long, T> {};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // end SEWENEW_REDISPLUSPLUS_COMMAND_OPTIONS_H
|
305
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/connection.cpp
vendored
Normal file
305
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/connection.cpp
vendored
Normal file
|
@ -0,0 +1,305 @@
|
|||
/**************************************************************************
|
||||
Copyright (c) 2017 sewenew
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*************************************************************************/
|
||||
|
||||
#include "connection.h"
|
||||
#include <cassert>
|
||||
#include "reply.h"
|
||||
#include "command.h"
|
||||
#include "command_args.h"
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
ConnectionOptions::ConnectionOptions(const std::string &uri) :
|
||||
ConnectionOptions(_parse_options(uri)) {}
|
||||
|
||||
ConnectionOptions ConnectionOptions::_parse_options(const std::string &uri) const {
|
||||
std::string type;
|
||||
std::string path;
|
||||
std::tie(type, path) = _split_string(uri, "://");
|
||||
|
||||
if (path.empty()) {
|
||||
throw Error("Invalid URI: no path");
|
||||
}
|
||||
|
||||
if (type == "tcp") {
|
||||
return _parse_tcp_options(path);
|
||||
} else if (type == "unix") {
|
||||
return _parse_unix_options(path);
|
||||
} else {
|
||||
throw Error("Invalid URI: invalid type");
|
||||
}
|
||||
}
|
||||
|
||||
ConnectionOptions ConnectionOptions::_parse_tcp_options(const std::string &path) const {
|
||||
ConnectionOptions options;
|
||||
|
||||
options.type = ConnectionType::TCP;
|
||||
|
||||
std::string host;
|
||||
std::string port;
|
||||
std::tie(host, port) = _split_string(path, ":");
|
||||
|
||||
options.host = host;
|
||||
try {
|
||||
if (!port.empty()) {
|
||||
options.port = std::stoi(port);
|
||||
} // else use default port, i.e. 6379.
|
||||
} catch (const std::exception &) {
|
||||
throw Error("Invalid URL: invalid port");
|
||||
}
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
ConnectionOptions ConnectionOptions::_parse_unix_options(const std::string &path) const {
|
||||
ConnectionOptions options;
|
||||
|
||||
options.type = ConnectionType::UNIX;
|
||||
options.path = path;
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
auto ConnectionOptions::_split_string(const std::string &str, const std::string &delimiter) const ->
|
||||
std::pair<std::string, std::string> {
|
||||
auto pos = str.rfind(delimiter);
|
||||
if (pos == std::string::npos) {
|
||||
return {str, ""};
|
||||
}
|
||||
|
||||
return {str.substr(0, pos), str.substr(pos + delimiter.size())};
|
||||
}
|
||||
|
||||
class Connection::Connector {
|
||||
public:
|
||||
explicit Connector(const ConnectionOptions &opts);
|
||||
|
||||
ContextUPtr connect() const;
|
||||
|
||||
private:
|
||||
ContextUPtr _connect() const;
|
||||
|
||||
redisContext* _connect_tcp() const;
|
||||
|
||||
redisContext* _connect_unix() const;
|
||||
|
||||
void _set_socket_timeout(redisContext &ctx) const;
|
||||
|
||||
void _enable_keep_alive(redisContext &ctx) const;
|
||||
|
||||
timeval _to_timeval(const std::chrono::milliseconds &dur) const;
|
||||
|
||||
const ConnectionOptions &_opts;
|
||||
};
|
||||
|
||||
Connection::Connector::Connector(const ConnectionOptions &opts) : _opts(opts) {}
|
||||
|
||||
Connection::ContextUPtr Connection::Connector::connect() const {
|
||||
auto ctx = _connect();
|
||||
|
||||
assert(ctx);
|
||||
|
||||
if (ctx->err != REDIS_OK) {
|
||||
throw_error(*ctx, "Failed to connect to Redis");
|
||||
}
|
||||
|
||||
_set_socket_timeout(*ctx);
|
||||
|
||||
_enable_keep_alive(*ctx);
|
||||
|
||||
return ctx;
|
||||
}
|
||||
|
||||
Connection::ContextUPtr Connection::Connector::_connect() const {
|
||||
redisContext *context = nullptr;
|
||||
switch (_opts.type) {
|
||||
case ConnectionType::TCP:
|
||||
context = _connect_tcp();
|
||||
break;
|
||||
|
||||
case ConnectionType::UNIX:
|
||||
context = _connect_unix();
|
||||
break;
|
||||
|
||||
default:
|
||||
// Never goes here.
|
||||
throw Error("Unkonw connection type");
|
||||
}
|
||||
|
||||
if (context == nullptr) {
|
||||
throw Error("Failed to allocate memory for connection.");
|
||||
}
|
||||
|
||||
return ContextUPtr(context);
|
||||
}
|
||||
|
||||
redisContext* Connection::Connector::_connect_tcp() const {
|
||||
if (_opts.connect_timeout > std::chrono::milliseconds(0)) {
|
||||
return redisConnectWithTimeout(_opts.host.c_str(),
|
||||
_opts.port,
|
||||
_to_timeval(_opts.connect_timeout));
|
||||
} else {
|
||||
return redisConnect(_opts.host.c_str(), _opts.port);
|
||||
}
|
||||
}
|
||||
|
||||
redisContext* Connection::Connector::_connect_unix() const {
|
||||
if (_opts.connect_timeout > std::chrono::milliseconds(0)) {
|
||||
return redisConnectUnixWithTimeout(
|
||||
_opts.path.c_str(),
|
||||
_to_timeval(_opts.connect_timeout));
|
||||
} else {
|
||||
return redisConnectUnix(_opts.path.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void Connection::Connector::_set_socket_timeout(redisContext &ctx) const {
|
||||
if (_opts.socket_timeout <= std::chrono::milliseconds(0)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (redisSetTimeout(&ctx, _to_timeval(_opts.socket_timeout)) != REDIS_OK) {
|
||||
throw_error(ctx, "Failed to set socket timeout");
|
||||
}
|
||||
}
|
||||
|
||||
void Connection::Connector::_enable_keep_alive(redisContext &ctx) const {
|
||||
if (!_opts.keep_alive) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (redisEnableKeepAlive(&ctx) != REDIS_OK) {
|
||||
throw_error(ctx, "Failed to enable keep alive option");
|
||||
}
|
||||
}
|
||||
|
||||
timeval Connection::Connector::_to_timeval(const std::chrono::milliseconds &dur) const {
|
||||
auto sec = std::chrono::duration_cast<std::chrono::seconds>(dur);
|
||||
auto msec = std::chrono::duration_cast<std::chrono::microseconds>(dur - sec);
|
||||
|
||||
return {
|
||||
static_cast<std::time_t>(sec.count()),
|
||||
static_cast<suseconds_t>(msec.count())
|
||||
};
|
||||
}
|
||||
|
||||
void swap(Connection &lhs, Connection &rhs) noexcept {
|
||||
std::swap(lhs._ctx, rhs._ctx);
|
||||
std::swap(lhs._last_active, rhs._last_active);
|
||||
std::swap(lhs._opts, rhs._opts);
|
||||
}
|
||||
|
||||
Connection::Connection(const ConnectionOptions &opts) :
|
||||
_ctx(Connector(opts).connect()),
|
||||
_last_active(std::chrono::steady_clock::now()),
|
||||
_opts(opts) {
|
||||
assert(_ctx && !broken());
|
||||
|
||||
_set_options();
|
||||
}
|
||||
|
||||
void Connection::reconnect() {
|
||||
Connection connection(_opts);
|
||||
|
||||
swap(*this, connection);
|
||||
}
|
||||
|
||||
void Connection::send(int argc, const char **argv, const std::size_t *argv_len) {
|
||||
auto ctx = _context();
|
||||
|
||||
assert(ctx != nullptr);
|
||||
|
||||
if (redisAppendCommandArgv(ctx,
|
||||
argc,
|
||||
argv,
|
||||
argv_len) != REDIS_OK) {
|
||||
throw_error(*ctx, "Failed to send command");
|
||||
}
|
||||
|
||||
assert(!broken());
|
||||
}
|
||||
|
||||
void Connection::send(CmdArgs &args) {
|
||||
auto ctx = _context();
|
||||
|
||||
assert(ctx != nullptr);
|
||||
|
||||
if (redisAppendCommandArgv(ctx,
|
||||
args.size(),
|
||||
args.argv(),
|
||||
args.argv_len()) != REDIS_OK) {
|
||||
throw_error(*ctx, "Failed to send command");
|
||||
}
|
||||
|
||||
assert(!broken());
|
||||
}
|
||||
|
||||
ReplyUPtr Connection::recv() {
|
||||
auto *ctx = _context();
|
||||
|
||||
assert(ctx != nullptr);
|
||||
|
||||
void *r = nullptr;
|
||||
if (redisGetReply(ctx, &r) != REDIS_OK) {
|
||||
throw_error(*ctx, "Failed to get reply");
|
||||
}
|
||||
|
||||
assert(!broken() && r != nullptr);
|
||||
|
||||
auto reply = ReplyUPtr(static_cast<redisReply*>(r));
|
||||
|
||||
if (reply::is_error(*reply)) {
|
||||
throw_error(*reply);
|
||||
}
|
||||
|
||||
return reply;
|
||||
}
|
||||
|
||||
void Connection::_set_options() {
|
||||
_auth();
|
||||
|
||||
_select_db();
|
||||
}
|
||||
|
||||
void Connection::_auth() {
|
||||
if (_opts.password.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
cmd::auth(*this, _opts.password);
|
||||
|
||||
auto reply = recv();
|
||||
|
||||
reply::parse<void>(*reply);
|
||||
}
|
||||
|
||||
void Connection::_select_db() {
|
||||
if (_opts.db == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
cmd::select(*this, _opts.db);
|
||||
|
||||
auto reply = recv();
|
||||
|
||||
reply::parse<void>(*reply);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
194
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/connection.h
vendored
Normal file
194
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/connection.h
vendored
Normal file
|
@ -0,0 +1,194 @@
|
|||
/**************************************************************************
|
||||
Copyright (c) 2017 sewenew
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*************************************************************************/
|
||||
|
||||
#ifndef SEWENEW_REDISPLUSPLUS_CONNECTION_H
|
||||
#define SEWENEW_REDISPLUSPLUS_CONNECTION_H
|
||||
|
||||
#include <cerrno>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <chrono>
|
||||
#include <hiredis/hiredis.h>
|
||||
#include "errors.h"
|
||||
#include "reply.h"
|
||||
#include "utils.h"
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
enum class ConnectionType {
|
||||
TCP = 0,
|
||||
UNIX
|
||||
};
|
||||
|
||||
struct ConnectionOptions {
|
||||
public:
|
||||
ConnectionOptions() = default;
|
||||
|
||||
explicit ConnectionOptions(const std::string &uri);
|
||||
|
||||
ConnectionOptions(const ConnectionOptions &) = default;
|
||||
ConnectionOptions& operator=(const ConnectionOptions &) = default;
|
||||
|
||||
ConnectionOptions(ConnectionOptions &&) = default;
|
||||
ConnectionOptions& operator=(ConnectionOptions &&) = default;
|
||||
|
||||
~ConnectionOptions() = default;
|
||||
|
||||
ConnectionType type = ConnectionType::TCP;
|
||||
|
||||
std::string host;
|
||||
|
||||
int port = 6379;
|
||||
|
||||
std::string path;
|
||||
|
||||
std::string password;
|
||||
|
||||
int db = 0;
|
||||
|
||||
bool keep_alive = false;
|
||||
|
||||
std::chrono::milliseconds connect_timeout{0};
|
||||
|
||||
std::chrono::milliseconds socket_timeout{0};
|
||||
|
||||
private:
|
||||
ConnectionOptions _parse_options(const std::string &uri) const;
|
||||
|
||||
ConnectionOptions _parse_tcp_options(const std::string &path) const;
|
||||
|
||||
ConnectionOptions _parse_unix_options(const std::string &path) const;
|
||||
|
||||
auto _split_string(const std::string &str, const std::string &delimiter) const ->
|
||||
std::pair<std::string, std::string>;
|
||||
};
|
||||
|
||||
class CmdArgs;
|
||||
|
||||
class Connection {
|
||||
public:
|
||||
explicit Connection(const ConnectionOptions &opts);
|
||||
|
||||
Connection(const Connection &) = delete;
|
||||
Connection& operator=(const Connection &) = delete;
|
||||
|
||||
Connection(Connection &&) = default;
|
||||
Connection& operator=(Connection &&) = default;
|
||||
|
||||
~Connection() = default;
|
||||
|
||||
// Check if the connection is broken. Client needs to do this check
|
||||
// before sending some command to the connection. If it's broken,
|
||||
// client needs to reconnect it.
|
||||
bool broken() const noexcept {
|
||||
return _ctx->err != REDIS_OK;
|
||||
}
|
||||
|
||||
void reset() noexcept {
|
||||
_ctx->err = 0;
|
||||
}
|
||||
|
||||
void reconnect();
|
||||
|
||||
auto last_active() const
|
||||
-> std::chrono::time_point<std::chrono::steady_clock> {
|
||||
return _last_active;
|
||||
}
|
||||
|
||||
template <typename ...Args>
|
||||
void send(const char *format, Args &&...args);
|
||||
|
||||
void send(int argc, const char **argv, const std::size_t *argv_len);
|
||||
|
||||
void send(CmdArgs &args);
|
||||
|
||||
ReplyUPtr recv();
|
||||
|
||||
const ConnectionOptions& options() const {
|
||||
return _opts;
|
||||
}
|
||||
|
||||
friend void swap(Connection &lhs, Connection &rhs) noexcept;
|
||||
|
||||
private:
|
||||
class Connector;
|
||||
|
||||
struct ContextDeleter {
|
||||
void operator()(redisContext *context) const {
|
||||
if (context != nullptr) {
|
||||
redisFree(context);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
using ContextUPtr = std::unique_ptr<redisContext, ContextDeleter>;
|
||||
|
||||
void _set_options();
|
||||
|
||||
void _auth();
|
||||
|
||||
void _select_db();
|
||||
|
||||
redisContext* _context();
|
||||
|
||||
ContextUPtr _ctx;
|
||||
|
||||
// The time that the connection is created or the time that
|
||||
// the connection is used, i.e. *context()* is called.
|
||||
std::chrono::time_point<std::chrono::steady_clock> _last_active{};
|
||||
|
||||
ConnectionOptions _opts;
|
||||
};
|
||||
|
||||
using ConnectionSPtr = std::shared_ptr<Connection>;
|
||||
|
||||
enum class Role {
|
||||
MASTER,
|
||||
SLAVE
|
||||
};
|
||||
|
||||
// Inline implementaions.
|
||||
|
||||
template <typename ...Args>
|
||||
inline void Connection::send(const char *format, Args &&...args) {
|
||||
auto ctx = _context();
|
||||
|
||||
assert(ctx != nullptr);
|
||||
|
||||
if (redisAppendCommand(ctx,
|
||||
format,
|
||||
std::forward<Args>(args)...) != REDIS_OK) {
|
||||
throw_error(*ctx, "Failed to send command");
|
||||
}
|
||||
|
||||
assert(!broken());
|
||||
}
|
||||
|
||||
inline redisContext* Connection::_context() {
|
||||
_last_active = std::chrono::steady_clock::now();
|
||||
|
||||
return _ctx.get();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // end SEWENEW_REDISPLUSPLUS_CONNECTION_H
|
249
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/connection_pool.cpp
vendored
Normal file
249
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/connection_pool.cpp
vendored
Normal file
|
@ -0,0 +1,249 @@
|
|||
/**************************************************************************
|
||||
Copyright (c) 2017 sewenew
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*************************************************************************/
|
||||
|
||||
#include "connection_pool.h"
|
||||
#include <cassert>
|
||||
#include "errors.h"
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
ConnectionPool::ConnectionPool(const ConnectionPoolOptions &pool_opts,
|
||||
const ConnectionOptions &connection_opts) :
|
||||
_opts(connection_opts),
|
||||
_pool_opts(pool_opts) {
|
||||
if (_pool_opts.size == 0) {
|
||||
throw Error("CANNOT create an empty pool");
|
||||
}
|
||||
|
||||
// Lazily create connections.
|
||||
}
|
||||
|
||||
ConnectionPool::ConnectionPool(SimpleSentinel sentinel,
|
||||
const ConnectionPoolOptions &pool_opts,
|
||||
const ConnectionOptions &connection_opts) :
|
||||
_opts(connection_opts),
|
||||
_pool_opts(pool_opts),
|
||||
_sentinel(std::move(sentinel)) {
|
||||
// In this case, the connection must be of TCP type.
|
||||
if (_opts.type != ConnectionType::TCP) {
|
||||
throw Error("Sentinel only supports TCP connection");
|
||||
}
|
||||
|
||||
if (_opts.connect_timeout == std::chrono::milliseconds(0)
|
||||
|| _opts.socket_timeout == std::chrono::milliseconds(0)) {
|
||||
throw Error("With sentinel, connection timeout and socket timeout cannot be 0");
|
||||
}
|
||||
|
||||
// Cleanup connection options.
|
||||
_update_connection_opts("", -1);
|
||||
|
||||
assert(_sentinel);
|
||||
}
|
||||
|
||||
ConnectionPool::ConnectionPool(ConnectionPool &&that) {
|
||||
std::lock_guard<std::mutex> lock(that._mutex);
|
||||
|
||||
_move(std::move(that));
|
||||
}
|
||||
|
||||
ConnectionPool& ConnectionPool::operator=(ConnectionPool &&that) {
|
||||
if (this != &that) {
|
||||
std::lock(_mutex, that._mutex);
|
||||
std::lock_guard<std::mutex> lock_this(_mutex, std::adopt_lock);
|
||||
std::lock_guard<std::mutex> lock_that(that._mutex, std::adopt_lock);
|
||||
|
||||
_move(std::move(that));
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
Connection ConnectionPool::fetch() {
|
||||
std::unique_lock<std::mutex> lock(_mutex);
|
||||
|
||||
if (_pool.empty()) {
|
||||
if (_used_connections == _pool_opts.size) {
|
||||
_wait_for_connection(lock);
|
||||
} else {
|
||||
// Lazily create a new connection.
|
||||
auto connection = _create();
|
||||
|
||||
++_used_connections;
|
||||
|
||||
return connection;
|
||||
}
|
||||
}
|
||||
|
||||
// _pool is NOT empty.
|
||||
auto connection = _fetch();
|
||||
|
||||
auto connection_lifetime = _pool_opts.connection_lifetime;
|
||||
|
||||
if (_sentinel) {
|
||||
auto opts = _opts;
|
||||
auto role_changed = _role_changed(connection.options());
|
||||
auto sentinel = _sentinel;
|
||||
|
||||
lock.unlock();
|
||||
|
||||
if (role_changed || _need_reconnect(connection, connection_lifetime)) {
|
||||
try {
|
||||
connection = _create(sentinel, opts, false);
|
||||
} catch (const Error &e) {
|
||||
// Failed to reconnect, return it to the pool, and retry latter.
|
||||
release(std::move(connection));
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
return connection;
|
||||
}
|
||||
|
||||
lock.unlock();
|
||||
|
||||
if (_need_reconnect(connection, connection_lifetime)) {
|
||||
try {
|
||||
connection.reconnect();
|
||||
} catch (const Error &e) {
|
||||
// Failed to reconnect, return it to the pool, and retry latter.
|
||||
release(std::move(connection));
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
return connection;
|
||||
}
|
||||
|
||||
ConnectionOptions ConnectionPool::connection_options() {
|
||||
std::lock_guard<std::mutex> lock(_mutex);
|
||||
|
||||
return _opts;
|
||||
}
|
||||
|
||||
void ConnectionPool::release(Connection connection) {
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_mutex);
|
||||
|
||||
_pool.push_back(std::move(connection));
|
||||
}
|
||||
|
||||
_cv.notify_one();
|
||||
}
|
||||
|
||||
Connection ConnectionPool::create() {
|
||||
std::unique_lock<std::mutex> lock(_mutex);
|
||||
|
||||
auto opts = _opts;
|
||||
|
||||
if (_sentinel) {
|
||||
auto sentinel = _sentinel;
|
||||
|
||||
lock.unlock();
|
||||
|
||||
return _create(sentinel, opts, false);
|
||||
} else {
|
||||
lock.unlock();
|
||||
|
||||
return Connection(opts);
|
||||
}
|
||||
}
|
||||
|
||||
void ConnectionPool::_move(ConnectionPool &&that) {
|
||||
_opts = std::move(that._opts);
|
||||
_pool_opts = std::move(that._pool_opts);
|
||||
_pool = std::move(that._pool);
|
||||
_used_connections = that._used_connections;
|
||||
_sentinel = std::move(that._sentinel);
|
||||
}
|
||||
|
||||
Connection ConnectionPool::_create() {
|
||||
if (_sentinel) {
|
||||
// Get Redis host and port info from sentinel.
|
||||
return _create(_sentinel, _opts, true);
|
||||
}
|
||||
|
||||
return Connection(_opts);
|
||||
}
|
||||
|
||||
Connection ConnectionPool::_create(SimpleSentinel &sentinel,
|
||||
const ConnectionOptions &opts,
|
||||
bool locked) {
|
||||
try {
|
||||
auto connection = sentinel.create(opts);
|
||||
|
||||
std::unique_lock<std::mutex> lock(_mutex, std::defer_lock);
|
||||
if (!locked) {
|
||||
lock.lock();
|
||||
}
|
||||
|
||||
const auto &connection_opts = connection.options();
|
||||
if (_role_changed(connection_opts)) {
|
||||
// Master/Slave has been changed, reconnect all connections.
|
||||
_update_connection_opts(connection_opts.host, connection_opts.port);
|
||||
}
|
||||
|
||||
return connection;
|
||||
} catch (const StopIterError &e) {
|
||||
throw Error("Failed to create connection with sentinel");
|
||||
}
|
||||
}
|
||||
|
||||
Connection ConnectionPool::_fetch() {
|
||||
assert(!_pool.empty());
|
||||
|
||||
auto connection = std::move(_pool.front());
|
||||
_pool.pop_front();
|
||||
|
||||
return connection;
|
||||
}
|
||||
|
||||
void ConnectionPool::_wait_for_connection(std::unique_lock<std::mutex> &lock) {
|
||||
auto timeout = _pool_opts.wait_timeout;
|
||||
if (timeout > std::chrono::milliseconds(0)) {
|
||||
// Wait until _pool is no longer empty or timeout.
|
||||
if (!_cv.wait_for(lock,
|
||||
timeout,
|
||||
[this] { return !(this->_pool).empty(); })) {
|
||||
throw Error("Failed to fetch a connection in "
|
||||
+ std::to_string(timeout.count()) + " milliseconds");
|
||||
}
|
||||
} else {
|
||||
// Wait forever.
|
||||
_cv.wait(lock, [this] { return !(this->_pool).empty(); });
|
||||
}
|
||||
}
|
||||
|
||||
bool ConnectionPool::_need_reconnect(const Connection &connection,
|
||||
const std::chrono::milliseconds &connection_lifetime) const {
|
||||
if (connection.broken()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (connection_lifetime > std::chrono::milliseconds(0)) {
|
||||
auto now = std::chrono::steady_clock::now();
|
||||
if (now - connection.last_active() > connection_lifetime) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
115
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/connection_pool.h
vendored
Normal file
115
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/connection_pool.h
vendored
Normal file
|
@ -0,0 +1,115 @@
|
|||
/**************************************************************************
|
||||
Copyright (c) 2017 sewenew
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*************************************************************************/
|
||||
|
||||
#ifndef SEWENEW_REDISPLUSPLUS_CONNECTION_POOL_H
|
||||
#define SEWENEW_REDISPLUSPLUS_CONNECTION_POOL_H
|
||||
|
||||
#include <chrono>
|
||||
#include <mutex>
|
||||
#include <memory>
|
||||
#include <condition_variable>
|
||||
#include <deque>
|
||||
#include "connection.h"
|
||||
#include "sentinel.h"
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
struct ConnectionPoolOptions {
|
||||
// Max number of connections, including both in-use and idle ones.
|
||||
std::size_t size = 1;
|
||||
|
||||
// Max time to wait for a connection. 0ms means client waits forever.
|
||||
std::chrono::milliseconds wait_timeout{0};
|
||||
|
||||
// Max lifetime of a connection. 0ms means we never expire the connection.
|
||||
std::chrono::milliseconds connection_lifetime{0};
|
||||
};
|
||||
|
||||
class ConnectionPool {
|
||||
public:
|
||||
ConnectionPool(const ConnectionPoolOptions &pool_opts,
|
||||
const ConnectionOptions &connection_opts);
|
||||
|
||||
ConnectionPool(SimpleSentinel sentinel,
|
||||
const ConnectionPoolOptions &pool_opts,
|
||||
const ConnectionOptions &connection_opts);
|
||||
|
||||
ConnectionPool() = default;
|
||||
|
||||
ConnectionPool(ConnectionPool &&that);
|
||||
ConnectionPool& operator=(ConnectionPool &&that);
|
||||
|
||||
ConnectionPool(const ConnectionPool &) = delete;
|
||||
ConnectionPool& operator=(const ConnectionPool &) = delete;
|
||||
|
||||
~ConnectionPool() = default;
|
||||
|
||||
// Fetch a connection from pool.
|
||||
Connection fetch();
|
||||
|
||||
ConnectionOptions connection_options();
|
||||
|
||||
void release(Connection connection);
|
||||
|
||||
// Create a new connection.
|
||||
Connection create();
|
||||
|
||||
private:
|
||||
void _move(ConnectionPool &&that);
|
||||
|
||||
// NOT thread-safe
|
||||
Connection _create();
|
||||
|
||||
Connection _create(SimpleSentinel &sentinel, const ConnectionOptions &opts, bool locked);
|
||||
|
||||
Connection _fetch();
|
||||
|
||||
void _wait_for_connection(std::unique_lock<std::mutex> &lock);
|
||||
|
||||
bool _need_reconnect(const Connection &connection,
|
||||
const std::chrono::milliseconds &connection_lifetime) const;
|
||||
|
||||
void _update_connection_opts(const std::string &host, int port) {
|
||||
_opts.host = host;
|
||||
_opts.port = port;
|
||||
}
|
||||
|
||||
bool _role_changed(const ConnectionOptions &opts) const {
|
||||
return opts.port != _opts.port || opts.host != _opts.host;
|
||||
}
|
||||
|
||||
ConnectionOptions _opts;
|
||||
|
||||
ConnectionPoolOptions _pool_opts;
|
||||
|
||||
std::deque<Connection> _pool;
|
||||
|
||||
std::size_t _used_connections = 0;
|
||||
|
||||
std::mutex _mutex;
|
||||
|
||||
std::condition_variable _cv;
|
||||
|
||||
SimpleSentinel _sentinel;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // end SEWENEW_REDISPLUSPLUS_CONNECTION_POOL_H
|
96
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/crc16.cpp
vendored
Normal file
96
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/crc16.cpp
vendored
Normal file
|
@ -0,0 +1,96 @@
|
|||
/*
|
||||
* Copyright 2001-2010 Georges Menie (www.menie.org)
|
||||
* Copyright 2010-2012 Salvatore Sanfilippo (adapted to Redis coding style)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the University of California, Berkeley nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/* CRC16 implementation according to CCITT standards.
|
||||
*
|
||||
* Note by @antirez: this is actually the XMODEM CRC 16 algorithm, using the
|
||||
* following parameters:
|
||||
*
|
||||
* Name : "XMODEM", also known as "ZMODEM", "CRC-16/ACORN"
|
||||
* Width : 16 bit
|
||||
* Poly : 1021 (That is actually x^16 + x^12 + x^5 + 1)
|
||||
* Initialization : 0000
|
||||
* Reflect Input byte : False
|
||||
* Reflect Output CRC : False
|
||||
* Xor constant to output CRC : 0000
|
||||
* Output for "123456789" : 31C3
|
||||
*/
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
static const uint16_t crc16tab[256]= {
|
||||
0x0000,0x1021,0x2042,0x3063,0x4084,0x50a5,0x60c6,0x70e7,
|
||||
0x8108,0x9129,0xa14a,0xb16b,0xc18c,0xd1ad,0xe1ce,0xf1ef,
|
||||
0x1231,0x0210,0x3273,0x2252,0x52b5,0x4294,0x72f7,0x62d6,
|
||||
0x9339,0x8318,0xb37b,0xa35a,0xd3bd,0xc39c,0xf3ff,0xe3de,
|
||||
0x2462,0x3443,0x0420,0x1401,0x64e6,0x74c7,0x44a4,0x5485,
|
||||
0xa56a,0xb54b,0x8528,0x9509,0xe5ee,0xf5cf,0xc5ac,0xd58d,
|
||||
0x3653,0x2672,0x1611,0x0630,0x76d7,0x66f6,0x5695,0x46b4,
|
||||
0xb75b,0xa77a,0x9719,0x8738,0xf7df,0xe7fe,0xd79d,0xc7bc,
|
||||
0x48c4,0x58e5,0x6886,0x78a7,0x0840,0x1861,0x2802,0x3823,
|
||||
0xc9cc,0xd9ed,0xe98e,0xf9af,0x8948,0x9969,0xa90a,0xb92b,
|
||||
0x5af5,0x4ad4,0x7ab7,0x6a96,0x1a71,0x0a50,0x3a33,0x2a12,
|
||||
0xdbfd,0xcbdc,0xfbbf,0xeb9e,0x9b79,0x8b58,0xbb3b,0xab1a,
|
||||
0x6ca6,0x7c87,0x4ce4,0x5cc5,0x2c22,0x3c03,0x0c60,0x1c41,
|
||||
0xedae,0xfd8f,0xcdec,0xddcd,0xad2a,0xbd0b,0x8d68,0x9d49,
|
||||
0x7e97,0x6eb6,0x5ed5,0x4ef4,0x3e13,0x2e32,0x1e51,0x0e70,
|
||||
0xff9f,0xefbe,0xdfdd,0xcffc,0xbf1b,0xaf3a,0x9f59,0x8f78,
|
||||
0x9188,0x81a9,0xb1ca,0xa1eb,0xd10c,0xc12d,0xf14e,0xe16f,
|
||||
0x1080,0x00a1,0x30c2,0x20e3,0x5004,0x4025,0x7046,0x6067,
|
||||
0x83b9,0x9398,0xa3fb,0xb3da,0xc33d,0xd31c,0xe37f,0xf35e,
|
||||
0x02b1,0x1290,0x22f3,0x32d2,0x4235,0x5214,0x6277,0x7256,
|
||||
0xb5ea,0xa5cb,0x95a8,0x8589,0xf56e,0xe54f,0xd52c,0xc50d,
|
||||
0x34e2,0x24c3,0x14a0,0x0481,0x7466,0x6447,0x5424,0x4405,
|
||||
0xa7db,0xb7fa,0x8799,0x97b8,0xe75f,0xf77e,0xc71d,0xd73c,
|
||||
0x26d3,0x36f2,0x0691,0x16b0,0x6657,0x7676,0x4615,0x5634,
|
||||
0xd94c,0xc96d,0xf90e,0xe92f,0x99c8,0x89e9,0xb98a,0xa9ab,
|
||||
0x5844,0x4865,0x7806,0x6827,0x18c0,0x08e1,0x3882,0x28a3,
|
||||
0xcb7d,0xdb5c,0xeb3f,0xfb1e,0x8bf9,0x9bd8,0xabbb,0xbb9a,
|
||||
0x4a75,0x5a54,0x6a37,0x7a16,0x0af1,0x1ad0,0x2ab3,0x3a92,
|
||||
0xfd2e,0xed0f,0xdd6c,0xcd4d,0xbdaa,0xad8b,0x9de8,0x8dc9,
|
||||
0x7c26,0x6c07,0x5c64,0x4c45,0x3ca2,0x2c83,0x1ce0,0x0cc1,
|
||||
0xef1f,0xff3e,0xcf5d,0xdf7c,0xaf9b,0xbfba,0x8fd9,0x9ff8,
|
||||
0x6e17,0x7e36,0x4e55,0x5e74,0x2e93,0x3eb2,0x0ed1,0x1ef0
|
||||
};
|
||||
|
||||
uint16_t crc16(const char *buf, int len) {
|
||||
int counter;
|
||||
uint16_t crc = 0;
|
||||
for (counter = 0; counter < len; counter++)
|
||||
crc = (crc<<8) ^ crc16tab[((crc>>8) ^ *buf++)&0x00FF];
|
||||
return crc;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
136
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/errors.cpp
vendored
Normal file
136
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/errors.cpp
vendored
Normal file
|
@ -0,0 +1,136 @@
|
|||
/**************************************************************************
|
||||
Copyright (c) 2017 sewenew
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*************************************************************************/
|
||||
|
||||
#include "errors.h"
|
||||
#include <cassert>
|
||||
#include <cerrno>
|
||||
#include <unordered_map>
|
||||
#include <tuple>
|
||||
#include "shards.h"
|
||||
|
||||
namespace {
|
||||
|
||||
using namespace sw::redis;
|
||||
|
||||
std::pair<ReplyErrorType, std::string> parse_error(const std::string &msg);
|
||||
|
||||
std::unordered_map<std::string, ReplyErrorType> error_map = {
|
||||
{"MOVED", ReplyErrorType::MOVED},
|
||||
{"ASK", ReplyErrorType::ASK}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
void throw_error(redisContext &context, const std::string &err_info) {
|
||||
auto err_code = context.err;
|
||||
const auto *err_str = context.errstr;
|
||||
if (err_str == nullptr) {
|
||||
throw Error(err_info + ": null error message: " + std::to_string(err_code));
|
||||
}
|
||||
|
||||
auto err_msg = err_info + ": " + err_str;
|
||||
|
||||
switch (err_code) {
|
||||
case REDIS_ERR_IO:
|
||||
if (errno == EAGAIN || errno == EINTR) {
|
||||
throw TimeoutError(err_msg);
|
||||
} else {
|
||||
throw IoError(err_msg);
|
||||
}
|
||||
break;
|
||||
|
||||
case REDIS_ERR_EOF:
|
||||
throw ClosedError(err_msg);
|
||||
break;
|
||||
|
||||
case REDIS_ERR_PROTOCOL:
|
||||
throw ProtoError(err_msg);
|
||||
break;
|
||||
|
||||
case REDIS_ERR_OOM:
|
||||
throw OomError(err_msg);
|
||||
break;
|
||||
|
||||
case REDIS_ERR_OTHER:
|
||||
throw Error(err_msg);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw Error(err_info + ": Unknown error code");
|
||||
}
|
||||
}
|
||||
|
||||
void throw_error(const redisReply &reply) {
|
||||
assert(reply.type == REDIS_REPLY_ERROR);
|
||||
|
||||
if (reply.str == nullptr) {
|
||||
throw Error("Null error reply");
|
||||
}
|
||||
|
||||
auto err_str = std::string(reply.str, reply.len);
|
||||
|
||||
auto err_type = ReplyErrorType::ERR;
|
||||
std::string err_msg;
|
||||
std::tie(err_type, err_msg) = parse_error(err_str);
|
||||
|
||||
switch (err_type) {
|
||||
case ReplyErrorType::MOVED:
|
||||
throw MovedError(err_msg);
|
||||
break;
|
||||
|
||||
case ReplyErrorType::ASK:
|
||||
throw AskError(err_msg);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw ReplyError(err_str);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
using namespace sw::redis;
|
||||
|
||||
std::pair<ReplyErrorType, std::string> parse_error(const std::string &err) {
|
||||
// The error contains an Error Prefix, and an optional error message.
|
||||
auto idx = err.find_first_of(" \n");
|
||||
|
||||
if (idx == std::string::npos) {
|
||||
throw ProtoError("No Error Prefix: " + err);
|
||||
}
|
||||
|
||||
auto err_prefix = err.substr(0, idx);
|
||||
auto err_type = ReplyErrorType::ERR;
|
||||
|
||||
auto iter = error_map.find(err_prefix);
|
||||
if (iter != error_map.end()) {
|
||||
// Specific error.
|
||||
err_type = iter->second;
|
||||
} // else Generic error.
|
||||
|
||||
return {err_type, err.substr(idx + 1)};
|
||||
}
|
||||
|
||||
}
|
159
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/errors.h
vendored
Normal file
159
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/errors.h
vendored
Normal file
|
@ -0,0 +1,159 @@
|
|||
/**************************************************************************
|
||||
Copyright (c) 2017 sewenew
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*************************************************************************/
|
||||
|
||||
#ifndef SEWENEW_REDISPLUSPLUS_ERRORS_H
|
||||
#define SEWENEW_REDISPLUSPLUS_ERRORS_H
|
||||
|
||||
#include <exception>
|
||||
#include <string>
|
||||
#include <hiredis/hiredis.h>
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
enum ReplyErrorType {
|
||||
ERR,
|
||||
MOVED,
|
||||
ASK
|
||||
};
|
||||
|
||||
class Error : public std::exception {
|
||||
public:
|
||||
explicit Error(const std::string &msg) : _msg(msg) {}
|
||||
|
||||
Error(const Error &) = default;
|
||||
Error& operator=(const Error &) = default;
|
||||
|
||||
Error(Error &&) = default;
|
||||
Error& operator=(Error &&) = default;
|
||||
|
||||
virtual ~Error() = default;
|
||||
|
||||
virtual const char* what() const noexcept {
|
||||
return _msg.data();
|
||||
}
|
||||
|
||||
private:
|
||||
std::string _msg;
|
||||
};
|
||||
|
||||
class IoError : public Error {
|
||||
public:
|
||||
explicit IoError(const std::string &msg) : Error(msg) {}
|
||||
|
||||
IoError(const IoError &) = default;
|
||||
IoError& operator=(const IoError &) = default;
|
||||
|
||||
IoError(IoError &&) = default;
|
||||
IoError& operator=(IoError &&) = default;
|
||||
|
||||
virtual ~IoError() = default;
|
||||
};
|
||||
|
||||
class TimeoutError : public IoError {
|
||||
public:
|
||||
explicit TimeoutError(const std::string &msg) : IoError(msg) {}
|
||||
|
||||
TimeoutError(const TimeoutError &) = default;
|
||||
TimeoutError& operator=(const TimeoutError &) = default;
|
||||
|
||||
TimeoutError(TimeoutError &&) = default;
|
||||
TimeoutError& operator=(TimeoutError &&) = default;
|
||||
|
||||
virtual ~TimeoutError() = default;
|
||||
};
|
||||
|
||||
class ClosedError : public Error {
|
||||
public:
|
||||
explicit ClosedError(const std::string &msg) : Error(msg) {}
|
||||
|
||||
ClosedError(const ClosedError &) = default;
|
||||
ClosedError& operator=(const ClosedError &) = default;
|
||||
|
||||
ClosedError(ClosedError &&) = default;
|
||||
ClosedError& operator=(ClosedError &&) = default;
|
||||
|
||||
virtual ~ClosedError() = default;
|
||||
};
|
||||
|
||||
class ProtoError : public Error {
|
||||
public:
|
||||
explicit ProtoError(const std::string &msg) : Error(msg) {}
|
||||
|
||||
ProtoError(const ProtoError &) = default;
|
||||
ProtoError& operator=(const ProtoError &) = default;
|
||||
|
||||
ProtoError(ProtoError &&) = default;
|
||||
ProtoError& operator=(ProtoError &&) = default;
|
||||
|
||||
virtual ~ProtoError() = default;
|
||||
};
|
||||
|
||||
class OomError : public Error {
|
||||
public:
|
||||
explicit OomError(const std::string &msg) : Error(msg) {}
|
||||
|
||||
OomError(const OomError &) = default;
|
||||
OomError& operator=(const OomError &) = default;
|
||||
|
||||
OomError(OomError &&) = default;
|
||||
OomError& operator=(OomError &&) = default;
|
||||
|
||||
virtual ~OomError() = default;
|
||||
};
|
||||
|
||||
class ReplyError : public Error {
|
||||
public:
|
||||
explicit ReplyError(const std::string &msg) : Error(msg) {}
|
||||
|
||||
ReplyError(const ReplyError &) = default;
|
||||
ReplyError& operator=(const ReplyError &) = default;
|
||||
|
||||
ReplyError(ReplyError &&) = default;
|
||||
ReplyError& operator=(ReplyError &&) = default;
|
||||
|
||||
virtual ~ReplyError() = default;
|
||||
};
|
||||
|
||||
class WatchError : public Error {
|
||||
public:
|
||||
explicit WatchError() : Error("Watched key has been modified") {}
|
||||
|
||||
WatchError(const WatchError &) = default;
|
||||
WatchError& operator=(const WatchError &) = default;
|
||||
|
||||
WatchError(WatchError &&) = default;
|
||||
WatchError& operator=(WatchError &&) = default;
|
||||
|
||||
virtual ~WatchError() = default;
|
||||
};
|
||||
|
||||
|
||||
// MovedError and AskError are defined in shards.h
|
||||
class MovedError;
|
||||
|
||||
class AskError;
|
||||
|
||||
void throw_error(redisContext &context, const std::string &err_info);
|
||||
|
||||
void throw_error(const redisReply &reply);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // end SEWENEW_REDISPLUSPLUS_ERRORS_H
|
35
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/pipeline.cpp
vendored
Normal file
35
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/pipeline.cpp
vendored
Normal file
|
@ -0,0 +1,35 @@
|
|||
/**************************************************************************
|
||||
Copyright (c) 2017 sewenew
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*************************************************************************/
|
||||
|
||||
#include "pipeline.h"
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
std::vector<ReplyUPtr> PipelineImpl::exec(Connection &connection, std::size_t cmd_num) {
|
||||
std::vector<ReplyUPtr> replies;
|
||||
while (cmd_num > 0) {
|
||||
replies.push_back(connection.recv());
|
||||
--cmd_num;
|
||||
}
|
||||
|
||||
return replies;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
49
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/pipeline.h
vendored
Normal file
49
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/pipeline.h
vendored
Normal file
|
@ -0,0 +1,49 @@
|
|||
/**************************************************************************
|
||||
Copyright (c) 2017 sewenew
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*************************************************************************/
|
||||
|
||||
#ifndef SEWENEW_REDISPLUSPLUS_PIPELINE_H
|
||||
#define SEWENEW_REDISPLUSPLUS_PIPELINE_H
|
||||
|
||||
#include <cassert>
|
||||
#include <vector>
|
||||
#include "connection.h"
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
class PipelineImpl {
|
||||
public:
|
||||
template <typename Cmd, typename ...Args>
|
||||
void command(Connection &connection, Cmd cmd, Args &&...args) {
|
||||
assert(!connection.broken());
|
||||
|
||||
cmd(connection, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
std::vector<ReplyUPtr> exec(Connection &connection, std::size_t cmd_num);
|
||||
|
||||
void discard(Connection &connection, std::size_t /*cmd_num*/) {
|
||||
// Reconnect to Redis to discard all commands.
|
||||
connection.reconnect();
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // end SEWENEW_REDISPLUSPLUS_PIPELINE_H
|
1844
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/queued_redis.h
vendored
Normal file
1844
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/queued_redis.h
vendored
Normal file
File diff suppressed because it is too large
Load diff
208
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/queued_redis.hpp
vendored
Normal file
208
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/queued_redis.hpp
vendored
Normal file
|
@ -0,0 +1,208 @@
|
|||
/**************************************************************************
|
||||
Copyright (c) 2017 sewenew
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*************************************************************************/
|
||||
|
||||
#ifndef SEWENEW_REDISPLUSPLUS_QUEUED_REDIS_HPP
|
||||
#define SEWENEW_REDISPLUSPLUS_QUEUED_REDIS_HPP
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
template <typename Impl>
|
||||
template <typename ...Args>
|
||||
QueuedRedis<Impl>::QueuedRedis(const ConnectionSPtr &connection, Args &&...args) :
|
||||
_connection(connection),
|
||||
_impl(std::forward<Args>(args)...) {
|
||||
assert(_connection);
|
||||
}
|
||||
|
||||
template <typename Impl>
|
||||
Redis QueuedRedis<Impl>::redis() {
|
||||
return Redis(_connection);
|
||||
}
|
||||
|
||||
template <typename Impl>
|
||||
template <typename Cmd, typename ...Args>
|
||||
auto QueuedRedis<Impl>::command(Cmd cmd, Args &&...args)
|
||||
-> typename std::enable_if<!std::is_convertible<Cmd, StringView>::value,
|
||||
QueuedRedis<Impl>&>::type {
|
||||
try {
|
||||
_sanity_check();
|
||||
|
||||
_impl.command(*_connection, cmd, std::forward<Args>(args)...);
|
||||
|
||||
++_cmd_num;
|
||||
} catch (const Error &e) {
|
||||
_invalidate();
|
||||
throw;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename Impl>
|
||||
template <typename ...Args>
|
||||
QueuedRedis<Impl>& QueuedRedis<Impl>::command(const StringView &cmd_name, Args &&...args) {
|
||||
auto cmd = [](Connection &connection, const StringView &cmd_name, Args &&...args) {
|
||||
CmdArgs cmd_args;
|
||||
cmd_args.append(cmd_name, std::forward<Args>(args)...);
|
||||
connection.send(cmd_args);
|
||||
};
|
||||
|
||||
return command(cmd, cmd_name, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <typename Impl>
|
||||
template <typename Input>
|
||||
auto QueuedRedis<Impl>::command(Input first, Input last)
|
||||
-> typename std::enable_if<IsIter<Input>::value, QueuedRedis<Impl>&>::type {
|
||||
if (first == last) {
|
||||
throw Error("command: empty range");
|
||||
}
|
||||
|
||||
auto cmd = [](Connection &connection, Input first, Input last) {
|
||||
CmdArgs cmd_args;
|
||||
while (first != last) {
|
||||
cmd_args.append(*first);
|
||||
++first;
|
||||
}
|
||||
connection.send(cmd_args);
|
||||
};
|
||||
|
||||
return command(cmd, first, last);
|
||||
}
|
||||
|
||||
template <typename Impl>
|
||||
QueuedReplies QueuedRedis<Impl>::exec() {
|
||||
try {
|
||||
_sanity_check();
|
||||
|
||||
auto replies = _impl.exec(*_connection, _cmd_num);
|
||||
|
||||
_rewrite_replies(replies);
|
||||
|
||||
_reset();
|
||||
|
||||
return QueuedReplies(std::move(replies));
|
||||
} catch (const Error &e) {
|
||||
_invalidate();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Impl>
|
||||
void QueuedRedis<Impl>::discard() {
|
||||
try {
|
||||
_sanity_check();
|
||||
|
||||
_impl.discard(*_connection, _cmd_num);
|
||||
|
||||
_reset();
|
||||
} catch (const Error &e) {
|
||||
_invalidate();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Impl>
|
||||
void QueuedRedis<Impl>::_sanity_check() const {
|
||||
if (!_valid) {
|
||||
throw Error("Not in valid state");
|
||||
}
|
||||
|
||||
if (_connection->broken()) {
|
||||
throw Error("Connection is broken");
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Impl>
|
||||
inline void QueuedRedis<Impl>::_reset() {
|
||||
_cmd_num = 0;
|
||||
|
||||
_set_cmd_indexes.clear();
|
||||
|
||||
_georadius_cmd_indexes.clear();
|
||||
}
|
||||
|
||||
template <typename Impl>
|
||||
void QueuedRedis<Impl>::_invalidate() {
|
||||
_valid = false;
|
||||
|
||||
_reset();
|
||||
}
|
||||
|
||||
template <typename Impl>
|
||||
void QueuedRedis<Impl>::_rewrite_replies(std::vector<ReplyUPtr> &replies) const {
|
||||
_rewrite_replies(_set_cmd_indexes, reply::rewrite_set_reply, replies);
|
||||
|
||||
_rewrite_replies(_georadius_cmd_indexes, reply::rewrite_georadius_reply, replies);
|
||||
}
|
||||
|
||||
template <typename Impl>
|
||||
template <typename Func>
|
||||
void QueuedRedis<Impl>::_rewrite_replies(const std::vector<std::size_t> &indexes,
|
||||
Func rewriter,
|
||||
std::vector<ReplyUPtr> &replies) const {
|
||||
for (auto idx : indexes) {
|
||||
assert(idx < replies.size());
|
||||
|
||||
auto &reply = replies[idx];
|
||||
|
||||
assert(reply);
|
||||
|
||||
rewriter(*reply);
|
||||
}
|
||||
}
|
||||
|
||||
inline std::size_t QueuedReplies::size() const {
|
||||
return _replies.size();
|
||||
}
|
||||
|
||||
inline redisReply& QueuedReplies::get(std::size_t idx) {
|
||||
_index_check(idx);
|
||||
|
||||
auto &reply = _replies[idx];
|
||||
|
||||
assert(reply);
|
||||
|
||||
return *reply;
|
||||
}
|
||||
|
||||
template <typename Result>
|
||||
inline Result QueuedReplies::get(std::size_t idx) {
|
||||
auto &reply = get(idx);
|
||||
|
||||
return reply::parse<Result>(reply);
|
||||
}
|
||||
|
||||
template <typename Output>
|
||||
inline void QueuedReplies::get(std::size_t idx, Output output) {
|
||||
auto &reply = get(idx);
|
||||
|
||||
reply::to_array(reply, output);
|
||||
}
|
||||
|
||||
inline void QueuedReplies::_index_check(std::size_t idx) const {
|
||||
if (idx >= size()) {
|
||||
throw Error("Out of range");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // end SEWENEW_REDISPLUSPLUS_QUEUED_REDIS_HPP
|
25
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/redis++.h
vendored
Normal file
25
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/redis++.h
vendored
Normal file
|
@ -0,0 +1,25 @@
|
|||
/**************************************************************************
|
||||
Copyright (c) 2017 sewenew
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*************************************************************************/
|
||||
|
||||
#ifndef SEWENEW_REDISPLUSPLUS_REDISPLUSPLUS_H
|
||||
#define SEWENEW_REDISPLUSPLUS_REDISPLUSPLUS_H
|
||||
|
||||
#include "redis.h"
|
||||
#include "redis_cluster.h"
|
||||
#include "queued_redis.h"
|
||||
#include "sentinel.h"
|
||||
|
||||
#endif // end SEWENEW_REDISPLUSPLUS_REDISPLUSPLUS_H
|
882
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/redis.cpp
vendored
Normal file
882
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/redis.cpp
vendored
Normal file
|
@ -0,0 +1,882 @@
|
|||
/**************************************************************************
|
||||
Copyright (c) 2017 sewenew
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*************************************************************************/
|
||||
|
||||
#include "redis.h"
|
||||
#include <hiredis/hiredis.h>
|
||||
#include "command.h"
|
||||
#include "errors.h"
|
||||
#include "queued_redis.h"
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
Redis::Redis(const std::string &uri) : Redis(ConnectionOptions(uri)) {}
|
||||
|
||||
Redis::Redis(const ConnectionSPtr &connection) : _connection(connection) {
|
||||
assert(_connection);
|
||||
}
|
||||
|
||||
Pipeline Redis::pipeline() {
|
||||
return Pipeline(std::make_shared<Connection>(_pool.create()));
|
||||
}
|
||||
|
||||
Transaction Redis::transaction(bool piped) {
|
||||
return Transaction(std::make_shared<Connection>(_pool.create()), piped);
|
||||
}
|
||||
|
||||
Subscriber Redis::subscriber() {
|
||||
return Subscriber(_pool.create());
|
||||
}
|
||||
|
||||
// CONNECTION commands.
|
||||
|
||||
void Redis::auth(const StringView &password) {
|
||||
auto reply = command(cmd::auth, password);
|
||||
|
||||
reply::parse<void>(*reply);
|
||||
}
|
||||
|
||||
std::string Redis::echo(const StringView &msg) {
|
||||
auto reply = command(cmd::echo, msg);
|
||||
|
||||
return reply::parse<std::string>(*reply);
|
||||
}
|
||||
|
||||
std::string Redis::ping() {
|
||||
auto reply = command<void (*)(Connection &)>(cmd::ping);
|
||||
|
||||
return reply::to_status(*reply);
|
||||
}
|
||||
|
||||
std::string Redis::ping(const StringView &msg) {
|
||||
auto reply = command<void (*)(Connection &, const StringView &)>(cmd::ping, msg);
|
||||
|
||||
return reply::parse<std::string>(*reply);
|
||||
}
|
||||
|
||||
void Redis::swapdb(long long idx1, long long idx2) {
|
||||
auto reply = command(cmd::swapdb, idx1, idx2);
|
||||
|
||||
reply::parse<void>(*reply);
|
||||
}
|
||||
|
||||
// SERVER commands.
|
||||
|
||||
void Redis::bgrewriteaof() {
|
||||
auto reply = command(cmd::bgrewriteaof);
|
||||
|
||||
reply::parse<void>(*reply);
|
||||
}
|
||||
|
||||
void Redis::bgsave() {
|
||||
auto reply = command(cmd::bgsave);
|
||||
|
||||
reply::parse<void>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::dbsize() {
|
||||
auto reply = command(cmd::dbsize);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
void Redis::flushall(bool async) {
|
||||
auto reply = command(cmd::flushall, async);
|
||||
|
||||
reply::parse<void>(*reply);
|
||||
}
|
||||
|
||||
void Redis::flushdb(bool async) {
|
||||
auto reply = command(cmd::flushdb, async);
|
||||
|
||||
reply::parse<void>(*reply);
|
||||
}
|
||||
|
||||
std::string Redis::info() {
|
||||
auto reply = command<void (*)(Connection &)>(cmd::info);
|
||||
|
||||
return reply::parse<std::string>(*reply);
|
||||
}
|
||||
|
||||
std::string Redis::info(const StringView §ion) {
|
||||
auto reply = command<void (*)(Connection &, const StringView &)>(cmd::info, section);
|
||||
|
||||
return reply::parse<std::string>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::lastsave() {
|
||||
auto reply = command(cmd::lastsave);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
void Redis::save() {
|
||||
auto reply = command(cmd::save);
|
||||
|
||||
reply::parse<void>(*reply);
|
||||
}
|
||||
|
||||
// KEY commands.
|
||||
|
||||
long long Redis::del(const StringView &key) {
|
||||
auto reply = command(cmd::del, key);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
OptionalString Redis::dump(const StringView &key) {
|
||||
auto reply = command(cmd::dump, key);
|
||||
|
||||
return reply::parse<OptionalString>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::exists(const StringView &key) {
|
||||
auto reply = command(cmd::exists, key);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
bool Redis::expire(const StringView &key, long long timeout) {
|
||||
auto reply = command(cmd::expire, key, timeout);
|
||||
|
||||
return reply::parse<bool>(*reply);
|
||||
}
|
||||
|
||||
bool Redis::expireat(const StringView &key, long long timestamp) {
|
||||
auto reply = command(cmd::expireat, key, timestamp);
|
||||
|
||||
return reply::parse<bool>(*reply);
|
||||
}
|
||||
|
||||
bool Redis::move(const StringView &key, long long db) {
|
||||
auto reply = command(cmd::move, key, db);
|
||||
|
||||
return reply::parse<bool>(*reply);
|
||||
}
|
||||
|
||||
bool Redis::persist(const StringView &key) {
|
||||
auto reply = command(cmd::persist, key);
|
||||
|
||||
return reply::parse<bool>(*reply);
|
||||
}
|
||||
|
||||
bool Redis::pexpire(const StringView &key, long long timeout) {
|
||||
auto reply = command(cmd::pexpire, key, timeout);
|
||||
|
||||
return reply::parse<bool>(*reply);
|
||||
}
|
||||
|
||||
bool Redis::pexpireat(const StringView &key, long long timestamp) {
|
||||
auto reply = command(cmd::pexpireat, key, timestamp);
|
||||
|
||||
return reply::parse<bool>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::pttl(const StringView &key) {
|
||||
auto reply = command(cmd::pttl, key);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
OptionalString Redis::randomkey() {
|
||||
auto reply = command(cmd::randomkey);
|
||||
|
||||
return reply::parse<OptionalString>(*reply);
|
||||
}
|
||||
|
||||
void Redis::rename(const StringView &key, const StringView &newkey) {
|
||||
auto reply = command(cmd::rename, key, newkey);
|
||||
|
||||
reply::parse<void>(*reply);
|
||||
}
|
||||
|
||||
bool Redis::renamenx(const StringView &key, const StringView &newkey) {
|
||||
auto reply = command(cmd::renamenx, key, newkey);
|
||||
|
||||
return reply::parse<bool>(*reply);
|
||||
}
|
||||
|
||||
void Redis::restore(const StringView &key,
|
||||
const StringView &val,
|
||||
long long ttl,
|
||||
bool replace) {
|
||||
auto reply = command(cmd::restore, key, val, ttl, replace);
|
||||
|
||||
reply::parse<void>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::touch(const StringView &key) {
|
||||
auto reply = command(cmd::touch, key);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::ttl(const StringView &key) {
|
||||
auto reply = command(cmd::ttl, key);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
std::string Redis::type(const StringView &key) {
|
||||
auto reply = command(cmd::type, key);
|
||||
|
||||
return reply::parse<std::string>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::unlink(const StringView &key) {
|
||||
auto reply = command(cmd::unlink, key);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::wait(long long numslaves, long long timeout) {
|
||||
auto reply = command(cmd::wait, numslaves, timeout);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
// STRING commands.
|
||||
|
||||
long long Redis::append(const StringView &key, const StringView &val) {
|
||||
auto reply = command(cmd::append, key, val);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::bitcount(const StringView &key, long long start, long long end) {
|
||||
auto reply = command(cmd::bitcount, key, start, end);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::bitop(BitOp op, const StringView &destination, const StringView &key) {
|
||||
auto reply = command(cmd::bitop, op, destination, key);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::bitpos(const StringView &key,
|
||||
long long bit,
|
||||
long long start,
|
||||
long long end) {
|
||||
auto reply = command(cmd::bitpos, key, bit, start, end);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::decr(const StringView &key) {
|
||||
auto reply = command(cmd::decr, key);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::decrby(const StringView &key, long long decrement) {
|
||||
auto reply = command(cmd::decrby, key, decrement);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
OptionalString Redis::get(const StringView &key) {
|
||||
auto reply = command(cmd::get, key);
|
||||
|
||||
return reply::parse<OptionalString>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::getbit(const StringView &key, long long offset) {
|
||||
auto reply = command(cmd::getbit, key, offset);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
std::string Redis::getrange(const StringView &key, long long start, long long end) {
|
||||
auto reply = command(cmd::getrange, key, start, end);
|
||||
|
||||
return reply::parse<std::string>(*reply);
|
||||
}
|
||||
|
||||
OptionalString Redis::getset(const StringView &key, const StringView &val) {
|
||||
auto reply = command(cmd::getset, key, val);
|
||||
|
||||
return reply::parse<OptionalString>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::incr(const StringView &key) {
|
||||
auto reply = command(cmd::incr, key);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::incrby(const StringView &key, long long increment) {
|
||||
auto reply = command(cmd::incrby, key, increment);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
double Redis::incrbyfloat(const StringView &key, double increment) {
|
||||
auto reply = command(cmd::incrbyfloat, key, increment);
|
||||
|
||||
return reply::parse<double>(*reply);
|
||||
}
|
||||
|
||||
void Redis::psetex(const StringView &key,
|
||||
long long ttl,
|
||||
const StringView &val) {
|
||||
auto reply = command(cmd::psetex, key, ttl, val);
|
||||
|
||||
reply::parse<void>(*reply);
|
||||
}
|
||||
|
||||
bool Redis::set(const StringView &key,
|
||||
const StringView &val,
|
||||
const std::chrono::milliseconds &ttl,
|
||||
UpdateType type) {
|
||||
auto reply = command(cmd::set, key, val, ttl.count(), type);
|
||||
|
||||
reply::rewrite_set_reply(*reply);
|
||||
|
||||
return reply::parse<bool>(*reply);
|
||||
}
|
||||
|
||||
void Redis::setex(const StringView &key,
|
||||
long long ttl,
|
||||
const StringView &val) {
|
||||
auto reply = command(cmd::setex, key, ttl, val);
|
||||
|
||||
reply::parse<void>(*reply);
|
||||
}
|
||||
|
||||
bool Redis::setnx(const StringView &key, const StringView &val) {
|
||||
auto reply = command(cmd::setnx, key, val);
|
||||
|
||||
return reply::parse<bool>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::setrange(const StringView &key, long long offset, const StringView &val) {
|
||||
auto reply = command(cmd::setrange, key, offset, val);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::strlen(const StringView &key) {
|
||||
auto reply = command(cmd::strlen, key);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
// LIST commands.
|
||||
|
||||
OptionalStringPair Redis::blpop(const StringView &key, long long timeout) {
|
||||
auto reply = command(cmd::blpop, key, timeout);
|
||||
|
||||
return reply::parse<OptionalStringPair>(*reply);
|
||||
}
|
||||
|
||||
OptionalStringPair Redis::blpop(const StringView &key, const std::chrono::seconds &timeout) {
|
||||
return blpop(key, timeout.count());
|
||||
}
|
||||
|
||||
OptionalStringPair Redis::brpop(const StringView &key, long long timeout) {
|
||||
auto reply = command(cmd::brpop, key, timeout);
|
||||
|
||||
return reply::parse<OptionalStringPair>(*reply);
|
||||
}
|
||||
|
||||
OptionalStringPair Redis::brpop(const StringView &key, const std::chrono::seconds &timeout) {
|
||||
return brpop(key, timeout.count());
|
||||
}
|
||||
|
||||
OptionalString Redis::brpoplpush(const StringView &source,
|
||||
const StringView &destination,
|
||||
long long timeout) {
|
||||
auto reply = command(cmd::brpoplpush, source, destination, timeout);
|
||||
|
||||
return reply::parse<OptionalString>(*reply);
|
||||
}
|
||||
|
||||
OptionalString Redis::lindex(const StringView &key, long long index) {
|
||||
auto reply = command(cmd::lindex, key, index);
|
||||
|
||||
return reply::parse<OptionalString>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::linsert(const StringView &key,
|
||||
InsertPosition position,
|
||||
const StringView &pivot,
|
||||
const StringView &val) {
|
||||
auto reply = command(cmd::linsert, key, position, pivot, val);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::llen(const StringView &key) {
|
||||
auto reply = command(cmd::llen, key);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
OptionalString Redis::lpop(const StringView &key) {
|
||||
auto reply = command(cmd::lpop, key);
|
||||
|
||||
return reply::parse<OptionalString>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::lpush(const StringView &key, const StringView &val) {
|
||||
auto reply = command(cmd::lpush, key, val);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::lpushx(const StringView &key, const StringView &val) {
|
||||
auto reply = command(cmd::lpushx, key, val);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::lrem(const StringView &key, long long count, const StringView &val) {
|
||||
auto reply = command(cmd::lrem, key, count, val);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
void Redis::lset(const StringView &key, long long index, const StringView &val) {
|
||||
auto reply = command(cmd::lset, key, index, val);
|
||||
|
||||
reply::parse<void>(*reply);
|
||||
}
|
||||
|
||||
void Redis::ltrim(const StringView &key, long long start, long long stop) {
|
||||
auto reply = command(cmd::ltrim, key, start, stop);
|
||||
|
||||
reply::parse<void>(*reply);
|
||||
}
|
||||
|
||||
OptionalString Redis::rpop(const StringView &key) {
|
||||
auto reply = command(cmd::rpop, key);
|
||||
|
||||
return reply::parse<OptionalString>(*reply);
|
||||
}
|
||||
|
||||
OptionalString Redis::rpoplpush(const StringView &source, const StringView &destination) {
|
||||
auto reply = command(cmd::rpoplpush, source, destination);
|
||||
|
||||
return reply::parse<OptionalString>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::rpush(const StringView &key, const StringView &val) {
|
||||
auto reply = command(cmd::rpush, key, val);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::rpushx(const StringView &key, const StringView &val) {
|
||||
auto reply = command(cmd::rpushx, key, val);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::hdel(const StringView &key, const StringView &field) {
|
||||
auto reply = command(cmd::hdel, key, field);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
bool Redis::hexists(const StringView &key, const StringView &field) {
|
||||
auto reply = command(cmd::hexists, key, field);
|
||||
|
||||
return reply::parse<bool>(*reply);
|
||||
}
|
||||
|
||||
OptionalString Redis::hget(const StringView &key, const StringView &field) {
|
||||
auto reply = command(cmd::hget, key, field);
|
||||
|
||||
return reply::parse<OptionalString>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::hincrby(const StringView &key, const StringView &field, long long increment) {
|
||||
auto reply = command(cmd::hincrby, key, field, increment);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
double Redis::hincrbyfloat(const StringView &key, const StringView &field, double increment) {
|
||||
auto reply = command(cmd::hincrbyfloat, key, field, increment);
|
||||
|
||||
return reply::parse<double>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::hlen(const StringView &key) {
|
||||
auto reply = command(cmd::hlen, key);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
bool Redis::hset(const StringView &key, const StringView &field, const StringView &val) {
|
||||
auto reply = command(cmd::hset, key, field, val);
|
||||
|
||||
return reply::parse<bool>(*reply);
|
||||
}
|
||||
|
||||
bool Redis::hset(const StringView &key, const std::pair<StringView, StringView> &item) {
|
||||
return hset(key, item.first, item.second);
|
||||
}
|
||||
|
||||
bool Redis::hsetnx(const StringView &key, const StringView &field, const StringView &val) {
|
||||
auto reply = command(cmd::hsetnx, key, field, val);
|
||||
|
||||
return reply::parse<bool>(*reply);
|
||||
}
|
||||
|
||||
bool Redis::hsetnx(const StringView &key, const std::pair<StringView, StringView> &item) {
|
||||
return hsetnx(key, item.first, item.second);
|
||||
}
|
||||
|
||||
long long Redis::hstrlen(const StringView &key, const StringView &field) {
|
||||
auto reply = command(cmd::hstrlen, key, field);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
// SET commands.
|
||||
|
||||
long long Redis::sadd(const StringView &key, const StringView &member) {
|
||||
auto reply = command(cmd::sadd, key, member);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::scard(const StringView &key) {
|
||||
auto reply = command(cmd::scard, key);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::sdiffstore(const StringView &destination, const StringView &key) {
|
||||
auto reply = command(cmd::sdiffstore, destination, key);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::sinterstore(const StringView &destination, const StringView &key) {
|
||||
auto reply = command(cmd::sinterstore, destination, key);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
bool Redis::sismember(const StringView &key, const StringView &member) {
|
||||
auto reply = command(cmd::sismember, key, member);
|
||||
|
||||
return reply::parse<bool>(*reply);
|
||||
}
|
||||
|
||||
bool Redis::smove(const StringView &source,
|
||||
const StringView &destination,
|
||||
const StringView &member) {
|
||||
auto reply = command(cmd::smove, source, destination, member);
|
||||
|
||||
return reply::parse<bool>(*reply);
|
||||
}
|
||||
|
||||
OptionalString Redis::spop(const StringView &key) {
|
||||
auto reply = command(cmd::spop, key);
|
||||
|
||||
return reply::parse<OptionalString>(*reply);
|
||||
}
|
||||
|
||||
OptionalString Redis::srandmember(const StringView &key) {
|
||||
auto reply = command(cmd::srandmember, key);
|
||||
|
||||
return reply::parse<OptionalString>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::srem(const StringView &key, const StringView &member) {
|
||||
auto reply = command(cmd::srem, key, member);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::sunionstore(const StringView &destination, const StringView &key) {
|
||||
auto reply = command(cmd::sunionstore, destination, key);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
// SORTED SET commands.
|
||||
|
||||
auto Redis::bzpopmax(const StringView &key, long long timeout)
|
||||
-> Optional<std::tuple<std::string, std::string, double>> {
|
||||
auto reply = command(cmd::bzpopmax, key, timeout);
|
||||
|
||||
return reply::parse<Optional<std::tuple<std::string, std::string, double>>>(*reply);
|
||||
}
|
||||
|
||||
auto Redis::bzpopmin(const StringView &key, long long timeout)
|
||||
-> Optional<std::tuple<std::string, std::string, double>> {
|
||||
auto reply = command(cmd::bzpopmin, key, timeout);
|
||||
|
||||
return reply::parse<Optional<std::tuple<std::string, std::string, double>>>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::zadd(const StringView &key,
|
||||
const StringView &member,
|
||||
double score,
|
||||
UpdateType type,
|
||||
bool changed) {
|
||||
auto reply = command(cmd::zadd, key, member, score, type, changed);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::zcard(const StringView &key) {
|
||||
auto reply = command(cmd::zcard, key);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
double Redis::zincrby(const StringView &key, double increment, const StringView &member) {
|
||||
auto reply = command(cmd::zincrby, key, increment, member);
|
||||
|
||||
return reply::parse<double>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::zinterstore(const StringView &destination, const StringView &key, double weight) {
|
||||
auto reply = command(cmd::zinterstore, destination, key, weight);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
Optional<std::pair<std::string, double>> Redis::zpopmax(const StringView &key) {
|
||||
auto reply = command(cmd::zpopmax, key, 1);
|
||||
|
||||
return reply::parse<Optional<std::pair<std::string, double>>>(*reply);
|
||||
}
|
||||
|
||||
Optional<std::pair<std::string, double>> Redis::zpopmin(const StringView &key) {
|
||||
auto reply = command(cmd::zpopmin, key, 1);
|
||||
|
||||
return reply::parse<Optional<std::pair<std::string, double>>>(*reply);
|
||||
}
|
||||
|
||||
OptionalLongLong Redis::zrank(const StringView &key, const StringView &member) {
|
||||
auto reply = command(cmd::zrank, key, member);
|
||||
|
||||
return reply::parse<OptionalLongLong>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::zrem(const StringView &key, const StringView &member) {
|
||||
auto reply = command(cmd::zrem, key, member);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::zremrangebyrank(const StringView &key, long long start, long long stop) {
|
||||
auto reply = command(cmd::zremrangebyrank, key, start, stop);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
OptionalLongLong Redis::zrevrank(const StringView &key, const StringView &member) {
|
||||
auto reply = command(cmd::zrevrank, key, member);
|
||||
|
||||
return reply::parse<OptionalLongLong>(*reply);
|
||||
}
|
||||
|
||||
OptionalDouble Redis::zscore(const StringView &key, const StringView &member) {
|
||||
auto reply = command(cmd::zscore, key, member);
|
||||
|
||||
return reply::parse<OptionalDouble>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::zunionstore(const StringView &destination, const StringView &key, double weight) {
|
||||
auto reply = command(cmd::zunionstore, destination, key, weight);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
// HYPERLOGLOG commands.
|
||||
|
||||
bool Redis::pfadd(const StringView &key, const StringView &element) {
|
||||
auto reply = command(cmd::pfadd, key, element);
|
||||
|
||||
return reply::parse<bool>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::pfcount(const StringView &key) {
|
||||
auto reply = command(cmd::pfcount, key);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
void Redis::pfmerge(const StringView &destination, const StringView &key) {
|
||||
auto reply = command(cmd::pfmerge, destination, key);
|
||||
|
||||
reply::parse<void>(*reply);
|
||||
}
|
||||
|
||||
// GEO commands.
|
||||
|
||||
long long Redis::geoadd(const StringView &key,
|
||||
const std::tuple<StringView, double, double> &member) {
|
||||
auto reply = command(cmd::geoadd, key, member);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
OptionalDouble Redis::geodist(const StringView &key,
|
||||
const StringView &member1,
|
||||
const StringView &member2,
|
||||
GeoUnit unit) {
|
||||
auto reply = command(cmd::geodist, key, member1, member2, unit);
|
||||
|
||||
return reply::parse<OptionalDouble>(*reply);
|
||||
}
|
||||
|
||||
OptionalLongLong Redis::georadius(const StringView &key,
|
||||
const std::pair<double, double> &loc,
|
||||
double radius,
|
||||
GeoUnit unit,
|
||||
const StringView &destination,
|
||||
bool store_dist,
|
||||
long long count) {
|
||||
auto reply = command(cmd::georadius_store,
|
||||
key,
|
||||
loc,
|
||||
radius,
|
||||
unit,
|
||||
destination,
|
||||
store_dist,
|
||||
count);
|
||||
|
||||
reply::rewrite_georadius_reply(*reply);
|
||||
|
||||
return reply::parse<OptionalLongLong>(*reply);
|
||||
}
|
||||
|
||||
OptionalLongLong Redis::georadiusbymember(const StringView &key,
|
||||
const StringView &member,
|
||||
double radius,
|
||||
GeoUnit unit,
|
||||
const StringView &destination,
|
||||
bool store_dist,
|
||||
long long count) {
|
||||
auto reply = command(cmd::georadiusbymember_store,
|
||||
key,
|
||||
member,
|
||||
radius,
|
||||
unit,
|
||||
destination,
|
||||
store_dist,
|
||||
count);
|
||||
|
||||
reply::rewrite_georadius_reply(*reply);
|
||||
|
||||
return reply::parse<OptionalLongLong>(*reply);
|
||||
}
|
||||
|
||||
// SCRIPTING commands.
|
||||
|
||||
void Redis::script_flush() {
|
||||
auto reply = command(cmd::script_flush);
|
||||
|
||||
reply::parse<void>(*reply);
|
||||
}
|
||||
|
||||
void Redis::script_kill() {
|
||||
auto reply = command(cmd::script_kill);
|
||||
|
||||
reply::parse<void>(*reply);
|
||||
}
|
||||
|
||||
std::string Redis::script_load(const StringView &script) {
|
||||
auto reply = command(cmd::script_load, script);
|
||||
|
||||
return reply::parse<std::string>(*reply);
|
||||
}
|
||||
|
||||
// PUBSUB commands.
|
||||
|
||||
long long Redis::publish(const StringView &channel, const StringView &message) {
|
||||
auto reply = command(cmd::publish, channel, message);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
// Transaction commands.
|
||||
|
||||
void Redis::watch(const StringView &key) {
|
||||
auto reply = command(cmd::watch, key);
|
||||
|
||||
reply::parse<void>(*reply);
|
||||
}
|
||||
|
||||
// Stream commands.
|
||||
|
||||
long long Redis::xack(const StringView &key, const StringView &group, const StringView &id) {
|
||||
auto reply = command(cmd::xack, key, group, id);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::xdel(const StringView &key, const StringView &id) {
|
||||
auto reply = command(cmd::xdel, key, id);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
void Redis::xgroup_create(const StringView &key,
|
||||
const StringView &group,
|
||||
const StringView &id,
|
||||
bool mkstream) {
|
||||
auto reply = command(cmd::xgroup_create, key, group, id, mkstream);
|
||||
|
||||
reply::parse<void>(*reply);
|
||||
}
|
||||
|
||||
void Redis::xgroup_setid(const StringView &key, const StringView &group, const StringView &id) {
|
||||
auto reply = command(cmd::xgroup_setid, key, group, id);
|
||||
|
||||
reply::parse<void>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::xgroup_destroy(const StringView &key, const StringView &group) {
|
||||
auto reply = command(cmd::xgroup_destroy, key, group);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::xgroup_delconsumer(const StringView &key,
|
||||
const StringView &group,
|
||||
const StringView &consumer) {
|
||||
auto reply = command(cmd::xgroup_delconsumer, key, group, consumer);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::xlen(const StringView &key) {
|
||||
auto reply = command(cmd::xlen, key);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long Redis::xtrim(const StringView &key, long long count, bool approx) {
|
||||
auto reply = command(cmd::xtrim, key, count, approx);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
1523
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/redis.h
vendored
Normal file
1523
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/redis.h
vendored
Normal file
File diff suppressed because it is too large
Load diff
1365
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/redis.hpp
vendored
Normal file
1365
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/redis.hpp
vendored
Normal file
File diff suppressed because it is too large
Load diff
769
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/redis_cluster.cpp
vendored
Normal file
769
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/redis_cluster.cpp
vendored
Normal file
|
@ -0,0 +1,769 @@
|
|||
/**************************************************************************
|
||||
Copyright (c) 2017 sewenew
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*************************************************************************/
|
||||
|
||||
#include "redis_cluster.h"
|
||||
#include <hiredis/hiredis.h>
|
||||
#include "command.h"
|
||||
#include "errors.h"
|
||||
#include "queued_redis.h"
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
RedisCluster::RedisCluster(const std::string &uri) : RedisCluster(ConnectionOptions(uri)) {}
|
||||
|
||||
Redis RedisCluster::redis(const StringView &hash_tag) {
|
||||
auto opts = _pool.connection_options(hash_tag);
|
||||
return Redis(std::make_shared<Connection>(opts));
|
||||
}
|
||||
|
||||
Pipeline RedisCluster::pipeline(const StringView &hash_tag) {
|
||||
auto opts = _pool.connection_options(hash_tag);
|
||||
return Pipeline(std::make_shared<Connection>(opts));
|
||||
}
|
||||
|
||||
Transaction RedisCluster::transaction(const StringView &hash_tag, bool piped) {
|
||||
auto opts = _pool.connection_options(hash_tag);
|
||||
return Transaction(std::make_shared<Connection>(opts), piped);
|
||||
}
|
||||
|
||||
Subscriber RedisCluster::subscriber() {
|
||||
auto opts = _pool.connection_options();
|
||||
return Subscriber(Connection(opts));
|
||||
}
|
||||
|
||||
// KEY commands.
|
||||
|
||||
long long RedisCluster::del(const StringView &key) {
|
||||
auto reply = command(cmd::del, key);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
OptionalString RedisCluster::dump(const StringView &key) {
|
||||
auto reply = command(cmd::dump, key);
|
||||
|
||||
return reply::parse<OptionalString>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::exists(const StringView &key) {
|
||||
auto reply = command(cmd::exists, key);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
bool RedisCluster::expire(const StringView &key, long long timeout) {
|
||||
auto reply = command(cmd::expire, key, timeout);
|
||||
|
||||
return reply::parse<bool>(*reply);
|
||||
}
|
||||
|
||||
bool RedisCluster::expireat(const StringView &key, long long timestamp) {
|
||||
auto reply = command(cmd::expireat, key, timestamp);
|
||||
|
||||
return reply::parse<bool>(*reply);
|
||||
}
|
||||
|
||||
bool RedisCluster::persist(const StringView &key) {
|
||||
auto reply = command(cmd::persist, key);
|
||||
|
||||
return reply::parse<bool>(*reply);
|
||||
}
|
||||
|
||||
bool RedisCluster::pexpire(const StringView &key, long long timeout) {
|
||||
auto reply = command(cmd::pexpire, key, timeout);
|
||||
|
||||
return reply::parse<bool>(*reply);
|
||||
}
|
||||
|
||||
bool RedisCluster::pexpireat(const StringView &key, long long timestamp) {
|
||||
auto reply = command(cmd::pexpireat, key, timestamp);
|
||||
|
||||
return reply::parse<bool>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::pttl(const StringView &key) {
|
||||
auto reply = command(cmd::pttl, key);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
void RedisCluster::rename(const StringView &key, const StringView &newkey) {
|
||||
auto reply = command(cmd::rename, key, newkey);
|
||||
|
||||
reply::parse<void>(*reply);
|
||||
}
|
||||
|
||||
bool RedisCluster::renamenx(const StringView &key, const StringView &newkey) {
|
||||
auto reply = command(cmd::renamenx, key, newkey);
|
||||
|
||||
return reply::parse<bool>(*reply);
|
||||
}
|
||||
|
||||
void RedisCluster::restore(const StringView &key,
|
||||
const StringView &val,
|
||||
long long ttl,
|
||||
bool replace) {
|
||||
auto reply = command(cmd::restore, key, val, ttl, replace);
|
||||
|
||||
reply::parse<void>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::touch(const StringView &key) {
|
||||
auto reply = command(cmd::touch, key);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::ttl(const StringView &key) {
|
||||
auto reply = command(cmd::ttl, key);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
std::string RedisCluster::type(const StringView &key) {
|
||||
auto reply = command(cmd::type, key);
|
||||
|
||||
return reply::parse<std::string>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::unlink(const StringView &key) {
|
||||
auto reply = command(cmd::unlink, key);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
// STRING commands.
|
||||
|
||||
long long RedisCluster::append(const StringView &key, const StringView &val) {
|
||||
auto reply = command(cmd::append, key, val);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::bitcount(const StringView &key, long long start, long long end) {
|
||||
auto reply = command(cmd::bitcount, key, start, end);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::bitop(BitOp op, const StringView &destination, const StringView &key) {
|
||||
auto reply = _command(cmd::bitop, destination, op, destination, key);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::bitpos(const StringView &key,
|
||||
long long bit,
|
||||
long long start,
|
||||
long long end) {
|
||||
auto reply = command(cmd::bitpos, key, bit, start, end);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::decr(const StringView &key) {
|
||||
auto reply = command(cmd::decr, key);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::decrby(const StringView &key, long long decrement) {
|
||||
auto reply = command(cmd::decrby, key, decrement);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
OptionalString RedisCluster::get(const StringView &key) {
|
||||
auto reply = command(cmd::get, key);
|
||||
|
||||
return reply::parse<OptionalString>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::getbit(const StringView &key, long long offset) {
|
||||
auto reply = command(cmd::getbit, key, offset);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
std::string RedisCluster::getrange(const StringView &key, long long start, long long end) {
|
||||
auto reply = command(cmd::getrange, key, start, end);
|
||||
|
||||
return reply::parse<std::string>(*reply);
|
||||
}
|
||||
|
||||
OptionalString RedisCluster::getset(const StringView &key, const StringView &val) {
|
||||
auto reply = command(cmd::getset, key, val);
|
||||
|
||||
return reply::parse<OptionalString>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::incr(const StringView &key) {
|
||||
auto reply = command(cmd::incr, key);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::incrby(const StringView &key, long long increment) {
|
||||
auto reply = command(cmd::incrby, key, increment);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
double RedisCluster::incrbyfloat(const StringView &key, double increment) {
|
||||
auto reply = command(cmd::incrbyfloat, key, increment);
|
||||
|
||||
return reply::parse<double>(*reply);
|
||||
}
|
||||
|
||||
void RedisCluster::psetex(const StringView &key,
|
||||
long long ttl,
|
||||
const StringView &val) {
|
||||
auto reply = command(cmd::psetex, key, ttl, val);
|
||||
|
||||
reply::parse<void>(*reply);
|
||||
}
|
||||
|
||||
bool RedisCluster::set(const StringView &key,
|
||||
const StringView &val,
|
||||
const std::chrono::milliseconds &ttl,
|
||||
UpdateType type) {
|
||||
auto reply = command(cmd::set, key, val, ttl.count(), type);
|
||||
|
||||
reply::rewrite_set_reply(*reply);
|
||||
|
||||
return reply::parse<bool>(*reply);
|
||||
}
|
||||
|
||||
void RedisCluster::setex(const StringView &key,
|
||||
long long ttl,
|
||||
const StringView &val) {
|
||||
auto reply = command(cmd::setex, key, ttl, val);
|
||||
|
||||
reply::parse<void>(*reply);
|
||||
}
|
||||
|
||||
bool RedisCluster::setnx(const StringView &key, const StringView &val) {
|
||||
auto reply = command(cmd::setnx, key, val);
|
||||
|
||||
return reply::parse<bool>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::setrange(const StringView &key, long long offset, const StringView &val) {
|
||||
auto reply = command(cmd::setrange, key, offset, val);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::strlen(const StringView &key) {
|
||||
auto reply = command(cmd::strlen, key);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
// LIST commands.
|
||||
|
||||
OptionalStringPair RedisCluster::blpop(const StringView &key, long long timeout) {
|
||||
auto reply = command(cmd::blpop, key, timeout);
|
||||
|
||||
return reply::parse<OptionalStringPair>(*reply);
|
||||
}
|
||||
|
||||
OptionalStringPair RedisCluster::blpop(const StringView &key, const std::chrono::seconds &timeout) {
|
||||
return blpop(key, timeout.count());
|
||||
}
|
||||
|
||||
OptionalStringPair RedisCluster::brpop(const StringView &key, long long timeout) {
|
||||
auto reply = command(cmd::brpop, key, timeout);
|
||||
|
||||
return reply::parse<OptionalStringPair>(*reply);
|
||||
}
|
||||
|
||||
OptionalStringPair RedisCluster::brpop(const StringView &key, const std::chrono::seconds &timeout) {
|
||||
return brpop(key, timeout.count());
|
||||
}
|
||||
|
||||
OptionalString RedisCluster::brpoplpush(const StringView &source,
|
||||
const StringView &destination,
|
||||
long long timeout) {
|
||||
auto reply = command(cmd::brpoplpush, source, destination, timeout);
|
||||
|
||||
return reply::parse<OptionalString>(*reply);
|
||||
}
|
||||
|
||||
OptionalString RedisCluster::lindex(const StringView &key, long long index) {
|
||||
auto reply = command(cmd::lindex, key, index);
|
||||
|
||||
return reply::parse<OptionalString>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::linsert(const StringView &key,
|
||||
InsertPosition position,
|
||||
const StringView &pivot,
|
||||
const StringView &val) {
|
||||
auto reply = command(cmd::linsert, key, position, pivot, val);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::llen(const StringView &key) {
|
||||
auto reply = command(cmd::llen, key);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
OptionalString RedisCluster::lpop(const StringView &key) {
|
||||
auto reply = command(cmd::lpop, key);
|
||||
|
||||
return reply::parse<OptionalString>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::lpush(const StringView &key, const StringView &val) {
|
||||
auto reply = command(cmd::lpush, key, val);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::lpushx(const StringView &key, const StringView &val) {
|
||||
auto reply = command(cmd::lpushx, key, val);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::lrem(const StringView &key, long long count, const StringView &val) {
|
||||
auto reply = command(cmd::lrem, key, count, val);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
void RedisCluster::lset(const StringView &key, long long index, const StringView &val) {
|
||||
auto reply = command(cmd::lset, key, index, val);
|
||||
|
||||
reply::parse<void>(*reply);
|
||||
}
|
||||
|
||||
void RedisCluster::ltrim(const StringView &key, long long start, long long stop) {
|
||||
auto reply = command(cmd::ltrim, key, start, stop);
|
||||
|
||||
reply::parse<void>(*reply);
|
||||
}
|
||||
|
||||
OptionalString RedisCluster::rpop(const StringView &key) {
|
||||
auto reply = command(cmd::rpop, key);
|
||||
|
||||
return reply::parse<OptionalString>(*reply);
|
||||
}
|
||||
|
||||
OptionalString RedisCluster::rpoplpush(const StringView &source, const StringView &destination) {
|
||||
auto reply = command(cmd::rpoplpush, source, destination);
|
||||
|
||||
return reply::parse<OptionalString>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::rpush(const StringView &key, const StringView &val) {
|
||||
auto reply = command(cmd::rpush, key, val);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::rpushx(const StringView &key, const StringView &val) {
|
||||
auto reply = command(cmd::rpushx, key, val);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::hdel(const StringView &key, const StringView &field) {
|
||||
auto reply = command(cmd::hdel, key, field);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
bool RedisCluster::hexists(const StringView &key, const StringView &field) {
|
||||
auto reply = command(cmd::hexists, key, field);
|
||||
|
||||
return reply::parse<bool>(*reply);
|
||||
}
|
||||
|
||||
OptionalString RedisCluster::hget(const StringView &key, const StringView &field) {
|
||||
auto reply = command(cmd::hget, key, field);
|
||||
|
||||
return reply::parse<OptionalString>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::hincrby(const StringView &key, const StringView &field, long long increment) {
|
||||
auto reply = command(cmd::hincrby, key, field, increment);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
double RedisCluster::hincrbyfloat(const StringView &key, const StringView &field, double increment) {
|
||||
auto reply = command(cmd::hincrbyfloat, key, field, increment);
|
||||
|
||||
return reply::parse<double>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::hlen(const StringView &key) {
|
||||
auto reply = command(cmd::hlen, key);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
bool RedisCluster::hset(const StringView &key, const StringView &field, const StringView &val) {
|
||||
auto reply = command(cmd::hset, key, field, val);
|
||||
|
||||
return reply::parse<bool>(*reply);
|
||||
}
|
||||
|
||||
bool RedisCluster::hset(const StringView &key, const std::pair<StringView, StringView> &item) {
|
||||
return hset(key, item.first, item.second);
|
||||
}
|
||||
|
||||
bool RedisCluster::hsetnx(const StringView &key, const StringView &field, const StringView &val) {
|
||||
auto reply = command(cmd::hsetnx, key, field, val);
|
||||
|
||||
return reply::parse<bool>(*reply);
|
||||
}
|
||||
|
||||
bool RedisCluster::hsetnx(const StringView &key, const std::pair<StringView, StringView> &item) {
|
||||
return hsetnx(key, item.first, item.second);
|
||||
}
|
||||
|
||||
long long RedisCluster::hstrlen(const StringView &key, const StringView &field) {
|
||||
auto reply = command(cmd::hstrlen, key, field);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
// SET commands.
|
||||
|
||||
long long RedisCluster::sadd(const StringView &key, const StringView &member) {
|
||||
auto reply = command(cmd::sadd, key, member);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::scard(const StringView &key) {
|
||||
auto reply = command(cmd::scard, key);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::sdiffstore(const StringView &destination, const StringView &key) {
|
||||
auto reply = command(cmd::sdiffstore, destination, key);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::sinterstore(const StringView &destination, const StringView &key) {
|
||||
auto reply = command(cmd::sinterstore, destination, key);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
bool RedisCluster::sismember(const StringView &key, const StringView &member) {
|
||||
auto reply = command(cmd::sismember, key, member);
|
||||
|
||||
return reply::parse<bool>(*reply);
|
||||
}
|
||||
|
||||
bool RedisCluster::smove(const StringView &source,
|
||||
const StringView &destination,
|
||||
const StringView &member) {
|
||||
auto reply = command(cmd::smove, source, destination, member);
|
||||
|
||||
return reply::parse<bool>(*reply);
|
||||
}
|
||||
|
||||
OptionalString RedisCluster::spop(const StringView &key) {
|
||||
auto reply = command(cmd::spop, key);
|
||||
|
||||
return reply::parse<OptionalString>(*reply);
|
||||
}
|
||||
|
||||
OptionalString RedisCluster::srandmember(const StringView &key) {
|
||||
auto reply = command(cmd::srandmember, key);
|
||||
|
||||
return reply::parse<OptionalString>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::srem(const StringView &key, const StringView &member) {
|
||||
auto reply = command(cmd::srem, key, member);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::sunionstore(const StringView &destination, const StringView &key) {
|
||||
auto reply = command(cmd::sunionstore, destination, key);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
// SORTED SET commands.
|
||||
|
||||
auto RedisCluster::bzpopmax(const StringView &key, long long timeout)
|
||||
-> Optional<std::tuple<std::string, std::string, double>> {
|
||||
auto reply = command(cmd::bzpopmax, key, timeout);
|
||||
|
||||
return reply::parse<Optional<std::tuple<std::string, std::string, double>>>(*reply);
|
||||
}
|
||||
|
||||
auto RedisCluster::bzpopmin(const StringView &key, long long timeout)
|
||||
-> Optional<std::tuple<std::string, std::string, double>> {
|
||||
auto reply = command(cmd::bzpopmin, key, timeout);
|
||||
|
||||
return reply::parse<Optional<std::tuple<std::string, std::string, double>>>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::zadd(const StringView &key,
|
||||
const StringView &member,
|
||||
double score,
|
||||
UpdateType type,
|
||||
bool changed) {
|
||||
auto reply = command(cmd::zadd, key, member, score, type, changed);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::zcard(const StringView &key) {
|
||||
auto reply = command(cmd::zcard, key);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
double RedisCluster::zincrby(const StringView &key, double increment, const StringView &member) {
|
||||
auto reply = command(cmd::zincrby, key, increment, member);
|
||||
|
||||
return reply::parse<double>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::zinterstore(const StringView &destination,
|
||||
const StringView &key,
|
||||
double weight) {
|
||||
auto reply = command(cmd::zinterstore, destination, key, weight);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
Optional<std::pair<std::string, double>> RedisCluster::zpopmax(const StringView &key) {
|
||||
auto reply = command(cmd::zpopmax, key, 1);
|
||||
|
||||
return reply::parse<Optional<std::pair<std::string, double>>>(*reply);
|
||||
}
|
||||
|
||||
Optional<std::pair<std::string, double>> RedisCluster::zpopmin(const StringView &key) {
|
||||
auto reply = command(cmd::zpopmin, key, 1);
|
||||
|
||||
return reply::parse<Optional<std::pair<std::string, double>>>(*reply);
|
||||
}
|
||||
|
||||
OptionalLongLong RedisCluster::zrank(const StringView &key, const StringView &member) {
|
||||
auto reply = command(cmd::zrank, key, member);
|
||||
|
||||
return reply::parse<OptionalLongLong>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::zrem(const StringView &key, const StringView &member) {
|
||||
auto reply = command(cmd::zrem, key, member);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::zremrangebyrank(const StringView &key, long long start, long long stop) {
|
||||
auto reply = command(cmd::zremrangebyrank, key, start, stop);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
OptionalLongLong RedisCluster::zrevrank(const StringView &key, const StringView &member) {
|
||||
auto reply = command(cmd::zrevrank, key, member);
|
||||
|
||||
return reply::parse<OptionalLongLong>(*reply);
|
||||
}
|
||||
|
||||
OptionalDouble RedisCluster::zscore(const StringView &key, const StringView &member) {
|
||||
auto reply = command(cmd::zscore, key, member);
|
||||
|
||||
return reply::parse<OptionalDouble>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::zunionstore(const StringView &destination,
|
||||
const StringView &key,
|
||||
double weight) {
|
||||
auto reply = command(cmd::zunionstore, destination, key, weight);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
// HYPERLOGLOG commands.
|
||||
|
||||
bool RedisCluster::pfadd(const StringView &key, const StringView &element) {
|
||||
auto reply = command(cmd::pfadd, key, element);
|
||||
|
||||
return reply::parse<bool>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::pfcount(const StringView &key) {
|
||||
auto reply = command(cmd::pfcount, key);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
void RedisCluster::pfmerge(const StringView &destination, const StringView &key) {
|
||||
auto reply = command(cmd::pfmerge, destination, key);
|
||||
|
||||
reply::parse<void>(*reply);
|
||||
}
|
||||
|
||||
// GEO commands.
|
||||
|
||||
long long RedisCluster::geoadd(const StringView &key,
|
||||
const std::tuple<StringView, double, double> &member) {
|
||||
auto reply = command(cmd::geoadd, key, member);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
OptionalDouble RedisCluster::geodist(const StringView &key,
|
||||
const StringView &member1,
|
||||
const StringView &member2,
|
||||
GeoUnit unit) {
|
||||
auto reply = command(cmd::geodist, key, member1, member2, unit);
|
||||
|
||||
return reply::parse<OptionalDouble>(*reply);
|
||||
}
|
||||
|
||||
OptionalLongLong RedisCluster::georadius(const StringView &key,
|
||||
const std::pair<double, double> &loc,
|
||||
double radius,
|
||||
GeoUnit unit,
|
||||
const StringView &destination,
|
||||
bool store_dist,
|
||||
long long count) {
|
||||
auto reply = command(cmd::georadius_store,
|
||||
key,
|
||||
loc,
|
||||
radius,
|
||||
unit,
|
||||
destination,
|
||||
store_dist,
|
||||
count);
|
||||
|
||||
reply::rewrite_georadius_reply(*reply);
|
||||
|
||||
return reply::parse<OptionalLongLong>(*reply);
|
||||
}
|
||||
|
||||
OptionalLongLong RedisCluster::georadiusbymember(const StringView &key,
|
||||
const StringView &member,
|
||||
double radius,
|
||||
GeoUnit unit,
|
||||
const StringView &destination,
|
||||
bool store_dist,
|
||||
long long count) {
|
||||
auto reply = command(cmd::georadiusbymember_store,
|
||||
key,
|
||||
member,
|
||||
radius,
|
||||
unit,
|
||||
destination,
|
||||
store_dist,
|
||||
count);
|
||||
|
||||
reply::rewrite_georadius_reply(*reply);
|
||||
|
||||
return reply::parse<OptionalLongLong>(*reply);
|
||||
}
|
||||
|
||||
// PUBSUB commands.
|
||||
|
||||
long long RedisCluster::publish(const StringView &channel, const StringView &message) {
|
||||
auto reply = command(cmd::publish, channel, message);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
// Stream commands.
|
||||
|
||||
long long RedisCluster::xack(const StringView &key, const StringView &group, const StringView &id) {
|
||||
auto reply = command(cmd::xack, key, group, id);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::xdel(const StringView &key, const StringView &id) {
|
||||
auto reply = command(cmd::xdel, key, id);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
void RedisCluster::xgroup_create(const StringView &key,
|
||||
const StringView &group,
|
||||
const StringView &id,
|
||||
bool mkstream) {
|
||||
auto reply = command(cmd::xgroup_create, key, group, id, mkstream);
|
||||
|
||||
reply::parse<void>(*reply);
|
||||
}
|
||||
|
||||
void RedisCluster::xgroup_setid(const StringView &key,
|
||||
const StringView &group,
|
||||
const StringView &id) {
|
||||
auto reply = command(cmd::xgroup_setid, key, group, id);
|
||||
|
||||
reply::parse<void>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::xgroup_destroy(const StringView &key, const StringView &group) {
|
||||
auto reply = command(cmd::xgroup_destroy, key, group);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::xgroup_delconsumer(const StringView &key,
|
||||
const StringView &group,
|
||||
const StringView &consumer) {
|
||||
auto reply = command(cmd::xgroup_delconsumer, key, group, consumer);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::xlen(const StringView &key) {
|
||||
auto reply = command(cmd::xlen, key);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
long long RedisCluster::xtrim(const StringView &key, long long count, bool approx) {
|
||||
auto reply = command(cmd::xtrim, key, count, approx);
|
||||
|
||||
return reply::parse<long long>(*reply);
|
||||
}
|
||||
|
||||
void RedisCluster::_asking(Connection &connection) {
|
||||
// Send ASKING command.
|
||||
connection.send("ASKING");
|
||||
|
||||
auto reply = connection.recv();
|
||||
|
||||
assert(reply);
|
||||
|
||||
reply::parse<void>(*reply);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
1395
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/redis_cluster.h
vendored
Normal file
1395
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/redis_cluster.h
vendored
Normal file
File diff suppressed because it is too large
Load diff
1415
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/redis_cluster.hpp
vendored
Normal file
1415
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/redis_cluster.hpp
vendored
Normal file
File diff suppressed because it is too large
Load diff
150
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/reply.cpp
vendored
Normal file
150
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/reply.cpp
vendored
Normal file
|
@ -0,0 +1,150 @@
|
|||
/**************************************************************************
|
||||
Copyright (c) 2017 sewenew
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*************************************************************************/
|
||||
|
||||
#include "reply.h"
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
namespace reply {
|
||||
|
||||
std::string to_status(redisReply &reply) {
|
||||
if (!reply::is_status(reply)) {
|
||||
throw ProtoError("Expect STATUS reply");
|
||||
}
|
||||
|
||||
if (reply.str == nullptr) {
|
||||
throw ProtoError("A null status reply");
|
||||
}
|
||||
|
||||
// Old version hiredis' *redisReply::len* is of type int.
|
||||
// So we CANNOT have something like: *return {reply.str, reply.len}*.
|
||||
return std::string(reply.str, reply.len);
|
||||
}
|
||||
|
||||
std::string parse(ParseTag<std::string>, redisReply &reply) {
|
||||
if (!reply::is_string(reply) && !reply::is_status(reply)) {
|
||||
throw ProtoError("Expect STRING reply");
|
||||
}
|
||||
|
||||
if (reply.str == nullptr) {
|
||||
throw ProtoError("A null string reply");
|
||||
}
|
||||
|
||||
// Old version hiredis' *redisReply::len* is of type int.
|
||||
// So we CANNOT have something like: *return {reply.str, reply.len}*.
|
||||
return std::string(reply.str, reply.len);
|
||||
}
|
||||
|
||||
long long parse(ParseTag<long long>, redisReply &reply) {
|
||||
if (!reply::is_integer(reply)) {
|
||||
throw ProtoError("Expect INTEGER reply");
|
||||
}
|
||||
|
||||
return reply.integer;
|
||||
}
|
||||
|
||||
double parse(ParseTag<double>, redisReply &reply) {
|
||||
return std::stod(parse<std::string>(reply));
|
||||
}
|
||||
|
||||
bool parse(ParseTag<bool>, redisReply &reply) {
|
||||
auto ret = parse<long long>(reply);
|
||||
|
||||
if (ret == 1) {
|
||||
return true;
|
||||
} else if (ret == 0) {
|
||||
return false;
|
||||
} else {
|
||||
throw ProtoError("Invalid bool reply: " + std::to_string(ret));
|
||||
}
|
||||
}
|
||||
|
||||
void parse(ParseTag<void>, redisReply &reply) {
|
||||
if (!reply::is_status(reply)) {
|
||||
throw ProtoError("Expect STATUS reply");
|
||||
}
|
||||
|
||||
if (reply.str == nullptr) {
|
||||
throw ProtoError("A null status reply");
|
||||
}
|
||||
|
||||
static const std::string OK = "OK";
|
||||
|
||||
// Old version hiredis' *redisReply::len* is of type int.
|
||||
// So we have to cast it to an unsigned int.
|
||||
if (static_cast<std::size_t>(reply.len) != OK.size()
|
||||
|| OK.compare(0, OK.size(), reply.str, reply.len) != 0) {
|
||||
throw ProtoError("NOT ok status reply: " + reply::to_status(reply));
|
||||
}
|
||||
}
|
||||
|
||||
void rewrite_set_reply(redisReply &reply) {
|
||||
if (is_nil(reply)) {
|
||||
// Failed to set, and make it a FALSE reply.
|
||||
reply.type = REDIS_REPLY_INTEGER;
|
||||
reply.integer = 0;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if it's a "OK" status reply.
|
||||
reply::parse<void>(reply);
|
||||
|
||||
assert(is_status(reply) && reply.str != nullptr);
|
||||
|
||||
free(reply.str);
|
||||
|
||||
// Make it a TRUE reply.
|
||||
reply.type = REDIS_REPLY_INTEGER;
|
||||
reply.integer = 1;
|
||||
}
|
||||
|
||||
void rewrite_georadius_reply(redisReply &reply) {
|
||||
if (is_array(reply) && reply.element == nullptr) {
|
||||
// Make it a nil reply.
|
||||
reply.type = REDIS_REPLY_NIL;
|
||||
}
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
|
||||
bool is_flat_array(redisReply &reply) {
|
||||
assert(reply::is_array(reply));
|
||||
|
||||
// Empty array reply.
|
||||
if (reply.element == nullptr || reply.elements == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto *sub_reply = reply.element[0];
|
||||
|
||||
// Null element.
|
||||
if (sub_reply == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return !reply::is_array(*sub_reply);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
363
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/reply.h
vendored
Normal file
363
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/reply.h
vendored
Normal file
|
@ -0,0 +1,363 @@
|
|||
/**************************************************************************
|
||||
Copyright (c) 2017 sewenew
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*************************************************************************/
|
||||
|
||||
#ifndef SEWENEW_REDISPLUSPLUS_REPLY_H
|
||||
#define SEWENEW_REDISPLUSPLUS_REPLY_H
|
||||
|
||||
#include <cassert>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
#include <tuple>
|
||||
#include <hiredis/hiredis.h>
|
||||
#include "errors.h"
|
||||
#include "utils.h"
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
struct ReplyDeleter {
|
||||
void operator()(redisReply *reply) const {
|
||||
if (reply != nullptr) {
|
||||
freeReplyObject(reply);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
using ReplyUPtr = std::unique_ptr<redisReply, ReplyDeleter>;
|
||||
|
||||
namespace reply {
|
||||
|
||||
template <typename T>
|
||||
struct ParseTag {};
|
||||
|
||||
template <typename T>
|
||||
inline T parse(redisReply &reply) {
|
||||
return parse(ParseTag<T>(), reply);
|
||||
}
|
||||
|
||||
void parse(ParseTag<void>, redisReply &reply);
|
||||
|
||||
std::string parse(ParseTag<std::string>, redisReply &reply);
|
||||
|
||||
long long parse(ParseTag<long long>, redisReply &reply);
|
||||
|
||||
double parse(ParseTag<double>, redisReply &reply);
|
||||
|
||||
bool parse(ParseTag<bool>, redisReply &reply);
|
||||
|
||||
template <typename T>
|
||||
Optional<T> parse(ParseTag<Optional<T>>, redisReply &reply);
|
||||
|
||||
template <typename T, typename U>
|
||||
std::pair<T, U> parse(ParseTag<std::pair<T, U>>, redisReply &reply);
|
||||
|
||||
template <typename ...Args>
|
||||
std::tuple<Args...> parse(ParseTag<std::tuple<Args...>>, redisReply &reply);
|
||||
|
||||
template <typename T, typename std::enable_if<IsSequenceContainer<T>::value, int>::type = 0>
|
||||
T parse(ParseTag<T>, redisReply &reply);
|
||||
|
||||
template <typename T, typename std::enable_if<IsAssociativeContainer<T>::value, int>::type = 0>
|
||||
T parse(ParseTag<T>, redisReply &reply);
|
||||
|
||||
template <typename Output>
|
||||
long long parse_scan_reply(redisReply &reply, Output output);
|
||||
|
||||
inline bool is_error(redisReply &reply) {
|
||||
return reply.type == REDIS_REPLY_ERROR;
|
||||
}
|
||||
|
||||
inline bool is_nil(redisReply &reply) {
|
||||
return reply.type == REDIS_REPLY_NIL;
|
||||
}
|
||||
|
||||
inline bool is_string(redisReply &reply) {
|
||||
return reply.type == REDIS_REPLY_STRING;
|
||||
}
|
||||
|
||||
inline bool is_status(redisReply &reply) {
|
||||
return reply.type == REDIS_REPLY_STATUS;
|
||||
}
|
||||
|
||||
inline bool is_integer(redisReply &reply) {
|
||||
return reply.type == REDIS_REPLY_INTEGER;
|
||||
}
|
||||
|
||||
inline bool is_array(redisReply &reply) {
|
||||
return reply.type == REDIS_REPLY_ARRAY;
|
||||
}
|
||||
|
||||
std::string to_status(redisReply &reply);
|
||||
|
||||
template <typename Output>
|
||||
void to_array(redisReply &reply, Output output);
|
||||
|
||||
// Rewrite set reply to bool type
|
||||
void rewrite_set_reply(redisReply &reply);
|
||||
|
||||
// Rewrite georadius reply to OptionalLongLong type
|
||||
void rewrite_georadius_reply(redisReply &reply);
|
||||
|
||||
template <typename Output>
|
||||
auto parse_xpending_reply(redisReply &reply, Output output)
|
||||
-> std::tuple<long long, OptionalString, OptionalString>;
|
||||
|
||||
}
|
||||
|
||||
// Inline implementations.
|
||||
|
||||
namespace reply {
|
||||
|
||||
namespace detail {
|
||||
|
||||
template <typename Output>
|
||||
void to_array(redisReply &reply, Output output) {
|
||||
if (!is_array(reply)) {
|
||||
throw ProtoError("Expect ARRAY reply");
|
||||
}
|
||||
|
||||
if (reply.element == nullptr) {
|
||||
// Empty array.
|
||||
return;
|
||||
}
|
||||
|
||||
for (std::size_t idx = 0; idx != reply.elements; ++idx) {
|
||||
auto *sub_reply = reply.element[idx];
|
||||
if (sub_reply == nullptr) {
|
||||
throw ProtoError("Null array element reply");
|
||||
}
|
||||
|
||||
*output = parse<typename IterType<Output>::type>(*sub_reply);
|
||||
|
||||
++output;
|
||||
}
|
||||
}
|
||||
|
||||
bool is_flat_array(redisReply &reply);
|
||||
|
||||
template <typename Output>
|
||||
void to_flat_array(redisReply &reply, Output output) {
|
||||
if (reply.element == nullptr) {
|
||||
// Empty array.
|
||||
return;
|
||||
}
|
||||
|
||||
if (reply.elements % 2 != 0) {
|
||||
throw ProtoError("Not string pair array reply");
|
||||
}
|
||||
|
||||
for (std::size_t idx = 0; idx != reply.elements; idx += 2) {
|
||||
auto *key_reply = reply.element[idx];
|
||||
auto *val_reply = reply.element[idx + 1];
|
||||
if (key_reply == nullptr || val_reply == nullptr) {
|
||||
throw ProtoError("Null string array reply");
|
||||
}
|
||||
|
||||
using Pair = typename IterType<Output>::type;
|
||||
using FirstType = typename std::decay<typename Pair::first_type>::type;
|
||||
using SecondType = typename std::decay<typename Pair::second_type>::type;
|
||||
*output = std::make_pair(parse<FirstType>(*key_reply),
|
||||
parse<SecondType>(*val_reply));
|
||||
|
||||
++output;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Output>
|
||||
void to_array(std::true_type, redisReply &reply, Output output) {
|
||||
if (is_flat_array(reply)) {
|
||||
to_flat_array(reply, output);
|
||||
} else {
|
||||
to_array(reply, output);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Output>
|
||||
void to_array(std::false_type, redisReply &reply, Output output) {
|
||||
to_array(reply, output);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::tuple<T> parse_tuple(redisReply **reply, std::size_t idx) {
|
||||
assert(reply != nullptr);
|
||||
|
||||
auto *sub_reply = reply[idx];
|
||||
if (sub_reply == nullptr) {
|
||||
throw ProtoError("Null reply");
|
||||
}
|
||||
|
||||
return std::make_tuple(parse<T>(*sub_reply));
|
||||
}
|
||||
|
||||
template <typename T, typename ...Args>
|
||||
auto parse_tuple(redisReply **reply, std::size_t idx) ->
|
||||
typename std::enable_if<sizeof...(Args) != 0, std::tuple<T, Args...>>::type {
|
||||
assert(reply != nullptr);
|
||||
|
||||
return std::tuple_cat(parse_tuple<T>(reply, idx),
|
||||
parse_tuple<Args...>(reply, idx + 1));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Optional<T> parse(ParseTag<Optional<T>>, redisReply &reply) {
|
||||
if (reply::is_nil(reply)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return Optional<T>(parse<T>(reply));
|
||||
}
|
||||
|
||||
template <typename T, typename U>
|
||||
std::pair<T, U> parse(ParseTag<std::pair<T, U>>, redisReply &reply) {
|
||||
if (!is_array(reply)) {
|
||||
throw ProtoError("Expect ARRAY reply");
|
||||
}
|
||||
|
||||
if (reply.elements != 2) {
|
||||
throw ProtoError("NOT key-value PAIR reply");
|
||||
}
|
||||
|
||||
if (reply.element == nullptr) {
|
||||
throw ProtoError("Null PAIR reply");
|
||||
}
|
||||
|
||||
auto *first = reply.element[0];
|
||||
auto *second = reply.element[1];
|
||||
if (first == nullptr || second == nullptr) {
|
||||
throw ProtoError("Null pair reply");
|
||||
}
|
||||
|
||||
return std::make_pair(parse<typename std::decay<T>::type>(*first),
|
||||
parse<typename std::decay<U>::type>(*second));
|
||||
}
|
||||
|
||||
template <typename ...Args>
|
||||
std::tuple<Args...> parse(ParseTag<std::tuple<Args...>>, redisReply &reply) {
|
||||
constexpr auto size = sizeof...(Args);
|
||||
|
||||
static_assert(size > 0, "DO NOT support parsing tuple with 0 element");
|
||||
|
||||
if (!is_array(reply)) {
|
||||
throw ProtoError("Expect ARRAY reply");
|
||||
}
|
||||
|
||||
if (reply.elements != size) {
|
||||
throw ProtoError("Expect tuple reply with " + std::to_string(size) + "elements");
|
||||
}
|
||||
|
||||
if (reply.element == nullptr) {
|
||||
throw ProtoError("Null TUPLE reply");
|
||||
}
|
||||
|
||||
return detail::parse_tuple<Args...>(reply.element, 0);
|
||||
}
|
||||
|
||||
template <typename T, typename std::enable_if<IsSequenceContainer<T>::value, int>::type>
|
||||
T parse(ParseTag<T>, redisReply &reply) {
|
||||
if (!is_array(reply)) {
|
||||
throw ProtoError("Expect ARRAY reply");
|
||||
}
|
||||
|
||||
T container;
|
||||
|
||||
to_array(reply, std::back_inserter(container));
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
template <typename T, typename std::enable_if<IsAssociativeContainer<T>::value, int>::type>
|
||||
T parse(ParseTag<T>, redisReply &reply) {
|
||||
if (!is_array(reply)) {
|
||||
throw ProtoError("Expect ARRAY reply");
|
||||
}
|
||||
|
||||
T container;
|
||||
|
||||
to_array(reply, std::inserter(container, container.end()));
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
template <typename Output>
|
||||
long long parse_scan_reply(redisReply &reply, Output output) {
|
||||
if (reply.elements != 2 || reply.element == nullptr) {
|
||||
throw ProtoError("Invalid scan reply");
|
||||
}
|
||||
|
||||
auto *cursor_reply = reply.element[0];
|
||||
auto *data_reply = reply.element[1];
|
||||
if (cursor_reply == nullptr || data_reply == nullptr) {
|
||||
throw ProtoError("Invalid cursor reply or data reply");
|
||||
}
|
||||
|
||||
auto cursor_str = reply::parse<std::string>(*cursor_reply);
|
||||
auto new_cursor = 0;
|
||||
try {
|
||||
new_cursor = std::stoll(cursor_str);
|
||||
} catch (const std::exception &e) {
|
||||
throw ProtoError("Invalid cursor reply: " + cursor_str);
|
||||
}
|
||||
|
||||
reply::to_array(*data_reply, output);
|
||||
|
||||
return new_cursor;
|
||||
}
|
||||
|
||||
template <typename Output>
|
||||
void to_array(redisReply &reply, Output output) {
|
||||
if (!is_array(reply)) {
|
||||
throw ProtoError("Expect ARRAY reply");
|
||||
}
|
||||
|
||||
detail::to_array(typename IsKvPairIter<Output>::type(), reply, output);
|
||||
}
|
||||
|
||||
template <typename Output>
|
||||
auto parse_xpending_reply(redisReply &reply, Output output)
|
||||
-> std::tuple<long long, OptionalString, OptionalString> {
|
||||
if (!is_array(reply) || reply.elements != 4) {
|
||||
throw ProtoError("expect array reply with 4 elements");
|
||||
}
|
||||
|
||||
for (std::size_t idx = 0; idx != reply.elements; ++idx) {
|
||||
if (reply.element[idx] == nullptr) {
|
||||
throw ProtoError("null array reply");
|
||||
}
|
||||
}
|
||||
|
||||
auto num = parse<long long>(*(reply.element[0]));
|
||||
auto start = parse<OptionalString>(*(reply.element[1]));
|
||||
auto end = parse<OptionalString>(*(reply.element[2]));
|
||||
|
||||
auto &entry_reply = *(reply.element[3]);
|
||||
if (!is_nil(entry_reply)) {
|
||||
to_array(entry_reply, output);
|
||||
}
|
||||
|
||||
return std::make_tuple(num, std::move(start), std::move(end));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // end SEWENEW_REDISPLUSPLUS_REPLY_H
|
361
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/sentinel.cpp
vendored
Normal file
361
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/sentinel.cpp
vendored
Normal file
|
@ -0,0 +1,361 @@
|
|||
/**************************************************************************
|
||||
Copyright (c) 2017 sewenew
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*************************************************************************/
|
||||
|
||||
#include "sentinel.h"
|
||||
#include <cassert>
|
||||
#include <thread>
|
||||
#include <random>
|
||||
#include <algorithm>
|
||||
#include "redis.h"
|
||||
#include "errors.h"
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
class Sentinel::Iterator {
|
||||
public:
|
||||
Iterator(std::list<Connection> &healthy_sentinels,
|
||||
std::list<ConnectionOptions> &broken_sentinels) :
|
||||
_healthy_sentinels(healthy_sentinels),
|
||||
_broken_sentinels(broken_sentinels) {
|
||||
reset();
|
||||
}
|
||||
|
||||
Connection& next();
|
||||
|
||||
void reset();
|
||||
|
||||
private:
|
||||
std::list<Connection> &_healthy_sentinels;
|
||||
|
||||
std::size_t _healthy_size = 0;
|
||||
|
||||
std::list<ConnectionOptions> &_broken_sentinels;
|
||||
|
||||
std::size_t _broken_size = 0;
|
||||
};
|
||||
|
||||
Connection& Sentinel::Iterator::next() {
|
||||
while (_healthy_size > 0) {
|
||||
assert(_healthy_sentinels.size() >= _healthy_size);
|
||||
|
||||
--_healthy_size;
|
||||
|
||||
auto &connection = _healthy_sentinels.front();
|
||||
if (connection.broken()) {
|
||||
_broken_sentinels.push_front(connection.options());
|
||||
++_broken_size;
|
||||
|
||||
_healthy_sentinels.pop_front();
|
||||
} else {
|
||||
_healthy_sentinels.splice(_healthy_sentinels.end(),
|
||||
_healthy_sentinels,
|
||||
_healthy_sentinels.begin());
|
||||
|
||||
return _healthy_sentinels.back();
|
||||
}
|
||||
}
|
||||
|
||||
while (_broken_size > 0) {
|
||||
assert(_broken_sentinels.size() >= _broken_size);
|
||||
|
||||
--_broken_size;
|
||||
|
||||
try {
|
||||
const auto &opt = _broken_sentinels.front();
|
||||
Connection connection(opt);
|
||||
_healthy_sentinels.push_back(std::move(connection));
|
||||
|
||||
_broken_sentinels.pop_front();
|
||||
|
||||
return _healthy_sentinels.back();
|
||||
} catch (const Error &e) {
|
||||
// Failed to connect to sentinel.
|
||||
_broken_sentinels.splice(_broken_sentinels.end(),
|
||||
_broken_sentinels,
|
||||
_broken_sentinels.begin());
|
||||
}
|
||||
}
|
||||
|
||||
throw StopIterError();
|
||||
}
|
||||
|
||||
void Sentinel::Iterator::reset() {
|
||||
_healthy_size = _healthy_sentinels.size();
|
||||
_broken_size = _broken_sentinels.size();
|
||||
}
|
||||
|
||||
Sentinel::Sentinel(const SentinelOptions &sentinel_opts) :
|
||||
_broken_sentinels(_parse_options(sentinel_opts)),
|
||||
_sentinel_opts(sentinel_opts) {
|
||||
if (_sentinel_opts.connect_timeout == std::chrono::milliseconds(0)
|
||||
|| _sentinel_opts.socket_timeout == std::chrono::milliseconds(0)) {
|
||||
throw Error("With sentinel, connection timeout and socket timeout cannot be 0");
|
||||
}
|
||||
}
|
||||
|
||||
Connection Sentinel::master(const std::string &master_name, const ConnectionOptions &opts) {
|
||||
std::lock_guard<std::mutex> lock(_mutex);
|
||||
|
||||
Iterator iter(_healthy_sentinels, _broken_sentinels);
|
||||
std::size_t retries = 0;
|
||||
while (true) {
|
||||
try {
|
||||
auto &sentinel = iter.next();
|
||||
|
||||
auto master = _get_master_addr_by_name(sentinel, master_name);
|
||||
if (!master) {
|
||||
// Try the next sentinel.
|
||||
continue;
|
||||
}
|
||||
|
||||
auto connection = _connect_redis(*master, opts);
|
||||
if (_get_role(connection) != Role::MASTER) {
|
||||
// Retry the whole process at most SentinelOptions::max_retry times.
|
||||
++retries;
|
||||
if (retries > _sentinel_opts.max_retry) {
|
||||
throw Error("Failed to get master from sentinel");
|
||||
}
|
||||
|
||||
std::this_thread::sleep_for(_sentinel_opts.retry_interval);
|
||||
|
||||
// Restart the iteration.
|
||||
iter.reset();
|
||||
continue;
|
||||
}
|
||||
|
||||
return connection;
|
||||
} catch (const StopIterError &e) {
|
||||
throw;
|
||||
} catch (const Error &e) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Connection Sentinel::slave(const std::string &master_name, const ConnectionOptions &opts) {
|
||||
std::lock_guard<std::mutex> lock(_mutex);
|
||||
|
||||
Iterator iter(_healthy_sentinels, _broken_sentinels);
|
||||
std::size_t retries = 0;
|
||||
while (true) {
|
||||
try {
|
||||
auto &sentinel = iter.next();
|
||||
|
||||
auto slaves = _get_slave_addr_by_name(sentinel, master_name);
|
||||
if (slaves.empty()) {
|
||||
// Try the next sentinel.
|
||||
continue;
|
||||
}
|
||||
|
||||
// Normally slaves list is NOT very large, so there won't be a performance problem.
|
||||
auto slave_iter = std::find(slaves.begin(),
|
||||
slaves.end(),
|
||||
Node{opts.host, opts.port});
|
||||
if (slave_iter != slaves.end() && slave_iter != slaves.begin()) {
|
||||
// The given node is still a valid slave. Try it first.
|
||||
std::swap(*(slaves.begin()), *slave_iter);
|
||||
}
|
||||
|
||||
for (const auto &slave : slaves) {
|
||||
try {
|
||||
auto connection = _connect_redis(slave, opts);
|
||||
if (_get_role(connection) != Role::SLAVE) {
|
||||
// Retry the whole process at most SentinelOptions::max_retry times.
|
||||
++retries;
|
||||
if (retries > _sentinel_opts.max_retry) {
|
||||
throw Error("Failed to get slave from sentinel");
|
||||
}
|
||||
|
||||
std::this_thread::sleep_for(_sentinel_opts.retry_interval);
|
||||
|
||||
// Restart the iteration.
|
||||
iter.reset();
|
||||
break;
|
||||
}
|
||||
|
||||
return connection;
|
||||
} catch (const Error &e) {
|
||||
// Try the next slave.
|
||||
continue;
|
||||
}
|
||||
}
|
||||
} catch (const StopIterError &e) {
|
||||
throw;
|
||||
} catch (const Error &e) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Optional<Node> Sentinel::_get_master_addr_by_name(Connection &connection, const StringView &name) {
|
||||
connection.send("SENTINEL GET-MASTER-ADDR-BY-NAME %b", name.data(), name.size());
|
||||
|
||||
auto reply = connection.recv();
|
||||
|
||||
assert(reply);
|
||||
|
||||
auto master = reply::parse<Optional<std::pair<std::string, std::string>>>(*reply);
|
||||
if (!master) {
|
||||
return {};
|
||||
}
|
||||
|
||||
int port = 0;
|
||||
try {
|
||||
port = std::stoi(master->second);
|
||||
} catch (const std::exception &) {
|
||||
throw ProtoError("Master port is invalid: " + master->second);
|
||||
}
|
||||
|
||||
return Optional<Node>{Node{master->first, port}};
|
||||
}
|
||||
|
||||
std::vector<Node> Sentinel::_get_slave_addr_by_name(Connection &connection,
|
||||
const StringView &name) {
|
||||
try {
|
||||
connection.send("SENTINEL SLAVES %b", name.data(), name.size());
|
||||
|
||||
auto reply = connection.recv();
|
||||
|
||||
assert(reply);
|
||||
|
||||
auto slaves = _parse_slave_info(*reply);
|
||||
|
||||
// Make slave list random.
|
||||
std::mt19937 gen(std::random_device{}());
|
||||
std::shuffle(slaves.begin(), slaves.end(), gen);
|
||||
|
||||
return slaves;
|
||||
} catch (const ReplyError &e) {
|
||||
// Unknown master name.
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<Node> Sentinel::_parse_slave_info(redisReply &reply) const {
|
||||
using SlaveInfo = std::unordered_map<std::string, std::string>;
|
||||
|
||||
auto slaves = reply::parse<std::vector<SlaveInfo>>(reply);
|
||||
|
||||
std::vector<Node> nodes;
|
||||
for (const auto &slave : slaves) {
|
||||
auto flags_iter = slave.find("flags");
|
||||
auto ip_iter = slave.find("ip");
|
||||
auto port_iter = slave.find("port");
|
||||
if (flags_iter == slave.end() || ip_iter == slave.end() || port_iter == slave.end()) {
|
||||
throw ProtoError("Invalid slave info");
|
||||
}
|
||||
|
||||
// This slave is down, e.g. 's_down,slave,disconnected'
|
||||
if (flags_iter->second != "slave") {
|
||||
continue;
|
||||
}
|
||||
|
||||
int port = 0;
|
||||
try {
|
||||
port = std::stoi(port_iter->second);
|
||||
} catch (const std::exception &) {
|
||||
throw ProtoError("Slave port is invalid: " + port_iter->second);
|
||||
}
|
||||
|
||||
nodes.push_back(Node{ip_iter->second, port});
|
||||
}
|
||||
|
||||
return nodes;
|
||||
}
|
||||
|
||||
Connection Sentinel::_connect_redis(const Node &node, ConnectionOptions opts) {
|
||||
opts.host = node.host;
|
||||
opts.port = node.port;
|
||||
|
||||
return Connection(opts);
|
||||
}
|
||||
|
||||
Role Sentinel::_get_role(Connection &connection) {
|
||||
connection.send("INFO REPLICATION");
|
||||
auto reply = connection.recv();
|
||||
|
||||
assert(reply);
|
||||
auto info = reply::parse<std::string>(*reply);
|
||||
|
||||
auto start = info.find("role:");
|
||||
if (start == std::string::npos) {
|
||||
throw ProtoError("Invalid INFO REPLICATION reply");
|
||||
}
|
||||
start += 5;
|
||||
auto stop = info.find("\r\n", start);
|
||||
if (stop == std::string::npos) {
|
||||
throw ProtoError("Invalid INFO REPLICATION reply");
|
||||
}
|
||||
|
||||
auto role = info.substr(start, stop - start);
|
||||
if (role == "master") {
|
||||
return Role::MASTER;
|
||||
} else if (role == "slave") {
|
||||
return Role::SLAVE;
|
||||
} else {
|
||||
throw Error("Invalid role: " + role);
|
||||
}
|
||||
}
|
||||
|
||||
std::list<ConnectionOptions> Sentinel::_parse_options(const SentinelOptions &opts) const {
|
||||
std::list<ConnectionOptions> options;
|
||||
for (const auto &node : opts.nodes) {
|
||||
ConnectionOptions opt;
|
||||
opt.host = node.first;
|
||||
opt.port = node.second;
|
||||
opt.password = opts.password;
|
||||
opt.keep_alive = opts.keep_alive;
|
||||
opt.connect_timeout = opts.connect_timeout;
|
||||
opt.socket_timeout = opts.socket_timeout;
|
||||
|
||||
options.push_back(opt);
|
||||
}
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
SimpleSentinel::SimpleSentinel(const std::shared_ptr<Sentinel> &sentinel,
|
||||
const std::string &master_name,
|
||||
Role role) :
|
||||
_sentinel(sentinel),
|
||||
_master_name(master_name),
|
||||
_role(role) {
|
||||
if (!_sentinel) {
|
||||
throw Error("Sentinel cannot be null");
|
||||
}
|
||||
|
||||
if (_role != Role::MASTER && _role != Role::SLAVE) {
|
||||
throw Error("Role must be Role::MASTER or Role::SLAVE");
|
||||
}
|
||||
}
|
||||
|
||||
Connection SimpleSentinel::create(const ConnectionOptions &opts) {
|
||||
assert(_sentinel);
|
||||
|
||||
if (_role == Role::MASTER) {
|
||||
return _sentinel->master(_master_name, opts);
|
||||
}
|
||||
|
||||
assert(_role == Role::SLAVE);
|
||||
|
||||
return _sentinel->slave(_master_name, opts);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
138
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/sentinel.h
vendored
Normal file
138
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/sentinel.h
vendored
Normal file
|
@ -0,0 +1,138 @@
|
|||
/**************************************************************************
|
||||
Copyright (c) 2017 sewenew
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*************************************************************************/
|
||||
|
||||
#ifndef SEWENEW_REDISPLUSPLUS_SENTINEL_H
|
||||
#define SEWENEW_REDISPLUSPLUS_SENTINEL_H
|
||||
|
||||
#include <string>
|
||||
#include <list>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include "connection.h"
|
||||
#include "shards.h"
|
||||
#include "reply.h"
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
struct SentinelOptions {
|
||||
std::vector<std::pair<std::string, int>> nodes;
|
||||
|
||||
std::string password;
|
||||
|
||||
bool keep_alive = true;
|
||||
|
||||
std::chrono::milliseconds connect_timeout{100};
|
||||
|
||||
std::chrono::milliseconds socket_timeout{100};
|
||||
|
||||
std::chrono::milliseconds retry_interval{100};
|
||||
|
||||
std::size_t max_retry = 2;
|
||||
};
|
||||
|
||||
class Sentinel {
|
||||
public:
|
||||
explicit Sentinel(const SentinelOptions &sentinel_opts);
|
||||
|
||||
Sentinel(const Sentinel &) = delete;
|
||||
Sentinel& operator=(const Sentinel &) = delete;
|
||||
|
||||
Sentinel(Sentinel &&) = delete;
|
||||
Sentinel& operator=(Sentinel &&) = delete;
|
||||
|
||||
~Sentinel() = default;
|
||||
|
||||
private:
|
||||
Connection master(const std::string &master_name, const ConnectionOptions &opts);
|
||||
|
||||
Connection slave(const std::string &master_name, const ConnectionOptions &opts);
|
||||
|
||||
class Iterator;
|
||||
|
||||
friend class SimpleSentinel;
|
||||
|
||||
std::list<ConnectionOptions> _parse_options(const SentinelOptions &opts) const;
|
||||
|
||||
Optional<Node> _get_master_addr_by_name(Connection &connection, const StringView &name);
|
||||
|
||||
std::vector<Node> _get_slave_addr_by_name(Connection &connection, const StringView &name);
|
||||
|
||||
Connection _connect_redis(const Node &node, ConnectionOptions opts);
|
||||
|
||||
Role _get_role(Connection &connection);
|
||||
|
||||
std::vector<Node> _parse_slave_info(redisReply &reply) const;
|
||||
|
||||
std::list<Connection> _healthy_sentinels;
|
||||
|
||||
std::list<ConnectionOptions> _broken_sentinels;
|
||||
|
||||
SentinelOptions _sentinel_opts;
|
||||
|
||||
std::mutex _mutex;
|
||||
};
|
||||
|
||||
class SimpleSentinel {
|
||||
public:
|
||||
SimpleSentinel(const std::shared_ptr<Sentinel> &sentinel,
|
||||
const std::string &master_name,
|
||||
Role role);
|
||||
|
||||
SimpleSentinel() = default;
|
||||
|
||||
SimpleSentinel(const SimpleSentinel &) = default;
|
||||
SimpleSentinel& operator=(const SimpleSentinel &) = default;
|
||||
|
||||
SimpleSentinel(SimpleSentinel &&) = default;
|
||||
SimpleSentinel& operator=(SimpleSentinel &&) = default;
|
||||
|
||||
~SimpleSentinel() = default;
|
||||
|
||||
explicit operator bool() const {
|
||||
return bool(_sentinel);
|
||||
}
|
||||
|
||||
Connection create(const ConnectionOptions &opts);
|
||||
|
||||
private:
|
||||
std::shared_ptr<Sentinel> _sentinel;
|
||||
|
||||
std::string _master_name;
|
||||
|
||||
Role _role = Role::MASTER;
|
||||
};
|
||||
|
||||
class StopIterError : public Error {
|
||||
public:
|
||||
StopIterError() : Error("StopIterError") {}
|
||||
|
||||
StopIterError(const StopIterError &) = default;
|
||||
StopIterError& operator=(const StopIterError &) = default;
|
||||
|
||||
StopIterError(StopIterError &&) = default;
|
||||
StopIterError& operator=(StopIterError &&) = default;
|
||||
|
||||
virtual ~StopIterError() = default;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // end SEWENEW_REDISPLUSPLUS_SENTINEL_H
|
50
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/shards.cpp
vendored
Normal file
50
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/shards.cpp
vendored
Normal file
|
@ -0,0 +1,50 @@
|
|||
/**************************************************************************
|
||||
Copyright (c) 2017 sewenew
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*************************************************************************/
|
||||
|
||||
#include "shards.h"
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
RedirectionError::RedirectionError(const std::string &msg): ReplyError(msg) {
|
||||
std::tie(_slot, _node) = _parse_error(msg);
|
||||
}
|
||||
|
||||
std::pair<Slot, Node> RedirectionError::_parse_error(const std::string &msg) const {
|
||||
// "slot ip:port"
|
||||
auto space_pos = msg.find(" ");
|
||||
auto colon_pos = msg.find(":");
|
||||
if (space_pos == std::string::npos
|
||||
|| colon_pos == std::string::npos
|
||||
|| colon_pos < space_pos) {
|
||||
throw ProtoError("Invalid ASK error message: " + msg);
|
||||
}
|
||||
|
||||
try {
|
||||
auto slot = std::stoull(msg.substr(0, space_pos));
|
||||
auto host = msg.substr(space_pos + 1, colon_pos - space_pos - 1);
|
||||
auto port = std::stoi(msg.substr(colon_pos + 1));
|
||||
|
||||
return {slot, {host, port}};
|
||||
} catch (const std::exception &e) {
|
||||
throw ProtoError("Invalid ASK error message: " + msg);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
115
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/shards.h
vendored
Normal file
115
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/shards.h
vendored
Normal file
|
@ -0,0 +1,115 @@
|
|||
/**************************************************************************
|
||||
Copyright (c) 2017 sewenew
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*************************************************************************/
|
||||
|
||||
#ifndef SEWENEW_REDISPLUSPLUS_SHARDS_H
|
||||
#define SEWENEW_REDISPLUSPLUS_SHARDS_H
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include "errors.h"
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
using Slot = std::size_t;
|
||||
|
||||
struct SlotRange {
|
||||
Slot min;
|
||||
Slot max;
|
||||
};
|
||||
|
||||
inline bool operator<(const SlotRange &lhs, const SlotRange &rhs) {
|
||||
return lhs.max < rhs.max;
|
||||
}
|
||||
|
||||
struct Node {
|
||||
std::string host;
|
||||
int port;
|
||||
};
|
||||
|
||||
inline bool operator==(const Node &lhs, const Node &rhs) {
|
||||
return lhs.host == rhs.host && lhs.port == rhs.port;
|
||||
}
|
||||
|
||||
struct NodeHash {
|
||||
std::size_t operator()(const Node &node) const noexcept {
|
||||
auto host_hash = std::hash<std::string>{}(node.host);
|
||||
auto port_hash = std::hash<int>{}(node.port);
|
||||
return host_hash ^ (port_hash << 1);
|
||||
}
|
||||
};
|
||||
|
||||
using Shards = std::map<SlotRange, Node>;
|
||||
|
||||
class RedirectionError : public ReplyError {
|
||||
public:
|
||||
RedirectionError(const std::string &msg);
|
||||
|
||||
RedirectionError(const RedirectionError &) = default;
|
||||
RedirectionError& operator=(const RedirectionError &) = default;
|
||||
|
||||
RedirectionError(RedirectionError &&) = default;
|
||||
RedirectionError& operator=(RedirectionError &&) = default;
|
||||
|
||||
virtual ~RedirectionError() = default;
|
||||
|
||||
Slot slot() const {
|
||||
return _slot;
|
||||
}
|
||||
|
||||
const Node& node() const {
|
||||
return _node;
|
||||
}
|
||||
|
||||
private:
|
||||
std::pair<Slot, Node> _parse_error(const std::string &msg) const;
|
||||
|
||||
Slot _slot = 0;
|
||||
Node _node;
|
||||
};
|
||||
|
||||
class MovedError : public RedirectionError {
|
||||
public:
|
||||
explicit MovedError(const std::string &msg) : RedirectionError(msg) {}
|
||||
|
||||
MovedError(const MovedError &) = default;
|
||||
MovedError& operator=(const MovedError &) = default;
|
||||
|
||||
MovedError(MovedError &&) = default;
|
||||
MovedError& operator=(MovedError &&) = default;
|
||||
|
||||
virtual ~MovedError() = default;
|
||||
};
|
||||
|
||||
class AskError : public RedirectionError {
|
||||
public:
|
||||
explicit AskError(const std::string &msg) : RedirectionError(msg) {}
|
||||
|
||||
AskError(const AskError &) = default;
|
||||
AskError& operator=(const AskError &) = default;
|
||||
|
||||
AskError(AskError &&) = default;
|
||||
AskError& operator=(AskError &&) = default;
|
||||
|
||||
virtual ~AskError() = default;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // end SEWENEW_REDISPLUSPLUS_SHARDS_H
|
319
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/shards_pool.cpp
vendored
Normal file
319
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/shards_pool.cpp
vendored
Normal file
|
@ -0,0 +1,319 @@
|
|||
/**************************************************************************
|
||||
Copyright (c) 2017 sewenew
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*************************************************************************/
|
||||
|
||||
#include "shards_pool.h"
|
||||
#include <unordered_set>
|
||||
#include "errors.h"
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
const std::size_t ShardsPool::SHARDS;
|
||||
|
||||
ShardsPool::ShardsPool(const ConnectionPoolOptions &pool_opts,
|
||||
const ConnectionOptions &connection_opts) :
|
||||
_pool_opts(pool_opts),
|
||||
_connection_opts(connection_opts) {
|
||||
if (_connection_opts.type != ConnectionType::TCP) {
|
||||
throw Error("Only support TCP connection for Redis Cluster");
|
||||
}
|
||||
|
||||
Connection connection(_connection_opts);
|
||||
|
||||
_shards = _cluster_slots(connection);
|
||||
|
||||
_init_pool(_shards);
|
||||
}
|
||||
|
||||
ShardsPool::ShardsPool(ShardsPool &&that) {
|
||||
std::lock_guard<std::mutex> lock(that._mutex);
|
||||
|
||||
_move(std::move(that));
|
||||
}
|
||||
|
||||
ShardsPool& ShardsPool::operator=(ShardsPool &&that) {
|
||||
if (this != &that) {
|
||||
std::lock(_mutex, that._mutex);
|
||||
std::lock_guard<std::mutex> lock_this(_mutex, std::adopt_lock);
|
||||
std::lock_guard<std::mutex> lock_that(that._mutex, std::adopt_lock);
|
||||
|
||||
_move(std::move(that));
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
GuardedConnection ShardsPool::fetch(const StringView &key) {
|
||||
auto slot = _slot(key);
|
||||
|
||||
return _fetch(slot);
|
||||
}
|
||||
|
||||
GuardedConnection ShardsPool::fetch() {
|
||||
auto slot = _slot();
|
||||
|
||||
return _fetch(slot);
|
||||
}
|
||||
|
||||
GuardedConnection ShardsPool::fetch(const Node &node) {
|
||||
std::lock_guard<std::mutex> lock(_mutex);
|
||||
|
||||
auto iter = _pools.find(node);
|
||||
if (iter == _pools.end()) {
|
||||
// Node doesn't exist, and it should be a newly created node.
|
||||
// So add a new connection pool.
|
||||
iter = _add_node(node);
|
||||
}
|
||||
|
||||
assert(iter != _pools.end());
|
||||
|
||||
return GuardedConnection(iter->second);
|
||||
}
|
||||
|
||||
void ShardsPool::update() {
|
||||
// My might send command to a removed node.
|
||||
// Try at most 3 times.
|
||||
for (auto idx = 0; idx < 3; ++idx) {
|
||||
try {
|
||||
// Randomly pick a connection.
|
||||
auto guarded_connection = fetch();
|
||||
auto shards = _cluster_slots(guarded_connection.connection());
|
||||
|
||||
std::unordered_set<Node, NodeHash> nodes;
|
||||
for (const auto &shard : shards) {
|
||||
nodes.insert(shard.second);
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(_mutex);
|
||||
|
||||
// TODO: If shards is unchanged, no need to update, and return immediately.
|
||||
|
||||
_shards = std::move(shards);
|
||||
|
||||
// Remove non-existent nodes.
|
||||
for (auto iter = _pools.begin(); iter != _pools.end(); ) {
|
||||
if (nodes.find(iter->first) == nodes.end()) {
|
||||
// Node has been removed.
|
||||
_pools.erase(iter++);
|
||||
} else {
|
||||
++iter;
|
||||
}
|
||||
}
|
||||
|
||||
// Add connection pool for new nodes.
|
||||
// In fact, connections will be created lazily.
|
||||
for (const auto &node : nodes) {
|
||||
if (_pools.find(node) == _pools.end()) {
|
||||
_add_node(node);
|
||||
}
|
||||
}
|
||||
|
||||
// Update successfully.
|
||||
return;
|
||||
} catch (const Error &) {
|
||||
// continue;
|
||||
}
|
||||
}
|
||||
|
||||
throw Error("Failed to update shards info");
|
||||
}
|
||||
|
||||
ConnectionOptions ShardsPool::connection_options(const StringView &key) {
|
||||
auto slot = _slot(key);
|
||||
|
||||
return _connection_options(slot);
|
||||
}
|
||||
|
||||
ConnectionOptions ShardsPool::connection_options() {
|
||||
auto slot = _slot();
|
||||
|
||||
return _connection_options(slot);
|
||||
}
|
||||
void ShardsPool::_move(ShardsPool &&that) {
|
||||
_pool_opts = that._pool_opts;
|
||||
_connection_opts = that._connection_opts;
|
||||
_shards = std::move(that._shards);
|
||||
_pools = std::move(that._pools);
|
||||
}
|
||||
|
||||
void ShardsPool::_init_pool(const Shards &shards) {
|
||||
for (const auto &shard : shards) {
|
||||
_add_node(shard.second);
|
||||
}
|
||||
}
|
||||
|
||||
Shards ShardsPool::_cluster_slots(Connection &connection) const {
|
||||
auto reply = _cluster_slots_command(connection);
|
||||
|
||||
assert(reply);
|
||||
|
||||
return _parse_reply(*reply);
|
||||
}
|
||||
|
||||
ReplyUPtr ShardsPool::_cluster_slots_command(Connection &connection) const {
|
||||
connection.send("CLUSTER SLOTS");
|
||||
|
||||
return connection.recv();
|
||||
}
|
||||
|
||||
Shards ShardsPool::_parse_reply(redisReply &reply) const {
|
||||
if (!reply::is_array(reply)) {
|
||||
throw ProtoError("Expect ARRAY reply");
|
||||
}
|
||||
|
||||
if (reply.element == nullptr || reply.elements == 0) {
|
||||
throw Error("Empty slots");
|
||||
}
|
||||
|
||||
Shards shards;
|
||||
for (std::size_t idx = 0; idx != reply.elements; ++idx) {
|
||||
auto *sub_reply = reply.element[idx];
|
||||
if (sub_reply == nullptr) {
|
||||
throw ProtoError("Null slot info");
|
||||
}
|
||||
|
||||
shards.emplace(_parse_slot_info(*sub_reply));
|
||||
}
|
||||
|
||||
return shards;
|
||||
}
|
||||
|
||||
std::pair<SlotRange, Node> ShardsPool::_parse_slot_info(redisReply &reply) const {
|
||||
if (reply.elements < 3 || reply.element == nullptr) {
|
||||
throw ProtoError("Invalid slot info");
|
||||
}
|
||||
|
||||
// Min slot id
|
||||
auto *min_slot_reply = reply.element[0];
|
||||
if (min_slot_reply == nullptr) {
|
||||
throw ProtoError("Invalid min slot");
|
||||
}
|
||||
std::size_t min_slot = reply::parse<long long>(*min_slot_reply);
|
||||
|
||||
// Max slot id
|
||||
auto *max_slot_reply = reply.element[1];
|
||||
if (max_slot_reply == nullptr) {
|
||||
throw ProtoError("Invalid max slot");
|
||||
}
|
||||
std::size_t max_slot = reply::parse<long long>(*max_slot_reply);
|
||||
|
||||
if (min_slot > max_slot) {
|
||||
throw ProtoError("Invalid slot range");
|
||||
}
|
||||
|
||||
// Master node info
|
||||
auto *node_reply = reply.element[2];
|
||||
if (node_reply == nullptr
|
||||
|| !reply::is_array(*node_reply)
|
||||
|| node_reply->element == nullptr
|
||||
|| node_reply->elements < 2) {
|
||||
throw ProtoError("Invalid node info");
|
||||
}
|
||||
|
||||
auto master_host = reply::parse<std::string>(*(node_reply->element[0]));
|
||||
int master_port = reply::parse<long long>(*(node_reply->element[1]));
|
||||
|
||||
// By now, we ignore node id and other replicas' info.
|
||||
|
||||
return {SlotRange{min_slot, max_slot}, Node{master_host, master_port}};
|
||||
}
|
||||
|
||||
Slot ShardsPool::_slot(const StringView &key) const {
|
||||
// The following code is copied from: https://redis.io/topics/cluster-spec
|
||||
// And I did some minor changes.
|
||||
|
||||
const auto *k = key.data();
|
||||
auto keylen = key.size();
|
||||
|
||||
// start-end indexes of { and }.
|
||||
std::size_t s = 0;
|
||||
std::size_t e = 0;
|
||||
|
||||
// Search the first occurrence of '{'.
|
||||
for (s = 0; s < keylen; s++)
|
||||
if (k[s] == '{') break;
|
||||
|
||||
// No '{' ? Hash the whole key. This is the base case.
|
||||
if (s == keylen) return crc16(k, keylen) & SHARDS;
|
||||
|
||||
// '{' found? Check if we have the corresponding '}'.
|
||||
for (e = s + 1; e < keylen; e++)
|
||||
if (k[e] == '}') break;
|
||||
|
||||
// No '}' or nothing between {} ? Hash the whole key.
|
||||
if (e == keylen || e == s + 1) return crc16(k, keylen) & SHARDS;
|
||||
|
||||
// If we are here there is both a { and a } on its right. Hash
|
||||
// what is in the middle between { and }.
|
||||
return crc16(k + s + 1, e - s - 1) & SHARDS;
|
||||
}
|
||||
|
||||
Slot ShardsPool::_slot() const {
|
||||
static thread_local std::default_random_engine engine;
|
||||
|
||||
std::uniform_int_distribution<std::size_t> uniform_dist(0, SHARDS);
|
||||
|
||||
return uniform_dist(engine);
|
||||
}
|
||||
|
||||
ConnectionPoolSPtr& ShardsPool::_get_pool(Slot slot) {
|
||||
auto shards_iter = _shards.lower_bound(SlotRange{slot, slot});
|
||||
if (shards_iter == _shards.end() || slot < shards_iter->first.min) {
|
||||
throw Error("Slot is out of range: " + std::to_string(slot));
|
||||
}
|
||||
|
||||
const auto &node = shards_iter->second;
|
||||
|
||||
auto node_iter = _pools.find(node);
|
||||
if (node_iter == _pools.end()) {
|
||||
throw Error("Slot is NOT covered: " + std::to_string(slot));
|
||||
}
|
||||
|
||||
return node_iter->second;
|
||||
}
|
||||
|
||||
GuardedConnection ShardsPool::_fetch(Slot slot) {
|
||||
std::lock_guard<std::mutex> lock(_mutex);
|
||||
|
||||
auto &pool = _get_pool(slot);
|
||||
|
||||
assert(pool);
|
||||
|
||||
return GuardedConnection(pool);
|
||||
}
|
||||
|
||||
ConnectionOptions ShardsPool::_connection_options(Slot slot) {
|
||||
std::lock_guard<std::mutex> lock(_mutex);
|
||||
|
||||
auto &pool = _get_pool(slot);
|
||||
|
||||
assert(pool);
|
||||
|
||||
return pool->connection_options();
|
||||
}
|
||||
|
||||
auto ShardsPool::_add_node(const Node &node) -> NodeMap::iterator {
|
||||
auto opts = _connection_opts;
|
||||
opts.host = node.host;
|
||||
opts.port = node.port;
|
||||
|
||||
return _pools.emplace(node, std::make_shared<ConnectionPool>(_pool_opts, opts)).first;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
137
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/shards_pool.h
vendored
Normal file
137
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/shards_pool.h
vendored
Normal file
|
@ -0,0 +1,137 @@
|
|||
/**************************************************************************
|
||||
Copyright (c) 2017 sewenew
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*************************************************************************/
|
||||
|
||||
#ifndef SEWENEW_REDISPLUSPLUS_SHARDS_POOL_H
|
||||
#define SEWENEW_REDISPLUSPLUS_SHARDS_POOL_H
|
||||
|
||||
#include <cassert>
|
||||
#include <unordered_map>
|
||||
#include <string>
|
||||
#include <random>
|
||||
#include <memory>
|
||||
#include "reply.h"
|
||||
#include "connection_pool.h"
|
||||
#include "shards.h"
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
using ConnectionPoolSPtr = std::shared_ptr<ConnectionPool>;
|
||||
|
||||
class GuardedConnection {
|
||||
public:
|
||||
GuardedConnection(const ConnectionPoolSPtr &pool) : _pool(pool),
|
||||
_connection(_pool->fetch()) {
|
||||
assert(!_connection.broken());
|
||||
}
|
||||
|
||||
GuardedConnection(const GuardedConnection &) = delete;
|
||||
GuardedConnection& operator=(const GuardedConnection &) = delete;
|
||||
|
||||
GuardedConnection(GuardedConnection &&) = default;
|
||||
GuardedConnection& operator=(GuardedConnection &&) = default;
|
||||
|
||||
~GuardedConnection() {
|
||||
_pool->release(std::move(_connection));
|
||||
}
|
||||
|
||||
Connection& connection() {
|
||||
return _connection;
|
||||
}
|
||||
|
||||
private:
|
||||
ConnectionPoolSPtr _pool;
|
||||
Connection _connection;
|
||||
};
|
||||
|
||||
class ShardsPool {
|
||||
public:
|
||||
ShardsPool() = default;
|
||||
|
||||
ShardsPool(const ShardsPool &that) = delete;
|
||||
ShardsPool& operator=(const ShardsPool &that) = delete;
|
||||
|
||||
ShardsPool(ShardsPool &&that);
|
||||
ShardsPool& operator=(ShardsPool &&that);
|
||||
|
||||
~ShardsPool() = default;
|
||||
|
||||
ShardsPool(const ConnectionPoolOptions &pool_opts,
|
||||
const ConnectionOptions &connection_opts);
|
||||
|
||||
// Fetch a connection by key.
|
||||
GuardedConnection fetch(const StringView &key);
|
||||
|
||||
// Randomly pick a connection.
|
||||
GuardedConnection fetch();
|
||||
|
||||
// Fetch a connection by node.
|
||||
GuardedConnection fetch(const Node &node);
|
||||
|
||||
void update();
|
||||
|
||||
ConnectionOptions connection_options(const StringView &key);
|
||||
|
||||
ConnectionOptions connection_options();
|
||||
|
||||
private:
|
||||
void _move(ShardsPool &&that);
|
||||
|
||||
void _init_pool(const Shards &shards);
|
||||
|
||||
Shards _cluster_slots(Connection &connection) const;
|
||||
|
||||
ReplyUPtr _cluster_slots_command(Connection &connection) const;
|
||||
|
||||
Shards _parse_reply(redisReply &reply) const;
|
||||
|
||||
std::pair<SlotRange, Node> _parse_slot_info(redisReply &reply) const;
|
||||
|
||||
// Get slot by key.
|
||||
std::size_t _slot(const StringView &key) const;
|
||||
|
||||
// Randomly pick a slot.
|
||||
std::size_t _slot() const;
|
||||
|
||||
ConnectionPoolSPtr& _get_pool(Slot slot);
|
||||
|
||||
GuardedConnection _fetch(Slot slot);
|
||||
|
||||
ConnectionOptions _connection_options(Slot slot);
|
||||
|
||||
using NodeMap = std::unordered_map<Node, ConnectionPoolSPtr, NodeHash>;
|
||||
|
||||
NodeMap::iterator _add_node(const Node &node);
|
||||
|
||||
ConnectionPoolOptions _pool_opts;
|
||||
|
||||
ConnectionOptions _connection_opts;
|
||||
|
||||
Shards _shards;
|
||||
|
||||
NodeMap _pools;
|
||||
|
||||
std::mutex _mutex;
|
||||
|
||||
static const std::size_t SHARDS = 16383;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // end SEWENEW_REDISPLUSPLUS_SHARDS_POOL_H
|
222
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/subscriber.cpp
vendored
Normal file
222
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/subscriber.cpp
vendored
Normal file
|
@ -0,0 +1,222 @@
|
|||
/**************************************************************************
|
||||
Copyright (c) 2017 sewenew
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*************************************************************************/
|
||||
|
||||
#include "subscriber.h"
|
||||
#include <cassert>
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
const Subscriber::TypeIndex Subscriber::_msg_type_index = {
|
||||
{"message", MsgType::MESSAGE},
|
||||
{"pmessage", MsgType::PMESSAGE},
|
||||
{"subscribe", MsgType::SUBSCRIBE},
|
||||
{"unsubscribe", MsgType::UNSUBSCRIBE},
|
||||
{"psubscribe", MsgType::PSUBSCRIBE},
|
||||
{"punsubscribe", MsgType::PUNSUBSCRIBE}
|
||||
};
|
||||
|
||||
Subscriber::Subscriber(Connection connection) : _connection(std::move(connection)) {}
|
||||
|
||||
void Subscriber::subscribe(const StringView &channel) {
|
||||
_check_connection();
|
||||
|
||||
// TODO: cmd::subscribe DOES NOT send the subscribe message to Redis.
|
||||
// In fact, it puts the command to network buffer.
|
||||
// So we need a queue to record these sub or unsub commands, and
|
||||
// ensure that before stopping the subscriber, all these commands
|
||||
// have really been sent to Redis.
|
||||
cmd::subscribe(_connection, channel);
|
||||
}
|
||||
|
||||
void Subscriber::unsubscribe() {
|
||||
_check_connection();
|
||||
|
||||
cmd::unsubscribe(_connection);
|
||||
}
|
||||
|
||||
void Subscriber::unsubscribe(const StringView &channel) {
|
||||
_check_connection();
|
||||
|
||||
cmd::unsubscribe(_connection, channel);
|
||||
}
|
||||
|
||||
void Subscriber::psubscribe(const StringView &pattern) {
|
||||
_check_connection();
|
||||
|
||||
cmd::psubscribe(_connection, pattern);
|
||||
}
|
||||
|
||||
void Subscriber::punsubscribe() {
|
||||
_check_connection();
|
||||
|
||||
cmd::punsubscribe(_connection);
|
||||
}
|
||||
|
||||
void Subscriber::punsubscribe(const StringView &pattern) {
|
||||
_check_connection();
|
||||
|
||||
cmd::punsubscribe(_connection, pattern);
|
||||
}
|
||||
|
||||
void Subscriber::consume() {
|
||||
_check_connection();
|
||||
|
||||
ReplyUPtr reply;
|
||||
try {
|
||||
reply = _connection.recv();
|
||||
} catch (const TimeoutError &) {
|
||||
_connection.reset();
|
||||
throw;
|
||||
}
|
||||
|
||||
assert(reply);
|
||||
|
||||
if (!reply::is_array(*reply) || reply->elements < 1 || reply->element == nullptr) {
|
||||
throw ProtoError("Invalid subscribe message");
|
||||
}
|
||||
|
||||
auto type = _msg_type(reply->element[0]);
|
||||
switch (type) {
|
||||
case MsgType::MESSAGE:
|
||||
_handle_message(*reply);
|
||||
break;
|
||||
|
||||
case MsgType::PMESSAGE:
|
||||
_handle_pmessage(*reply);
|
||||
break;
|
||||
|
||||
case MsgType::SUBSCRIBE:
|
||||
case MsgType::UNSUBSCRIBE:
|
||||
case MsgType::PSUBSCRIBE:
|
||||
case MsgType::PUNSUBSCRIBE:
|
||||
_handle_meta(type, *reply);
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
Subscriber::MsgType Subscriber::_msg_type(redisReply *reply) const {
|
||||
if (reply == nullptr) {
|
||||
throw ProtoError("Null type reply.");
|
||||
}
|
||||
|
||||
auto type = reply::parse<std::string>(*reply);
|
||||
|
||||
auto iter = _msg_type_index.find(type);
|
||||
if (iter == _msg_type_index.end()) {
|
||||
throw ProtoError("Invalid message type.");
|
||||
}
|
||||
|
||||
return iter->second;
|
||||
}
|
||||
|
||||
void Subscriber::_check_connection() {
|
||||
if (_connection.broken()) {
|
||||
throw Error("Connection is broken");
|
||||
}
|
||||
}
|
||||
|
||||
void Subscriber::_handle_message(redisReply &reply) {
|
||||
if (_msg_callback == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (reply.elements != 3) {
|
||||
throw ProtoError("Expect 3 sub replies");
|
||||
}
|
||||
|
||||
assert(reply.element != nullptr);
|
||||
|
||||
auto *channel_reply = reply.element[1];
|
||||
if (channel_reply == nullptr) {
|
||||
throw ProtoError("Null channel reply");
|
||||
}
|
||||
auto channel = reply::parse<std::string>(*channel_reply);
|
||||
|
||||
auto *msg_reply = reply.element[2];
|
||||
if (msg_reply == nullptr) {
|
||||
throw ProtoError("Null message reply");
|
||||
}
|
||||
auto msg = reply::parse<std::string>(*msg_reply);
|
||||
|
||||
_msg_callback(std::move(channel), std::move(msg));
|
||||
}
|
||||
|
||||
void Subscriber::_handle_pmessage(redisReply &reply) {
|
||||
if (_pmsg_callback == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (reply.elements != 4) {
|
||||
throw ProtoError("Expect 4 sub replies");
|
||||
}
|
||||
|
||||
assert(reply.element != nullptr);
|
||||
|
||||
auto *pattern_reply = reply.element[1];
|
||||
if (pattern_reply == nullptr) {
|
||||
throw ProtoError("Null pattern reply");
|
||||
}
|
||||
auto pattern = reply::parse<std::string>(*pattern_reply);
|
||||
|
||||
auto *channel_reply = reply.element[2];
|
||||
if (channel_reply == nullptr) {
|
||||
throw ProtoError("Null channel reply");
|
||||
}
|
||||
auto channel = reply::parse<std::string>(*channel_reply);
|
||||
|
||||
auto *msg_reply = reply.element[3];
|
||||
if (msg_reply == nullptr) {
|
||||
throw ProtoError("Null message reply");
|
||||
}
|
||||
auto msg = reply::parse<std::string>(*msg_reply);
|
||||
|
||||
_pmsg_callback(std::move(pattern), std::move(channel), std::move(msg));
|
||||
}
|
||||
|
||||
void Subscriber::_handle_meta(MsgType type, redisReply &reply) {
|
||||
if (_meta_callback == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (reply.elements != 3) {
|
||||
throw ProtoError("Expect 3 sub replies");
|
||||
}
|
||||
|
||||
assert(reply.element != nullptr);
|
||||
|
||||
auto *channel_reply = reply.element[1];
|
||||
if (channel_reply == nullptr) {
|
||||
throw ProtoError("Null channel reply");
|
||||
}
|
||||
auto channel = reply::parse<OptionalString>(*channel_reply);
|
||||
|
||||
auto *num_reply = reply.element[2];
|
||||
if (num_reply == nullptr) {
|
||||
throw ProtoError("Null num reply");
|
||||
}
|
||||
auto num = reply::parse<long long>(*num_reply);
|
||||
|
||||
_meta_callback(type, std::move(channel), num);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
231
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/subscriber.h
vendored
Normal file
231
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/subscriber.h
vendored
Normal file
|
@ -0,0 +1,231 @@
|
|||
/**************************************************************************
|
||||
Copyright (c) 2017 sewenew
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*************************************************************************/
|
||||
|
||||
#ifndef SEWENEW_REDISPLUSPLUS_SUBSCRIBER_H
|
||||
#define SEWENEW_REDISPLUSPLUS_SUBSCRIBER_H
|
||||
|
||||
#include <unordered_map>
|
||||
#include <string>
|
||||
#include <functional>
|
||||
#include "connection.h"
|
||||
#include "reply.h"
|
||||
#include "command.h"
|
||||
#include "utils.h"
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
// @NOTE: Subscriber is NOT thread-safe.
|
||||
// Subscriber uses callbacks to handle messages. There are 6 kinds of messages:
|
||||
// 1) MESSAGE: message sent to a channel.
|
||||
// 2) PMESSAGE: message sent to channels of a given pattern.
|
||||
// 3) SUBSCRIBE: meta message sent when we successfully subscribe to a channel.
|
||||
// 4) UNSUBSCRIBE: meta message sent when we successfully unsubscribe to a channel.
|
||||
// 5) PSUBSCRIBE: meta message sent when we successfully subscribe to a channel pattern.
|
||||
// 6) PUNSUBSCRIBE: meta message sent when we successfully unsubscribe to a channel pattern.
|
||||
//
|
||||
// Use Subscriber::on_message(MsgCallback) to set the callback function for message of
|
||||
// *MESSAGE* type, and the callback interface is:
|
||||
// void (std::string channel, std::string msg)
|
||||
//
|
||||
// Use Subscriber::on_pmessage(PatternMsgCallback) to set the callback function for message of
|
||||
// *PMESSAGE* type, and the callback interface is:
|
||||
// void (std::string pattern, std::string channel, std::string msg)
|
||||
//
|
||||
// Messages of other types are called *META MESSAGE*, they have the same callback interface.
|
||||
// Use Subscriber::on_meta(MetaCallback) to set the callback function:
|
||||
// void (Subscriber::MsgType type, OptionalString channel, long long num)
|
||||
//
|
||||
// NOTE: If we haven't subscribe/psubscribe to any channel/pattern, and try to
|
||||
// unsubscribe/punsubscribe without any parameter, i.e. unsubscribe/punsubscribe all
|
||||
// channels/patterns, *channel* will be null. So the second parameter of meta callback
|
||||
// is of type *OptionalString*.
|
||||
//
|
||||
// All these callback interfaces pass std::string by value, and you can take their ownership
|
||||
// (i.e. std::move) safely.
|
||||
//
|
||||
// If you don't set callback for a specific kind of message, Subscriber::consume() will
|
||||
// receive the message, and ignore it, i.e. no callback will be called.
|
||||
class Subscriber {
|
||||
public:
|
||||
Subscriber(const Subscriber &) = delete;
|
||||
Subscriber& operator=(const Subscriber &) = delete;
|
||||
|
||||
Subscriber(Subscriber &&) = default;
|
||||
Subscriber& operator=(Subscriber &&) = default;
|
||||
|
||||
~Subscriber() = default;
|
||||
|
||||
enum class MsgType {
|
||||
SUBSCRIBE,
|
||||
UNSUBSCRIBE,
|
||||
PSUBSCRIBE,
|
||||
PUNSUBSCRIBE,
|
||||
MESSAGE,
|
||||
PMESSAGE
|
||||
};
|
||||
|
||||
template <typename MsgCb>
|
||||
void on_message(MsgCb msg_callback);
|
||||
|
||||
template <typename PMsgCb>
|
||||
void on_pmessage(PMsgCb pmsg_callback);
|
||||
|
||||
template <typename MetaCb>
|
||||
void on_meta(MetaCb meta_callback);
|
||||
|
||||
void subscribe(const StringView &channel);
|
||||
|
||||
template <typename Input>
|
||||
void subscribe(Input first, Input last);
|
||||
|
||||
template <typename T>
|
||||
void subscribe(std::initializer_list<T> channels) {
|
||||
subscribe(channels.begin(), channels.end());
|
||||
}
|
||||
|
||||
void unsubscribe();
|
||||
|
||||
void unsubscribe(const StringView &channel);
|
||||
|
||||
template <typename Input>
|
||||
void unsubscribe(Input first, Input last);
|
||||
|
||||
template <typename T>
|
||||
void unsubscribe(std::initializer_list<T> channels) {
|
||||
unsubscribe(channels.begin(), channels.end());
|
||||
}
|
||||
|
||||
void psubscribe(const StringView &pattern);
|
||||
|
||||
template <typename Input>
|
||||
void psubscribe(Input first, Input last);
|
||||
|
||||
template <typename T>
|
||||
void psubscribe(std::initializer_list<T> channels) {
|
||||
psubscribe(channels.begin(), channels.end());
|
||||
}
|
||||
|
||||
void punsubscribe();
|
||||
|
||||
void punsubscribe(const StringView &channel);
|
||||
|
||||
template <typename Input>
|
||||
void punsubscribe(Input first, Input last);
|
||||
|
||||
template <typename T>
|
||||
void punsubscribe(std::initializer_list<T> channels) {
|
||||
punsubscribe(channels.begin(), channels.end());
|
||||
}
|
||||
|
||||
void consume();
|
||||
|
||||
private:
|
||||
friend class Redis;
|
||||
|
||||
friend class RedisCluster;
|
||||
|
||||
explicit Subscriber(Connection connection);
|
||||
|
||||
MsgType _msg_type(redisReply *reply) const;
|
||||
|
||||
void _check_connection();
|
||||
|
||||
void _handle_message(redisReply &reply);
|
||||
|
||||
void _handle_pmessage(redisReply &reply);
|
||||
|
||||
void _handle_meta(MsgType type, redisReply &reply);
|
||||
|
||||
using MsgCallback = std::function<void (std::string channel, std::string msg)>;
|
||||
|
||||
using PatternMsgCallback = std::function<void (std::string pattern,
|
||||
std::string channel,
|
||||
std::string msg)>;
|
||||
|
||||
using MetaCallback = std::function<void (MsgType type,
|
||||
OptionalString channel,
|
||||
long long num)>;
|
||||
|
||||
using TypeIndex = std::unordered_map<std::string, MsgType>;
|
||||
static const TypeIndex _msg_type_index;
|
||||
|
||||
Connection _connection;
|
||||
|
||||
MsgCallback _msg_callback = nullptr;
|
||||
|
||||
PatternMsgCallback _pmsg_callback = nullptr;
|
||||
|
||||
MetaCallback _meta_callback = nullptr;
|
||||
};
|
||||
|
||||
template <typename MsgCb>
|
||||
void Subscriber::on_message(MsgCb msg_callback) {
|
||||
_msg_callback = msg_callback;
|
||||
}
|
||||
|
||||
template <typename PMsgCb>
|
||||
void Subscriber::on_pmessage(PMsgCb pmsg_callback) {
|
||||
_pmsg_callback = pmsg_callback;
|
||||
}
|
||||
|
||||
template <typename MetaCb>
|
||||
void Subscriber::on_meta(MetaCb meta_callback) {
|
||||
_meta_callback = meta_callback;
|
||||
}
|
||||
|
||||
template <typename Input>
|
||||
void Subscriber::subscribe(Input first, Input last) {
|
||||
if (first == last) {
|
||||
return;
|
||||
}
|
||||
|
||||
_check_connection();
|
||||
|
||||
cmd::subscribe_range(_connection, first, last);
|
||||
}
|
||||
|
||||
template <typename Input>
|
||||
void Subscriber::unsubscribe(Input first, Input last) {
|
||||
_check_connection();
|
||||
|
||||
cmd::unsubscribe_range(_connection, first, last);
|
||||
}
|
||||
|
||||
template <typename Input>
|
||||
void Subscriber::psubscribe(Input first, Input last) {
|
||||
if (first == last) {
|
||||
return;
|
||||
}
|
||||
|
||||
_check_connection();
|
||||
|
||||
cmd::psubscribe_range(_connection, first, last);
|
||||
}
|
||||
|
||||
template <typename Input>
|
||||
void Subscriber::punsubscribe(Input first, Input last) {
|
||||
_check_connection();
|
||||
|
||||
cmd::punsubscribe_range(_connection, first, last);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // end SEWENEW_REDISPLUSPLUS_SUBSCRIBER_H
|
123
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/transaction.cpp
vendored
Normal file
123
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/transaction.cpp
vendored
Normal file
|
@ -0,0 +1,123 @@
|
|||
/**************************************************************************
|
||||
Copyright (c) 2017 sewenew
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*************************************************************************/
|
||||
|
||||
#include "transaction.h"
|
||||
#include "command.h"
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
std::vector<ReplyUPtr> TransactionImpl::exec(Connection &connection, std::size_t cmd_num) {
|
||||
_close_transaction();
|
||||
|
||||
_get_queued_replies(connection, cmd_num);
|
||||
|
||||
return _exec(connection);
|
||||
}
|
||||
|
||||
void TransactionImpl::discard(Connection &connection, std::size_t cmd_num) {
|
||||
_close_transaction();
|
||||
|
||||
_get_queued_replies(connection, cmd_num);
|
||||
|
||||
_discard(connection);
|
||||
}
|
||||
|
||||
void TransactionImpl::_open_transaction(Connection &connection) {
|
||||
assert(!_in_transaction);
|
||||
|
||||
cmd::multi(connection);
|
||||
auto reply = connection.recv();
|
||||
auto status = reply::to_status(*reply);
|
||||
if (status != "OK") {
|
||||
throw Error("Failed to open transaction: " + status);
|
||||
}
|
||||
|
||||
_in_transaction = true;
|
||||
}
|
||||
|
||||
void TransactionImpl::_close_transaction() {
|
||||
if (!_in_transaction) {
|
||||
throw Error("No command in transaction");
|
||||
}
|
||||
|
||||
_in_transaction = false;
|
||||
}
|
||||
|
||||
void TransactionImpl::_get_queued_reply(Connection &connection) {
|
||||
auto reply = connection.recv();
|
||||
auto status = reply::to_status(*reply);
|
||||
if (status != "QUEUED") {
|
||||
throw Error("Invalid QUEUED reply: " + status);
|
||||
}
|
||||
}
|
||||
|
||||
void TransactionImpl::_get_queued_replies(Connection &connection, std::size_t cmd_num) {
|
||||
if (_piped) {
|
||||
// Get all QUEUED reply
|
||||
while (cmd_num > 0) {
|
||||
_get_queued_reply(connection);
|
||||
|
||||
--cmd_num;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<ReplyUPtr> TransactionImpl::_exec(Connection &connection) {
|
||||
cmd::exec(connection);
|
||||
|
||||
auto reply = connection.recv();
|
||||
|
||||
if (reply::is_nil(*reply)) {
|
||||
// Execution has been aborted, i.e. watched key has been modified.
|
||||
throw WatchError();
|
||||
}
|
||||
|
||||
if (!reply::is_array(*reply)) {
|
||||
throw ProtoError("Expect ARRAY reply");
|
||||
}
|
||||
|
||||
if (reply->element == nullptr || reply->elements == 0) {
|
||||
// Since we don't allow EXEC without any command, this ARRAY reply
|
||||
// should NOT be null or empty.
|
||||
throw ProtoError("Null ARRAY reply");
|
||||
}
|
||||
|
||||
std::vector<ReplyUPtr> replies;
|
||||
for (std::size_t idx = 0; idx != reply->elements; ++idx) {
|
||||
auto *sub_reply = reply->element[idx];
|
||||
if (sub_reply == nullptr) {
|
||||
throw ProtoError("Null sub reply");
|
||||
}
|
||||
|
||||
auto r = ReplyUPtr(sub_reply);
|
||||
reply->element[idx] = nullptr;
|
||||
replies.push_back(std::move(r));
|
||||
}
|
||||
|
||||
return replies;
|
||||
}
|
||||
|
||||
void TransactionImpl::_discard(Connection &connection) {
|
||||
cmd::discard(connection);
|
||||
auto reply = connection.recv();
|
||||
reply::parse<void>(*reply);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
77
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/transaction.h
vendored
Normal file
77
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/transaction.h
vendored
Normal file
|
@ -0,0 +1,77 @@
|
|||
/**************************************************************************
|
||||
Copyright (c) 2017 sewenew
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*************************************************************************/
|
||||
|
||||
#ifndef SEWENEW_REDISPLUSPLUS_TRANSACTION_H
|
||||
#define SEWENEW_REDISPLUSPLUS_TRANSACTION_H
|
||||
|
||||
#include <cassert>
|
||||
#include <vector>
|
||||
#include "connection.h"
|
||||
#include "errors.h"
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
class TransactionImpl {
|
||||
public:
|
||||
explicit TransactionImpl(bool piped) : _piped(piped) {}
|
||||
|
||||
template <typename Cmd, typename ...Args>
|
||||
void command(Connection &connection, Cmd cmd, Args &&...args);
|
||||
|
||||
std::vector<ReplyUPtr> exec(Connection &connection, std::size_t cmd_num);
|
||||
|
||||
void discard(Connection &connection, std::size_t cmd_num);
|
||||
|
||||
private:
|
||||
void _open_transaction(Connection &connection);
|
||||
|
||||
void _close_transaction();
|
||||
|
||||
void _get_queued_reply(Connection &connection);
|
||||
|
||||
void _get_queued_replies(Connection &connection, std::size_t cmd_num);
|
||||
|
||||
std::vector<ReplyUPtr> _exec(Connection &connection);
|
||||
|
||||
void _discard(Connection &connection);
|
||||
|
||||
bool _in_transaction = false;
|
||||
|
||||
bool _piped;
|
||||
};
|
||||
|
||||
template <typename Cmd, typename ...Args>
|
||||
void TransactionImpl::command(Connection &connection, Cmd cmd, Args &&...args) {
|
||||
assert(!connection.broken());
|
||||
|
||||
if (!_in_transaction) {
|
||||
_open_transaction(connection);
|
||||
}
|
||||
|
||||
cmd(connection, std::forward<Args>(args)...);
|
||||
|
||||
if (!_piped) {
|
||||
_get_queued_reply(connection);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // end SEWENEW_REDISPLUSPLUS_TRANSACTION_H
|
269
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/utils.h
vendored
Normal file
269
controller/thirdparty/redis-plus-plus-1.1.1/src/sw/redis++/utils.h
vendored
Normal file
|
@ -0,0 +1,269 @@
|
|||
/**************************************************************************
|
||||
Copyright (c) 2017 sewenew
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*************************************************************************/
|
||||
|
||||
#ifndef SEWENEW_REDISPLUSPLUS_UTILS_H
|
||||
#define SEWENEW_REDISPLUSPLUS_UTILS_H
|
||||
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
// By now, not all compilers support std::string_view,
|
||||
// so we make our own implementation.
|
||||
class StringView {
|
||||
public:
|
||||
constexpr StringView() noexcept = default;
|
||||
|
||||
constexpr StringView(const char *data, std::size_t size) : _data(data), _size(size) {}
|
||||
|
||||
StringView(const char *data) : _data(data), _size(std::strlen(data)) {}
|
||||
|
||||
StringView(const std::string &str) : _data(str.data()), _size(str.size()) {}
|
||||
|
||||
constexpr StringView(const StringView &) noexcept = default;
|
||||
|
||||
StringView& operator=(const StringView &) noexcept = default;
|
||||
|
||||
constexpr const char* data() const noexcept {
|
||||
return _data;
|
||||
}
|
||||
|
||||
constexpr std::size_t size() const noexcept {
|
||||
return _size;
|
||||
}
|
||||
|
||||
private:
|
||||
const char *_data = nullptr;
|
||||
std::size_t _size = 0;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class Optional {
|
||||
public:
|
||||
Optional() = default;
|
||||
|
||||
Optional(const Optional &) = default;
|
||||
Optional& operator=(const Optional &) = default;
|
||||
|
||||
Optional(Optional &&) = default;
|
||||
Optional& operator=(Optional &&) = default;
|
||||
|
||||
~Optional() = default;
|
||||
|
||||
template <typename ...Args>
|
||||
explicit Optional(Args &&...args) : _value(true, T(std::forward<Args>(args)...)) {}
|
||||
|
||||
explicit operator bool() const {
|
||||
return _value.first;
|
||||
}
|
||||
|
||||
T& value() {
|
||||
return _value.second;
|
||||
}
|
||||
|
||||
const T& value() const {
|
||||
return _value.second;
|
||||
}
|
||||
|
||||
T* operator->() {
|
||||
return &(_value.second);
|
||||
}
|
||||
|
||||
const T* operator->() const {
|
||||
return &(_value.second);
|
||||
}
|
||||
|
||||
T& operator*() {
|
||||
return _value.second;
|
||||
}
|
||||
|
||||
const T& operator*() const {
|
||||
return _value.second;
|
||||
}
|
||||
|
||||
private:
|
||||
std::pair<bool, T> _value;
|
||||
};
|
||||
|
||||
using OptionalString = Optional<std::string>;
|
||||
|
||||
using OptionalLongLong = Optional<long long>;
|
||||
|
||||
using OptionalDouble = Optional<double>;
|
||||
|
||||
using OptionalStringPair = Optional<std::pair<std::string, std::string>>;
|
||||
|
||||
template <typename ...>
|
||||
struct IsKvPair : std::false_type {};
|
||||
|
||||
template <typename T, typename U>
|
||||
struct IsKvPair<std::pair<T, U>> : std::true_type {};
|
||||
|
||||
template <typename ...>
|
||||
using Void = void;
|
||||
|
||||
template <typename T, typename U = Void<>>
|
||||
struct IsInserter : std::false_type {};
|
||||
|
||||
template <typename T>
|
||||
//struct IsInserter<T, Void<typename T::container_type>> : std::true_type {};
|
||||
struct IsInserter<T,
|
||||
typename std::enable_if<!std::is_void<typename T::container_type>::value>::type>
|
||||
: std::true_type {};
|
||||
|
||||
template <typename Iter, typename T = Void<>>
|
||||
struct IterType {
|
||||
using type = typename std::iterator_traits<Iter>::value_type;
|
||||
};
|
||||
|
||||
template <typename Iter>
|
||||
//struct IterType<Iter, Void<typename Iter::container_type>> {
|
||||
struct IterType<Iter,
|
||||
//typename std::enable_if<std::is_void<typename Iter::value_type>::value>::type> {
|
||||
typename std::enable_if<IsInserter<Iter>::value>::type> {
|
||||
using type = typename std::decay<typename Iter::container_type::value_type>::type;
|
||||
};
|
||||
|
||||
template <typename Iter, typename T = Void<>>
|
||||
struct IsIter : std::false_type {};
|
||||
|
||||
template <typename Iter>
|
||||
struct IsIter<Iter, typename std::enable_if<IsInserter<Iter>::value>::type> : std::true_type {};
|
||||
|
||||
template <typename Iter>
|
||||
//struct IsIter<Iter, Void<typename std::iterator_traits<Iter>::iterator_category>>
|
||||
struct IsIter<Iter,
|
||||
typename std::enable_if<!std::is_void<
|
||||
typename std::iterator_traits<Iter>::value_type>::value>::type>
|
||||
: std::integral_constant<bool, !std::is_convertible<Iter, StringView>::value> {};
|
||||
|
||||
template <typename T>
|
||||
struct IsKvPairIter : IsKvPair<typename IterType<T>::type> {};
|
||||
|
||||
template <typename T, typename Tuple>
|
||||
struct TupleWithType : std::false_type {};
|
||||
|
||||
template <typename T>
|
||||
struct TupleWithType<T, std::tuple<>> : std::false_type {};
|
||||
|
||||
template <typename T, typename U, typename ...Args>
|
||||
struct TupleWithType<T, std::tuple<U, Args...>> : TupleWithType<T, std::tuple<Args...>> {};
|
||||
|
||||
template <typename T, typename ...Args>
|
||||
struct TupleWithType<T, std::tuple<T, Args...>> : std::true_type {};
|
||||
|
||||
template <std::size_t ...Is>
|
||||
struct IndexSequence {};
|
||||
|
||||
template <std::size_t I, std::size_t ...Is>
|
||||
struct MakeIndexSequence : MakeIndexSequence<I - 1, I - 1, Is...> {};
|
||||
|
||||
template <std::size_t ...Is>
|
||||
struct MakeIndexSequence<0, Is...> : IndexSequence<Is...> {};
|
||||
|
||||
// NthType and NthValue are taken from
|
||||
// https://stackoverflow.com/questions/14261183
|
||||
template <std::size_t I, typename ...Args>
|
||||
struct NthType {};
|
||||
|
||||
template <typename Arg, typename ...Args>
|
||||
struct NthType<0, Arg, Args...> {
|
||||
using type = Arg;
|
||||
};
|
||||
|
||||
template <std::size_t I, typename Arg, typename ...Args>
|
||||
struct NthType<I, Arg, Args...> {
|
||||
using type = typename NthType<I - 1, Args...>::type;
|
||||
};
|
||||
|
||||
template <typename ...Args>
|
||||
struct LastType {
|
||||
using type = typename NthType<sizeof...(Args) - 1, Args...>::type;
|
||||
};
|
||||
|
||||
struct InvalidLastType {};
|
||||
|
||||
template <>
|
||||
struct LastType<> {
|
||||
using type = InvalidLastType;
|
||||
};
|
||||
|
||||
template <std::size_t I, typename Arg, typename ...Args>
|
||||
auto NthValue(Arg &&arg, Args &&...)
|
||||
-> typename std::enable_if<(I == 0), decltype(std::forward<Arg>(arg))>::type {
|
||||
return std::forward<Arg>(arg);
|
||||
}
|
||||
|
||||
template <std::size_t I, typename Arg, typename ...Args>
|
||||
auto NthValue(Arg &&, Args &&...args)
|
||||
-> typename std::enable_if<(I > 0),
|
||||
decltype(std::forward<typename NthType<I, Arg, Args...>::type>(
|
||||
std::declval<typename NthType<I, Arg, Args...>::type>()))>::type {
|
||||
return std::forward<typename NthType<I, Arg, Args...>::type>(
|
||||
NthValue<I - 1>(std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
template <typename ...Args>
|
||||
auto LastValue(Args &&...args)
|
||||
-> decltype(std::forward<typename LastType<Args...>::type>(
|
||||
std::declval<typename LastType<Args...>::type>())) {
|
||||
return std::forward<typename LastType<Args...>::type>(
|
||||
NthValue<sizeof...(Args) - 1>(std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
template <typename T, typename = Void<>>
|
||||
struct HasPushBack : std::false_type {};
|
||||
|
||||
template <typename T>
|
||||
struct HasPushBack<T,
|
||||
typename std::enable_if<
|
||||
std::is_void<decltype(
|
||||
std::declval<T>().push_back(std::declval<typename T::value_type>())
|
||||
)>::value>::type> : std::true_type {};
|
||||
|
||||
template <typename T, typename = Void<>>
|
||||
struct HasInsert : std::false_type {};
|
||||
|
||||
template <typename T>
|
||||
struct HasInsert<T,
|
||||
typename std::enable_if<
|
||||
std::is_same<
|
||||
decltype(std::declval<T>().insert(std::declval<typename T::const_iterator>(),
|
||||
std::declval<typename T::value_type>())),
|
||||
typename T::iterator>::value>::type> : std::true_type {};
|
||||
|
||||
template <typename T>
|
||||
struct IsSequenceContainer
|
||||
: std::integral_constant<bool,
|
||||
HasPushBack<T>::value
|
||||
&& !std::is_same<typename std::decay<T>::type, std::string>::value> {};
|
||||
|
||||
template <typename T>
|
||||
struct IsAssociativeContainer
|
||||
: std::integral_constant<bool,
|
||||
HasInsert<T>::value && !HasPushBack<T>::value> {};
|
||||
|
||||
uint16_t crc16(const char *buf, int len);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // end SEWENEW_REDISPLUSPLUS_UTILS_H
|
33
controller/thirdparty/redis-plus-plus-1.1.1/test/CMakeLists.txt
vendored
Normal file
33
controller/thirdparty/redis-plus-plus-1.1.1/test/CMakeLists.txt
vendored
Normal file
|
@ -0,0 +1,33 @@
|
|||
project(test_redis++)
|
||||
|
||||
if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
|
||||
cmake_minimum_required(VERSION 3.0.0)
|
||||
else()
|
||||
cmake_minimum_required(VERSION 2.8.0)
|
||||
endif()
|
||||
|
||||
set(PROJECT_SOURCE_DIR ${PROJECT_SOURCE_DIR}/src/sw/redis++)
|
||||
|
||||
file(GLOB PROJECT_SOURCE_FILES "${PROJECT_SOURCE_DIR}/*.cpp")
|
||||
|
||||
add_executable(${PROJECT_NAME} ${PROJECT_SOURCE_FILES})
|
||||
|
||||
# hiredis dependency
|
||||
find_path(HIREDIS_HEADER hiredis)
|
||||
target_include_directories(${PROJECT_NAME} PUBLIC ${HIREDIS_HEADER})
|
||||
|
||||
find_library(HIREDIS_STATIC_LIB libhiredis.a)
|
||||
target_link_libraries(${PROJECT_NAME} ${HIREDIS_STATIC_LIB})
|
||||
|
||||
# redis++ dependency
|
||||
target_include_directories(${PROJECT_NAME} PUBLIC ../src)
|
||||
set(REDIS_PLUS_PLUS_LIB ${CMAKE_CURRENT_BINARY_DIR}/../lib/libredis++.a)
|
||||
|
||||
## solaris socket dependency
|
||||
IF (CMAKE_SYSTEM_NAME MATCHES "(Solaris|SunOS)" )
|
||||
target_link_libraries(${PROJECT_NAME} -lsocket)
|
||||
ENDIF(CMAKE_SYSTEM_NAME MATCHES "(Solaris|SunOS)" )
|
||||
|
||||
find_package(Threads REQUIRED)
|
||||
|
||||
target_link_libraries(${PROJECT_NAME} ${REDIS_PLUS_PLUS_LIB} ${CMAKE_THREAD_LIBS_INIT})
|
83
controller/thirdparty/redis-plus-plus-1.1.1/test/src/sw/redis++/benchmark_test.h
vendored
Normal file
83
controller/thirdparty/redis-plus-plus-1.1.1/test/src/sw/redis++/benchmark_test.h
vendored
Normal file
|
@ -0,0 +1,83 @@
|
|||
/**************************************************************************
|
||||
Copyright (c) 2017 sewenew
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*************************************************************************/
|
||||
|
||||
#ifndef SEWENEW_REDISPLUSPLUS_TEST_BENCHMARK_TEST_H
|
||||
#define SEWENEW_REDISPLUSPLUS_TEST_BENCHMARK_TEST_H
|
||||
|
||||
#include <sw/redis++/redis++.h>
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
namespace test {
|
||||
|
||||
struct BenchmarkOptions {
|
||||
std::size_t pool_size = 5;
|
||||
std::size_t thread_num = 10;
|
||||
std::size_t total_request_num = 100000;
|
||||
std::size_t key_len = 10;
|
||||
std::size_t val_len = 10;
|
||||
};
|
||||
|
||||
template <typename RedisInstance>
|
||||
class BenchmarkTest {
|
||||
public:
|
||||
BenchmarkTest(const BenchmarkOptions &opts, RedisInstance &instance);
|
||||
|
||||
~BenchmarkTest() {
|
||||
_cleanup();
|
||||
}
|
||||
|
||||
void run();
|
||||
|
||||
private:
|
||||
template <typename Func>
|
||||
void _run(const std::string &title, Func &&func);
|
||||
|
||||
template <typename Func>
|
||||
std::size_t _run(Func &&func, std::size_t request_num);
|
||||
|
||||
void _test_get();
|
||||
|
||||
std::vector<std::string> _gen_keys() const;
|
||||
|
||||
std::string _gen_value() const;
|
||||
|
||||
void _cleanup();
|
||||
|
||||
const std::string& _key(std::size_t idx) const {
|
||||
return _keys[idx % _keys.size()];
|
||||
}
|
||||
|
||||
BenchmarkOptions _opts;
|
||||
|
||||
RedisInstance &_redis;
|
||||
|
||||
std::vector<std::string> _keys;
|
||||
|
||||
std::string _value;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#include "benchmark_test.hpp"
|
||||
|
||||
#endif // end SEWENEW_REDISPLUSPLUS_TEST_BENCHMARK_TEST_H
|
178
controller/thirdparty/redis-plus-plus-1.1.1/test/src/sw/redis++/benchmark_test.hpp
vendored
Normal file
178
controller/thirdparty/redis-plus-plus-1.1.1/test/src/sw/redis++/benchmark_test.hpp
vendored
Normal file
|
@ -0,0 +1,178 @@
|
|||
/**************************************************************************
|
||||
Copyright (c) 2017 sewenew
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*************************************************************************/
|
||||
|
||||
#ifndef SEWENEW_REDISPLUSPLUS_TEST_BENCHMARK_TEST_HPP
|
||||
#define SEWENEW_REDISPLUSPLUS_TEST_BENCHMARK_TEST_HPP
|
||||
|
||||
#include <chrono>
|
||||
#include <random>
|
||||
#include <future>
|
||||
#include <algorithm>
|
||||
#include "utils.h"
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
namespace test {
|
||||
|
||||
template <typename RedisInstance>
|
||||
BenchmarkTest<RedisInstance>::BenchmarkTest(const BenchmarkOptions &opts,
|
||||
RedisInstance &instance) : _opts(opts), _redis(instance) {
|
||||
REDIS_ASSERT(_opts.pool_size > 0
|
||||
&& _opts.thread_num > 0
|
||||
&& _opts.total_request_num > 0
|
||||
&& _opts.key_len > 0
|
||||
&& _opts.val_len > 0,
|
||||
"Invalid benchmark test options.");
|
||||
|
||||
_keys = _gen_keys();
|
||||
_value = _gen_value();
|
||||
}
|
||||
|
||||
template <typename RedisInstance>
|
||||
void BenchmarkTest<RedisInstance>::run() {
|
||||
_cleanup();
|
||||
|
||||
_run("SET key value", [this](std::size_t idx) { this->_redis.set(this->_key(idx), _value); });
|
||||
|
||||
_run("GET key", [this](std::size_t idx) {
|
||||
auto res = this->_redis.get(this->_key(idx));
|
||||
(void)res;
|
||||
});
|
||||
|
||||
_cleanup();
|
||||
|
||||
_run("LPUSH key value", [this](std::size_t idx) {
|
||||
this->_redis.lpush(this->_key(idx), _value);
|
||||
});
|
||||
|
||||
_run("LRANGE key 0 10", [this](std::size_t idx) {
|
||||
std::vector<std::string> res;
|
||||
res.reserve(10);
|
||||
this->_redis.lrange(this->_key(idx), 0, 10, std::back_inserter(res));
|
||||
});
|
||||
|
||||
_run("LPOP key", [this](std::size_t idx) {
|
||||
auto res = this->_redis.lpop(this->_key(idx));
|
||||
(void)res;
|
||||
});
|
||||
|
||||
_cleanup();
|
||||
|
||||
_run("INCR key", [this](std::size_t idx) {
|
||||
auto num = this->_redis.incr(this->_key(idx));
|
||||
(void)num;
|
||||
});
|
||||
|
||||
_cleanup();
|
||||
|
||||
_run("SADD key member", [this](std::size_t idx) {
|
||||
auto num = this->_redis.sadd(this->_key(idx), _value);
|
||||
(void)num;
|
||||
});
|
||||
|
||||
_run("SPOP key", [this](std::size_t idx) {
|
||||
auto res = this->_redis.spop(this->_key(idx));
|
||||
(void)res;
|
||||
});
|
||||
|
||||
_cleanup();
|
||||
}
|
||||
|
||||
template <typename RedisInstance>
|
||||
template <typename Func>
|
||||
void BenchmarkTest<RedisInstance>::_run(const std::string &title, Func &&func) {
|
||||
auto thread_num = _opts.thread_num;
|
||||
auto requests_per_thread = _opts.total_request_num / thread_num;
|
||||
auto total_request_num = requests_per_thread * thread_num;
|
||||
std::vector<std::future<std::size_t>> res;
|
||||
res.reserve(thread_num);
|
||||
res.push_back(std::async(std::launch::async,
|
||||
[this](Func &&func, std::size_t request_num) {
|
||||
return this->_run(std::forward<Func>(func), request_num);
|
||||
},
|
||||
std::forward<Func>(func),
|
||||
requests_per_thread));
|
||||
|
||||
auto total_in_msec = 0;
|
||||
for (auto &fut : res) {
|
||||
total_in_msec += fut.get();
|
||||
}
|
||||
|
||||
auto total_in_sec = total_in_msec * 1.0 / 1000;
|
||||
|
||||
auto avg = total_in_msec * 1.0 / total_request_num;
|
||||
|
||||
auto ops = static_cast<std::size_t>(1000 / avg);
|
||||
|
||||
std::cout << "-----" << title << "-----" << std::endl;
|
||||
std::cout << total_request_num << " requests cost " << total_in_sec << " seconds" << std::endl;
|
||||
std::cout << ops << " requests per second" << std::endl;
|
||||
}
|
||||
|
||||
template <typename RedisInstance>
|
||||
template <typename Func>
|
||||
std::size_t BenchmarkTest<RedisInstance>::_run(Func &&func, std::size_t request_num) {
|
||||
auto start = std::chrono::steady_clock::now();
|
||||
|
||||
for (auto idx = 0U; idx != request_num; ++idx) {
|
||||
func(idx);
|
||||
}
|
||||
|
||||
auto stop = std::chrono::steady_clock::now();
|
||||
|
||||
return std::chrono::duration_cast<std::chrono::milliseconds>(stop - start).count();
|
||||
}
|
||||
|
||||
template <typename RedisInstance>
|
||||
std::vector<std::string> BenchmarkTest<RedisInstance>::_gen_keys() const {
|
||||
const auto KEY_NUM = 100;
|
||||
std::vector<std::string> res;
|
||||
res.reserve(KEY_NUM);
|
||||
std::default_random_engine engine(std::random_device{}());
|
||||
std::uniform_int_distribution<int> uniform_dist(0, 255);
|
||||
for (auto i = 0; i != KEY_NUM; ++i) {
|
||||
std::string str;
|
||||
str.reserve(_opts.key_len);
|
||||
for (std::size_t j = 0; j != _opts.key_len; ++j) {
|
||||
str.push_back(static_cast<char>(uniform_dist(engine)));
|
||||
}
|
||||
res.push_back(str);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
template <typename RedisInstance>
|
||||
std::string BenchmarkTest<RedisInstance>::_gen_value() const {
|
||||
return std::string(_opts.val_len, 'x');
|
||||
}
|
||||
|
||||
template <typename RedisInstance>
|
||||
void BenchmarkTest<RedisInstance>::_cleanup() {
|
||||
for (const auto &key : _keys) {
|
||||
_redis.del(key);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // end SEWENEW_REDISPLUSPLUS_TEST_BENCHMARK_TEST_HPP
|
49
controller/thirdparty/redis-plus-plus-1.1.1/test/src/sw/redis++/connection_cmds_test.h
vendored
Normal file
49
controller/thirdparty/redis-plus-plus-1.1.1/test/src/sw/redis++/connection_cmds_test.h
vendored
Normal file
|
@ -0,0 +1,49 @@
|
|||
/**************************************************************************
|
||||
Copyright (c) 2017 sewenew
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*************************************************************************/
|
||||
|
||||
#ifndef SEWENEW_REDISPLUSPLUS_TEST_CONNECTION_CMDS_TEST_H
|
||||
#define SEWENEW_REDISPLUSPLUS_TEST_CONNECTION_CMDS_TEST_H
|
||||
|
||||
#include <sw/redis++/redis++.h>
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
namespace test {
|
||||
|
||||
template <typename RedisInstance>
|
||||
class ConnectionCmdTest {
|
||||
public:
|
||||
explicit ConnectionCmdTest(RedisInstance &instance) : _redis(instance) {}
|
||||
|
||||
void run();
|
||||
|
||||
private:
|
||||
void _run(Redis &redis);
|
||||
|
||||
RedisInstance &_redis;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#include "connection_cmds_test.hpp"
|
||||
|
||||
#endif // end SEWENEW_REDISPLUSPLUS_TEST_CONNECTION_CMDS_TEST_H
|
50
controller/thirdparty/redis-plus-plus-1.1.1/test/src/sw/redis++/connection_cmds_test.hpp
vendored
Normal file
50
controller/thirdparty/redis-plus-plus-1.1.1/test/src/sw/redis++/connection_cmds_test.hpp
vendored
Normal file
|
@ -0,0 +1,50 @@
|
|||
/**************************************************************************
|
||||
Copyright (c) 2017 sewenew
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*************************************************************************/
|
||||
|
||||
#ifndef SEWENEW_REDISPLUSPLUS_TEST_CONNECTION_CMDS_TEST_HPP
|
||||
#define SEWENEW_REDISPLUSPLUS_TEST_CONNECTION_CMDS_TEST_HPP
|
||||
|
||||
#include "utils.h"
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
namespace test {
|
||||
|
||||
template <typename RedisInstance>
|
||||
void ConnectionCmdTest<RedisInstance>::run() {
|
||||
cluster_specializing_test(*this, &ConnectionCmdTest<RedisInstance>::_run, _redis);
|
||||
}
|
||||
|
||||
template <typename RedisInstance>
|
||||
void ConnectionCmdTest<RedisInstance>::_run(Redis &instance) {
|
||||
auto message = std::string("hello");
|
||||
|
||||
REDIS_ASSERT(instance.echo(message) == message, "failed to test echo");
|
||||
|
||||
REDIS_ASSERT(instance.ping() == "PONG", "failed to test ping");
|
||||
|
||||
REDIS_ASSERT(instance.ping(message) == message, "failed to test ping");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // end SEWENEW_REDISPLUSPLUS_TEST_CONNECTION_CMDS_TEST_HPP
|
47
controller/thirdparty/redis-plus-plus-1.1.1/test/src/sw/redis++/geo_cmds_test.h
vendored
Normal file
47
controller/thirdparty/redis-plus-plus-1.1.1/test/src/sw/redis++/geo_cmds_test.h
vendored
Normal file
|
@ -0,0 +1,47 @@
|
|||
/**************************************************************************
|
||||
Copyright (c) 2017 sewenew
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*************************************************************************/
|
||||
|
||||
#ifndef SEWENEW_REDISPLUSPLUS_TEST_GEO_CMDS_TEST_H
|
||||
#define SEWENEW_REDISPLUSPLUS_TEST_GEO_CMDS_TEST_H
|
||||
|
||||
#include <sw/redis++/redis++.h>
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
namespace test {
|
||||
|
||||
template <typename RedisInstance>
|
||||
class GeoCmdTest {
|
||||
public:
|
||||
explicit GeoCmdTest(RedisInstance &instance) : _redis(instance) {}
|
||||
|
||||
void run();
|
||||
|
||||
private:
|
||||
RedisInstance &_redis;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#include "geo_cmds_test.hpp"
|
||||
|
||||
#endif // end SEWENEW_REDISPLUSPLUS_TEST_GEO_CMDS_TEST_H
|
149
controller/thirdparty/redis-plus-plus-1.1.1/test/src/sw/redis++/geo_cmds_test.hpp
vendored
Normal file
149
controller/thirdparty/redis-plus-plus-1.1.1/test/src/sw/redis++/geo_cmds_test.hpp
vendored
Normal file
|
@ -0,0 +1,149 @@
|
|||
/**************************************************************************
|
||||
Copyright (c) 2017 sewenew
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*************************************************************************/
|
||||
|
||||
#ifndef SEWENEW_REDISPLUSPLUS_TEST_GEO_CMDS_TEST_HPP
|
||||
#define SEWENEW_REDISPLUSPLUS_TEST_GEO_CMDS_TEST_HPP
|
||||
|
||||
#include <vector>
|
||||
#include <tuple>
|
||||
#include "utils.h"
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
namespace test {
|
||||
|
||||
template <typename RedisInstance>
|
||||
void GeoCmdTest<RedisInstance>::run() {
|
||||
auto key = test_key("geo");
|
||||
auto dest = test_key("dest");
|
||||
|
||||
KeyDeleter<RedisInstance> deleter(_redis, {key, dest});
|
||||
|
||||
auto members = {
|
||||
std::make_tuple("m1", 10.0, 11.0),
|
||||
std::make_tuple("m2", 10.1, 11.1),
|
||||
std::make_tuple("m3", 10.2, 11.2)
|
||||
};
|
||||
|
||||
REDIS_ASSERT(_redis.geoadd(key, std::make_tuple("m1", 10.0, 11.0)) == 1,
|
||||
"failed to test geoadd");
|
||||
REDIS_ASSERT(_redis.geoadd(key, members) == 2, "failed to test geoadd");
|
||||
|
||||
auto dist = _redis.geodist(key, "m1", "m4", GeoUnit::KM);
|
||||
REDIS_ASSERT(!dist, "failed to test geodist with nonexistent member");
|
||||
|
||||
std::vector<OptionalString> hashes;
|
||||
_redis.geohash(key, {"m1", "m4"}, std::back_inserter(hashes));
|
||||
REDIS_ASSERT(hashes.size() == 2, "failed to test geohash");
|
||||
REDIS_ASSERT(bool(hashes[0]) && *(hashes[0]) == "s1zned3z8u0" && !(hashes[1]),
|
||||
"failed to test geohash");
|
||||
hashes.clear();
|
||||
_redis.geohash(key, {"m4"}, std::back_inserter(hashes));
|
||||
REDIS_ASSERT(hashes.size() == 1 && !(hashes[0]), "failed to test geohash");
|
||||
|
||||
std::vector<Optional<std::pair<double, double>>> pos;
|
||||
_redis.geopos(key, {"m4"}, std::back_inserter(pos));
|
||||
REDIS_ASSERT(pos.size() == 1 && !(pos[0]), "failed to test geopos");
|
||||
|
||||
auto num = _redis.georadius(key,
|
||||
std::make_pair(10.1, 11.1),
|
||||
100,
|
||||
GeoUnit::KM,
|
||||
dest,
|
||||
false,
|
||||
10);
|
||||
REDIS_ASSERT(bool(num) && *num == 3, "failed to test georadius with store option");
|
||||
|
||||
std::vector<std::string> mems;
|
||||
_redis.georadius(key,
|
||||
std::make_pair(10.1, 11.1),
|
||||
100,
|
||||
GeoUnit::KM,
|
||||
10,
|
||||
true,
|
||||
std::back_inserter(mems));
|
||||
REDIS_ASSERT(mems.size() == 3, "failed to test georadius with no option");
|
||||
|
||||
std::vector<std::tuple<std::string, double>> with_dist;
|
||||
_redis.georadius(key,
|
||||
std::make_pair(10.1, 11.1),
|
||||
100,
|
||||
GeoUnit::KM,
|
||||
10,
|
||||
true,
|
||||
std::back_inserter(with_dist));
|
||||
REDIS_ASSERT(with_dist.size() == 3, "failed to test georadius with dist");
|
||||
|
||||
std::vector<std::tuple<std::string, double, std::pair<double, double>>> with_dist_coord;
|
||||
_redis.georadius(key,
|
||||
std::make_pair(10.1, 11.1),
|
||||
100,
|
||||
GeoUnit::KM,
|
||||
10,
|
||||
true,
|
||||
std::back_inserter(with_dist_coord));
|
||||
REDIS_ASSERT(with_dist_coord.size() == 3, "failed to test georadius with dist and coord");
|
||||
|
||||
num = _redis.georadiusbymember(key,
|
||||
"m1",
|
||||
100,
|
||||
GeoUnit::KM,
|
||||
dest,
|
||||
false,
|
||||
10);
|
||||
REDIS_ASSERT(bool(num) && *num == 3, "failed to test georadiusbymember with store option");
|
||||
|
||||
mems.clear();
|
||||
_redis.georadiusbymember(key,
|
||||
"m1",
|
||||
100,
|
||||
GeoUnit::KM,
|
||||
10,
|
||||
true,
|
||||
std::back_inserter(mems));
|
||||
REDIS_ASSERT(mems.size() == 3, "failed to test georadiusbymember with no option");
|
||||
|
||||
with_dist.clear();
|
||||
_redis.georadiusbymember(key,
|
||||
"m1",
|
||||
100,
|
||||
GeoUnit::KM,
|
||||
10,
|
||||
true,
|
||||
std::back_inserter(with_dist));
|
||||
REDIS_ASSERT(with_dist.size() == 3, "failed to test georadiusbymember with dist");
|
||||
|
||||
with_dist_coord.clear();
|
||||
_redis.georadiusbymember(key,
|
||||
"m1",
|
||||
100,
|
||||
GeoUnit::KM,
|
||||
10,
|
||||
true,
|
||||
std::back_inserter(with_dist_coord));
|
||||
REDIS_ASSERT(with_dist_coord.size() == 3,
|
||||
"failed to test georadiusbymember with dist and coord");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // end SEWENEW_REDISPLUSPLUS_TEST_GEO_CMDS_TEST_HPP
|
55
controller/thirdparty/redis-plus-plus-1.1.1/test/src/sw/redis++/hash_cmds_test.h
vendored
Normal file
55
controller/thirdparty/redis-plus-plus-1.1.1/test/src/sw/redis++/hash_cmds_test.h
vendored
Normal file
|
@ -0,0 +1,55 @@
|
|||
/**************************************************************************
|
||||
Copyright (c) 2017 sewenew
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*************************************************************************/
|
||||
|
||||
#ifndef SEWENEW_REDISPLUSPLUS_TEST_HASH_CMDS_TEST_H
|
||||
#define SEWENEW_REDISPLUSPLUS_TEST_HASH_CMDS_TEST_H
|
||||
|
||||
#include <sw/redis++/redis++.h>
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
namespace test {
|
||||
|
||||
template <typename RedisInstance>
|
||||
class HashCmdTest {
|
||||
public:
|
||||
explicit HashCmdTest(RedisInstance &instance) : _redis(instance) {}
|
||||
|
||||
void run();
|
||||
|
||||
private:
|
||||
void _test_hash();
|
||||
|
||||
void _test_hash_batch();
|
||||
|
||||
void _test_numeric();
|
||||
|
||||
void _test_hscan();
|
||||
|
||||
RedisInstance &_redis;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#include "hash_cmds_test.hpp"
|
||||
|
||||
#endif // end SEWENEW_REDISPLUSPLUS_TEST_HASH_CMDS_TEST_H
|
177
controller/thirdparty/redis-plus-plus-1.1.1/test/src/sw/redis++/hash_cmds_test.hpp
vendored
Normal file
177
controller/thirdparty/redis-plus-plus-1.1.1/test/src/sw/redis++/hash_cmds_test.hpp
vendored
Normal file
|
@ -0,0 +1,177 @@
|
|||
/**************************************************************************
|
||||
Copyright (c) 2017 sewenew
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*************************************************************************/
|
||||
|
||||
#ifndef SEWENEW_REDISPLUSPLUS_TEST_HASH_CMDS_TEST_HPP
|
||||
#define SEWENEW_REDISPLUSPLUS_TEST_HASH_CMDS_TEST_HPP
|
||||
|
||||
#include <unordered_map>
|
||||
#include "utils.h"
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
namespace test {
|
||||
|
||||
template <typename RedisInstance>
|
||||
void HashCmdTest<RedisInstance>::run() {
|
||||
_test_hash();
|
||||
|
||||
_test_hash_batch();
|
||||
|
||||
_test_numeric();
|
||||
|
||||
_test_hscan();
|
||||
}
|
||||
|
||||
template <typename RedisInstance>
|
||||
void HashCmdTest<RedisInstance>::_test_hash() {
|
||||
auto key = test_key("hash");
|
||||
|
||||
KeyDeleter<RedisInstance> deleter(_redis, key);
|
||||
|
||||
auto f1 = std::string("f1");
|
||||
auto v1 = std::string("v1");
|
||||
auto f2 = std::string("f2");
|
||||
auto v2 = std::string("v2");
|
||||
auto f3 = std::string("f3");
|
||||
auto v3 = std::string("v3");
|
||||
|
||||
REDIS_ASSERT(_redis.hset(key, f1, v1), "failed to test hset");
|
||||
REDIS_ASSERT(!_redis.hset(key, f1, v2), "failed to test hset with exist field");
|
||||
|
||||
auto res = _redis.hget(key, f1);
|
||||
REDIS_ASSERT(res && *res == v2, "failed to test hget");
|
||||
|
||||
REDIS_ASSERT(_redis.hsetnx(key, f2, v1), "failed to test hsetnx");
|
||||
REDIS_ASSERT(!_redis.hsetnx(key, f2, v2), "failed to test hsetnx with exist field");
|
||||
|
||||
res = _redis.hget(key, f2);
|
||||
REDIS_ASSERT(res && *res == v1, "failed to test hget");
|
||||
|
||||
REDIS_ASSERT(!_redis.hexists(key, f3), "failed to test hexists");
|
||||
REDIS_ASSERT(_redis.hset(key, std::make_pair(f3, v3)), "failed to test hset");
|
||||
REDIS_ASSERT(_redis.hexists(key, f3), "failed to test hexists");
|
||||
|
||||
REDIS_ASSERT(_redis.hlen(key) == 3, "failed to test hlen");
|
||||
REDIS_ASSERT(_redis.hstrlen(key, f1) == static_cast<long long>(v1.size()),
|
||||
"failed to test hstrlen");
|
||||
|
||||
REDIS_ASSERT(_redis.hdel(key, f1) == 1, "failed to test hdel");
|
||||
REDIS_ASSERT(_redis.hdel(key, {f1, f2, f3}) == 2, "failed to test hdel range");
|
||||
}
|
||||
|
||||
template <typename RedisInstance>
|
||||
void HashCmdTest<RedisInstance>::_test_hash_batch() {
|
||||
auto key = test_key("hash");
|
||||
|
||||
KeyDeleter<RedisInstance> deleter(_redis, key);
|
||||
|
||||
auto f1 = std::string("f1");
|
||||
auto v1 = std::string("v1");
|
||||
auto f2 = std::string("f2");
|
||||
auto v2 = std::string("v2");
|
||||
auto f3 = std::string("f3");
|
||||
|
||||
_redis.hmset(key, {std::make_pair(f1, v1),
|
||||
std::make_pair(f2, v2)});
|
||||
|
||||
std::vector<std::string> fields;
|
||||
_redis.hkeys(key, std::back_inserter(fields));
|
||||
REDIS_ASSERT(fields.size() == 2, "failed to test hkeys");
|
||||
|
||||
std::vector<std::string> vals;
|
||||
_redis.hvals(key, std::back_inserter(vals));
|
||||
REDIS_ASSERT(vals.size() == 2, "failed to test hvals");
|
||||
|
||||
std::unordered_map<std::string, std::string> items;
|
||||
_redis.hgetall(key, std::inserter(items, items.end()));
|
||||
REDIS_ASSERT(items.size() == 2 && items[f1] == v1 && items[f2] == v2,
|
||||
"failed to test hgetall");
|
||||
|
||||
std::vector<std::pair<std::string, std::string>> item_vec;
|
||||
_redis.hgetall(key, std::back_inserter(item_vec));
|
||||
REDIS_ASSERT(item_vec.size() == 2, "failed to test hgetall");
|
||||
|
||||
std::vector<OptionalString> res;
|
||||
_redis.hmget(key, {f1, f2, f3}, std::back_inserter(res));
|
||||
REDIS_ASSERT(res.size() == 3
|
||||
&& bool(res[0]) && *(res[0]) == v1
|
||||
&& bool(res[1]) && *(res[1]) == v2
|
||||
&& !res[2],
|
||||
"failed to test hmget");
|
||||
}
|
||||
|
||||
template <typename RedisInstance>
|
||||
void HashCmdTest<RedisInstance>::_test_numeric() {
|
||||
auto key = test_key("numeric");
|
||||
|
||||
KeyDeleter<RedisInstance> deleter(_redis, key);
|
||||
|
||||
auto field = "field";
|
||||
|
||||
REDIS_ASSERT(_redis.hincrby(key, field, 1) == 1, "failed to test hincrby");
|
||||
REDIS_ASSERT(_redis.hincrby(key, field, -1) == 0, "failed to test hincrby");
|
||||
REDIS_ASSERT(_redis.hincrbyfloat(key, field, 1.5) == 1.5, "failed to test hincrbyfloat");
|
||||
}
|
||||
|
||||
template <typename RedisInstance>
|
||||
void HashCmdTest<RedisInstance>::_test_hscan() {
|
||||
auto key = test_key("hscan");
|
||||
|
||||
KeyDeleter<RedisInstance> deleter(_redis, key);
|
||||
|
||||
auto items = std::unordered_map<std::string, std::string>{
|
||||
std::make_pair("f1", "v1"),
|
||||
std::make_pair("f2", "v2"),
|
||||
std::make_pair("f3", "v3"),
|
||||
};
|
||||
|
||||
_redis.hmset(key, items.begin(), items.end());
|
||||
|
||||
std::unordered_map<std::string, std::string> item_map;
|
||||
auto cursor = 0;
|
||||
while (true) {
|
||||
cursor = _redis.hscan(key, cursor, "f*", 2, std::inserter(item_map, item_map.end()));
|
||||
if (cursor == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
REDIS_ASSERT(item_map == items, "failed to test hscan with pattern and count");
|
||||
|
||||
std::vector<std::pair<std::string, std::string>> item_vec;
|
||||
cursor = 0;
|
||||
while (true) {
|
||||
cursor = _redis.hscan(key, cursor, std::back_inserter(item_vec));
|
||||
if (cursor == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
REDIS_ASSERT(item_vec.size() == items.size(), "failed to test hscan");
|
||||
for (const auto &ele : item_vec) {
|
||||
REDIS_ASSERT(items.find(ele.first) != items.end(), "failed to test hscan");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // end SEWENEW_REDISPLUSPLUS_TEST_HASH_CMDS_TEST_HPP
|
47
controller/thirdparty/redis-plus-plus-1.1.1/test/src/sw/redis++/hyperloglog_cmds_test.h
vendored
Normal file
47
controller/thirdparty/redis-plus-plus-1.1.1/test/src/sw/redis++/hyperloglog_cmds_test.h
vendored
Normal file
|
@ -0,0 +1,47 @@
|
|||
/**************************************************************************
|
||||
Copyright (c) 2017 sewenew
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*************************************************************************/
|
||||
|
||||
#ifndef SEWENEW_REDISPLUSPLUS_TEST_HYPERLOGLOG_CMDS_TEST_H
|
||||
#define SEWENEW_REDISPLUSPLUS_TEST_HYPERLOGLOG_CMDS_TEST_H
|
||||
|
||||
#include <sw/redis++/redis++.h>
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
namespace test {
|
||||
|
||||
template <typename RedisInstance>
|
||||
class HyperloglogCmdTest {
|
||||
public:
|
||||
explicit HyperloglogCmdTest(RedisInstance &instance) : _redis(instance) {}
|
||||
|
||||
void run();
|
||||
|
||||
private:
|
||||
RedisInstance &_redis;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#include "hyperloglog_cmds_test.hpp"
|
||||
|
||||
#endif // end SEWENEW_REDISPLUSPLUS_TEST_HYPERLOGLOG_CMDS_TEST_H
|
67
controller/thirdparty/redis-plus-plus-1.1.1/test/src/sw/redis++/hyperloglog_cmds_test.hpp
vendored
Normal file
67
controller/thirdparty/redis-plus-plus-1.1.1/test/src/sw/redis++/hyperloglog_cmds_test.hpp
vendored
Normal file
|
@ -0,0 +1,67 @@
|
|||
/**************************************************************************
|
||||
Copyright (c) 2017 sewenew
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*************************************************************************/
|
||||
|
||||
#ifndef SEWENEW_REDISPLUSPLUS_TEST_HYPERLOGLOG_CMDS_TEST_HPP
|
||||
#define SEWENEW_REDISPLUSPLUS_TEST_HYPERLOGLOG_CMDS_TEST_HPP
|
||||
|
||||
#include "utils.h"
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
namespace test {
|
||||
|
||||
template <typename RedisInstance>
|
||||
void HyperloglogCmdTest<RedisInstance>::run() {
|
||||
auto k1 = test_key("k1");
|
||||
auto k2 = test_key("k2");
|
||||
auto k3 = test_key("k3");
|
||||
|
||||
KeyDeleter<RedisInstance> deleter(_redis, {k1, k2, k3});
|
||||
|
||||
_redis.pfadd(k1, "a");
|
||||
auto members1 = {"b", "c", "d", "e", "f", "g"};
|
||||
_redis.pfadd(k1, members1);
|
||||
|
||||
auto cnt = _redis.pfcount(k1);
|
||||
auto err = cnt * 1.0 / (1 + members1.size());
|
||||
REDIS_ASSERT(err < 1.02 && err > 0.98, "failed to test pfadd and pfcount");
|
||||
|
||||
auto members2 = {"a", "b", "c", "h", "i", "j", "k"};
|
||||
_redis.pfadd(k2, members2);
|
||||
auto total = 1 + members1.size() + members2.size() - 3;
|
||||
|
||||
cnt = _redis.pfcount({k1, k2});
|
||||
err = cnt * 1.0 / total;
|
||||
REDIS_ASSERT(err < 1.02 && err > 0.98, "failed to test pfcount");
|
||||
|
||||
_redis.pfmerge(k3, {k1, k2});
|
||||
cnt = _redis.pfcount(k3);
|
||||
err = cnt * 1.0 / total;
|
||||
REDIS_ASSERT(err < 1.02 && err > 0.98, "failed to test pfcount");
|
||||
|
||||
_redis.pfmerge(k3, k1);
|
||||
REDIS_ASSERT(cnt == _redis.pfcount(k3), "failed to test pfmerge");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // end SEWENEW_REDISPLUSPLUS_TEST_HYPERLOGLOG_CMDS_TEST_HPP
|
55
controller/thirdparty/redis-plus-plus-1.1.1/test/src/sw/redis++/keys_cmds_test.h
vendored
Normal file
55
controller/thirdparty/redis-plus-plus-1.1.1/test/src/sw/redis++/keys_cmds_test.h
vendored
Normal file
|
@ -0,0 +1,55 @@
|
|||
/**************************************************************************
|
||||
Copyright (c) 2017 sewenew
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*************************************************************************/
|
||||
|
||||
#ifndef SEWENEW_REDISPLUSPLUS_TEST_KEYS_CMDS_TEST_H
|
||||
#define SEWENEW_REDISPLUSPLUS_TEST_KEYS_CMDS_TEST_H
|
||||
|
||||
#include <sw/redis++/redis++.h>
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
namespace test {
|
||||
|
||||
template <typename RedisInstance>
|
||||
class KeysCmdTest {
|
||||
public:
|
||||
explicit KeysCmdTest(RedisInstance &instance) : _redis(instance) {}
|
||||
|
||||
void run();
|
||||
|
||||
private:
|
||||
void _test_key();
|
||||
|
||||
void _test_randomkey(Redis &instance);
|
||||
|
||||
void _test_ttl();
|
||||
|
||||
void _test_scan(Redis &instance);
|
||||
|
||||
RedisInstance &_redis;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#include "keys_cmds_test.hpp"
|
||||
|
||||
#endif // end SEWENEW_REDISPLUSPLUS_TEST_KEYS_CMDS_TEST_H
|
166
controller/thirdparty/redis-plus-plus-1.1.1/test/src/sw/redis++/keys_cmds_test.hpp
vendored
Normal file
166
controller/thirdparty/redis-plus-plus-1.1.1/test/src/sw/redis++/keys_cmds_test.hpp
vendored
Normal file
|
@ -0,0 +1,166 @@
|
|||
/**************************************************************************
|
||||
Copyright (c) 2017 sewenew
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*************************************************************************/
|
||||
|
||||
#ifndef SEWENEW_REDISPLUSPLUS_TEST_KEYS_CMDS_TEST_HPP
|
||||
#define SEWENEW_REDISPLUSPLUS_TEST_KEYS_CMDS_TEST_HPP
|
||||
|
||||
#include <vector>
|
||||
#include <unordered_set>
|
||||
#include "utils.h"
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
namespace test {
|
||||
|
||||
template <typename RedisInstance>
|
||||
void KeysCmdTest<RedisInstance>::run() {
|
||||
_test_key();
|
||||
|
||||
cluster_specializing_test(*this, &KeysCmdTest<RedisInstance>::_test_randomkey, _redis);
|
||||
|
||||
_test_ttl();
|
||||
|
||||
cluster_specializing_test(*this, &KeysCmdTest<RedisInstance>::_test_scan, _redis);
|
||||
}
|
||||
|
||||
template <typename RedisInstance>
|
||||
void KeysCmdTest<RedisInstance>::_test_key() {
|
||||
auto key = test_key("key");
|
||||
auto dest = test_key("dest");
|
||||
auto new_key_name = test_key("new-key");
|
||||
auto not_exist_key = test_key("not-exist");
|
||||
|
||||
KeyDeleter<RedisInstance> deleter(_redis, {key, dest, new_key_name});
|
||||
|
||||
REDIS_ASSERT(_redis.exists(key) == 0, "failed to test exists");
|
||||
|
||||
auto val = std::string("val");
|
||||
_redis.set(key, val);
|
||||
|
||||
REDIS_ASSERT(_redis.exists({key, not_exist_key}) == 1, "failed to test exists");
|
||||
|
||||
auto new_val = _redis.dump(key);
|
||||
REDIS_ASSERT(bool(new_val), "failed to test dump");
|
||||
|
||||
_redis.restore(dest, *new_val, std::chrono::seconds(1000));
|
||||
|
||||
new_val = _redis.get(dest);
|
||||
REDIS_ASSERT(bool(new_val) && *new_val == val, "failed to test dump and restore");
|
||||
|
||||
_redis.rename(dest, new_key_name);
|
||||
|
||||
bool not_exist = false;
|
||||
try {
|
||||
_redis.rename(not_exist_key, new_key_name);
|
||||
} catch (const Error &e) {
|
||||
not_exist = true;
|
||||
}
|
||||
REDIS_ASSERT(not_exist, "failed to test rename with nonexistent key");
|
||||
|
||||
REDIS_ASSERT(_redis.renamenx(new_key_name, dest), "failed to test renamenx");
|
||||
|
||||
REDIS_ASSERT(_redis.touch(not_exist_key) == 0, "failed to test touch");
|
||||
REDIS_ASSERT(_redis.touch({key, dest, new_key_name}) == 2, "failed to test touch");
|
||||
|
||||
REDIS_ASSERT(_redis.type(key) == "string", "failed to test type");
|
||||
|
||||
REDIS_ASSERT(_redis.del({new_key_name, dest}) == 1, "failed to test del");
|
||||
REDIS_ASSERT(_redis.unlink({new_key_name, key}) == 1, "failed to test unlink");
|
||||
}
|
||||
|
||||
template <typename RedisInstance>
|
||||
void KeysCmdTest<RedisInstance>::_test_randomkey(Redis &instance) {
|
||||
auto key = test_key("randomkey");
|
||||
|
||||
KeyDeleter<Redis> deleter(instance, key);
|
||||
|
||||
instance.set(key, "value");
|
||||
|
||||
auto rand_key = instance.randomkey();
|
||||
REDIS_ASSERT(bool(rand_key), "failed to test randomkey");
|
||||
}
|
||||
|
||||
template <typename RedisInstance>
|
||||
void KeysCmdTest<RedisInstance>::_test_ttl() {
|
||||
using namespace std::chrono;
|
||||
|
||||
auto key = test_key("ttl");
|
||||
|
||||
KeyDeleter<RedisInstance> deleter(_redis, key);
|
||||
|
||||
_redis.set(key, "val", seconds(100));
|
||||
auto ttl = _redis.ttl(key);
|
||||
REDIS_ASSERT(ttl > 0 && ttl <= 100, "failed to test ttl");
|
||||
|
||||
REDIS_ASSERT(_redis.persist(key), "failed to test persist");
|
||||
ttl = _redis.ttl(key);
|
||||
REDIS_ASSERT(ttl == -1, "failed to test ttl");
|
||||
|
||||
REDIS_ASSERT(_redis.expire(key, seconds(100)),
|
||||
"failed to test expire");
|
||||
|
||||
auto tp = time_point_cast<seconds>(system_clock::now() + seconds(100));
|
||||
REDIS_ASSERT(_redis.expireat(key, tp), "failed to test expireat");
|
||||
ttl = _redis.ttl(key);
|
||||
REDIS_ASSERT(ttl > 0, "failed to test expireat");
|
||||
|
||||
REDIS_ASSERT(_redis.pexpire(key, milliseconds(100000)), "failed to test expire");
|
||||
|
||||
auto pttl = _redis.pttl(key);
|
||||
REDIS_ASSERT(pttl > 0 && pttl <= 100000, "failed to test pttl");
|
||||
|
||||
auto tp_milli = time_point_cast<milliseconds>(system_clock::now() + milliseconds(100000));
|
||||
REDIS_ASSERT(_redis.pexpireat(key, tp_milli), "failed to test pexpireat");
|
||||
pttl = _redis.pttl(key);
|
||||
REDIS_ASSERT(pttl > 0, "failed to test pexpireat");
|
||||
}
|
||||
|
||||
template <typename RedisInstance>
|
||||
void KeysCmdTest<RedisInstance>::_test_scan(Redis &instance) {
|
||||
std::string key_pattern = "!@#$%^&()_+alseufoawhnlkszd";
|
||||
auto k1 = test_key(key_pattern + "k1");
|
||||
auto k2 = test_key(key_pattern + "k2");
|
||||
auto k3 = test_key(key_pattern + "k3");
|
||||
|
||||
auto keys = {k1, k2, k3};
|
||||
|
||||
KeyDeleter<Redis> deleter(instance, keys);
|
||||
|
||||
instance.set(k1, "v");
|
||||
instance.set(k2, "v");
|
||||
instance.set(k3, "v");
|
||||
|
||||
auto cursor = 0;
|
||||
std::unordered_set<std::string> res;
|
||||
while (true) {
|
||||
cursor = instance.scan(cursor, "*" + key_pattern + "*", 2, std::inserter(res, res.end()));
|
||||
if (cursor == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
REDIS_ASSERT(res == std::unordered_set<std::string>(keys),
|
||||
"failed to test scan");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // end SEWENEW_REDISPLUSPLUS_TEST_KEYS_CMDS_TEST_HPP
|
55
controller/thirdparty/redis-plus-plus-1.1.1/test/src/sw/redis++/list_cmds_test.h
vendored
Normal file
55
controller/thirdparty/redis-plus-plus-1.1.1/test/src/sw/redis++/list_cmds_test.h
vendored
Normal file
|
@ -0,0 +1,55 @@
|
|||
/**************************************************************************
|
||||
Copyright (c) 2017 sewenew
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*************************************************************************/
|
||||
|
||||
#ifndef SEWENEW_REDISPLUSPLUS_TEST_LIST_CMDS_TEST_H
|
||||
#define SEWENEW_REDISPLUSPLUS_TEST_LIST_CMDS_TEST_H
|
||||
|
||||
#include <sw/redis++/redis++.h>
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
namespace test {
|
||||
|
||||
template <typename RedisInstance>
|
||||
class ListCmdTest {
|
||||
public:
|
||||
explicit ListCmdTest(RedisInstance &instance) : _redis(instance) {}
|
||||
|
||||
void run();
|
||||
|
||||
private:
|
||||
void _test_lpoppush();
|
||||
|
||||
void _test_rpoppush();
|
||||
|
||||
void _test_list();
|
||||
|
||||
void _test_blocking();
|
||||
|
||||
RedisInstance &_redis;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#include "list_cmds_test.hpp"
|
||||
|
||||
#endif // end SEWENEW_REDISPLUSPLUS_TEST_LIST_CMDS_TEST_H
|
154
controller/thirdparty/redis-plus-plus-1.1.1/test/src/sw/redis++/list_cmds_test.hpp
vendored
Normal file
154
controller/thirdparty/redis-plus-plus-1.1.1/test/src/sw/redis++/list_cmds_test.hpp
vendored
Normal file
|
@ -0,0 +1,154 @@
|
|||
/**************************************************************************
|
||||
Copyright (c) 2017 sewenew
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*************************************************************************/
|
||||
|
||||
#ifndef SEWENEW_REDISPLUSPLUS_TEST_LIST_CMDS_TEST_HPP
|
||||
#define SEWENEW_REDISPLUSPLUS_TEST_LIST_CMDS_TEST_HPP
|
||||
|
||||
#include "utils.h"
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
namespace test {
|
||||
|
||||
template <typename RedisInstance>
|
||||
void ListCmdTest<RedisInstance>::run() {
|
||||
_test_lpoppush();
|
||||
|
||||
_test_rpoppush();
|
||||
|
||||
_test_list();
|
||||
|
||||
_test_blocking();
|
||||
}
|
||||
|
||||
template <typename RedisInstance>
|
||||
void ListCmdTest<RedisInstance>::_test_lpoppush() {
|
||||
auto key = test_key("lpoppush");
|
||||
|
||||
KeyDeleter<RedisInstance> deleter(_redis, key);
|
||||
|
||||
auto item = _redis.lpop(key);
|
||||
REDIS_ASSERT(!item, "failed to test lpop");
|
||||
|
||||
REDIS_ASSERT(_redis.lpushx(key, "1") == 0, "failed to test lpushx");
|
||||
REDIS_ASSERT(_redis.lpush(key, "1") == 1, "failed to test lpush");
|
||||
REDIS_ASSERT(_redis.lpushx(key, "2") == 2, "failed to test lpushx");
|
||||
REDIS_ASSERT(_redis.lpush(key, {"3", "4", "5"}) == 5, "failed to test lpush");
|
||||
|
||||
item = _redis.lpop(key);
|
||||
REDIS_ASSERT(item && *item == "5", "failed to test lpop");
|
||||
}
|
||||
|
||||
template <typename RedisInstance>
|
||||
void ListCmdTest<RedisInstance>::_test_rpoppush() {
|
||||
auto key = test_key("rpoppush");
|
||||
|
||||
KeyDeleter<RedisInstance> deleter(_redis, key);
|
||||
|
||||
auto item = _redis.rpop(key);
|
||||
REDIS_ASSERT(!item, "failed to test rpop");
|
||||
|
||||
REDIS_ASSERT(_redis.rpushx(key, "1") == 0, "failed to test rpushx");
|
||||
REDIS_ASSERT(_redis.rpush(key, "1") == 1, "failed to test rpush");
|
||||
REDIS_ASSERT(_redis.rpushx(key, "2") == 2, "failed to test rpushx");
|
||||
REDIS_ASSERT(_redis.rpush(key, {"3", "4", "5"}) == 5, "failed to test rpush");
|
||||
|
||||
item = _redis.rpop(key);
|
||||
REDIS_ASSERT(item && *item == "5", "failed to test rpop");
|
||||
}
|
||||
|
||||
template <typename RedisInstance>
|
||||
void ListCmdTest<RedisInstance>::_test_list() {
|
||||
auto key = test_key("list");
|
||||
|
||||
KeyDeleter<RedisInstance> deleter(_redis, key);
|
||||
|
||||
auto item = _redis.lindex(key, 0);
|
||||
REDIS_ASSERT(!item, "failed to test lindex");
|
||||
|
||||
_redis.lpush(key, {"1", "2", "3", "4", "5"});
|
||||
|
||||
REDIS_ASSERT(_redis.lrem(key, 0, "3") == 1, "failed to test lrem");
|
||||
|
||||
REDIS_ASSERT(_redis.linsert(key, InsertPosition::BEFORE, "2", "3") == 5,
|
||||
"failed to test lindex");
|
||||
|
||||
REDIS_ASSERT(_redis.llen(key) == 5, "failed to test llen");
|
||||
|
||||
_redis.lset(key, 0, "6");
|
||||
item = _redis.lindex(key, 0);
|
||||
REDIS_ASSERT(item && *item == "6", "failed to test lindex");
|
||||
|
||||
_redis.ltrim(key, 0, 2);
|
||||
|
||||
std::vector<std::string> res;
|
||||
_redis.lrange(key, 0, -1, std::back_inserter(res));
|
||||
REDIS_ASSERT(res == std::vector<std::string>({"6", "4", "3"}), "failed to test ltrim");
|
||||
}
|
||||
|
||||
template <typename RedisInstance>
|
||||
void ListCmdTest<RedisInstance>::_test_blocking() {
|
||||
auto k1 = test_key("k1");
|
||||
auto k2 = test_key("k2");
|
||||
auto k3 = test_key("k3");
|
||||
|
||||
auto keys = {k1, k2, k3};
|
||||
|
||||
KeyDeleter<RedisInstance> deleter(_redis, keys);
|
||||
|
||||
std::string val("value");
|
||||
_redis.lpush(k1, val);
|
||||
|
||||
auto res = _redis.blpop(keys.begin(), keys.end());
|
||||
REDIS_ASSERT(res && *res == std::make_pair(k1, val), "failed to test blpop");
|
||||
|
||||
res = _redis.brpop(keys, std::chrono::seconds(1));
|
||||
REDIS_ASSERT(!res, "failed to test brpop with timeout");
|
||||
|
||||
_redis.lpush(k1, val);
|
||||
res = _redis.blpop(k1);
|
||||
REDIS_ASSERT(res && *res == std::make_pair(k1, val), "failed to test blpop");
|
||||
|
||||
res = _redis.blpop(k1, std::chrono::seconds(1));
|
||||
REDIS_ASSERT(!res, "failed to test blpop with timeout");
|
||||
|
||||
_redis.lpush(k1, val);
|
||||
res = _redis.brpop(k1);
|
||||
REDIS_ASSERT(res && *res == std::make_pair(k1, val), "failed to test brpop");
|
||||
|
||||
res = _redis.brpop(k1, std::chrono::seconds(1));
|
||||
REDIS_ASSERT(!res, "failed to test brpop with timeout");
|
||||
|
||||
auto str = _redis.brpoplpush(k2, k3, std::chrono::seconds(1));
|
||||
REDIS_ASSERT(!str, "failed to test brpoplpush with timeout");
|
||||
|
||||
_redis.lpush(k2, val);
|
||||
str = _redis.brpoplpush(k2, k3);
|
||||
REDIS_ASSERT(str && *str == val, "failed to test brpoplpush");
|
||||
|
||||
str = _redis.rpoplpush(k3, k2);
|
||||
REDIS_ASSERT(str && *str == val, "failed to test rpoplpush");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // end SEWENEW_REDISPLUSPLUS_TEST_LIST_CMDS_TEST_HPP
|
57
controller/thirdparty/redis-plus-plus-1.1.1/test/src/sw/redis++/pipeline_transaction_test.h
vendored
Normal file
57
controller/thirdparty/redis-plus-plus-1.1.1/test/src/sw/redis++/pipeline_transaction_test.h
vendored
Normal file
|
@ -0,0 +1,57 @@
|
|||
/**************************************************************************
|
||||
Copyright (c) 2017 sewenew
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*************************************************************************/
|
||||
|
||||
#ifndef SEWENEW_REDISPLUSPLUS_TEST_PIPELINE_TRANSACTION_TEST_H
|
||||
#define SEWENEW_REDISPLUSPLUS_TEST_PIPELINE_TRANSACTION_TEST_H
|
||||
|
||||
#include <sw/redis++/redis++.h>
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
namespace test {
|
||||
|
||||
template <typename RedisInstance>
|
||||
class PipelineTransactionTest {
|
||||
public:
|
||||
explicit PipelineTransactionTest(RedisInstance &instance) : _redis(instance) {}
|
||||
|
||||
void run();
|
||||
|
||||
private:
|
||||
Pipeline _pipeline(const StringView &key);
|
||||
|
||||
Transaction _transaction(const StringView &key, bool piped);
|
||||
|
||||
void _test_pipeline(const StringView &key, Pipeline &pipe);
|
||||
|
||||
void _test_transaction(const StringView &key, Transaction &tx);
|
||||
|
||||
void _test_watch();
|
||||
|
||||
RedisInstance &_redis;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#include "pipeline_transaction_test.hpp"
|
||||
|
||||
#endif // end SEWENEW_REDISPLUSPLUS_TEST_PIPELINE_TRANSACTION_TEST_H
|
184
controller/thirdparty/redis-plus-plus-1.1.1/test/src/sw/redis++/pipeline_transaction_test.hpp
vendored
Normal file
184
controller/thirdparty/redis-plus-plus-1.1.1/test/src/sw/redis++/pipeline_transaction_test.hpp
vendored
Normal file
|
@ -0,0 +1,184 @@
|
|||
/**************************************************************************
|
||||
Copyright (c) 2017 sewenew
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*************************************************************************/
|
||||
|
||||
#ifndef SEWENEW_REDISPLUSPLUS_TEST_PIPELINE_TRANSACTION_TEST_HPP
|
||||
#define SEWENEW_REDISPLUSPLUS_TEST_PIPELINE_TRANSACTION_TEST_HPP
|
||||
|
||||
#include <string>
|
||||
#include "utils.h"
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
namespace test {
|
||||
|
||||
template <typename RedisInstance>
|
||||
void PipelineTransactionTest<RedisInstance>::run() {
|
||||
{
|
||||
auto key = test_key("pipeline");
|
||||
KeyDeleter<RedisInstance> deleter(_redis, key);
|
||||
auto pipe = _pipeline(key);
|
||||
_test_pipeline(key, pipe);
|
||||
}
|
||||
|
||||
{
|
||||
auto key = test_key("transaction");
|
||||
KeyDeleter<RedisInstance> deleter(_redis, key);
|
||||
auto tx = _transaction(key, true);
|
||||
_test_transaction(key, tx);
|
||||
}
|
||||
|
||||
{
|
||||
auto key = test_key("transaction");
|
||||
KeyDeleter<RedisInstance> deleter(_redis, key);
|
||||
auto tx = _transaction(key, false);
|
||||
_test_transaction(key, tx);
|
||||
}
|
||||
|
||||
_test_watch();
|
||||
}
|
||||
|
||||
template <typename RedisInstance>
|
||||
Pipeline PipelineTransactionTest<RedisInstance>::_pipeline(const StringView &) {
|
||||
return _redis.pipeline();
|
||||
}
|
||||
|
||||
template <>
|
||||
inline Pipeline PipelineTransactionTest<RedisCluster>::_pipeline(const StringView &key) {
|
||||
return _redis.pipeline(key);
|
||||
}
|
||||
|
||||
template <typename RedisInstance>
|
||||
Transaction PipelineTransactionTest<RedisInstance>::_transaction(const StringView &, bool piped) {
|
||||
return _redis.transaction(piped);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline Transaction PipelineTransactionTest<RedisCluster>::_transaction(const StringView &key,
|
||||
bool piped) {
|
||||
return _redis.transaction(key, piped);
|
||||
}
|
||||
|
||||
template <typename RedisInstance>
|
||||
void PipelineTransactionTest<RedisInstance>::_test_pipeline(const StringView &key,
|
||||
Pipeline &pipe) {
|
||||
std::string val("value");
|
||||
auto replies = pipe.set(key, val)
|
||||
.get(key)
|
||||
.strlen(key)
|
||||
.exec();
|
||||
|
||||
REDIS_ASSERT(replies.get<bool>(0), "failed to test pipeline with set operation");
|
||||
|
||||
auto new_val = replies.get<OptionalString>(1);
|
||||
std::size_t len = replies.get<long long>(2);
|
||||
REDIS_ASSERT(bool(new_val) && *new_val == val && len == val.size(),
|
||||
"failed to test pipeline with string operations");
|
||||
|
||||
REDIS_ASSERT(reply::parse<bool>(replies.get(0)), "failed to test pipeline with set operation");
|
||||
|
||||
new_val = reply::parse<OptionalString>(replies.get(1));
|
||||
len = reply::parse<long long>(replies.get(2));
|
||||
REDIS_ASSERT(bool(new_val) && *new_val == val && len == val.size(),
|
||||
"failed to test pipeline with string operations");
|
||||
}
|
||||
|
||||
template <typename RedisInstance>
|
||||
void PipelineTransactionTest<RedisInstance>::_test_transaction(const StringView &key,
|
||||
Transaction &tx) {
|
||||
std::unordered_map<std::string, std::string> m = {
|
||||
std::make_pair("f1", "v1"),
|
||||
std::make_pair("f2", "v2"),
|
||||
std::make_pair("f3", "v3")
|
||||
};
|
||||
auto replies = tx.hmset(key, m.begin(), m.end())
|
||||
.hgetall(key)
|
||||
.hdel(key, "f1")
|
||||
.exec();
|
||||
|
||||
replies.get<void>(0);
|
||||
|
||||
decltype(m) mm;
|
||||
replies.get(1, std::inserter(mm, mm.end()));
|
||||
REDIS_ASSERT(mm == m, "failed to test transaction");
|
||||
|
||||
REDIS_ASSERT(replies.get<long long>(2) == 1, "failed to test transaction");
|
||||
|
||||
tx.set(key, "value")
|
||||
.get(key)
|
||||
.incr(key);
|
||||
|
||||
tx.discard();
|
||||
|
||||
replies = tx.del(key)
|
||||
.set(key, "value")
|
||||
.exec();
|
||||
|
||||
REDIS_ASSERT(replies.get<long long>(0) == 1, "failed to test transaction");
|
||||
|
||||
REDIS_ASSERT(replies.get<bool>(1), "failed to test transaction");
|
||||
}
|
||||
|
||||
template <typename RedisInstance>
|
||||
void PipelineTransactionTest<RedisInstance>::_test_watch() {
|
||||
auto key = test_key("watch");
|
||||
|
||||
KeyDeleter<RedisInstance> deleter(_redis, key);
|
||||
|
||||
{
|
||||
auto tx = _transaction(key, false);
|
||||
|
||||
auto redis = tx.redis();
|
||||
|
||||
redis.watch(key);
|
||||
|
||||
auto replies = tx.set(key, "1").get(key).exec();
|
||||
|
||||
REDIS_ASSERT(replies.size() == 2
|
||||
&& replies.template get<bool>(0) == true, "failed to test watch");
|
||||
|
||||
auto val = replies.template get<sw::redis::OptionalString>(1);
|
||||
|
||||
REDIS_ASSERT(val && *val == "1", "failed to test watch");
|
||||
}
|
||||
|
||||
try {
|
||||
auto tx = _transaction(key, false);
|
||||
|
||||
auto redis = tx.redis();
|
||||
|
||||
redis.watch(key);
|
||||
|
||||
// Key has been modified by other client.
|
||||
_redis.set(key, "val");
|
||||
|
||||
// Transaction should fail, and throw WatchError
|
||||
tx.set(key, "1").exec();
|
||||
|
||||
REDIS_ASSERT(false, "failed to test watch");
|
||||
} catch (const sw::redis::WatchError &err) {
|
||||
// Catch the error.
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // end SEWENEW_REDISPLUSPLUS_TEST_PIPELINE_TRANSACTION_TEST_HPP
|
53
controller/thirdparty/redis-plus-plus-1.1.1/test/src/sw/redis++/pubsub_test.h
vendored
Normal file
53
controller/thirdparty/redis-plus-plus-1.1.1/test/src/sw/redis++/pubsub_test.h
vendored
Normal file
|
@ -0,0 +1,53 @@
|
|||
/**************************************************************************
|
||||
Copyright (c) 2017 sewenew
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*************************************************************************/
|
||||
|
||||
#ifndef SEWENEW_REDISPLUSPLUS_TEST_SUBPUB_TEST_H
|
||||
#define SEWENEW_REDISPLUSPLUS_TEST_SUBPUB_TEST_H
|
||||
|
||||
#include <sw/redis++/redis++.h>
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
namespace test {
|
||||
|
||||
template <typename RedisInstance>
|
||||
class PubSubTest {
|
||||
public:
|
||||
explicit PubSubTest(RedisInstance &instance) : _redis(instance) {}
|
||||
|
||||
void run();
|
||||
|
||||
private:
|
||||
void _test_sub_channel();
|
||||
|
||||
void _test_sub_pattern();
|
||||
|
||||
void _test_unsubscribe();
|
||||
|
||||
RedisInstance &_redis;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#include "pubsub_test.hpp"
|
||||
|
||||
#endif // end SEWENEW_REDISPLUSPLUS_TEST_SUBPUB_TEST_H
|
244
controller/thirdparty/redis-plus-plus-1.1.1/test/src/sw/redis++/pubsub_test.hpp
vendored
Normal file
244
controller/thirdparty/redis-plus-plus-1.1.1/test/src/sw/redis++/pubsub_test.hpp
vendored
Normal file
|
@ -0,0 +1,244 @@
|
|||
/**************************************************************************
|
||||
Copyright (c) 2017 sewenew
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*************************************************************************/
|
||||
|
||||
#ifndef SEWENEW_REDISPLUSPLUS_TEST_SUBPUB_TEST_HPP
|
||||
#define SEWENEW_REDISPLUSPLUS_TEST_SUBPUB_TEST_HPP
|
||||
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include "utils.h"
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
namespace test {
|
||||
|
||||
template <typename RedisInstance>
|
||||
void PubSubTest<RedisInstance>::run() {
|
||||
_test_sub_channel();
|
||||
|
||||
_test_sub_pattern();
|
||||
|
||||
_test_unsubscribe();
|
||||
}
|
||||
|
||||
template <typename RedisInstance>
|
||||
void PubSubTest<RedisInstance>::_test_sub_channel() {
|
||||
auto sub = _redis.subscriber();
|
||||
|
||||
auto msgs = {"msg1", "msg2"};
|
||||
auto channel1 = test_key("c1");
|
||||
sub.on_message([&msgs, &channel1](std::string channel, std::string msg) {
|
||||
static std::size_t idx = 0;
|
||||
REDIS_ASSERT(channel == channel1 && msg == *(msgs.begin() + idx),
|
||||
"failed to test subscribe");
|
||||
++idx;
|
||||
});
|
||||
|
||||
sub.subscribe(channel1);
|
||||
|
||||
// Consume the SUBSCRIBE message.
|
||||
sub.consume();
|
||||
|
||||
for (const auto &msg : msgs) {
|
||||
_redis.publish(channel1, msg);
|
||||
sub.consume();
|
||||
}
|
||||
|
||||
sub.unsubscribe(channel1);
|
||||
|
||||
// Consume the UNSUBSCRIBE message.
|
||||
sub.consume();
|
||||
|
||||
auto channel2 = test_key("c2");
|
||||
auto channel3 = test_key("c3");
|
||||
auto channel4 = test_key("c4");
|
||||
std::unordered_set<std::string> channels;
|
||||
sub.on_meta([&channels](Subscriber::MsgType type,
|
||||
OptionalString channel,
|
||||
long long num) {
|
||||
REDIS_ASSERT(bool(channel), "failed to test subscribe");
|
||||
|
||||
if (type == Subscriber::MsgType::SUBSCRIBE) {
|
||||
auto iter = channels.find(*channel);
|
||||
REDIS_ASSERT(iter == channels.end(), "failed to test subscribe");
|
||||
channels.insert(*channel);
|
||||
REDIS_ASSERT(static_cast<std::size_t>(num) == channels.size(),
|
||||
"failed to test subscribe");
|
||||
} else if (type == Subscriber::MsgType::UNSUBSCRIBE) {
|
||||
auto iter = channels.find(*channel);
|
||||
REDIS_ASSERT(iter != channels.end(), "failed to test subscribe");
|
||||
channels.erase(*channel);
|
||||
REDIS_ASSERT(static_cast<std::size_t>(num) == channels.size(),
|
||||
"failed to test subscribe");
|
||||
} else {
|
||||
REDIS_ASSERT(false, "Unknown message type");
|
||||
}
|
||||
});
|
||||
|
||||
std::unordered_map<std::string, std::string> messages = {
|
||||
{channel2, "msg2"},
|
||||
{channel3, "msg3"},
|
||||
{channel4, "msg4"},
|
||||
};
|
||||
sub.on_message([&messages](std::string channel, std::string msg) {
|
||||
REDIS_ASSERT(messages.find(channel) != messages.end(),
|
||||
"failed to test subscribe");
|
||||
REDIS_ASSERT(messages[channel] == msg, "failed to test subscribe");
|
||||
});
|
||||
|
||||
sub.subscribe({channel2, channel3, channel4});
|
||||
|
||||
for (std::size_t idx = 0; idx != channels.size(); ++idx) {
|
||||
sub.consume();
|
||||
}
|
||||
|
||||
for (const auto &ele : messages) {
|
||||
_redis.publish(ele.first, ele.second);
|
||||
sub.consume();
|
||||
}
|
||||
|
||||
auto tmp = {channel2, channel3, channel4};
|
||||
sub.unsubscribe(tmp);
|
||||
|
||||
for (std::size_t idx = 0; idx != tmp.size(); ++idx) {
|
||||
sub.consume();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename RedisInstance>
|
||||
void PubSubTest<RedisInstance>::_test_sub_pattern() {
|
||||
auto sub = _redis.subscriber();
|
||||
|
||||
auto msgs = {"msg1", "msg2"};
|
||||
auto pattern1 = test_key("pattern*");
|
||||
std::string channel1 = test_key("pattern1");
|
||||
sub.on_pmessage([&msgs, &pattern1, &channel1](std::string pattern,
|
||||
std::string channel,
|
||||
std::string msg) {
|
||||
static std::size_t idx = 0;
|
||||
REDIS_ASSERT(pattern == pattern1
|
||||
&& channel == channel1
|
||||
&& msg == *(msgs.begin() + idx),
|
||||
"failed to test psubscribe");
|
||||
++idx;
|
||||
});
|
||||
|
||||
sub.psubscribe(pattern1);
|
||||
|
||||
// Consume the PSUBSCRIBE message.
|
||||
sub.consume();
|
||||
|
||||
for (const auto &msg : msgs) {
|
||||
_redis.publish(channel1, msg);
|
||||
sub.consume();
|
||||
}
|
||||
|
||||
sub.punsubscribe(pattern1);
|
||||
|
||||
// Consume the PUNSUBSCRIBE message.
|
||||
sub.consume();
|
||||
|
||||
auto channel2 = test_key("pattern22");
|
||||
auto channel3 = test_key("pattern33");
|
||||
auto channel4 = test_key("pattern44");
|
||||
std::unordered_set<std::string> channels;
|
||||
sub.on_meta([&channels](Subscriber::MsgType type,
|
||||
OptionalString channel,
|
||||
long long num) {
|
||||
REDIS_ASSERT(bool(channel), "failed to test psubscribe");
|
||||
|
||||
if (type == Subscriber::MsgType::PSUBSCRIBE) {
|
||||
auto iter = channels.find(*channel);
|
||||
REDIS_ASSERT(iter == channels.end(), "failed to test psubscribe");
|
||||
channels.insert(*channel);
|
||||
REDIS_ASSERT(static_cast<std::size_t>(num) == channels.size(),
|
||||
"failed to test psubscribe");
|
||||
} else if (type == Subscriber::MsgType::PUNSUBSCRIBE) {
|
||||
auto iter = channels.find(*channel);
|
||||
REDIS_ASSERT(iter != channels.end(), "failed to test psubscribe");
|
||||
channels.erase(*channel);
|
||||
REDIS_ASSERT(static_cast<std::size_t>(num) == channels.size(),
|
||||
"failed to test psubscribe");
|
||||
} else {
|
||||
REDIS_ASSERT(false, "Unknown message type");
|
||||
}
|
||||
});
|
||||
|
||||
auto pattern2 = test_key("pattern2*");
|
||||
auto pattern3 = test_key("pattern3*");
|
||||
auto pattern4 = test_key("pattern4*");
|
||||
std::unordered_set<std::string> patterns = {pattern2, pattern3, pattern4};
|
||||
|
||||
std::unordered_map<std::string, std::string> messages = {
|
||||
{channel2, "msg2"},
|
||||
{channel3, "msg3"},
|
||||
{channel4, "msg4"},
|
||||
};
|
||||
sub.on_pmessage([&patterns, &messages](std::string pattern,
|
||||
std::string channel,
|
||||
std::string msg) {
|
||||
REDIS_ASSERT(patterns.find(pattern) != patterns.end(),
|
||||
"failed to test psubscribe");
|
||||
REDIS_ASSERT(messages[channel] == msg, "failed to test psubscribe");
|
||||
});
|
||||
|
||||
sub.psubscribe({pattern2, pattern3, pattern4});
|
||||
|
||||
for (std::size_t idx = 0; idx != channels.size(); ++idx) {
|
||||
sub.consume();
|
||||
}
|
||||
|
||||
for (const auto &ele : messages) {
|
||||
_redis.publish(ele.first, ele.second);
|
||||
sub.consume();
|
||||
}
|
||||
|
||||
auto tmp = {pattern2, pattern3, pattern4};
|
||||
sub.punsubscribe(tmp);
|
||||
|
||||
for (std::size_t idx = 0; idx != tmp.size(); ++idx) {
|
||||
sub.consume();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename RedisInstance>
|
||||
void PubSubTest<RedisInstance>::_test_unsubscribe() {
|
||||
auto sub = _redis.subscriber();
|
||||
|
||||
sub.on_meta([](Subscriber::MsgType type,
|
||||
OptionalString channel,
|
||||
long long num) {
|
||||
REDIS_ASSERT(type == Subscriber::MsgType::UNSUBSCRIBE,
|
||||
"failed to test unsub");
|
||||
|
||||
REDIS_ASSERT(!channel, "failed to test unsub");
|
||||
|
||||
REDIS_ASSERT(num == 0, "failed to test unsub");
|
||||
});
|
||||
|
||||
sub.unsubscribe();
|
||||
sub.consume();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // end SEWENEW_REDISPLUSPLUS_TEST_SUBPUB_TEST_HPP
|
76
controller/thirdparty/redis-plus-plus-1.1.1/test/src/sw/redis++/sanity_test.h
vendored
Normal file
76
controller/thirdparty/redis-plus-plus-1.1.1/test/src/sw/redis++/sanity_test.h
vendored
Normal file
|
@ -0,0 +1,76 @@
|
|||
/**************************************************************************
|
||||
Copyright (c) 2017 sewenew
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*************************************************************************/
|
||||
|
||||
#ifndef SEWENEW_REDISPLUSPLUS_TEST_SANITY_TEST_H
|
||||
#define SEWENEW_REDISPLUSPLUS_TEST_SANITY_TEST_H
|
||||
|
||||
#include <sw/redis++/redis++.h>
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
namespace test {
|
||||
|
||||
template <typename RedisInstance>
|
||||
class SanityTest {
|
||||
public:
|
||||
SanityTest(const ConnectionOptions &opts, RedisInstance &instance)
|
||||
: _opts(opts), _redis(instance) {}
|
||||
|
||||
void run();
|
||||
|
||||
private:
|
||||
void _test_uri_ctor();
|
||||
|
||||
void _ping(Redis &instance);
|
||||
|
||||
void _test_move_ctor();
|
||||
|
||||
void _test_cmdargs();
|
||||
|
||||
void _test_generic_command();
|
||||
|
||||
void _test_hash_tag();
|
||||
|
||||
void _test_hash_tag(std::initializer_list<std::string> keys);
|
||||
|
||||
std::string _test_key(const std::string &key);
|
||||
|
||||
void _test_ping(Redis &instance);
|
||||
|
||||
void _test_pipeline(const StringView &key, Pipeline &pipeline);
|
||||
|
||||
void _test_transaction(const StringView &key, Transaction &transaction);
|
||||
|
||||
Pipeline _pipeline(const StringView &key);
|
||||
|
||||
Transaction _transaction(const StringView &key);
|
||||
|
||||
ConnectionOptions _opts;
|
||||
|
||||
RedisInstance &_redis;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#include "sanity_test.hpp"
|
||||
|
||||
#endif // end SEWENEW_REDISPLUSPLUS_TEST_SANITY_TEST_H
|
299
controller/thirdparty/redis-plus-plus-1.1.1/test/src/sw/redis++/sanity_test.hpp
vendored
Normal file
299
controller/thirdparty/redis-plus-plus-1.1.1/test/src/sw/redis++/sanity_test.hpp
vendored
Normal file
|
@ -0,0 +1,299 @@
|
|||
/**************************************************************************
|
||||
Copyright (c) 2017 sewenew
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*************************************************************************/
|
||||
|
||||
#ifndef SEWENEW_REDISPLUSPLUS_TEST_SANITY_TEST_HPP
|
||||
#define SEWENEW_REDISPLUSPLUS_TEST_SANITY_TEST_HPP
|
||||
|
||||
#include "utils.h"
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
namespace test {
|
||||
|
||||
template <typename RedisInstance>
|
||||
void SanityTest<RedisInstance>::run() {
|
||||
_test_uri_ctor();
|
||||
|
||||
_test_move_ctor();
|
||||
|
||||
cluster_specializing_test(*this, &SanityTest<RedisInstance>::_test_ping, _redis);
|
||||
|
||||
auto pipe_key = test_key("pipeline");
|
||||
auto tx_key = test_key("transaction");
|
||||
|
||||
KeyDeleter<RedisInstance> deleter(_redis, {pipe_key, tx_key});
|
||||
|
||||
auto pipeline = _pipeline(pipe_key);
|
||||
_test_pipeline(pipe_key, pipeline);
|
||||
|
||||
auto transaction = _transaction(tx_key);
|
||||
_test_transaction(tx_key, transaction);
|
||||
|
||||
_test_cmdargs();
|
||||
|
||||
_test_generic_command();
|
||||
}
|
||||
|
||||
template <typename RedisInstance>
|
||||
void SanityTest<RedisInstance>::_test_uri_ctor() {
|
||||
std::string uri;
|
||||
switch (_opts.type) {
|
||||
case sw::redis::ConnectionType::TCP:
|
||||
uri = "tcp://" + _opts.host + ":" + std::to_string(_opts.port);
|
||||
break;
|
||||
|
||||
case sw::redis::ConnectionType::UNIX:
|
||||
REDIS_ASSERT(false, "NO test for UNIX Domain Socket");
|
||||
break;
|
||||
|
||||
default:
|
||||
REDIS_ASSERT(false, "Unknown connection type");
|
||||
}
|
||||
|
||||
auto instance = RedisInstance(uri);
|
||||
|
||||
cluster_specializing_test(*this, &SanityTest<RedisInstance>::_ping, instance);
|
||||
}
|
||||
|
||||
template <typename RedisInstance>
|
||||
void SanityTest<RedisInstance>::_ping(Redis &instance) {
|
||||
try {
|
||||
auto pong = instance.ping();
|
||||
REDIS_ASSERT(pong == "PONG", "Failed to test constructing Redis with uri");
|
||||
} catch (const sw::redis::ReplyError &e) {
|
||||
REDIS_ASSERT(e.what() == std::string("NOAUTH Authentication required."),
|
||||
"Failed to test constructing Redis with uri");
|
||||
}
|
||||
}
|
||||
|
||||
template <typename RedisInstance>
|
||||
void SanityTest<RedisInstance>::_test_move_ctor() {
|
||||
auto test_move_ctor = std::move(_redis);
|
||||
|
||||
_redis = std::move(test_move_ctor);
|
||||
}
|
||||
|
||||
template <typename RedisInstance>
|
||||
void SanityTest<RedisInstance>::_test_cmdargs() {
|
||||
auto lpush_num = [](Connection &connection, const StringView &key, long long num) {
|
||||
connection.send("LPUSH %b %lld",
|
||||
key.data(), key.size(),
|
||||
num);
|
||||
};
|
||||
|
||||
auto lpush_nums = [](Connection &connection,
|
||||
const StringView &key,
|
||||
const std::vector<long long> &nums) {
|
||||
CmdArgs args;
|
||||
args.append("LPUSH").append(key);
|
||||
for (auto num : nums) {
|
||||
args.append(std::to_string(num));
|
||||
}
|
||||
|
||||
connection.send(args);
|
||||
};
|
||||
|
||||
auto key = test_key("lpush_num");
|
||||
|
||||
KeyDeleter<RedisInstance> deleter(_redis, key);
|
||||
|
||||
auto reply = _redis.command(lpush_num, key, 1);
|
||||
REDIS_ASSERT(reply::parse<long long>(*reply) == 1, "failed to test cmdargs");
|
||||
|
||||
std::vector<long long> nums = {2, 3, 4, 5};
|
||||
reply = _redis.command(lpush_nums, key, nums);
|
||||
REDIS_ASSERT(reply::parse<long long>(*reply) == 5, "failed to test cmdargs");
|
||||
|
||||
std::vector<std::string> res;
|
||||
_redis.lrange(key, 0, -1, std::back_inserter(res));
|
||||
REDIS_ASSERT((res == std::vector<std::string>{"5", "4", "3", "2", "1"}),
|
||||
"failed to test cmdargs");
|
||||
}
|
||||
|
||||
template <typename RedisInstance>
|
||||
void SanityTest<RedisInstance>::_test_generic_command() {
|
||||
auto key = test_key("key");
|
||||
auto not_exist_key = test_key("not_exist_key");
|
||||
auto k1 = test_key("k1");
|
||||
auto k2 = test_key("k2");
|
||||
|
||||
KeyDeleter<RedisInstance> deleter(_redis, {key, not_exist_key, k1, k2});
|
||||
|
||||
std::string cmd("set");
|
||||
_redis.command(cmd, key, 123);
|
||||
auto reply = _redis.command("get", key);
|
||||
auto val = reply::parse<OptionalString>(*reply);
|
||||
REDIS_ASSERT(val && *val == "123", "failed to test generic command");
|
||||
|
||||
val = _redis.template command<OptionalString>("get", key);
|
||||
REDIS_ASSERT(val && *val == "123", "failed to test generic command");
|
||||
|
||||
std::vector<OptionalString> res;
|
||||
_redis.command("mget", key, not_exist_key, std::back_inserter(res));
|
||||
REDIS_ASSERT(res.size() == 2 && res[0] && *res[0] == "123" && !res[1],
|
||||
"failed to test generic command");
|
||||
|
||||
reply = _redis.command("incr", key);
|
||||
REDIS_ASSERT(reply::parse<long long>(*reply) == 124, "failed to test generic command");
|
||||
|
||||
_redis.command("mset", k1.c_str(), "v", k2.c_str(), "v");
|
||||
reply = _redis.command("mget", k1, k2);
|
||||
res.clear();
|
||||
reply::to_array(*reply, std::back_inserter(res));
|
||||
REDIS_ASSERT(res.size() == 2 && res[0] && *(res[0]) == "v" && res[1] && *(res[1]) == "v",
|
||||
"failed to test generic command");
|
||||
|
||||
res = _redis.template command<std::vector<OptionalString>>("mget", k1, k2);
|
||||
REDIS_ASSERT(res.size() == 2 && res[0] && *(res[0]) == "v" && res[1] && *(res[1]) == "v",
|
||||
"failed to test generic command");
|
||||
|
||||
res.clear();
|
||||
_redis.command("mget", k1, k2, std::back_inserter(res));
|
||||
REDIS_ASSERT(res.size() == 2 && res[0] && *(res[0]) == "v" && res[1] && *(res[1]) == "v",
|
||||
"failed to test generic command");
|
||||
|
||||
auto set_cmd_str = {"set", key.c_str(), "new_value"};
|
||||
_redis.command(set_cmd_str.begin(), set_cmd_str.end());
|
||||
|
||||
auto get_cmd_str = {"get", key.c_str()};
|
||||
reply = _redis.command(get_cmd_str.begin(), get_cmd_str.end());
|
||||
val = reply::parse<OptionalString>(*reply);
|
||||
REDIS_ASSERT(val && *val == "new_value", "failed to test generic command");
|
||||
|
||||
val = _redis.template command<OptionalString>(get_cmd_str.begin(), get_cmd_str.end());
|
||||
REDIS_ASSERT(val && *val == "new_value", "failed to test generic command");
|
||||
|
||||
auto mget_cmd_str = {"mget", key.c_str(), not_exist_key.c_str()};
|
||||
res.clear();
|
||||
_redis.command(mget_cmd_str.begin(), mget_cmd_str.end(), std::back_inserter(res));
|
||||
REDIS_ASSERT(res.size() == 2 && res[0] && *res[0] == "new_value" && !res[1],
|
||||
"failed to test generic command");
|
||||
}
|
||||
|
||||
template <typename RedisInstance>
|
||||
void SanityTest<RedisInstance>::_test_hash_tag() {
|
||||
_test_hash_tag({_test_key("{tag}postfix1"),
|
||||
_test_key("{tag}postfix2"),
|
||||
_test_key("{tag}postfix3")});
|
||||
|
||||
_test_hash_tag({_test_key("prefix1{tag}postfix1"),
|
||||
_test_key("prefix2{tag}postfix2"),
|
||||
_test_key("prefix3{tag}postfix3")});
|
||||
|
||||
_test_hash_tag({_test_key("prefix1{tag}"),
|
||||
_test_key("prefix2{tag}"),
|
||||
_test_key("prefix3{tag}")});
|
||||
|
||||
_test_hash_tag({_test_key("prefix{}postfix"),
|
||||
_test_key("prefix{}postfix"),
|
||||
_test_key("prefix{}postfix")});
|
||||
|
||||
_test_hash_tag({_test_key("prefix1{tag}post}fix1"),
|
||||
_test_key("prefix2{tag}pos}tfix2"),
|
||||
_test_key("prefix3{tag}postfi}x3")});
|
||||
|
||||
_test_hash_tag({_test_key("prefix1{t{ag}postfix1"),
|
||||
_test_key("prefix2{t{ag}postfix2"),
|
||||
_test_key("prefix3{t{ag}postfix3")});
|
||||
|
||||
_test_hash_tag({_test_key("prefix1{t{ag}postfi}x1"),
|
||||
_test_key("prefix2{t{ag}post}fix2"),
|
||||
_test_key("prefix3{t{ag}po}stfix3")});
|
||||
}
|
||||
|
||||
template <typename RedisInstance>
|
||||
void SanityTest<RedisInstance>::_test_hash_tag(std::initializer_list<std::string> keys) {
|
||||
KeyDeleter<RedisInstance> deleter(_redis, keys.begin(), keys.end());
|
||||
|
||||
std::string value = "value";
|
||||
std::vector<std::pair<std::string, std::string>> kvs;
|
||||
for (const auto &key : keys) {
|
||||
kvs.emplace_back(key, value);
|
||||
}
|
||||
|
||||
_redis.mset(kvs.begin(), kvs.end());
|
||||
|
||||
std::vector<OptionalString> res;
|
||||
res.reserve(keys.size());
|
||||
_redis.mget(keys.begin(), keys.end(), std::back_inserter(res));
|
||||
|
||||
REDIS_ASSERT(res.size() == keys.size(), "failed to test hash tag");
|
||||
|
||||
for (const auto &ele : res) {
|
||||
REDIS_ASSERT(ele && *ele == value, "failed to test hash tag");
|
||||
}
|
||||
}
|
||||
|
||||
template <typename RedisInstance>
|
||||
std::string SanityTest<RedisInstance>::_test_key(const std::string &key) {
|
||||
REDIS_ASSERT(key.size() > 1, "failed to generate key");
|
||||
|
||||
// Ensure that key prefix has NO hash tag. Also see the implementation of test_key.
|
||||
return key.substr(1);
|
||||
}
|
||||
|
||||
template <typename RedisInstance>
|
||||
void SanityTest<RedisInstance>::_test_ping(Redis &instance) {
|
||||
auto reply = instance.command("ping");
|
||||
REDIS_ASSERT(reply && reply::parse<std::string>(*reply) == "PONG",
|
||||
"failed to test generic command");
|
||||
|
||||
auto pong = instance.command<std::string>("ping");
|
||||
REDIS_ASSERT(pong == "PONG", "failed to test generic command");
|
||||
}
|
||||
|
||||
template <typename RedisInstance>
|
||||
void SanityTest<RedisInstance>::_test_pipeline(const StringView &key, Pipeline &pipeline) {
|
||||
auto pipe_replies = pipeline.command("set", key, "value").command("get", key).exec();
|
||||
auto val = pipe_replies.get<OptionalString>(1);
|
||||
REDIS_ASSERT(val && *val == "value", "failed to test generic command");
|
||||
}
|
||||
|
||||
template <typename RedisInstance>
|
||||
void SanityTest<RedisInstance>::_test_transaction(const StringView &key, Transaction &transaction) {
|
||||
auto tx_replies = transaction.command("set", key, 456).command("incr", key).exec();
|
||||
REDIS_ASSERT(tx_replies.get<long long>(1) == 457, "failed to test generic command");
|
||||
}
|
||||
|
||||
template <typename RedisInstance>
|
||||
Pipeline SanityTest<RedisInstance>::_pipeline(const StringView &) {
|
||||
return _redis.pipeline();
|
||||
}
|
||||
|
||||
template <>
|
||||
inline Pipeline SanityTest<RedisCluster>::_pipeline(const StringView &key) {
|
||||
return _redis.pipeline(key);
|
||||
}
|
||||
|
||||
template <typename RedisInstance>
|
||||
Transaction SanityTest<RedisInstance>::_transaction(const StringView &) {
|
||||
return _redis.transaction();
|
||||
}
|
||||
|
||||
template <>
|
||||
inline Transaction SanityTest<RedisCluster>::_transaction(const StringView &key) {
|
||||
return _redis.transaction(key);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // end SEWENEW_REDISPLUSPLUS_TEST_SANITY_TEST_HPP
|
49
controller/thirdparty/redis-plus-plus-1.1.1/test/src/sw/redis++/script_cmds_test.h
vendored
Normal file
49
controller/thirdparty/redis-plus-plus-1.1.1/test/src/sw/redis++/script_cmds_test.h
vendored
Normal file
|
@ -0,0 +1,49 @@
|
|||
/**************************************************************************
|
||||
Copyright (c) 2017 sewenew
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*************************************************************************/
|
||||
|
||||
#ifndef SEWENEW_REDISPLUSPLUS_TEST_SCRIPT_CMDS_TEST_H
|
||||
#define SEWENEW_REDISPLUSPLUS_TEST_SCRIPT_CMDS_TEST_H
|
||||
|
||||
#include <sw/redis++/redis++.h>
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
namespace test {
|
||||
|
||||
template <typename RedisInstance>
|
||||
class ScriptCmdTest {
|
||||
public:
|
||||
explicit ScriptCmdTest(RedisInstance &instance) : _redis(instance) {}
|
||||
|
||||
void run();
|
||||
|
||||
private:
|
||||
void _run(Redis &instance);
|
||||
|
||||
RedisInstance &_redis;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#include "script_cmds_test.hpp"
|
||||
|
||||
#endif // end SEWENEW_REDISPLUSPLUS_TEST_SCRIPT_CMDS_TEST_H
|
97
controller/thirdparty/redis-plus-plus-1.1.1/test/src/sw/redis++/script_cmds_test.hpp
vendored
Normal file
97
controller/thirdparty/redis-plus-plus-1.1.1/test/src/sw/redis++/script_cmds_test.hpp
vendored
Normal file
|
@ -0,0 +1,97 @@
|
|||
/**************************************************************************
|
||||
Copyright (c) 2017 sewenew
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*************************************************************************/
|
||||
|
||||
#ifndef SEWENEW_REDISPLUSPLUS_TEST_SCRIPT_CMDS_TEST_HPP
|
||||
#define SEWENEW_REDISPLUSPLUS_TEST_SCRIPT_CMDS_TEST_HPP
|
||||
|
||||
#include <list>
|
||||
#include <vector>
|
||||
#include "utils.h"
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
namespace test {
|
||||
|
||||
template <typename RedisInstance>
|
||||
void ScriptCmdTest<RedisInstance>::run() {
|
||||
cluster_specializing_test(*this,
|
||||
&ScriptCmdTest<RedisInstance>::_run,
|
||||
_redis);
|
||||
}
|
||||
|
||||
template <typename RedisInstance>
|
||||
void ScriptCmdTest<RedisInstance>::_run(Redis &instance) {
|
||||
auto key1 = test_key("k1");
|
||||
auto key2 = test_key("k2");
|
||||
|
||||
KeyDeleter<Redis> deleter(instance, {key1, key2});
|
||||
|
||||
std::string script = "redis.call('set', KEYS[1], 1);"
|
||||
"redis.call('set', KEYS[2], 2);"
|
||||
"local first = redis.call('get', KEYS[1]);"
|
||||
"local second = redis.call('get', KEYS[2]);"
|
||||
"return first + second";
|
||||
|
||||
auto num = instance.eval<long long>(script, {key1, key2}, {});
|
||||
REDIS_ASSERT(num == 3, "failed to test scripting for cluster");
|
||||
|
||||
script = "return 1";
|
||||
num = instance.eval<long long>(script, {}, {});
|
||||
REDIS_ASSERT(num == 1, "failed to test eval");
|
||||
|
||||
auto script_with_args = "return {ARGV[1] + 1, ARGV[2] + 2, ARGV[3] + 3}";
|
||||
std::vector<long long> res;
|
||||
instance.eval(script_with_args,
|
||||
{"k"},
|
||||
{"1", "2", "3"},
|
||||
std::back_inserter(res));
|
||||
REDIS_ASSERT(res == std::vector<long long>({2, 4, 6}),
|
||||
"failed to test eval with array reply");
|
||||
|
||||
auto sha1 = instance.script_load(script);
|
||||
num = instance.evalsha<long long>(sha1, {}, {});
|
||||
REDIS_ASSERT(num == 1, "failed to test evalsha");
|
||||
|
||||
auto sha2 = instance.script_load(script_with_args);
|
||||
res.clear();
|
||||
instance.evalsha(sha2,
|
||||
{"k"},
|
||||
{"1", "2", "3"},
|
||||
std::back_inserter(res));
|
||||
REDIS_ASSERT(res == std::vector<long long>({2, 4, 6}),
|
||||
"failed to test evalsha with array reply");
|
||||
|
||||
std::list<bool> exist_res;
|
||||
instance.script_exists({sha1, sha2, std::string("not exist")}, std::back_inserter(exist_res));
|
||||
REDIS_ASSERT(exist_res == std::list<bool>({true, true, false}),
|
||||
"failed to test script exists");
|
||||
|
||||
instance.script_flush();
|
||||
exist_res.clear();
|
||||
instance.script_exists({sha1, sha2, std::string("not exist")}, std::back_inserter(exist_res));
|
||||
REDIS_ASSERT(exist_res == std::list<bool>({false, false, false}),
|
||||
"failed to test script flush");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // end SEWENEW_REDISPLUSPLUS_TEST_SCRIPT_CMDS_TEST_HPP
|
53
controller/thirdparty/redis-plus-plus-1.1.1/test/src/sw/redis++/set_cmds_test.h
vendored
Normal file
53
controller/thirdparty/redis-plus-plus-1.1.1/test/src/sw/redis++/set_cmds_test.h
vendored
Normal file
|
@ -0,0 +1,53 @@
|
|||
/**************************************************************************
|
||||
Copyright (c) 2017 sewenew
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*************************************************************************/
|
||||
|
||||
#ifndef SEWENEW_REDISPLUSPLUS_TEST_SET_CMDS_TEST_H
|
||||
#define SEWENEW_REDISPLUSPLUS_TEST_SET_CMDS_TEST_H
|
||||
|
||||
#include <sw/redis++/redis++.h>
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
namespace test {
|
||||
|
||||
template <typename RedisInstance>
|
||||
class SetCmdTest {
|
||||
public:
|
||||
explicit SetCmdTest(RedisInstance &instance) : _redis(instance) {}
|
||||
|
||||
void run();
|
||||
|
||||
private:
|
||||
void _test_set();
|
||||
|
||||
void _test_multi_set();
|
||||
|
||||
void _test_sscan();
|
||||
|
||||
RedisInstance &_redis;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#include "set_cmds_test.hpp"
|
||||
|
||||
#endif // end SEWENEW_REDISPLUSPLUS_TEST_SET_CMDS_TEST_H
|
184
controller/thirdparty/redis-plus-plus-1.1.1/test/src/sw/redis++/set_cmds_test.hpp
vendored
Normal file
184
controller/thirdparty/redis-plus-plus-1.1.1/test/src/sw/redis++/set_cmds_test.hpp
vendored
Normal file
|
@ -0,0 +1,184 @@
|
|||
/**************************************************************************
|
||||
Copyright (c) 2017 sewenew
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*************************************************************************/
|
||||
|
||||
#ifndef SEWENEW_REDISPLUSPLUS_TEST_SET_CMDS_TEST_HPP
|
||||
#define SEWENEW_REDISPLUSPLUS_TEST_SET_CMDS_TEST_HPP
|
||||
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
#include "utils.h"
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
namespace test {
|
||||
|
||||
template <typename RedisInstance>
|
||||
void SetCmdTest<RedisInstance>::run() {
|
||||
_test_set();
|
||||
|
||||
_test_multi_set();
|
||||
|
||||
_test_sscan();
|
||||
}
|
||||
|
||||
template <typename RedisInstance>
|
||||
void SetCmdTest<RedisInstance>::_test_set() {
|
||||
auto key = test_key("set");
|
||||
|
||||
KeyDeleter<RedisInstance> deleter(_redis, key);
|
||||
|
||||
std::string m1("m1");
|
||||
std::string m2("m2");
|
||||
std::string m3("m3");
|
||||
|
||||
REDIS_ASSERT(_redis.sadd(key, m1) == 1, "failed to test sadd");
|
||||
|
||||
auto members = {m1, m2, m3};
|
||||
REDIS_ASSERT(_redis.sadd(key, members) == 2, "failed to test sadd with multiple members");
|
||||
|
||||
REDIS_ASSERT(_redis.scard(key) == 3, "failed to test scard");
|
||||
|
||||
REDIS_ASSERT(_redis.sismember(key, m1), "failed to test sismember");
|
||||
|
||||
std::unordered_set<std::string> res;
|
||||
_redis.smembers(key, std::inserter(res, res.end()));
|
||||
REDIS_ASSERT(res.find(m1) != res.end()
|
||||
&& res.find(m2) != res.end()
|
||||
&& res.find(m3) != res.end(),
|
||||
"failed to test smembers");
|
||||
|
||||
auto ele = _redis.srandmember(key);
|
||||
REDIS_ASSERT(bool(ele) && res.find(*ele) != res.end(), "failed to test srandmember");
|
||||
|
||||
std::vector<std::string> rand_members;
|
||||
_redis.srandmember(key, 2, std::back_inserter(rand_members));
|
||||
REDIS_ASSERT(rand_members.size() == 2, "failed to test srandmember");
|
||||
|
||||
ele = _redis.spop(key);
|
||||
REDIS_ASSERT(bool(ele) && res.find(*ele) != res.end(), "failed to test spop");
|
||||
|
||||
rand_members.clear();
|
||||
_redis.spop(key, 3, std::back_inserter(rand_members));
|
||||
REDIS_ASSERT(rand_members.size() == 2, "failed to test srandmember");
|
||||
|
||||
rand_members.clear();
|
||||
_redis.srandmember(key, 2, std::back_inserter(rand_members));
|
||||
REDIS_ASSERT(rand_members.empty(), "failed to test srandmember with empty set");
|
||||
|
||||
_redis.spop(key, 2, std::back_inserter(rand_members));
|
||||
REDIS_ASSERT(rand_members.empty(), "failed to test spop with empty set");
|
||||
|
||||
_redis.sadd(key, members);
|
||||
REDIS_ASSERT(_redis.srem(key, m1) == 1, "failed to test srem");
|
||||
REDIS_ASSERT(_redis.srem(key, members) == 2, "failed to test srem with mulitple members");
|
||||
REDIS_ASSERT(_redis.srem(key, members) == 0, "failed to test srem with mulitple members");
|
||||
}
|
||||
|
||||
template <typename RedisInstance>
|
||||
void SetCmdTest<RedisInstance>::_test_multi_set() {
|
||||
auto k1 = test_key("s1");
|
||||
auto k2 = test_key("s2");
|
||||
auto k3 = test_key("s3");
|
||||
auto k4 = test_key("s4");
|
||||
auto k5 = test_key("s5");
|
||||
auto k6 = test_key("s6");
|
||||
|
||||
KeyDeleter<RedisInstance> keys(_redis, {k1, k2, k3, k4, k5, k6});
|
||||
|
||||
_redis.sadd(k1, {"a", "c"});
|
||||
_redis.sadd(k2, {"a", "b"});
|
||||
std::vector<std::string> sdiff;
|
||||
_redis.sdiff({k1, k1}, std::back_inserter(sdiff));
|
||||
REDIS_ASSERT(sdiff.empty(), "failed to test sdiff");
|
||||
|
||||
_redis.sdiff({k1, k2}, std::back_inserter(sdiff));
|
||||
REDIS_ASSERT(sdiff == std::vector<std::string>({"c"}), "failed to test sdiff");
|
||||
|
||||
_redis.sdiffstore(k3, {k1, k2});
|
||||
sdiff.clear();
|
||||
_redis.smembers(k3, std::back_inserter(sdiff));
|
||||
REDIS_ASSERT(sdiff == std::vector<std::string>({"c"}), "failed to test sdiffstore");
|
||||
|
||||
REDIS_ASSERT(_redis.sdiffstore(k3, k1) == 2, "failed to test sdiffstore");
|
||||
|
||||
REDIS_ASSERT(_redis.sinterstore(k3, k1) == 2, "failed to test sinterstore");
|
||||
|
||||
REDIS_ASSERT(_redis.sunionstore(k3, k1) == 2, "failed to test sunionstore");
|
||||
|
||||
std::vector<std::string> sinter;
|
||||
_redis.sinter({k1, k2}, std::back_inserter(sinter));
|
||||
REDIS_ASSERT(sinter == std::vector<std::string>({"a"}), "failed to test sinter");
|
||||
|
||||
_redis.sinterstore(k4, {k1, k2});
|
||||
sinter.clear();
|
||||
_redis.smembers(k4, std::back_inserter(sinter));
|
||||
REDIS_ASSERT(sinter == std::vector<std::string>({"a"}), "failed to test sinterstore");
|
||||
|
||||
std::unordered_set<std::string> sunion;
|
||||
_redis.sunion({k1, k2}, std::inserter(sunion, sunion.end()));
|
||||
REDIS_ASSERT(sunion == std::unordered_set<std::string>({"a", "b", "c"}),
|
||||
"failed to test sunion");
|
||||
|
||||
_redis.sunionstore(k5, {k1, k2});
|
||||
sunion.clear();
|
||||
_redis.smembers(k5, std::inserter(sunion, sunion.end()));
|
||||
REDIS_ASSERT(sunion == std::unordered_set<std::string>({"a", "b", "c"}),
|
||||
"failed to test sunionstore");
|
||||
|
||||
REDIS_ASSERT(_redis.smove(k5, k6, "a"), "failed to test smove");
|
||||
}
|
||||
|
||||
template <typename RedisInstance>
|
||||
void SetCmdTest<RedisInstance>::_test_sscan() {
|
||||
auto key = test_key("sscan");
|
||||
|
||||
KeyDeleter<RedisInstance> deleter(_redis, key);
|
||||
|
||||
std::unordered_set<std::string> members = {"m1", "m2", "m3"};
|
||||
_redis.sadd(key, members.begin(), members.end());
|
||||
|
||||
std::unordered_set<std::string> res;
|
||||
long long cursor = 0;
|
||||
while (true) {
|
||||
cursor = _redis.sscan(key, cursor, "m*", 1, std::inserter(res, res.end()));
|
||||
if (cursor == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
REDIS_ASSERT(res == members, "failed to test sscan");
|
||||
|
||||
res.clear();
|
||||
cursor = 0;
|
||||
while (true) {
|
||||
cursor = _redis.sscan(key, cursor, std::inserter(res, res.end()));
|
||||
if (cursor == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
REDIS_ASSERT(res == members, "failed to test sscan");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // end SEWENEW_REDISPLUSPLUS_TEST_SET_CMDS_TEST_HPP
|
54
controller/thirdparty/redis-plus-plus-1.1.1/test/src/sw/redis++/stream_cmds_test.h
vendored
Normal file
54
controller/thirdparty/redis-plus-plus-1.1.1/test/src/sw/redis++/stream_cmds_test.h
vendored
Normal file
|
@ -0,0 +1,54 @@
|
|||
/**************************************************************************
|
||||
Copyright (c) 2017 sewenew
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*************************************************************************/
|
||||
|
||||
#ifndef SEWENEW_REDISPLUSPLUS_TEST_STREAM_CMDS_TEST_H
|
||||
#define SEWENEW_REDISPLUSPLUS_TEST_STREAM_CMDS_TEST_H
|
||||
|
||||
#include <sw/redis++/redis++.h>
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
namespace test {
|
||||
|
||||
template <typename RedisInstance>
|
||||
class StreamCmdsTest {
|
||||
public:
|
||||
explicit StreamCmdsTest(RedisInstance &instance) : _redis(instance) {}
|
||||
|
||||
void run();
|
||||
|
||||
private:
|
||||
using Item = std::pair<std::string, std::unordered_map<std::string, std::string>>;
|
||||
using Result = std::unordered_map<std::string, std::vector<Item>>;
|
||||
|
||||
void _test_stream_cmds();
|
||||
|
||||
void _test_group_cmds();
|
||||
|
||||
RedisInstance &_redis;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#include "stream_cmds_test.hpp"
|
||||
|
||||
#endif // end SEWENEW_REDISPLUSPLUS_TEST_STREAM_CMDS_TEST_H
|
225
controller/thirdparty/redis-plus-plus-1.1.1/test/src/sw/redis++/stream_cmds_test.hpp
vendored
Normal file
225
controller/thirdparty/redis-plus-plus-1.1.1/test/src/sw/redis++/stream_cmds_test.hpp
vendored
Normal file
|
@ -0,0 +1,225 @@
|
|||
/**************************************************************************
|
||||
Copyright (c) 2017 sewenew
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*************************************************************************/
|
||||
|
||||
#ifndef SEWENEW_REDISPLUSPLUS_TEST_STREAM_CMDS_TEST_HPP
|
||||
#define SEWENEW_REDISPLUSPLUS_TEST_STREAM_CMDS_TEST_HPP
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
#include <unordered_map>
|
||||
#include "utils.h"
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
namespace test {
|
||||
|
||||
template <typename RedisInstance>
|
||||
void StreamCmdsTest<RedisInstance>::run() {
|
||||
_test_stream_cmds();
|
||||
|
||||
_test_group_cmds();
|
||||
}
|
||||
|
||||
template <typename RedisInstance>
|
||||
void StreamCmdsTest<RedisInstance>::_test_stream_cmds() {
|
||||
auto key = test_key("stream");
|
||||
|
||||
KeyDeleter<RedisInstance> deleter(_redis, key);
|
||||
|
||||
std::vector<std::pair<std::string, std::string>> attrs = {
|
||||
{"f1", "v1"},
|
||||
{"f2", "v2"}
|
||||
};
|
||||
auto id = "1565427842-0";
|
||||
REDIS_ASSERT(_redis.xadd(key, id, attrs.begin(), attrs.end()) == id,
|
||||
"failed to test xadd");
|
||||
|
||||
std::vector<std::pair<std::string, std::string>> keys = {std::make_pair(key, "0-0")};
|
||||
Result result;
|
||||
_redis.xread(keys.begin(), keys.end(), 1, std::inserter(result, result.end()));
|
||||
|
||||
REDIS_ASSERT(result.size() == 1
|
||||
&& result.find(key) != result.end()
|
||||
&& result[key].size() == 1
|
||||
&& result[key][0].first == id
|
||||
&& result[key][0].second.size() == 2,
|
||||
"failed to test xread");
|
||||
|
||||
result.clear();
|
||||
_redis.xread(key, std::string("0-0"), 1, std::inserter(result, result.end()));
|
||||
|
||||
REDIS_ASSERT(result.size() == 1
|
||||
&& result.find(key) != result.end()
|
||||
&& result[key].size() == 1
|
||||
&& result[key][0].first == id
|
||||
&& result[key][0].second.size() == 2,
|
||||
"failed to test xread");
|
||||
|
||||
result.clear();
|
||||
keys = {std::make_pair(key, id)};
|
||||
_redis.xread(keys.begin(),
|
||||
keys.end(),
|
||||
std::chrono::seconds(1),
|
||||
2,
|
||||
std::inserter(result, result.end()));
|
||||
REDIS_ASSERT(result.size() == 0, "failed to test xread");
|
||||
|
||||
_redis.xread(key,
|
||||
id,
|
||||
std::chrono::seconds(1),
|
||||
2,
|
||||
std::inserter(result, result.end()));
|
||||
REDIS_ASSERT(result.size() == 0, "failed to test xread");
|
||||
|
||||
id = "1565427842-1";
|
||||
REDIS_ASSERT(_redis.xadd(key, id, attrs.begin(), attrs.end()) == id,
|
||||
"failed to test xadd");
|
||||
|
||||
REDIS_ASSERT(_redis.xlen(key) == 2, "failed to test xlen");
|
||||
|
||||
REDIS_ASSERT(_redis.xtrim(key, 1, false) == 1, "failed to test xtrim");
|
||||
|
||||
std::vector<Item> items;
|
||||
_redis.xrange(key, "-", "+", std::back_inserter(items));
|
||||
REDIS_ASSERT(items.size() == 1 && items[0].first == id, "failed to test xrange");
|
||||
|
||||
items.clear();
|
||||
_redis.xrevrange(key, "+", "-", std::back_inserter(items));
|
||||
REDIS_ASSERT(items.size() == 1 && items[0].first == id, "failed to test xrevrange");
|
||||
|
||||
REDIS_ASSERT(_redis.xdel(key, {id, "111-111"}) == 1, "failed to test xdel");
|
||||
}
|
||||
|
||||
template <typename RedisInstance>
|
||||
void StreamCmdsTest<RedisInstance>::_test_group_cmds() {
|
||||
auto key = test_key("stream");
|
||||
|
||||
KeyDeleter<RedisInstance> deleter(_redis, key);
|
||||
|
||||
auto group = "group";
|
||||
auto consumer1 = "consumer1";
|
||||
|
||||
_redis.xgroup_create(key, group, "$", true);
|
||||
|
||||
std::vector<std::pair<std::string, std::string>> attrs = {
|
||||
{"f1", "v1"},
|
||||
{"f2", "v2"}
|
||||
};
|
||||
auto id = _redis.xadd(key, "*", attrs.begin(), attrs.end());
|
||||
auto keys = {std::make_pair(key, ">")};
|
||||
|
||||
Result result;
|
||||
_redis.xreadgroup(group,
|
||||
consumer1,
|
||||
keys.begin(),
|
||||
keys.end(),
|
||||
1,
|
||||
std::inserter(result, result.end()));
|
||||
REDIS_ASSERT(result.size() == 1
|
||||
&& result.find(key) != result.end()
|
||||
&& result[key].size() == 1
|
||||
&& result[key][0].first == id,
|
||||
"failed to test xreadgroup");
|
||||
|
||||
result.clear();
|
||||
_redis.xreadgroup(group,
|
||||
consumer1,
|
||||
key,
|
||||
std::string(">"),
|
||||
1,
|
||||
std::inserter(result, result.end()));
|
||||
REDIS_ASSERT(result.size() == 0, "failed to test xreadgroup");
|
||||
|
||||
result.clear();
|
||||
_redis.xreadgroup(group,
|
||||
"not-exist-consumer",
|
||||
keys.begin(),
|
||||
keys.end(),
|
||||
1,
|
||||
std::inserter(result, result.end()));
|
||||
REDIS_ASSERT(result.size() == 0, "failed to test xreadgroup");
|
||||
|
||||
result.clear();
|
||||
_redis.xreadgroup(group,
|
||||
consumer1,
|
||||
keys.begin(),
|
||||
keys.end(),
|
||||
std::chrono::seconds(1),
|
||||
1,
|
||||
std::inserter(result, result.end()));
|
||||
REDIS_ASSERT(result.size() == 0, "failed to test xreadgroup");
|
||||
|
||||
result.clear();
|
||||
_redis.xreadgroup(group,
|
||||
consumer1,
|
||||
key,
|
||||
">",
|
||||
std::chrono::seconds(1),
|
||||
1,
|
||||
std::inserter(result, result.end()));
|
||||
REDIS_ASSERT(result.size() == 0, "failed to test xreadgroup");
|
||||
|
||||
using PendingResult = std::vector<std::tuple<std::string, std::string, long long, long long>>;
|
||||
PendingResult pending_result;
|
||||
_redis.xpending(key, group, "-", "+", 1, consumer1, std::back_inserter(pending_result));
|
||||
|
||||
REDIS_ASSERT(pending_result.size() == 1
|
||||
&& std::get<0>(pending_result[0]) == id
|
||||
&& std::get<1>(pending_result[0]) == consumer1,
|
||||
"failed to test xpending");
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
|
||||
auto consumer2 = "consumer2";
|
||||
std::vector<Item> items;
|
||||
auto ids = {id};
|
||||
_redis.xclaim(key,
|
||||
group,
|
||||
consumer2,
|
||||
std::chrono::milliseconds(10),
|
||||
ids,
|
||||
std::back_inserter(items));
|
||||
REDIS_ASSERT(items.size() == 1 && items[0].first == id, "failed to test xclaim");
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
|
||||
items.clear();
|
||||
_redis.xclaim(key, group, consumer1, std::chrono::milliseconds(10), id, std::back_inserter(items));
|
||||
REDIS_ASSERT(items.size() == 1 && items[0].first == id, "failed to test xclaim: " + std::to_string(items.size()));
|
||||
|
||||
_redis.xack(key, group, id);
|
||||
|
||||
REDIS_ASSERT(_redis.xgroup_delconsumer(key, group, consumer1) == 0,
|
||||
"failed to test xgroup_delconsumer");
|
||||
|
||||
REDIS_ASSERT(_redis.xgroup_delconsumer(key, group, consumer2) == 0,
|
||||
"failed to test xgroup_delconsumer");
|
||||
|
||||
REDIS_ASSERT(_redis.xgroup_destroy(key, group) == 1,
|
||||
"failed to test xgroup_destroy");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // end SEWENEW_REDISPLUSPLUS_TEST_STREAM_CMDS_TEST_HPP
|
57
controller/thirdparty/redis-plus-plus-1.1.1/test/src/sw/redis++/string_cmds_test.h
vendored
Normal file
57
controller/thirdparty/redis-plus-plus-1.1.1/test/src/sw/redis++/string_cmds_test.h
vendored
Normal file
|
@ -0,0 +1,57 @@
|
|||
/**************************************************************************
|
||||
Copyright (c) 2017 sewenew
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*************************************************************************/
|
||||
|
||||
#ifndef SEWENEW_REDISPLUSPLUS_TEST_STRING_CMDS_TEST_H
|
||||
#define SEWENEW_REDISPLUSPLUS_TEST_STRING_CMDS_TEST_H
|
||||
|
||||
#include <sw/redis++/redis++.h>
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
namespace test {
|
||||
|
||||
template <typename RedisInstance>
|
||||
class StringCmdTest {
|
||||
public:
|
||||
explicit StringCmdTest(RedisInstance &instance) : _redis(instance) {}
|
||||
|
||||
void run();
|
||||
|
||||
private:
|
||||
void _test_str();
|
||||
|
||||
void _test_bit();
|
||||
|
||||
void _test_numeric();
|
||||
|
||||
void _test_getset();
|
||||
|
||||
void _test_mgetset();
|
||||
|
||||
RedisInstance &_redis;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#include "string_cmds_test.hpp"
|
||||
|
||||
#endif // end SEWENEW_REDISPLUSPLUS_TEST_STRING_CMDS_TEST_H
|
247
controller/thirdparty/redis-plus-plus-1.1.1/test/src/sw/redis++/string_cmds_test.hpp
vendored
Normal file
247
controller/thirdparty/redis-plus-plus-1.1.1/test/src/sw/redis++/string_cmds_test.hpp
vendored
Normal file
|
@ -0,0 +1,247 @@
|
|||
/**************************************************************************
|
||||
Copyright (c) 2017 sewenew
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*************************************************************************/
|
||||
|
||||
#ifndef SEWENEW_REDISPLUSPLUS_TEST_STRING_CMDS_TEST_HPP
|
||||
#define SEWENEW_REDISPLUSPLUS_TEST_STRING_CMDS_TEST_HPP
|
||||
|
||||
#include <vector>
|
||||
#include "utils.h"
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
namespace test {
|
||||
|
||||
template <typename RedisInstance>
|
||||
void StringCmdTest<RedisInstance>::run() {
|
||||
_test_str();
|
||||
|
||||
_test_bit();
|
||||
|
||||
_test_numeric();
|
||||
|
||||
_test_getset();
|
||||
|
||||
_test_mgetset();
|
||||
}
|
||||
|
||||
template <typename RedisInstance>
|
||||
void StringCmdTest<RedisInstance>::_test_str() {
|
||||
auto key = test_key("str");
|
||||
|
||||
KeyDeleter<RedisInstance> deleter(_redis, key);
|
||||
|
||||
std::string val("value");
|
||||
|
||||
long long val_size = val.size();
|
||||
|
||||
auto len1 = _redis.append(key, val);
|
||||
REDIS_ASSERT(len1 == val_size, "failed to append to non-existent key");
|
||||
|
||||
auto len2 = _redis.append(key, val);
|
||||
REDIS_ASSERT(len2 == len1 + val_size, "failed to append to non-empty string");
|
||||
|
||||
auto len3 = _redis.append(key, {});
|
||||
REDIS_ASSERT(len3 == len2, "failed to append empty string");
|
||||
|
||||
auto len4 = _redis.strlen(key);
|
||||
REDIS_ASSERT(len4 == len3, "failed to test strlen");
|
||||
|
||||
REDIS_ASSERT(_redis.del(key) == 1, "failed to remove key");
|
||||
|
||||
auto len5 = _redis.append(key, {});
|
||||
REDIS_ASSERT(len5 == 0, "failed to append empty string to non-existent key");
|
||||
|
||||
_redis.del(key);
|
||||
|
||||
REDIS_ASSERT(_redis.getrange(key, 0, 2) == "", "failed to test getrange on non-existent key");
|
||||
|
||||
_redis.set(key, val);
|
||||
|
||||
REDIS_ASSERT(_redis.getrange(key, 1, 2) == val.substr(1, 2), "failed to test getrange");
|
||||
|
||||
long long new_size = val.size() * 2;
|
||||
REDIS_ASSERT(_redis.setrange(key, val.size(), val) == new_size, "failed to test setrange");
|
||||
REDIS_ASSERT(_redis.getrange(key, 0, -1) == val + val, "failed to test setrange");
|
||||
}
|
||||
|
||||
template <typename RedisInstance>
|
||||
void StringCmdTest<RedisInstance>::_test_bit() {
|
||||
auto key = test_key("bit");
|
||||
|
||||
KeyDeleter<RedisInstance> deleter(_redis, key);
|
||||
|
||||
REDIS_ASSERT(_redis.bitcount(key) == 0, "failed to test bitcount on non-existent key");
|
||||
|
||||
REDIS_ASSERT(_redis.getbit(key, 5) == 0, "failed to test getbit");
|
||||
|
||||
REDIS_ASSERT(_redis.template command<long long>("SETBIT", key, 1, 1) == 0,
|
||||
"failed to test setbit");
|
||||
REDIS_ASSERT(_redis.template command<long long>("SETBIT", key, 3, 1) == 0,
|
||||
"failed to test setbit");
|
||||
REDIS_ASSERT(_redis.template command<long long>("SETBIT", key, 7, 1) == 0,
|
||||
"failed to test setbit");
|
||||
REDIS_ASSERT(_redis.template command<long long>("SETBIT", key, 10, 1) == 0,
|
||||
"failed to test setbit");
|
||||
REDIS_ASSERT(_redis.template command<long long>("SETBIT", key, 10, 0) == 1,
|
||||
"failed to test setbit");
|
||||
REDIS_ASSERT(_redis.template command<long long>("SETBIT", key, 11, 1) == 0,
|
||||
"failed to test setbit");
|
||||
REDIS_ASSERT(_redis.template command<long long>("SETBIT", key, 21, 1) == 0,
|
||||
"failed to test setbit");
|
||||
|
||||
// key -> 01010001, 00010000, 00000100
|
||||
|
||||
REDIS_ASSERT(_redis.getbit(key, 1) == 1, "failed to test getbit");
|
||||
REDIS_ASSERT(_redis.getbit(key, 2) == 0, "failed to test getbit");
|
||||
REDIS_ASSERT(_redis.getbit(key, 7) == 1, "failed to test getbit");
|
||||
REDIS_ASSERT(_redis.getbit(key, 10) == 0, "failed to test getbit");
|
||||
REDIS_ASSERT(_redis.getbit(key, 100) == 0, "failed to test getbit");
|
||||
|
||||
REDIS_ASSERT(_redis.bitcount(key) == 5, "failed to test bitcount");
|
||||
REDIS_ASSERT(_redis.bitcount(key, 0, 0) == 3, "failed to test bitcount");
|
||||
REDIS_ASSERT(_redis.bitcount(key, 0, 1) == 4, "failed to test bitcount");
|
||||
REDIS_ASSERT(_redis.bitcount(key, -2, -1) == 2, "failed to test bitcount");
|
||||
|
||||
REDIS_ASSERT(_redis.bitpos(key, 1) == 1, "failed to test bitpos");
|
||||
REDIS_ASSERT(_redis.bitpos(key, 0) == 0, "failed to test bitpos");
|
||||
REDIS_ASSERT(_redis.bitpos(key, 1, 1, 1) == 11, "failed to test bitpos");
|
||||
REDIS_ASSERT(_redis.bitpos(key, 0, 1, 1) == 8, "failed to test bitpos");
|
||||
REDIS_ASSERT(_redis.bitpos(key, 1, -1, -1) == 21, "failed to test bitpos");
|
||||
REDIS_ASSERT(_redis.bitpos(key, 0, -1, -1) == 16, "failed to test bitpos");
|
||||
|
||||
auto dest_key = test_key("bitop_dest");
|
||||
auto src_key1 = test_key("bitop_src1");
|
||||
auto src_key2 = test_key("bitop_src2");
|
||||
|
||||
KeyDeleter<RedisInstance> deleters(_redis, {dest_key, src_key1, src_key2});
|
||||
|
||||
// src_key1 -> 00010000
|
||||
_redis.template command<long long>("SETBIT", src_key1, 3, 1);
|
||||
|
||||
// src_key2 -> 00000000, 00001000
|
||||
_redis.template command<long long>("SETBIT", src_key2, 12, 1);
|
||||
|
||||
REDIS_ASSERT(_redis.bitop(BitOp::AND, dest_key, {src_key1, src_key2}) == 2,
|
||||
"failed to test bitop");
|
||||
|
||||
// dest_key -> 00000000, 00000000
|
||||
auto v = _redis.get(dest_key);
|
||||
REDIS_ASSERT(v && *v == std::string(2, 0), "failed to test bitop");
|
||||
|
||||
REDIS_ASSERT(_redis.bitop(BitOp::NOT, dest_key, src_key1) == 1,
|
||||
"failed to test bitop");
|
||||
|
||||
// dest_key -> 11101111
|
||||
v = _redis.get(dest_key);
|
||||
REDIS_ASSERT(v && *v == std::string(1, 0xEF), "failed to test bitop");
|
||||
}
|
||||
|
||||
template <typename RedisInstance>
|
||||
void StringCmdTest<RedisInstance>::_test_numeric() {
|
||||
auto key = test_key("numeric");
|
||||
|
||||
KeyDeleter<RedisInstance> deleter(_redis, key);
|
||||
|
||||
REDIS_ASSERT(_redis.incr(key) == 1, "failed to test incr");
|
||||
REDIS_ASSERT(_redis.decr(key) == 0, "failed to test decr");
|
||||
REDIS_ASSERT(_redis.incrby(key, 3) == 3, "failed to test incrby");
|
||||
REDIS_ASSERT(_redis.decrby(key, 3) == 0, "failed to test decrby");
|
||||
REDIS_ASSERT(_redis.incrby(key, -3) == -3, "failed to test incrby");
|
||||
REDIS_ASSERT(_redis.decrby(key, -3) == 0, "failed to test incrby");
|
||||
REDIS_ASSERT(_redis.incrbyfloat(key, 1.5) == 1.5, "failed to test incrbyfloat");
|
||||
}
|
||||
|
||||
template <typename RedisInstance>
|
||||
void StringCmdTest<RedisInstance>::_test_getset() {
|
||||
auto key = test_key("getset");
|
||||
auto non_exist_key = test_key("non-existent");
|
||||
|
||||
KeyDeleter<RedisInstance> deleter(_redis, {key, non_exist_key});
|
||||
|
||||
std::string val("value");
|
||||
REDIS_ASSERT(_redis.set(key, val), "failed to test set");
|
||||
|
||||
auto v = _redis.get(key);
|
||||
REDIS_ASSERT(v && *v == val, "failed to test get");
|
||||
|
||||
v = _redis.getset(key, val + val);
|
||||
REDIS_ASSERT(v && *v == val, "failed to test get");
|
||||
|
||||
REDIS_ASSERT(!_redis.set(key, val, std::chrono::milliseconds(0), UpdateType::NOT_EXIST),
|
||||
"failed to test set with NOT_EXIST type");
|
||||
REDIS_ASSERT(!_redis.set(non_exist_key, val, std::chrono::milliseconds(0), UpdateType::EXIST),
|
||||
"failed to test set with EXIST type");
|
||||
|
||||
REDIS_ASSERT(!_redis.setnx(key, val), "failed to test setnx");
|
||||
REDIS_ASSERT(_redis.setnx(non_exist_key, val), "failed to test setnx");
|
||||
|
||||
auto ttl = std::chrono::seconds(10);
|
||||
|
||||
_redis.set(key, val, ttl);
|
||||
REDIS_ASSERT(_redis.ttl(key) <= ttl.count(), "failed to test set key with ttl");
|
||||
|
||||
_redis.setex(key, ttl, val);
|
||||
REDIS_ASSERT(_redis.ttl(key) <= ttl.count(), "failed to test setex");
|
||||
|
||||
auto pttl = std::chrono::milliseconds(10000);
|
||||
|
||||
_redis.psetex(key, ttl, val);
|
||||
REDIS_ASSERT(_redis.pttl(key) <= pttl.count(), "failed to test psetex");
|
||||
}
|
||||
|
||||
template <typename RedisInstance>
|
||||
void StringCmdTest<RedisInstance>::_test_mgetset() {
|
||||
auto kvs = {std::make_pair(test_key("k1"), "v1"),
|
||||
std::make_pair(test_key("k2"), "v2"),
|
||||
std::make_pair(test_key("k3"), "v3")};
|
||||
|
||||
std::vector<std::string> keys;
|
||||
std::vector<std::string> vals;
|
||||
for (const auto &kv : kvs) {
|
||||
keys.push_back(kv.first);
|
||||
vals.push_back(kv.second);
|
||||
}
|
||||
|
||||
KeyDeleter<RedisInstance> deleter(_redis, keys.begin(), keys.end());
|
||||
|
||||
_redis.mset(kvs);
|
||||
|
||||
std::vector<OptionalString> res;
|
||||
_redis.mget(keys.begin(), keys.end(), std::back_inserter(res));
|
||||
|
||||
REDIS_ASSERT(res.size() == kvs.size(), "failed to test mget");
|
||||
|
||||
std::vector<std::string> res_vals;
|
||||
for (const auto &ele : res) {
|
||||
REDIS_ASSERT(bool(ele), "failed to test mget");
|
||||
|
||||
res_vals.push_back(*ele);
|
||||
}
|
||||
|
||||
REDIS_ASSERT(vals == res_vals, "failed to test mget");
|
||||
|
||||
REDIS_ASSERT(!_redis.msetnx(kvs), "failed to test msetnx");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // end SEWENEW_REDISPLUSPLUS_TEST_STRING_CMDS_TEST_HPP
|
303
controller/thirdparty/redis-plus-plus-1.1.1/test/src/sw/redis++/test_main.cpp
vendored
Normal file
303
controller/thirdparty/redis-plus-plus-1.1.1/test/src/sw/redis++/test_main.cpp
vendored
Normal file
|
@ -0,0 +1,303 @@
|
|||
/**************************************************************************
|
||||
Copyright (c) 2017 sewenew
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*************************************************************************/
|
||||
|
||||
#include <unistd.h>
|
||||
#include <chrono>
|
||||
#include <tuple>
|
||||
#include <iostream>
|
||||
#include <sw/redis++/redis++.h>
|
||||
#include "sanity_test.h"
|
||||
#include "connection_cmds_test.h"
|
||||
#include "keys_cmds_test.h"
|
||||
#include "string_cmds_test.h"
|
||||
#include "list_cmds_test.h"
|
||||
#include "hash_cmds_test.h"
|
||||
#include "set_cmds_test.h"
|
||||
#include "zset_cmds_test.h"
|
||||
#include "hyperloglog_cmds_test.h"
|
||||
#include "geo_cmds_test.h"
|
||||
#include "script_cmds_test.h"
|
||||
#include "pubsub_test.h"
|
||||
#include "pipeline_transaction_test.h"
|
||||
#include "threads_test.h"
|
||||
#include "stream_cmds_test.h"
|
||||
#include "benchmark_test.h"
|
||||
|
||||
namespace {
|
||||
|
||||
void print_help();
|
||||
|
||||
auto parse_options(int argc, char **argv)
|
||||
-> std::tuple<sw::redis::Optional<sw::redis::ConnectionOptions>,
|
||||
sw::redis::Optional<sw::redis::ConnectionOptions>,
|
||||
sw::redis::Optional<sw::redis::test::BenchmarkOptions>>;
|
||||
|
||||
template <typename RedisInstance>
|
||||
void run_test(const sw::redis::ConnectionOptions &opts);
|
||||
|
||||
template <typename RedisInstance>
|
||||
void run_benchmark(const sw::redis::ConnectionOptions &opts,
|
||||
const sw::redis::test::BenchmarkOptions &benchmark_opts);
|
||||
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
try {
|
||||
sw::redis::Optional<sw::redis::ConnectionOptions> opts;
|
||||
sw::redis::Optional<sw::redis::ConnectionOptions> cluster_node_opts;
|
||||
sw::redis::Optional<sw::redis::test::BenchmarkOptions> benchmark_opts;
|
||||
std::tie(opts, cluster_node_opts, benchmark_opts) = parse_options(argc, argv);
|
||||
|
||||
if (opts) {
|
||||
std::cout << "Testing Redis..." << std::endl;
|
||||
|
||||
if (benchmark_opts) {
|
||||
run_benchmark<sw::redis::Redis>(*opts, *benchmark_opts);
|
||||
} else {
|
||||
run_test<sw::redis::Redis>(*opts);
|
||||
}
|
||||
}
|
||||
|
||||
if (cluster_node_opts) {
|
||||
std::cout << "Testing RedisCluster..." << std::endl;
|
||||
|
||||
if (benchmark_opts) {
|
||||
run_benchmark<sw::redis::RedisCluster>(*cluster_node_opts, *benchmark_opts);
|
||||
} else {
|
||||
run_test<sw::redis::RedisCluster>(*cluster_node_opts);
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << "Pass all tests" << std::endl;
|
||||
} catch (const sw::redis::Error &e) {
|
||||
std::cerr << "Test failed: " << e.what() << std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
void print_help() {
|
||||
std::cerr << "Usage: test_redis++ -h host -p port"
|
||||
<< " -n cluster_node -c cluster_port [-a auth] [-b]\n\n";
|
||||
std::cerr << "See https://github.com/sewenew/redis-plus-plus#run-tests-optional"
|
||||
<< " for details on how to run test" << std::endl;
|
||||
}
|
||||
|
||||
auto parse_options(int argc, char **argv)
|
||||
-> std::tuple<sw::redis::Optional<sw::redis::ConnectionOptions>,
|
||||
sw::redis::Optional<sw::redis::ConnectionOptions>,
|
||||
sw::redis::Optional<sw::redis::test::BenchmarkOptions>> {
|
||||
std::string host;
|
||||
int port = 0;
|
||||
std::string auth;
|
||||
std::string cluster_node;
|
||||
int cluster_port = 0;
|
||||
bool benchmark = false;
|
||||
sw::redis::test::BenchmarkOptions tmp_benchmark_opts;
|
||||
|
||||
int opt = 0;
|
||||
while ((opt = getopt(argc, argv, "h:p:a:n:c:k:v:r:t:bs:")) != -1) {
|
||||
try {
|
||||
switch (opt) {
|
||||
case 'h':
|
||||
host = optarg;
|
||||
break;
|
||||
|
||||
case 'p':
|
||||
port = std::stoi(optarg);
|
||||
break;
|
||||
|
||||
case 'a':
|
||||
auth = optarg;
|
||||
break;
|
||||
|
||||
case 'n':
|
||||
cluster_node = optarg;
|
||||
break;
|
||||
|
||||
case 'c':
|
||||
cluster_port = std::stoi(optarg);
|
||||
break;
|
||||
|
||||
case 'b':
|
||||
benchmark = true;
|
||||
break;
|
||||
|
||||
case 'k':
|
||||
tmp_benchmark_opts.key_len = std::stoi(optarg);
|
||||
break;
|
||||
|
||||
case 'v':
|
||||
tmp_benchmark_opts.val_len = std::stoi(optarg);
|
||||
break;
|
||||
|
||||
case 'r':
|
||||
tmp_benchmark_opts.total_request_num = std::stoi(optarg);
|
||||
break;
|
||||
|
||||
case 't':
|
||||
tmp_benchmark_opts.thread_num = std::stoi(optarg);
|
||||
break;
|
||||
|
||||
case 's':
|
||||
tmp_benchmark_opts.pool_size = std::stoi(optarg);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw sw::redis::Error("Unknow command line option");
|
||||
break;
|
||||
}
|
||||
} catch (const sw::redis::Error &e) {
|
||||
print_help();
|
||||
throw;
|
||||
} catch (const std::exception &e) {
|
||||
print_help();
|
||||
throw sw::redis::Error("Invalid command line option");
|
||||
}
|
||||
}
|
||||
|
||||
sw::redis::Optional<sw::redis::ConnectionOptions> opts;
|
||||
if (!host.empty() && port > 0) {
|
||||
sw::redis::ConnectionOptions tmp;
|
||||
tmp.host = host;
|
||||
tmp.port = port;
|
||||
tmp.password = auth;
|
||||
|
||||
opts = sw::redis::Optional<sw::redis::ConnectionOptions>(tmp);
|
||||
}
|
||||
|
||||
sw::redis::Optional<sw::redis::ConnectionOptions> cluster_opts;
|
||||
if (!cluster_node.empty() && cluster_port > 0) {
|
||||
sw::redis::ConnectionOptions tmp;
|
||||
tmp.host = cluster_node;
|
||||
tmp.port = cluster_port;
|
||||
tmp.password = auth;
|
||||
|
||||
cluster_opts = sw::redis::Optional<sw::redis::ConnectionOptions>(tmp);
|
||||
}
|
||||
|
||||
if (!opts && !cluster_opts) {
|
||||
print_help();
|
||||
throw sw::redis::Error("Invalid connection options");
|
||||
}
|
||||
|
||||
sw::redis::Optional<sw::redis::test::BenchmarkOptions> benchmark_opts;
|
||||
if (benchmark) {
|
||||
benchmark_opts = sw::redis::Optional<sw::redis::test::BenchmarkOptions>(tmp_benchmark_opts);
|
||||
}
|
||||
|
||||
return std::make_tuple(std::move(opts), std::move(cluster_opts), std::move(benchmark_opts));
|
||||
}
|
||||
|
||||
template <typename RedisInstance>
|
||||
void run_test(const sw::redis::ConnectionOptions &opts) {
|
||||
auto instance = RedisInstance(opts);
|
||||
|
||||
sw::redis::test::SanityTest<RedisInstance> sanity_test(opts, instance);
|
||||
sanity_test.run();
|
||||
|
||||
std::cout << "Pass sanity tests" << std::endl;
|
||||
|
||||
sw::redis::test::ConnectionCmdTest<RedisInstance> connection_test(instance);
|
||||
connection_test.run();
|
||||
|
||||
std::cout << "Pass connection commands tests" << std::endl;
|
||||
|
||||
sw::redis::test::KeysCmdTest<RedisInstance> keys_test(instance);
|
||||
keys_test.run();
|
||||
|
||||
std::cout << "Pass keys commands tests" << std::endl;
|
||||
|
||||
sw::redis::test::StringCmdTest<RedisInstance> string_test(instance);
|
||||
string_test.run();
|
||||
|
||||
std::cout << "Pass string commands tests" << std::endl;
|
||||
|
||||
sw::redis::test::ListCmdTest<RedisInstance> list_test(instance);
|
||||
list_test.run();
|
||||
|
||||
std::cout << "Pass list commands tests" << std::endl;
|
||||
|
||||
sw::redis::test::HashCmdTest<RedisInstance> hash_test(instance);
|
||||
hash_test.run();
|
||||
|
||||
std::cout << "Pass hash commands tests" << std::endl;
|
||||
|
||||
sw::redis::test::SetCmdTest<RedisInstance> set_test(instance);
|
||||
set_test.run();
|
||||
|
||||
std::cout << "Pass set commands tests" << std::endl;
|
||||
|
||||
sw::redis::test::ZSetCmdTest<RedisInstance> zset_test(instance);
|
||||
zset_test.run();
|
||||
|
||||
std::cout << "Pass zset commands tests" << std::endl;
|
||||
|
||||
sw::redis::test::HyperloglogCmdTest<RedisInstance> hll_test(instance);
|
||||
hll_test.run();
|
||||
|
||||
std::cout << "Pass hyperloglog commands tests" << std::endl;
|
||||
|
||||
sw::redis::test::GeoCmdTest<RedisInstance> geo_test(instance);
|
||||
geo_test.run();
|
||||
|
||||
std::cout << "Pass geo commands tests" << std::endl;
|
||||
|
||||
sw::redis::test::ScriptCmdTest<RedisInstance> script_test(instance);
|
||||
script_test.run();
|
||||
|
||||
std::cout << "Pass script commands tests" << std::endl;
|
||||
|
||||
sw::redis::test::PubSubTest<RedisInstance> pubsub_test(instance);
|
||||
pubsub_test.run();
|
||||
|
||||
std::cout << "Pass pubsub tests" << std::endl;
|
||||
|
||||
sw::redis::test::PipelineTransactionTest<RedisInstance> pipe_tx_test(instance);
|
||||
pipe_tx_test.run();
|
||||
|
||||
std::cout << "Pass pipeline and transaction tests" << std::endl;
|
||||
|
||||
sw::redis::test::ThreadsTest<RedisInstance> threads_test(opts);
|
||||
threads_test.run();
|
||||
|
||||
std::cout << "Pass threads tests" << std::endl;
|
||||
|
||||
sw::redis::test::StreamCmdsTest<RedisInstance> stream_test(instance);
|
||||
stream_test.run();
|
||||
|
||||
std::cout << "Pass stream commands tests" << std::endl;
|
||||
}
|
||||
|
||||
template <typename RedisInstance>
|
||||
void run_benchmark(const sw::redis::ConnectionOptions &opts,
|
||||
const sw::redis::test::BenchmarkOptions &benchmark_opts) {
|
||||
std::cout << "Benchmark test options:" << std::endl;
|
||||
std::cout << " Thread number: " << benchmark_opts.thread_num << std::endl;
|
||||
std::cout << " Connection pool size: " << benchmark_opts.pool_size << std::endl;
|
||||
std::cout << " Length of key: " << benchmark_opts.key_len << std::endl;
|
||||
std::cout << " Length of value: " << benchmark_opts.val_len << std::endl;
|
||||
|
||||
auto instance = RedisInstance(opts);
|
||||
|
||||
sw::redis::test::BenchmarkTest<RedisInstance> benchmark_test(benchmark_opts, instance);
|
||||
benchmark_test.run();
|
||||
}
|
||||
|
||||
}
|
51
controller/thirdparty/redis-plus-plus-1.1.1/test/src/sw/redis++/threads_test.h
vendored
Normal file
51
controller/thirdparty/redis-plus-plus-1.1.1/test/src/sw/redis++/threads_test.h
vendored
Normal file
|
@ -0,0 +1,51 @@
|
|||
/**************************************************************************
|
||||
Copyright (c) 2017 sewenew
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*************************************************************************/
|
||||
|
||||
#ifndef SEWENEW_REDISPLUSPLUS_TEST_THREADS_TEST_H
|
||||
#define SEWENEW_REDISPLUSPLUS_TEST_THREADS_TEST_H
|
||||
|
||||
#include <sw/redis++/redis++.h>
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
namespace test {
|
||||
|
||||
template <typename RedisInstance>
|
||||
class ThreadsTest {
|
||||
public:
|
||||
explicit ThreadsTest(const ConnectionOptions &opts) : _opts(opts) {}
|
||||
|
||||
void run();
|
||||
|
||||
private:
|
||||
void _test_multithreads(RedisInstance redis, int threads_num, int times);
|
||||
|
||||
void _test_timeout();
|
||||
|
||||
ConnectionOptions _opts;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#include "threads_test.hpp"
|
||||
|
||||
#endif // end SEWENEW_REDISPLUSPLUS_TEST_THREADS_TEST_H
|
147
controller/thirdparty/redis-plus-plus-1.1.1/test/src/sw/redis++/threads_test.hpp
vendored
Normal file
147
controller/thirdparty/redis-plus-plus-1.1.1/test/src/sw/redis++/threads_test.hpp
vendored
Normal file
|
@ -0,0 +1,147 @@
|
|||
/**************************************************************************
|
||||
Copyright (c) 2017 sewenew
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*************************************************************************/
|
||||
|
||||
#ifndef SEWENEW_REDISPLUSPLUS_TEST_THREADS_TEST_HPP
|
||||
#define SEWENEW_REDISPLUSPLUS_TEST_THREADS_TEST_HPP
|
||||
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
#include "utils.h"
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
namespace test {
|
||||
|
||||
template <typename RedisInstance>
|
||||
void ThreadsTest<RedisInstance>::run() {
|
||||
// 100 * 10000 = 1 million writes
|
||||
auto thread_num = 100;
|
||||
auto times = 10000;
|
||||
|
||||
// Default pool options: single connection and wait forever.
|
||||
_test_multithreads(RedisInstance(_opts), thread_num, times);
|
||||
|
||||
// Pool with 10 connections.
|
||||
ConnectionPoolOptions pool_opts;
|
||||
pool_opts.size = 10;
|
||||
_test_multithreads(RedisInstance(_opts, pool_opts), thread_num, times);
|
||||
|
||||
_test_timeout();
|
||||
}
|
||||
|
||||
template <typename RedisInstance>
|
||||
void ThreadsTest<RedisInstance>::_test_multithreads(RedisInstance redis,
|
||||
int thread_num,
|
||||
int times) {
|
||||
std::vector<std::string> keys;
|
||||
keys.reserve(thread_num);
|
||||
for (auto idx = 0; idx != thread_num; ++idx) {
|
||||
auto key = test_key("multi-threads::" + std::to_string(idx));
|
||||
keys.push_back(key);
|
||||
}
|
||||
|
||||
using DeleterUPtr = std::unique_ptr<KeyDeleter<RedisInstance>>;
|
||||
std::vector<DeleterUPtr> deleters;
|
||||
for (const auto &key : keys) {
|
||||
deleters.emplace_back(new KeyDeleter<RedisInstance>(redis, key));
|
||||
}
|
||||
|
||||
std::vector<std::thread> workers;
|
||||
workers.reserve(thread_num);
|
||||
for (const auto &key : keys) {
|
||||
workers.emplace_back([&redis, key, times]() {
|
||||
try {
|
||||
for (auto i = 0; i != times; ++i) {
|
||||
redis.incr(key);
|
||||
}
|
||||
} catch (...) {
|
||||
// Something bad happens.
|
||||
return;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
for (auto &worker : workers) {
|
||||
worker.join();
|
||||
}
|
||||
|
||||
for (const auto &key : keys) {
|
||||
auto val = redis.get(key);
|
||||
REDIS_ASSERT(bool(val), "failed to test multithreads, cannot get value of " + key);
|
||||
|
||||
auto num = std::stoi(*val);
|
||||
REDIS_ASSERT(num == times, "failed to test multithreads, num: "
|
||||
+ *val + ", times: " + std::to_string(times));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename RedisInstance>
|
||||
void ThreadsTest<RedisInstance>::_test_timeout() {
|
||||
using namespace std::chrono;
|
||||
|
||||
ConnectionPoolOptions pool_opts;
|
||||
pool_opts.size = 1;
|
||||
pool_opts.wait_timeout = milliseconds(100);
|
||||
|
||||
auto redis = RedisInstance(_opts, pool_opts);
|
||||
|
||||
auto key = test_key("key");
|
||||
|
||||
std::atomic<bool> slow_get_is_running{false};
|
||||
auto slow_get = [&slow_get_is_running](Connection &connection, const StringView &key) {
|
||||
slow_get_is_running = true;
|
||||
|
||||
// Sleep a while to simulate a slow get.
|
||||
std::this_thread::sleep_for(seconds(5));
|
||||
|
||||
connection.send("GET %b", key.data(), key.size());
|
||||
};
|
||||
auto slow_get_thread = std::thread([&redis, slow_get, &key]() {
|
||||
redis.command(slow_get, key);
|
||||
});
|
||||
|
||||
auto get_thread = std::thread([&redis, &slow_get_is_running, &key]() {
|
||||
try {
|
||||
while (!slow_get_is_running) {
|
||||
std::this_thread::sleep_for(milliseconds(10));
|
||||
}
|
||||
|
||||
redis.get(key);
|
||||
|
||||
// Slow get is running, this thread should
|
||||
// timeout before obtaining the connection.
|
||||
// So it never reaches here.
|
||||
REDIS_ASSERT(false, "failed to test pool timeout");
|
||||
} catch (const Error &err) {
|
||||
// This thread timeout.
|
||||
}
|
||||
});
|
||||
|
||||
slow_get_thread.join();
|
||||
get_thread.join();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // end SEWENEW_REDISPLUSPLUS_TEST_THREADS_TEST_HPP
|
96
controller/thirdparty/redis-plus-plus-1.1.1/test/src/sw/redis++/utils.h
vendored
Normal file
96
controller/thirdparty/redis-plus-plus-1.1.1/test/src/sw/redis++/utils.h
vendored
Normal file
|
@ -0,0 +1,96 @@
|
|||
/**************************************************************************
|
||||
Copyright (c) 2017 sewenew
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*************************************************************************/
|
||||
|
||||
#ifndef SEWENEW_REDISPLUSPLUS_TEST_UTILS_H
|
||||
#define SEWENEW_REDISPLUSPLUS_TEST_UTILS_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <sw/redis++/redis++.h>
|
||||
|
||||
#define REDIS_ASSERT(condition, msg) \
|
||||
sw::redis::test::redis_assert((condition), (msg), __FILE__, __LINE__)
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
namespace test {
|
||||
|
||||
inline void redis_assert(bool condition,
|
||||
const std::string &msg,
|
||||
const std::string &file,
|
||||
int line) {
|
||||
if (!condition) {
|
||||
auto err_msg = "ASSERT: " + msg + ". " + file + ":" + std::to_string(line);
|
||||
throw Error(err_msg);
|
||||
}
|
||||
}
|
||||
|
||||
inline std::string test_key(const std::string &k) {
|
||||
// Key prefix with hash tag,
|
||||
// so that we can call multiple-key commands on RedisCluster.
|
||||
return "{sw::redis::test}::" + k;
|
||||
}
|
||||
|
||||
template <typename Test>
|
||||
void cluster_specializing_test(Test &test, void (Test::*func)(Redis &instance), Redis &instance) {
|
||||
(test.*func)(instance);
|
||||
}
|
||||
|
||||
template <typename Test>
|
||||
void cluster_specializing_test(Test &test,
|
||||
void (Test::*func)(Redis &instance),
|
||||
RedisCluster &cluster) {
|
||||
auto instance = cluster.redis("hash-tag");
|
||||
(test.*func)(instance);
|
||||
}
|
||||
|
||||
template <typename RedisInstance>
|
||||
class KeyDeleter {
|
||||
public:
|
||||
template <typename Input>
|
||||
KeyDeleter(RedisInstance &redis, Input first, Input last) : _redis(redis), _keys(first, last) {
|
||||
_delete();
|
||||
}
|
||||
|
||||
KeyDeleter(RedisInstance &redis, std::initializer_list<std::string> il) :
|
||||
KeyDeleter(redis, il.begin(), il.end()) {}
|
||||
|
||||
KeyDeleter(RedisInstance &redis, const std::string &key) : KeyDeleter(redis, {key}) {}
|
||||
|
||||
~KeyDeleter() {
|
||||
_delete();
|
||||
}
|
||||
|
||||
private:
|
||||
void _delete() {
|
||||
if (!_keys.empty()) {
|
||||
_redis.del(_keys.begin(), _keys.end());
|
||||
}
|
||||
}
|
||||
|
||||
RedisInstance &_redis;
|
||||
std::vector<std::string> _keys;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // end SEWENEW_REDISPLUSPLUS_TEST_UTILS_H
|
61
controller/thirdparty/redis-plus-plus-1.1.1/test/src/sw/redis++/zset_cmds_test.h
vendored
Normal file
61
controller/thirdparty/redis-plus-plus-1.1.1/test/src/sw/redis++/zset_cmds_test.h
vendored
Normal file
|
@ -0,0 +1,61 @@
|
|||
/**************************************************************************
|
||||
Copyright (c) 2017 sewenew
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*************************************************************************/
|
||||
|
||||
#ifndef SEWENEW_REDISPLUSPLUS_TEST_ZSET_CMDS_TEST_H
|
||||
#define SEWENEW_REDISPLUSPLUS_TEST_ZSET_CMDS_TEST_H
|
||||
|
||||
#include <sw/redis++/redis++.h>
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
namespace test {
|
||||
|
||||
template <typename RedisInstance>
|
||||
class ZSetCmdTest {
|
||||
public:
|
||||
explicit ZSetCmdTest(RedisInstance &instance) : _redis(instance) {}
|
||||
|
||||
void run();
|
||||
|
||||
private:
|
||||
void _test_zset();
|
||||
|
||||
void _test_zscan();
|
||||
|
||||
void _test_range();
|
||||
|
||||
void _test_lex();
|
||||
|
||||
void _test_multi_zset();
|
||||
|
||||
void _test_zpop();
|
||||
|
||||
void _test_bzpop();
|
||||
|
||||
RedisInstance &_redis;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#include "zset_cmds_test.hpp"
|
||||
|
||||
#endif // end SEWENEW_REDISPLUSPLUS_TEST_ZSET_CMDS_TEST_H
|
350
controller/thirdparty/redis-plus-plus-1.1.1/test/src/sw/redis++/zset_cmds_test.hpp
vendored
Normal file
350
controller/thirdparty/redis-plus-plus-1.1.1/test/src/sw/redis++/zset_cmds_test.hpp
vendored
Normal file
|
@ -0,0 +1,350 @@
|
|||
/**************************************************************************
|
||||
Copyright (c) 2017 sewenew
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*************************************************************************/
|
||||
|
||||
#ifndef SEWENEW_REDISPLUSPLUS_TEST_ZSET_CMDS_TEST_HPP
|
||||
#define SEWENEW_REDISPLUSPLUS_TEST_ZSET_CMDS_TEST_HPP
|
||||
|
||||
#include <map>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include "utils.h"
|
||||
|
||||
namespace sw {
|
||||
|
||||
namespace redis {
|
||||
|
||||
namespace test {
|
||||
|
||||
template <typename RedisInstance>
|
||||
void ZSetCmdTest<RedisInstance>::run() {
|
||||
_test_zset();
|
||||
|
||||
_test_zscan();
|
||||
|
||||
_test_range();
|
||||
|
||||
_test_lex();
|
||||
|
||||
_test_multi_zset();
|
||||
|
||||
_test_zpop();
|
||||
|
||||
_test_bzpop();
|
||||
}
|
||||
|
||||
template <typename RedisInstance>
|
||||
void ZSetCmdTest<RedisInstance>::_test_zset() {
|
||||
auto key = test_key("zset");
|
||||
|
||||
KeyDeleter<RedisInstance> deleter(_redis, key);
|
||||
|
||||
std::map<std::string, double> s = {
|
||||
std::make_pair("m1", 1.2),
|
||||
std::make_pair("m2", 2),
|
||||
std::make_pair("m3", 3),
|
||||
};
|
||||
|
||||
const auto &ele = *(s.begin());
|
||||
REDIS_ASSERT(_redis.zadd(key, ele.first, ele.second, UpdateType::EXIST) == 0,
|
||||
"failed to test zadd with noexistent member");
|
||||
|
||||
REDIS_ASSERT(_redis.zadd(key, s.begin(), s.end()) == 3, "failed to test zadd");
|
||||
|
||||
REDIS_ASSERT(_redis.zadd(key, ele.first, ele.second, UpdateType::NOT_EXIST) == 0,
|
||||
"failed to test zadd with exist member");
|
||||
|
||||
REDIS_ASSERT(_redis.zadd(key, s.begin(), s.end(), UpdateType::ALWAYS, true) == 0,
|
||||
"failed to test zadd");
|
||||
|
||||
REDIS_ASSERT(_redis.zcard(key) == 3, "failed to test zcard");
|
||||
|
||||
auto rank = _redis.zrank(key, "m2");
|
||||
REDIS_ASSERT(bool(rank) && *rank == 1, "failed to test zrank");
|
||||
rank = _redis.zrevrank(key, "m4");
|
||||
REDIS_ASSERT(!rank, "failed to test zrevrank with nonexistent member");
|
||||
|
||||
auto score = _redis.zscore(key, "m4");
|
||||
REDIS_ASSERT(!score, "failed to test zscore with nonexistent member");
|
||||
|
||||
REDIS_ASSERT(_redis.zincrby(key, 1, "m3") == 4, "failed to test zincrby");
|
||||
|
||||
score = _redis.zscore(key, "m3");
|
||||
REDIS_ASSERT(score && *score == 4, "failed to test zscore");
|
||||
|
||||
REDIS_ASSERT(_redis.zrem(key, "m1") == 1, "failed to test zrem");
|
||||
REDIS_ASSERT(_redis.zrem(key, {"m1", "m2", "m3", "m4"}) == 2, "failed to test zrem");
|
||||
}
|
||||
|
||||
template <typename RedisInstance>
|
||||
void ZSetCmdTest<RedisInstance>::_test_zscan() {
|
||||
auto key = test_key("zscan");
|
||||
|
||||
KeyDeleter<RedisInstance> deleter(_redis, key);
|
||||
|
||||
std::map<std::string, double> s = {
|
||||
std::make_pair("m1", 1.2),
|
||||
std::make_pair("m2", 2),
|
||||
std::make_pair("m3", 3),
|
||||
};
|
||||
_redis.zadd(key, s.begin(), s.end());
|
||||
|
||||
std::map<std::string, double> res;
|
||||
auto cursor = 0;
|
||||
while (true) {
|
||||
cursor = _redis.zscan(key, cursor, "m*", 2, std::inserter(res, res.end()));
|
||||
if (cursor == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
REDIS_ASSERT(res == s, "failed to test zscan");
|
||||
}
|
||||
|
||||
template <typename RedisInstance>
|
||||
void ZSetCmdTest<RedisInstance>::_test_range() {
|
||||
auto key = test_key("range");
|
||||
|
||||
KeyDeleter<RedisInstance> deleter(_redis, key);
|
||||
|
||||
std::map<std::string, double> s = {
|
||||
std::make_pair("m1", 1),
|
||||
std::make_pair("m2", 2),
|
||||
std::make_pair("m3", 3),
|
||||
};
|
||||
_redis.zadd(key, s.begin(), s.end());
|
||||
|
||||
REDIS_ASSERT(_redis.zcount(key, UnboundedInterval<double>{}) == 3, "failed to test zcount");
|
||||
|
||||
std::vector<std::string> members;
|
||||
_redis.zrange(key, 0, -1, std::back_inserter(members));
|
||||
REDIS_ASSERT(members.size() == s.size(), "failed to test zrange");
|
||||
for (const auto &mem : {"m1", "m2", "m3"}) {
|
||||
REDIS_ASSERT(std::find(members.begin(), members.end(), mem) != members.end(),
|
||||
"failed to test zrange");
|
||||
}
|
||||
|
||||
std::map<std::string, double> res;
|
||||
_redis.zrange(key, 0, -1, std::inserter(res, res.end()));
|
||||
REDIS_ASSERT(s == res, "failed to test zrange with score");
|
||||
|
||||
members.clear();
|
||||
_redis.zrevrange(key, 0, 0, std::back_inserter(members));
|
||||
REDIS_ASSERT(members.size() == 1 && members[0] == "m3", "failed to test zrevrange");
|
||||
|
||||
res.clear();
|
||||
_redis.zrevrange(key, 0, 0, std::inserter(res, res.end()));
|
||||
REDIS_ASSERT(res.size() == 1 && res.find("m3") != res.end() && res["m3"] == 3,
|
||||
"failed to test zrevrange with score");
|
||||
|
||||
members.clear();
|
||||
_redis.zrangebyscore(key, UnboundedInterval<double>{}, std::back_inserter(members));
|
||||
REDIS_ASSERT(members.size() == s.size(), "failed to test zrangebyscore");
|
||||
for (const auto &mem : {"m1", "m2", "m3"}) {
|
||||
REDIS_ASSERT(std::find(members.begin(), members.end(), mem) != members.end(),
|
||||
"failed to test zrangebyscore");
|
||||
}
|
||||
|
||||
members.clear();
|
||||
_redis.zrangebyscore(key,
|
||||
BoundedInterval<double>(1, 2, BoundType::RIGHT_OPEN),
|
||||
std::back_inserter(members));
|
||||
REDIS_ASSERT(members.size() == 1 && members[0] == "m1", "failed to test zrangebyscore");
|
||||
|
||||
res.clear();
|
||||
_redis.zrangebyscore(key,
|
||||
LeftBoundedInterval<double>(2, BoundType::OPEN),
|
||||
std::inserter(res, res.end()));
|
||||
REDIS_ASSERT(res.size() == 1 && res.find("m3") != res.end() && res["m3"] == 3,
|
||||
"failed to test zrangebyscore");
|
||||
|
||||
members.clear();
|
||||
_redis.zrevrangebyscore(key,
|
||||
BoundedInterval<double>(1, 3, BoundType::CLOSED),
|
||||
std::back_inserter(members));
|
||||
REDIS_ASSERT(members == std::vector<std::string>({"m3", "m2", "m1"}),
|
||||
"failed to test zrevrangebyscore");
|
||||
|
||||
res.clear();
|
||||
_redis.zrevrangebyscore(key,
|
||||
RightBoundedInterval<double>(1, BoundType::LEFT_OPEN),
|
||||
std::inserter(res, res.end()));
|
||||
REDIS_ASSERT(res.size() == 1 && res.find("m1") != res.end() && res["m1"] == 1,
|
||||
"failed to test zrevrangebyscore");
|
||||
|
||||
REDIS_ASSERT(_redis.zremrangebyrank(key, 0, 0) == 1, "failed to test zremrangebyrank");
|
||||
|
||||
REDIS_ASSERT(_redis.zremrangebyscore(key,
|
||||
BoundedInterval<double>(2, 3, BoundType::LEFT_OPEN)) == 1,
|
||||
"failed to test zremrangebyscore");
|
||||
}
|
||||
|
||||
template <typename RedisInstance>
|
||||
void ZSetCmdTest<RedisInstance>::_test_lex() {
|
||||
auto key = test_key("lex");
|
||||
|
||||
KeyDeleter<RedisInstance> deleter(_redis, key);
|
||||
|
||||
auto s = {
|
||||
std::make_pair("m1", 0),
|
||||
std::make_pair("m2", 0),
|
||||
std::make_pair("m3", 0),
|
||||
};
|
||||
_redis.zadd(key, s.begin(), s.end());
|
||||
|
||||
REDIS_ASSERT(_redis.zlexcount(key, UnboundedInterval<std::string>{}) == 3,
|
||||
"failed to test zlexcount");
|
||||
|
||||
std::vector<std::string> members;
|
||||
_redis.zrangebylex(key,
|
||||
LeftBoundedInterval<std::string>("m2", BoundType::OPEN),
|
||||
std::back_inserter(members));
|
||||
REDIS_ASSERT(members.size() == 1 && members[0] == "m3",
|
||||
"failed to test zrangebylex");
|
||||
|
||||
members.clear();
|
||||
_redis.zrevrangebylex(key,
|
||||
RightBoundedInterval<std::string>("m1", BoundType::LEFT_OPEN),
|
||||
std::back_inserter(members));
|
||||
REDIS_ASSERT(members.size() == 1 && members[0] == "m1",
|
||||
"failed to test zrevrangebylex");
|
||||
|
||||
REDIS_ASSERT(_redis.zremrangebylex(key,
|
||||
BoundedInterval<std::string>("m1", "m3", BoundType::OPEN)) == 1,
|
||||
"failed to test zremrangebylex");
|
||||
}
|
||||
|
||||
template <typename RedisInstance>
|
||||
void ZSetCmdTest<RedisInstance>::_test_multi_zset() {
|
||||
auto k1 = test_key("k1");
|
||||
auto k2 = test_key("k2");
|
||||
auto k3 = test_key("k3");
|
||||
|
||||
KeyDeleter<RedisInstance> deleter(_redis, {k1, k2, k3});
|
||||
|
||||
_redis.zadd(k1, {std::make_pair("a", 1), std::make_pair("b", 2)});
|
||||
_redis.zadd(k2, {std::make_pair("a", 2), std::make_pair("c", 3)});
|
||||
|
||||
REDIS_ASSERT(_redis.zinterstore(k3, {k1, k2}) == 1, "failed to test zinterstore");
|
||||
auto score = _redis.zscore(k3, "a");
|
||||
REDIS_ASSERT(bool(score) && *score == 3, "failed to test zinterstore");
|
||||
|
||||
REDIS_ASSERT(_redis.zinterstore(k3, k1, 2) == 2, "failed to test zinterstore");
|
||||
|
||||
_redis.del(k3);
|
||||
|
||||
REDIS_ASSERT(_redis.zinterstore(k3, {k1, k2}, Aggregation::MAX) == 1,
|
||||
"failed to test zinterstore");
|
||||
score = _redis.zscore(k3, "a");
|
||||
REDIS_ASSERT(bool(score) && *score == 2, "failed to test zinterstore");
|
||||
|
||||
_redis.del(k3);
|
||||
|
||||
REDIS_ASSERT(_redis.zunionstore(k3,
|
||||
{std::make_pair(k1, 1), std::make_pair(k2, 2)},
|
||||
Aggregation::MIN) == 3,
|
||||
"failed to test zunionstore");
|
||||
std::vector<std::pair<std::string, double>> res;
|
||||
_redis.zrange(k3, 0, -1, std::back_inserter(res));
|
||||
for (const auto &ele : res) {
|
||||
if (ele.first == "a") {
|
||||
REDIS_ASSERT(ele.second == 1, "failed to test zunionstore");
|
||||
} else if (ele.first == "b") {
|
||||
REDIS_ASSERT(ele.second == 2, "failed to test zunionstore");
|
||||
} else if (ele.first == "c") {
|
||||
REDIS_ASSERT(ele.second == 6, "failed to test zunionstore");
|
||||
} else {
|
||||
REDIS_ASSERT(false, "failed to test zuionstore");
|
||||
}
|
||||
}
|
||||
|
||||
REDIS_ASSERT(_redis.zunionstore(k3, k1, 2) == 2, "failed to test zunionstore");
|
||||
}
|
||||
|
||||
template <typename RedisInstance>
|
||||
void ZSetCmdTest<RedisInstance>::_test_zpop() {
|
||||
auto key = test_key("zpop");
|
||||
|
||||
KeyDeleter<RedisInstance> deleter(_redis, key);
|
||||
|
||||
_redis.zadd(key, {std::make_pair("m1", 1.1),
|
||||
std::make_pair("m2", 2.2),
|
||||
std::make_pair("m3", 3.3),
|
||||
std::make_pair("m4", 4.4),
|
||||
std::make_pair("m5", 5.5),
|
||||
std::make_pair("m6", 6.6)});
|
||||
|
||||
auto item = _redis.zpopmax(key);
|
||||
REDIS_ASSERT(item && item->first == "m6", "failed to test zpopmax");
|
||||
|
||||
item = _redis.zpopmin(key);
|
||||
REDIS_ASSERT(item && item->first == "m1", "failed to test zpopmin");
|
||||
|
||||
std::vector<std::pair<std::string, double>> vec;
|
||||
_redis.zpopmax(key, 2, std::back_inserter(vec));
|
||||
REDIS_ASSERT(vec.size() == 2 && vec[0].first == "m5" && vec[1].first == "m4",
|
||||
"failed to test zpopmax");
|
||||
|
||||
std::unordered_map<std::string, double> m;
|
||||
_redis.zpopmin(key, 2, std::inserter(m, m.end()));
|
||||
REDIS_ASSERT(m.size() == 2 && m.find("m3") != m.end() && m.find("m2") != m.end(),
|
||||
"failed to test zpopmin");
|
||||
}
|
||||
|
||||
template <typename RedisInstance>
|
||||
void ZSetCmdTest<RedisInstance>::_test_bzpop() {
|
||||
auto key1 = test_key("bzpop1");
|
||||
auto key2 = test_key("bzpop2");
|
||||
|
||||
KeyDeleter<RedisInstance> deleter(_redis, {key1, key2});
|
||||
|
||||
_redis.zadd(key1, {std::make_pair("m1", 1.1),
|
||||
std::make_pair("m2", 2.2),
|
||||
std::make_pair("m3", 3.3),
|
||||
std::make_pair("m4", 4.4),
|
||||
std::make_pair("m5", 5.5),
|
||||
std::make_pair("m6", 6.6)});
|
||||
|
||||
_redis.zadd(key2, {std::make_pair("m1", 1.1),
|
||||
std::make_pair("m2", 2.2),
|
||||
std::make_pair("m3", 3.3),
|
||||
std::make_pair("m4", 4.4),
|
||||
std::make_pair("m5", 5.5),
|
||||
std::make_pair("m6", 6.6)});
|
||||
|
||||
auto item = _redis.bzpopmax(key1);
|
||||
REDIS_ASSERT(item && std::get<0>(*item) == key1 && std::get<1>(*item) == "m6",
|
||||
"failed to test bzpopmax");
|
||||
|
||||
item = _redis.bzpopmin(key1, std::chrono::seconds(1));
|
||||
REDIS_ASSERT(item && std::get<0>(*item) == key1 && std::get<1>(*item) == "m1",
|
||||
"failed to test zpopmin");
|
||||
|
||||
item = _redis.bzpopmax({key1, key2}, std::chrono::seconds(1));
|
||||
REDIS_ASSERT(item && std::get<0>(*item) == key1 && std::get<1>(*item) == "m5",
|
||||
"failed to test zpopmax");
|
||||
|
||||
item = _redis.bzpopmin({key2, key1});
|
||||
REDIS_ASSERT(item && std::get<0>(*item) == key2 && std::get<1>(*item) == "m1",
|
||||
"failed to test zpopmin");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // end SEWENEW_REDISPLUSPLUS_TEST_ZSET_CMDS_TEST_HPP
|
Loading…
Add table
Reference in a new issue