mirror of
https://gitee.com/acl-dev/acl.git
synced 2024-11-30 10:57:34 +08:00
changed the file's format to DOS format
This commit is contained in:
parent
e7f83a889c
commit
772ff217e9
@ -1,243 +1,243 @@
|
||||
#include "stdafx.h"
|
||||
#include "redis_util.h"
|
||||
#include "redis_migrate.h"
|
||||
|
||||
redis_migrate::redis_migrate(std::vector<acl::redis_node*>& masters)
|
||||
: masters_(masters)
|
||||
{
|
||||
}
|
||||
|
||||
redis_migrate::~redis_migrate(void)
|
||||
{
|
||||
}
|
||||
|
||||
int redis_migrate::move_slots(std::vector<acl::redis_node*>& from,
|
||||
acl::redis_node& to, int count)
|
||||
{
|
||||
assert(!from.empty());
|
||||
|
||||
int base = count / (int) from.size(), nslots_moved = 0;
|
||||
int first = base + count % (int) from.size(), ret;
|
||||
|
||||
std::vector<acl::redis_node*>::iterator it;
|
||||
for (it = from.begin(); it != from.end(); ++it)
|
||||
{
|
||||
// move some slots from one source node to the target node
|
||||
if (it == from.begin())
|
||||
ret = move_slots(**it, to, first);
|
||||
else
|
||||
ret = move_slots(**it, to, base);
|
||||
if (ret < 0)
|
||||
{
|
||||
printf("move failed, stop!\r\n");
|
||||
break;
|
||||
}
|
||||
|
||||
nslots_moved += ret;
|
||||
|
||||
// check if all the slots have been moved
|
||||
if (nslots_moved >= count)
|
||||
{
|
||||
printf("moved %d slots ok\r\n", nslots_moved);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
printf("move over!\r\n");
|
||||
return nslots_moved;
|
||||
}
|
||||
|
||||
bool redis_migrate::check_nodes_id(acl::redis& from, acl::redis& to)
|
||||
{
|
||||
if (from_id_.empty() && redis_util::get_node_id(from, from_id_) == false)
|
||||
{
|
||||
printf("can't get source node id, addr: %s\r\n",
|
||||
from.get_client_addr());
|
||||
return false;
|
||||
}
|
||||
if (to_id_.empty() && redis_util::get_node_id(to, to_id_) == false)
|
||||
{
|
||||
printf("can't get target node id, addr: %s\r\n",
|
||||
to.get_client_addr());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int redis_migrate::move_slots(acl::redis_node& from,
|
||||
acl::redis_node& to, int count)
|
||||
{
|
||||
acl::redis_client from_conn(from.get_addr());
|
||||
acl::redis from_redis(&from_conn);
|
||||
acl::redis_client to_conn(to.get_addr());
|
||||
acl::redis to_redis(&to_conn);
|
||||
|
||||
// get all the specified source node's slots
|
||||
const std::vector<std::pair<size_t, size_t> >& slots = from.get_slots();
|
||||
return move_slots(from_redis, to_redis, count, slots);
|
||||
}
|
||||
|
||||
int redis_migrate::move_slots(acl::redis& from, acl::redis& to, int count,
|
||||
const std::vector<std::pair<size_t, size_t> >& slots)
|
||||
{
|
||||
std::vector<std::pair<size_t, size_t> >::const_iterator cit;
|
||||
int nslots_moved = 0;
|
||||
|
||||
// iterate the source node's slots, and move them to the target
|
||||
for (cit = slots.begin(); cit != slots.end(); ++cit)
|
||||
{
|
||||
size_t min = cit->first;
|
||||
size_t max = cit->second;
|
||||
int ret = move_slots(from, to, count, min, max);
|
||||
if (ret < 0)
|
||||
return -1;
|
||||
nslots_moved += ret;
|
||||
}
|
||||
|
||||
return nslots_moved;
|
||||
}
|
||||
|
||||
int redis_migrate::move_slots(acl::redis& from, acl::redis& to, int count,
|
||||
size_t min_slot, size_t max_slot)
|
||||
{
|
||||
int nslots_moved = 0;
|
||||
|
||||
for (size_t slot = min_slot; slot <= max_slot; slot++)
|
||||
{
|
||||
// move the specified slot from source to target
|
||||
if (move_slot(slot, from, to) == false)
|
||||
{
|
||||
printf("move slots error, slot: %d\r\n",
|
||||
(int) slot);
|
||||
return -1;
|
||||
}
|
||||
nslots_moved++;
|
||||
|
||||
// if the specified number slots have been moved ?
|
||||
if (nslots_moved >= count)
|
||||
return nslots_moved;
|
||||
}
|
||||
return nslots_moved;
|
||||
}
|
||||
|
||||
bool redis_migrate::move_slot(size_t slot, acl::redis& from, acl::redis& to)
|
||||
{
|
||||
if (check_nodes_id(from, to) == false)
|
||||
return false;
|
||||
|
||||
// set the slot in migrating status for the source node
|
||||
if (from.cluster_setslot_migrating(slot, to_id_.c_str()) == false)
|
||||
return false;
|
||||
// set the slot in importing status for the target node
|
||||
if (to.cluster_setslot_importing(slot, from_id_.c_str()) == false)
|
||||
return false;
|
||||
|
||||
// the number of keys to be moved in each moving
|
||||
size_t max = 1000;
|
||||
std::list<acl::string> keys;
|
||||
|
||||
std::list<acl::string>::const_iterator cit;
|
||||
|
||||
while (true)
|
||||
{
|
||||
keys.clear();
|
||||
int nkeys = from.cluster_getkeysinslot(slot, max, keys);
|
||||
if (nkeys == 0)
|
||||
break;
|
||||
if (nkeys < 0)
|
||||
{
|
||||
printf("cluster_getkeysinslot error: %s\r\n",
|
||||
from.result_error());
|
||||
return false;
|
||||
}
|
||||
|
||||
printf("Moving slot %d from %s to %s: ", (int) slot,
|
||||
from.get_client_addr(), to.get_client_addr());
|
||||
fflush(stdout);
|
||||
|
||||
// move all the keys stored by the specifed key
|
||||
for (cit = keys.begin(); cit != keys.end(); ++cit)
|
||||
{
|
||||
if (move_key((*cit).c_str(), from,
|
||||
to.get_client_addr()) == false)
|
||||
{
|
||||
printf("move key: %s error, from: %s"
|
||||
", to: %s\r\n", (*cit).c_str(),
|
||||
from.get_client_addr(),
|
||||
to.get_client_addr());
|
||||
return false;
|
||||
}
|
||||
|
||||
putchar('.');
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
printf("\r\n");
|
||||
}
|
||||
|
||||
return notify_cluster(slot, to_id_.c_str());
|
||||
}
|
||||
|
||||
bool redis_migrate::move_key(const char* key, acl::redis& from,
|
||||
const char* to_addr)
|
||||
{
|
||||
bool ret = from.migrate(key, to_addr, 0, 15000);
|
||||
if (ret == true)
|
||||
return true;
|
||||
acl::string error(from.result_error());
|
||||
if (error.find("BUSYKEY", false) == NULL)
|
||||
{
|
||||
printf("move key: %s error: %s, from: %s, to: %s\r\n",
|
||||
key, error.c_str(), from.get_client_addr(), to_addr);
|
||||
return false;
|
||||
}
|
||||
|
||||
printf("*** Target key: %s exists, Replace it fix? yes/no: ", key);
|
||||
char buf[256];
|
||||
int n = acl_vstream_gets_nonl(ACL_VSTREAM_IN, buf, sizeof(buf));
|
||||
if (n == ACL_VSTREAM_EOF)
|
||||
{
|
||||
printf("Input error, key: %s\r\n", key);
|
||||
return false;
|
||||
}
|
||||
|
||||
#define NQ(x, y) strcasecmp((x), (y))
|
||||
|
||||
if (NQ(buf, "yes") && NQ(buf, "y") && NQ(buf, "true"))
|
||||
{
|
||||
printf("No replace key: %s in target: %s\r\n", key, to_addr);
|
||||
return false;
|
||||
}
|
||||
|
||||
ret = from.migrate(key, to_addr, 0, 15000, "REPLACE");
|
||||
if (ret == true)
|
||||
return true;
|
||||
|
||||
printf("move key: %s error: %s, from: %s, to: %s\r\n",
|
||||
key, from.result_error(), from.get_client_addr(), to_addr);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool redis_migrate::notify_cluster(size_t slot, const char* id)
|
||||
{
|
||||
acl::redis redis;
|
||||
std::vector<acl::redis_node*>::const_iterator cit;
|
||||
|
||||
for (cit = masters_.begin(); cit != masters_.end(); ++cit)
|
||||
{
|
||||
acl::redis_client client((*cit)->get_addr());
|
||||
redis.set_client(&client);
|
||||
redis.clear();
|
||||
|
||||
if (redis.cluster_setslot_node(slot, id) == false)
|
||||
{
|
||||
printf("cluster_setslot_node error: %s, slot: %d, "
|
||||
"addr: %s\r\n", redis.result_error(),
|
||||
(int) slot, (*cit)->get_addr());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
printf("Notify all: slot %d, moved to %s ok\r\n", (int) slot, id);
|
||||
return true;
|
||||
}
|
||||
#include "stdafx.h"
|
||||
#include "redis_util.h"
|
||||
#include "redis_migrate.h"
|
||||
|
||||
redis_migrate::redis_migrate(std::vector<acl::redis_node*>& masters)
|
||||
: masters_(masters)
|
||||
{
|
||||
}
|
||||
|
||||
redis_migrate::~redis_migrate(void)
|
||||
{
|
||||
}
|
||||
|
||||
int redis_migrate::move_slots(std::vector<acl::redis_node*>& from,
|
||||
acl::redis_node& to, int count)
|
||||
{
|
||||
assert(!from.empty());
|
||||
|
||||
int base = count / (int) from.size(), nslots_moved = 0;
|
||||
int first = base + count % (int) from.size(), ret;
|
||||
|
||||
std::vector<acl::redis_node*>::iterator it;
|
||||
for (it = from.begin(); it != from.end(); ++it)
|
||||
{
|
||||
// move some slots from one source node to the target node
|
||||
if (it == from.begin())
|
||||
ret = move_slots(**it, to, first);
|
||||
else
|
||||
ret = move_slots(**it, to, base);
|
||||
if (ret < 0)
|
||||
{
|
||||
printf("move failed, stop!\r\n");
|
||||
break;
|
||||
}
|
||||
|
||||
nslots_moved += ret;
|
||||
|
||||
// check if all the slots have been moved
|
||||
if (nslots_moved >= count)
|
||||
{
|
||||
printf("moved %d slots ok\r\n", nslots_moved);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
printf("move over!\r\n");
|
||||
return nslots_moved;
|
||||
}
|
||||
|
||||
bool redis_migrate::check_nodes_id(acl::redis& from, acl::redis& to)
|
||||
{
|
||||
if (from_id_.empty() && redis_util::get_node_id(from, from_id_) == false)
|
||||
{
|
||||
printf("can't get source node id, addr: %s\r\n",
|
||||
from.get_client_addr());
|
||||
return false;
|
||||
}
|
||||
if (to_id_.empty() && redis_util::get_node_id(to, to_id_) == false)
|
||||
{
|
||||
printf("can't get target node id, addr: %s\r\n",
|
||||
to.get_client_addr());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int redis_migrate::move_slots(acl::redis_node& from,
|
||||
acl::redis_node& to, int count)
|
||||
{
|
||||
acl::redis_client from_conn(from.get_addr());
|
||||
acl::redis from_redis(&from_conn);
|
||||
acl::redis_client to_conn(to.get_addr());
|
||||
acl::redis to_redis(&to_conn);
|
||||
|
||||
// get all the specified source node's slots
|
||||
const std::vector<std::pair<size_t, size_t> >& slots = from.get_slots();
|
||||
return move_slots(from_redis, to_redis, count, slots);
|
||||
}
|
||||
|
||||
int redis_migrate::move_slots(acl::redis& from, acl::redis& to, int count,
|
||||
const std::vector<std::pair<size_t, size_t> >& slots)
|
||||
{
|
||||
std::vector<std::pair<size_t, size_t> >::const_iterator cit;
|
||||
int nslots_moved = 0;
|
||||
|
||||
// iterate the source node's slots, and move them to the target
|
||||
for (cit = slots.begin(); cit != slots.end(); ++cit)
|
||||
{
|
||||
size_t min = cit->first;
|
||||
size_t max = cit->second;
|
||||
int ret = move_slots(from, to, count, min, max);
|
||||
if (ret < 0)
|
||||
return -1;
|
||||
nslots_moved += ret;
|
||||
}
|
||||
|
||||
return nslots_moved;
|
||||
}
|
||||
|
||||
int redis_migrate::move_slots(acl::redis& from, acl::redis& to, int count,
|
||||
size_t min_slot, size_t max_slot)
|
||||
{
|
||||
int nslots_moved = 0;
|
||||
|
||||
for (size_t slot = min_slot; slot <= max_slot; slot++)
|
||||
{
|
||||
// move the specified slot from source to target
|
||||
if (move_slot(slot, from, to) == false)
|
||||
{
|
||||
printf("move slots error, slot: %d\r\n",
|
||||
(int) slot);
|
||||
return -1;
|
||||
}
|
||||
nslots_moved++;
|
||||
|
||||
// if the specified number slots have been moved ?
|
||||
if (nslots_moved >= count)
|
||||
return nslots_moved;
|
||||
}
|
||||
return nslots_moved;
|
||||
}
|
||||
|
||||
bool redis_migrate::move_slot(size_t slot, acl::redis& from, acl::redis& to)
|
||||
{
|
||||
if (check_nodes_id(from, to) == false)
|
||||
return false;
|
||||
|
||||
// set the slot in migrating status for the source node
|
||||
if (from.cluster_setslot_migrating(slot, to_id_.c_str()) == false)
|
||||
return false;
|
||||
// set the slot in importing status for the target node
|
||||
if (to.cluster_setslot_importing(slot, from_id_.c_str()) == false)
|
||||
return false;
|
||||
|
||||
// the number of keys to be moved in each moving
|
||||
size_t max = 1000;
|
||||
std::list<acl::string> keys;
|
||||
|
||||
std::list<acl::string>::const_iterator cit;
|
||||
|
||||
while (true)
|
||||
{
|
||||
keys.clear();
|
||||
int nkeys = from.cluster_getkeysinslot(slot, max, keys);
|
||||
if (nkeys == 0)
|
||||
break;
|
||||
if (nkeys < 0)
|
||||
{
|
||||
printf("cluster_getkeysinslot error: %s\r\n",
|
||||
from.result_error());
|
||||
return false;
|
||||
}
|
||||
|
||||
printf("Moving slot %d from %s to %s: ", (int) slot,
|
||||
from.get_client_addr(), to.get_client_addr());
|
||||
fflush(stdout);
|
||||
|
||||
// move all the keys stored by the specifed key
|
||||
for (cit = keys.begin(); cit != keys.end(); ++cit)
|
||||
{
|
||||
if (move_key((*cit).c_str(), from,
|
||||
to.get_client_addr()) == false)
|
||||
{
|
||||
printf("move key: %s error, from: %s"
|
||||
", to: %s\r\n", (*cit).c_str(),
|
||||
from.get_client_addr(),
|
||||
to.get_client_addr());
|
||||
return false;
|
||||
}
|
||||
|
||||
putchar('.');
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
printf("\r\n");
|
||||
}
|
||||
|
||||
return notify_cluster(slot, to_id_.c_str());
|
||||
}
|
||||
|
||||
bool redis_migrate::move_key(const char* key, acl::redis& from,
|
||||
const char* to_addr)
|
||||
{
|
||||
bool ret = from.migrate(key, to_addr, 0, 15000);
|
||||
if (ret == true)
|
||||
return true;
|
||||
acl::string error(from.result_error());
|
||||
if (error.find("BUSYKEY", false) == NULL)
|
||||
{
|
||||
printf("move key: %s error: %s, from: %s, to: %s\r\n",
|
||||
key, error.c_str(), from.get_client_addr(), to_addr);
|
||||
return false;
|
||||
}
|
||||
|
||||
printf("*** Target key: %s exists, Replace it fix? yes/no: ", key);
|
||||
char buf[256];
|
||||
int n = acl_vstream_gets_nonl(ACL_VSTREAM_IN, buf, sizeof(buf));
|
||||
if (n == ACL_VSTREAM_EOF)
|
||||
{
|
||||
printf("Input error, key: %s\r\n", key);
|
||||
return false;
|
||||
}
|
||||
|
||||
#define NQ(x, y) strcasecmp((x), (y))
|
||||
|
||||
if (NQ(buf, "yes") && NQ(buf, "y") && NQ(buf, "true"))
|
||||
{
|
||||
printf("No replace key: %s in target: %s\r\n", key, to_addr);
|
||||
return false;
|
||||
}
|
||||
|
||||
ret = from.migrate(key, to_addr, 0, 15000, "REPLACE");
|
||||
if (ret == true)
|
||||
return true;
|
||||
|
||||
printf("move key: %s error: %s, from: %s, to: %s\r\n",
|
||||
key, from.result_error(), from.get_client_addr(), to_addr);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool redis_migrate::notify_cluster(size_t slot, const char* id)
|
||||
{
|
||||
acl::redis redis;
|
||||
std::vector<acl::redis_node*>::const_iterator cit;
|
||||
|
||||
for (cit = masters_.begin(); cit != masters_.end(); ++cit)
|
||||
{
|
||||
acl::redis_client client((*cit)->get_addr());
|
||||
redis.set_client(&client);
|
||||
redis.clear();
|
||||
|
||||
if (redis.cluster_setslot_node(slot, id) == false)
|
||||
{
|
||||
printf("cluster_setslot_node error: %s, slot: %d, "
|
||||
"addr: %s\r\n", redis.result_error(),
|
||||
(int) slot, (*cit)->get_addr());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
printf("Notify all: slot %d, moved to %s ok\r\n", (int) slot, id);
|
||||
return true;
|
||||
}
|
||||
|
@ -1,181 +1,181 @@
|
||||
#include "stdafx.h"
|
||||
#include "redis_util.h"
|
||||
#include "redis_migrate.h"
|
||||
#include "redis_reshard.h"
|
||||
|
||||
redis_reshard::redis_reshard(const char* addr)
|
||||
: addr_(addr)
|
||||
{
|
||||
}
|
||||
|
||||
redis_reshard::~redis_reshard()
|
||||
{
|
||||
std::vector<acl::redis_node*>::iterator it = masters_.begin();
|
||||
for (; it != masters_.end(); ++it)
|
||||
delete *it;
|
||||
}
|
||||
|
||||
void redis_reshard::copy_slots(acl::redis_node& from, acl::redis_node& to)
|
||||
{
|
||||
const std::vector<std::pair<size_t, size_t> >& slots = from.get_slots();
|
||||
std::vector<std::pair<size_t, size_t> >::const_iterator cit;
|
||||
for (cit = slots.begin(); cit != slots.end(); ++cit)
|
||||
to.add_slot_range(cit->first, cit->second);
|
||||
}
|
||||
|
||||
acl::redis_node* redis_reshard::find_node(const char* id)
|
||||
{
|
||||
std::vector<acl::redis_node*>::iterator it;
|
||||
for (it = masters_.begin(); it != masters_.end(); ++it)
|
||||
{
|
||||
if (strcmp(id, (*it)->get_id()) == 0)
|
||||
return *it;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void redis_reshard::copy_all(std::vector<acl::redis_node*>& src,
|
||||
const char* exclude)
|
||||
{
|
||||
std::vector<acl::redis_node*>::iterator it;
|
||||
for (it = masters_.begin(); it != masters_.end(); ++it)
|
||||
{
|
||||
if (strcmp((*it)->get_id(), exclude) != 0)
|
||||
src.push_back(*it);
|
||||
}
|
||||
}
|
||||
|
||||
void redis_reshard::run()
|
||||
{
|
||||
if (get_masters_info() == false)
|
||||
return;
|
||||
|
||||
show_nodes();
|
||||
fflush(stdout);
|
||||
char buf[1024];
|
||||
|
||||
int nslots = 0;
|
||||
while (true)
|
||||
{
|
||||
printf("How many slots do you want to move (from 1 to 16384) ? ");
|
||||
fflush(stdout);
|
||||
int ret = acl_vstream_gets_nonl(ACL_VSTREAM_IN, buf, sizeof(buf));
|
||||
if (ret == ACL_VSTREAM_EOF)
|
||||
exit(1);
|
||||
acl_mystr_trim(buf);
|
||||
nslots = atoi(buf);
|
||||
if (nslots > 0 && nslots < 16384)
|
||||
break;
|
||||
printf("invalid value: %d\r\n", ret);
|
||||
}
|
||||
|
||||
acl::redis_node* target = NULL;
|
||||
while (true)
|
||||
{
|
||||
printf("What is the receiving node ID? ");
|
||||
fflush(stdout);
|
||||
int ret = acl_vstream_gets_nonl(ACL_VSTREAM_IN, buf, sizeof(buf));
|
||||
if (ret == ACL_VSTREAM_EOF)
|
||||
exit(1);
|
||||
|
||||
acl_mystr_trim(buf);
|
||||
target = find_node(buf);
|
||||
if (target != NULL)
|
||||
break;
|
||||
|
||||
printf("...The specified node(%s) is not known or not "
|
||||
"a master, please try again.\r\n", buf);
|
||||
}
|
||||
assert(target != NULL);
|
||||
|
||||
printf("Please input all the source node IDs.\r\n");
|
||||
printf(" Type 'all' to use all the nodes as source nodes for the hash slots\r\n");
|
||||
printf(" Type 'done' once you entered all the source node IDs.\r\n");
|
||||
|
||||
std::vector<acl::redis_node*> sources;
|
||||
while (true)
|
||||
{
|
||||
printf("Source node #%d: ", (int) sources.size() + 1);
|
||||
fflush(stdout);
|
||||
int ret = acl_vstream_gets_nonl(ACL_VSTREAM_IN, buf, sizeof(buf));
|
||||
if (ret == ACL_VSTREAM_EOF)
|
||||
exit(1);
|
||||
|
||||
acl_mystr_trim(buf);
|
||||
if (strcasecmp(buf, "done") == 0)
|
||||
break;
|
||||
if (strcasecmp(buf, "all") == 0)
|
||||
{
|
||||
copy_all(sources, target->get_id());
|
||||
break;
|
||||
}
|
||||
|
||||
acl::redis_node* source = find_node(buf);
|
||||
if (source == NULL)
|
||||
{
|
||||
printf("...The source node(%s) is not known\r\n", buf);
|
||||
continue;
|
||||
}
|
||||
if (strcmp(target->get_id(), buf) == 0)
|
||||
{
|
||||
printf("... It is not possible to use the target node as source node\r\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
sources.push_back(source);
|
||||
}
|
||||
if (sources.empty())
|
||||
{
|
||||
printf("*** No source nodes given, operation aborted\r\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
redis_migrate migrate(masters_);
|
||||
migrate.move_slots(sources, *target, nslots);
|
||||
}
|
||||
|
||||
bool redis_reshard::get_masters_info()
|
||||
{
|
||||
acl::redis_client client(addr_, 30, 30);
|
||||
acl::redis redis(&client);
|
||||
|
||||
const std::map<acl::string, acl::redis_node*>* masters;
|
||||
if ((masters = redis.cluster_nodes()) == NULL)
|
||||
{
|
||||
printf("%s: master nodes empty\r\n", __FUNCTION__);
|
||||
return false;
|
||||
}
|
||||
|
||||
std::map<acl::string, acl::redis_node*>::const_iterator cit;
|
||||
for (cit = masters->begin(); cit != masters->end(); ++cit)
|
||||
{
|
||||
acl::redis_node* master = new acl::redis_node;
|
||||
master->set_id(cit->second->get_id());
|
||||
master->set_addr(cit->second->get_addr());
|
||||
copy_slots(*cit->second, *master);
|
||||
masters_.push_back(master);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void redis_reshard::show_nodes()
|
||||
{
|
||||
std::vector<acl::redis_node*>::const_iterator cit;
|
||||
for (cit = masters_.begin(); cit != masters_.end(); ++cit)
|
||||
{
|
||||
printf("-----------------------------------------------\r\n");
|
||||
printf("addr: %s\r\nid: %s\r\n", (*cit)->get_addr(),
|
||||
(*cit)->get_id());
|
||||
show_slots(**cit);
|
||||
}
|
||||
}
|
||||
|
||||
void redis_reshard::show_slots(const acl::redis_node& node)
|
||||
{
|
||||
const std::vector<std::pair<size_t, size_t> > slots = node.get_slots();
|
||||
std::vector<std::pair<size_t, size_t> >::const_iterator cit;
|
||||
for (cit = slots.begin(); cit != slots.end(); ++cit)
|
||||
printf("slots: %d - %d\r\n",
|
||||
(int) cit->first, (int) cit->second);
|
||||
}
|
||||
#include "stdafx.h"
|
||||
#include "redis_util.h"
|
||||
#include "redis_migrate.h"
|
||||
#include "redis_reshard.h"
|
||||
|
||||
redis_reshard::redis_reshard(const char* addr)
|
||||
: addr_(addr)
|
||||
{
|
||||
}
|
||||
|
||||
redis_reshard::~redis_reshard()
|
||||
{
|
||||
std::vector<acl::redis_node*>::iterator it = masters_.begin();
|
||||
for (; it != masters_.end(); ++it)
|
||||
delete *it;
|
||||
}
|
||||
|
||||
void redis_reshard::copy_slots(acl::redis_node& from, acl::redis_node& to)
|
||||
{
|
||||
const std::vector<std::pair<size_t, size_t> >& slots = from.get_slots();
|
||||
std::vector<std::pair<size_t, size_t> >::const_iterator cit;
|
||||
for (cit = slots.begin(); cit != slots.end(); ++cit)
|
||||
to.add_slot_range(cit->first, cit->second);
|
||||
}
|
||||
|
||||
acl::redis_node* redis_reshard::find_node(const char* id)
|
||||
{
|
||||
std::vector<acl::redis_node*>::iterator it;
|
||||
for (it = masters_.begin(); it != masters_.end(); ++it)
|
||||
{
|
||||
if (strcmp(id, (*it)->get_id()) == 0)
|
||||
return *it;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void redis_reshard::copy_all(std::vector<acl::redis_node*>& src,
|
||||
const char* exclude)
|
||||
{
|
||||
std::vector<acl::redis_node*>::iterator it;
|
||||
for (it = masters_.begin(); it != masters_.end(); ++it)
|
||||
{
|
||||
if (strcmp((*it)->get_id(), exclude) != 0)
|
||||
src.push_back(*it);
|
||||
}
|
||||
}
|
||||
|
||||
void redis_reshard::run()
|
||||
{
|
||||
if (get_masters_info() == false)
|
||||
return;
|
||||
|
||||
show_nodes();
|
||||
fflush(stdout);
|
||||
char buf[1024];
|
||||
|
||||
int nslots = 0;
|
||||
while (true)
|
||||
{
|
||||
printf("How many slots do you want to move (from 1 to 16384) ? ");
|
||||
fflush(stdout);
|
||||
int ret = acl_vstream_gets_nonl(ACL_VSTREAM_IN, buf, sizeof(buf));
|
||||
if (ret == ACL_VSTREAM_EOF)
|
||||
exit(1);
|
||||
acl_mystr_trim(buf);
|
||||
nslots = atoi(buf);
|
||||
if (nslots > 0 && nslots < 16384)
|
||||
break;
|
||||
printf("invalid value: %d\r\n", ret);
|
||||
}
|
||||
|
||||
acl::redis_node* target = NULL;
|
||||
while (true)
|
||||
{
|
||||
printf("What is the receiving node ID? ");
|
||||
fflush(stdout);
|
||||
int ret = acl_vstream_gets_nonl(ACL_VSTREAM_IN, buf, sizeof(buf));
|
||||
if (ret == ACL_VSTREAM_EOF)
|
||||
exit(1);
|
||||
|
||||
acl_mystr_trim(buf);
|
||||
target = find_node(buf);
|
||||
if (target != NULL)
|
||||
break;
|
||||
|
||||
printf("...The specified node(%s) is not known or not "
|
||||
"a master, please try again.\r\n", buf);
|
||||
}
|
||||
assert(target != NULL);
|
||||
|
||||
printf("Please input all the source node IDs.\r\n");
|
||||
printf(" Type 'all' to use all the nodes as source nodes for the hash slots\r\n");
|
||||
printf(" Type 'done' once you entered all the source node IDs.\r\n");
|
||||
|
||||
std::vector<acl::redis_node*> sources;
|
||||
while (true)
|
||||
{
|
||||
printf("Source node #%d: ", (int) sources.size() + 1);
|
||||
fflush(stdout);
|
||||
int ret = acl_vstream_gets_nonl(ACL_VSTREAM_IN, buf, sizeof(buf));
|
||||
if (ret == ACL_VSTREAM_EOF)
|
||||
exit(1);
|
||||
|
||||
acl_mystr_trim(buf);
|
||||
if (strcasecmp(buf, "done") == 0)
|
||||
break;
|
||||
if (strcasecmp(buf, "all") == 0)
|
||||
{
|
||||
copy_all(sources, target->get_id());
|
||||
break;
|
||||
}
|
||||
|
||||
acl::redis_node* source = find_node(buf);
|
||||
if (source == NULL)
|
||||
{
|
||||
printf("...The source node(%s) is not known\r\n", buf);
|
||||
continue;
|
||||
}
|
||||
if (strcmp(target->get_id(), buf) == 0)
|
||||
{
|
||||
printf("... It is not possible to use the target node as source node\r\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
sources.push_back(source);
|
||||
}
|
||||
if (sources.empty())
|
||||
{
|
||||
printf("*** No source nodes given, operation aborted\r\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
redis_migrate migrate(masters_);
|
||||
migrate.move_slots(sources, *target, nslots);
|
||||
}
|
||||
|
||||
bool redis_reshard::get_masters_info()
|
||||
{
|
||||
acl::redis_client client(addr_, 30, 30);
|
||||
acl::redis redis(&client);
|
||||
|
||||
const std::map<acl::string, acl::redis_node*>* masters;
|
||||
if ((masters = redis.cluster_nodes()) == NULL)
|
||||
{
|
||||
printf("%s: master nodes empty\r\n", __FUNCTION__);
|
||||
return false;
|
||||
}
|
||||
|
||||
std::map<acl::string, acl::redis_node*>::const_iterator cit;
|
||||
for (cit = masters->begin(); cit != masters->end(); ++cit)
|
||||
{
|
||||
acl::redis_node* master = new acl::redis_node;
|
||||
master->set_id(cit->second->get_id());
|
||||
master->set_addr(cit->second->get_addr());
|
||||
copy_slots(*cit->second, *master);
|
||||
masters_.push_back(master);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void redis_reshard::show_nodes()
|
||||
{
|
||||
std::vector<acl::redis_node*>::const_iterator cit;
|
||||
for (cit = masters_.begin(); cit != masters_.end(); ++cit)
|
||||
{
|
||||
printf("-----------------------------------------------\r\n");
|
||||
printf("addr: %s\r\nid: %s\r\n", (*cit)->get_addr(),
|
||||
(*cit)->get_id());
|
||||
show_slots(**cit);
|
||||
}
|
||||
}
|
||||
|
||||
void redis_reshard::show_slots(const acl::redis_node& node)
|
||||
{
|
||||
const std::vector<std::pair<size_t, size_t> > slots = node.get_slots();
|
||||
std::vector<std::pair<size_t, size_t> >::const_iterator cit;
|
||||
for (cit = slots.begin(); cit != slots.end(); ++cit)
|
||||
printf("slots: %d - %d\r\n",
|
||||
(int) cit->first, (int) cit->second);
|
||||
}
|
||||
|
@ -1,22 +1,22 @@
|
||||
#pragma once
|
||||
#include <vector>
|
||||
|
||||
class redis_reshard
|
||||
{
|
||||
public:
|
||||
redis_reshard(const char* addr);
|
||||
~redis_reshard();
|
||||
|
||||
void run();
|
||||
|
||||
private:
|
||||
acl::string addr_;
|
||||
std::vector<acl::redis_node*> masters_;
|
||||
|
||||
acl::redis_node* find_node(const char* id);
|
||||
bool get_masters_info();
|
||||
void copy_all(std::vector<acl::redis_node*>& src, const char* exclude);
|
||||
void show_nodes();
|
||||
void show_slots(const acl::redis_node& node);
|
||||
void copy_slots(acl::redis_node& from, acl::redis_node& to);
|
||||
};
|
||||
#pragma once
|
||||
#include <vector>
|
||||
|
||||
class redis_reshard
|
||||
{
|
||||
public:
|
||||
redis_reshard(const char* addr);
|
||||
~redis_reshard();
|
||||
|
||||
void run();
|
||||
|
||||
private:
|
||||
acl::string addr_;
|
||||
std::vector<acl::redis_node*> masters_;
|
||||
|
||||
acl::redis_node* find_node(const char* id);
|
||||
bool get_masters_info();
|
||||
void copy_all(std::vector<acl::redis_node*>& src, const char* exclude);
|
||||
void show_nodes();
|
||||
void show_slots(const acl::redis_node& node);
|
||||
void copy_slots(acl::redis_node& from, acl::redis_node& to);
|
||||
};
|
||||
|
@ -1,77 +1,77 @@
|
||||
#ifndef __MY_RFC1035_INCLUDE_H__
|
||||
#define __MY_RFC1035_INCLUDE_H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "stdlib/acl_define.h"
|
||||
#ifdef ACL_UNIX
|
||||
#include <netinet/in.h>
|
||||
#endif
|
||||
|
||||
/* rfc1035 - DNS */
|
||||
#define RFC1035_MAXHOSTNAMESZ 256
|
||||
|
||||
typedef struct rfc1035_rr {
|
||||
char name[RFC1035_MAXHOSTNAMESZ];
|
||||
unsigned short type;
|
||||
unsigned short tclass; /* class */
|
||||
unsigned int ttl;
|
||||
unsigned short rdlength;
|
||||
char *rdata;
|
||||
} rfc1035_rr;
|
||||
|
||||
typedef struct rfc1035_query {
|
||||
char name[RFC1035_MAXHOSTNAMESZ];
|
||||
unsigned short qtype;
|
||||
unsigned short qclass;
|
||||
} rfc1035_query;
|
||||
|
||||
typedef struct rfc1035_message {
|
||||
unsigned short id;
|
||||
unsigned int qr:1;
|
||||
unsigned int opcode:4;
|
||||
unsigned int aa:1;
|
||||
unsigned int tc:1;
|
||||
unsigned int rd:1;
|
||||
unsigned int ra:1;
|
||||
unsigned int rcode:4;
|
||||
unsigned short qdcount;
|
||||
unsigned short ancount;
|
||||
unsigned short nscount;
|
||||
unsigned short arcount;
|
||||
rfc1035_query *query;
|
||||
rfc1035_rr *answer;
|
||||
} rfc1035_message;
|
||||
|
||||
const char *rfc1035Strerror(int errnum);
|
||||
ssize_t rfc1035BuildAQuery(const char *hostname, char *buf, size_t sz,
|
||||
unsigned short qid, rfc1035_query * query);
|
||||
ssize_t rfc1035BuildPTRQuery(const struct in_addr, char *buf, size_t sz,
|
||||
unsigned short qid, rfc1035_query * query);
|
||||
void rfc1035SetQueryID(char *, unsigned short qid);
|
||||
int rfc1035MessageUnpack(const char *buf, size_t sz,
|
||||
rfc1035_message ** answer);
|
||||
int rfc1035QueryCompare(const rfc1035_query *, const rfc1035_query *);
|
||||
void rfc1035MessageDestroy(rfc1035_message * message);
|
||||
ssize_t rfc1035BuildAReply(const char *hostname, const ACL_ARGV *ip_argv,
|
||||
const char *dnsname, const char *dns_ip,
|
||||
unsigned short qid, char *buf, size_t sz);
|
||||
|
||||
extern int rfc1035_errno;
|
||||
extern const char *rfc1035_error_message;
|
||||
|
||||
#define RFC1035_TYPE_A 1
|
||||
#define RFC1035_TYPE_NS 2
|
||||
#define RFC1035_TYPE_CNAME 5
|
||||
#define RFC1035_TYPE_PTR 12
|
||||
#define RFC1035_TYPE_AAAA 28
|
||||
#define RFC1035_CLASS_IN 1
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#ifndef __MY_RFC1035_INCLUDE_H__
|
||||
#define __MY_RFC1035_INCLUDE_H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "stdlib/acl_define.h"
|
||||
#ifdef ACL_UNIX
|
||||
#include <netinet/in.h>
|
||||
#endif
|
||||
|
||||
/* rfc1035 - DNS */
|
||||
#define RFC1035_MAXHOSTNAMESZ 256
|
||||
|
||||
typedef struct rfc1035_rr {
|
||||
char name[RFC1035_MAXHOSTNAMESZ];
|
||||
unsigned short type;
|
||||
unsigned short tclass; /* class */
|
||||
unsigned int ttl;
|
||||
unsigned short rdlength;
|
||||
char *rdata;
|
||||
} rfc1035_rr;
|
||||
|
||||
typedef struct rfc1035_query {
|
||||
char name[RFC1035_MAXHOSTNAMESZ];
|
||||
unsigned short qtype;
|
||||
unsigned short qclass;
|
||||
} rfc1035_query;
|
||||
|
||||
typedef struct rfc1035_message {
|
||||
unsigned short id;
|
||||
unsigned int qr:1;
|
||||
unsigned int opcode:4;
|
||||
unsigned int aa:1;
|
||||
unsigned int tc:1;
|
||||
unsigned int rd:1;
|
||||
unsigned int ra:1;
|
||||
unsigned int rcode:4;
|
||||
unsigned short qdcount;
|
||||
unsigned short ancount;
|
||||
unsigned short nscount;
|
||||
unsigned short arcount;
|
||||
rfc1035_query *query;
|
||||
rfc1035_rr *answer;
|
||||
} rfc1035_message;
|
||||
|
||||
const char *rfc1035Strerror(int errnum);
|
||||
ssize_t rfc1035BuildAQuery(const char *hostname, char *buf, size_t sz,
|
||||
unsigned short qid, rfc1035_query * query);
|
||||
ssize_t rfc1035BuildPTRQuery(const struct in_addr, char *buf, size_t sz,
|
||||
unsigned short qid, rfc1035_query * query);
|
||||
void rfc1035SetQueryID(char *, unsigned short qid);
|
||||
int rfc1035MessageUnpack(const char *buf, size_t sz,
|
||||
rfc1035_message ** answer);
|
||||
int rfc1035QueryCompare(const rfc1035_query *, const rfc1035_query *);
|
||||
void rfc1035MessageDestroy(rfc1035_message * message);
|
||||
ssize_t rfc1035BuildAReply(const char *hostname, const ACL_ARGV *ip_argv,
|
||||
const char *dnsname, const char *dns_ip,
|
||||
unsigned short qid, char *buf, size_t sz);
|
||||
|
||||
extern int rfc1035_errno;
|
||||
extern const char *rfc1035_error_message;
|
||||
|
||||
#define RFC1035_TYPE_A 1
|
||||
#define RFC1035_TYPE_NS 2
|
||||
#define RFC1035_TYPE_CNAME 5
|
||||
#define RFC1035_TYPE_PTR 12
|
||||
#define RFC1035_TYPE_AAAA 28
|
||||
#define RFC1035_CLASS_IN 1
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,149 +1,149 @@
|
||||
/*
|
||||
Copyright (c) 2009 Dave Gamble
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef cJSON__h
|
||||
#define cJSON__h
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
/* cJSON Types: */
|
||||
#define cJSON_False 0
|
||||
#define cJSON_True 1
|
||||
#define cJSON_NULL 2
|
||||
#define cJSON_Number 3
|
||||
#define cJSON_String 4
|
||||
#define cJSON_Array 5
|
||||
#define cJSON_Object 6
|
||||
|
||||
#define cJSON_IsReference 256
|
||||
#define cJSON_StringIsConst 512
|
||||
|
||||
/* The cJSON structure: */
|
||||
typedef struct cJSON {
|
||||
struct cJSON *next,*prev; /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */
|
||||
struct cJSON *child; /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */
|
||||
|
||||
int type; /* The type of the item, as above. */
|
||||
|
||||
char *valuestring; /* The item's string, if type==cJSON_String */
|
||||
int valueint; /* The item's number, if type==cJSON_Number */
|
||||
double valuedouble; /* The item's number, if type==cJSON_Number */
|
||||
|
||||
char *string; /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */
|
||||
} cJSON;
|
||||
|
||||
typedef struct cJSON_Hooks {
|
||||
void *(*malloc_fn)(size_t sz);
|
||||
void (*free_fn)(void *ptr);
|
||||
} cJSON_Hooks;
|
||||
|
||||
/* Supply malloc, realloc and free functions to cJSON */
|
||||
extern void cJSON_InitHooks(cJSON_Hooks* hooks);
|
||||
|
||||
|
||||
/* Supply a block of JSON, and this returns a cJSON object you can interrogate. Call cJSON_Delete when finished. */
|
||||
extern cJSON *cJSON_Parse(const char *value);
|
||||
/* Render a cJSON entity to text for transfer/storage. Free the char* when finished. */
|
||||
extern char *cJSON_Print(cJSON *item);
|
||||
/* Render a cJSON entity to text for transfer/storage without any formatting. Free the char* when finished. */
|
||||
extern char *cJSON_PrintUnformatted(cJSON *item);
|
||||
/* Render a cJSON entity to text using a buffered strategy. prebuffer is a guess at the final size. guessing well reduces reallocation. fmt=0 gives unformatted, =1 gives formatted */
|
||||
extern char *cJSON_PrintBuffered(cJSON *item,int prebuffer,int fmt);
|
||||
/* Delete a cJSON entity and all subentities. */
|
||||
extern void cJSON_Delete(cJSON *c);
|
||||
|
||||
/* Returns the number of items in an array (or object). */
|
||||
extern int cJSON_GetArraySize(cJSON *array);
|
||||
/* Retrieve item number "item" from array "array". Returns NULL if unsuccessful. */
|
||||
extern cJSON *cJSON_GetArrayItem(cJSON *array,int item);
|
||||
/* Get item "string" from object. Case insensitive. */
|
||||
extern cJSON *cJSON_GetObjectItem(cJSON *object,const char *string);
|
||||
|
||||
/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */
|
||||
extern const char *cJSON_GetErrorPtr(void);
|
||||
|
||||
/* These calls create a cJSON item of the appropriate type. */
|
||||
extern cJSON *cJSON_CreateNull(void);
|
||||
extern cJSON *cJSON_CreateTrue(void);
|
||||
extern cJSON *cJSON_CreateFalse(void);
|
||||
extern cJSON *cJSON_CreateBool(int b);
|
||||
extern cJSON *cJSON_CreateNumber(double num);
|
||||
extern cJSON *cJSON_CreateString(const char *string);
|
||||
extern cJSON *cJSON_CreateArray(void);
|
||||
extern cJSON *cJSON_CreateObject(void);
|
||||
|
||||
/* These utilities create an Array of count items. */
|
||||
extern cJSON *cJSON_CreateIntArray(const int *numbers,int count);
|
||||
extern cJSON *cJSON_CreateFloatArray(const float *numbers,int count);
|
||||
extern cJSON *cJSON_CreateDoubleArray(const double *numbers,int count);
|
||||
extern cJSON *cJSON_CreateStringArray(const char **strings,int count);
|
||||
|
||||
/* Append item to the specified array/object. */
|
||||
extern void cJSON_AddItemToArray(cJSON *array, cJSON *item);
|
||||
extern void cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item);
|
||||
extern void cJSON_AddItemToObjectCS(cJSON *object,const char *string,cJSON *item); /* Use this when string is definitely const (i.e. a literal, or as good as), and will definitely survive the cJSON object */
|
||||
/* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */
|
||||
extern void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item);
|
||||
extern void cJSON_AddItemReferenceToObject(cJSON *object,const char *string,cJSON *item);
|
||||
|
||||
/* Remove/Detatch items from Arrays/Objects. */
|
||||
extern cJSON *cJSON_DetachItemFromArray(cJSON *array,int which);
|
||||
extern void cJSON_DeleteItemFromArray(cJSON *array,int which);
|
||||
extern cJSON *cJSON_DetachItemFromObject(cJSON *object,const char *string);
|
||||
extern void cJSON_DeleteItemFromObject(cJSON *object,const char *string);
|
||||
|
||||
/* Update array items. */
|
||||
extern void cJSON_InsertItemInArray(cJSON *array,int which,cJSON *newitem); /* Shifts pre-existing items to the right. */
|
||||
extern void cJSON_ReplaceItemInArray(cJSON *array,int which,cJSON *newitem);
|
||||
extern void cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem);
|
||||
|
||||
/* Duplicate a cJSON item */
|
||||
extern cJSON *cJSON_Duplicate(cJSON *item,int recurse);
|
||||
/* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will
|
||||
need to be released. With recurse!=0, it will duplicate any children connected to the item.
|
||||
The item->next and ->prev pointers are always zero on return from Duplicate. */
|
||||
|
||||
/* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */
|
||||
extern cJSON *cJSON_ParseWithOpts(const char *value,const char **return_parse_end,int require_null_terminated);
|
||||
|
||||
extern void cJSON_Minify(char *json);
|
||||
|
||||
/* Macros for creating things quickly. */
|
||||
#define cJSON_AddNullToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateNull())
|
||||
#define cJSON_AddTrueToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateTrue())
|
||||
#define cJSON_AddFalseToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateFalse())
|
||||
#define cJSON_AddBoolToObject(object,name,b) cJSON_AddItemToObject(object, name, cJSON_CreateBool(b))
|
||||
#define cJSON_AddNumberToObject(object,name,n) cJSON_AddItemToObject(object, name, cJSON_CreateNumber(n))
|
||||
#define cJSON_AddStringToObject(object,name,s) cJSON_AddItemToObject(object, name, cJSON_CreateString(s))
|
||||
|
||||
/* When assigning an integer value, it needs to be propagated to valuedouble too. */
|
||||
#define cJSON_SetIntValue(object,val) ((object)?(object)->valueint=(object)->valuedouble=(val):(val))
|
||||
#define cJSON_SetNumberValue(object,val) ((object)?(object)->valueint=(object)->valuedouble=(val):(val))
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
/*
|
||||
Copyright (c) 2009 Dave Gamble
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef cJSON__h
|
||||
#define cJSON__h
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
/* cJSON Types: */
|
||||
#define cJSON_False 0
|
||||
#define cJSON_True 1
|
||||
#define cJSON_NULL 2
|
||||
#define cJSON_Number 3
|
||||
#define cJSON_String 4
|
||||
#define cJSON_Array 5
|
||||
#define cJSON_Object 6
|
||||
|
||||
#define cJSON_IsReference 256
|
||||
#define cJSON_StringIsConst 512
|
||||
|
||||
/* The cJSON structure: */
|
||||
typedef struct cJSON {
|
||||
struct cJSON *next,*prev; /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */
|
||||
struct cJSON *child; /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */
|
||||
|
||||
int type; /* The type of the item, as above. */
|
||||
|
||||
char *valuestring; /* The item's string, if type==cJSON_String */
|
||||
int valueint; /* The item's number, if type==cJSON_Number */
|
||||
double valuedouble; /* The item's number, if type==cJSON_Number */
|
||||
|
||||
char *string; /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */
|
||||
} cJSON;
|
||||
|
||||
typedef struct cJSON_Hooks {
|
||||
void *(*malloc_fn)(size_t sz);
|
||||
void (*free_fn)(void *ptr);
|
||||
} cJSON_Hooks;
|
||||
|
||||
/* Supply malloc, realloc and free functions to cJSON */
|
||||
extern void cJSON_InitHooks(cJSON_Hooks* hooks);
|
||||
|
||||
|
||||
/* Supply a block of JSON, and this returns a cJSON object you can interrogate. Call cJSON_Delete when finished. */
|
||||
extern cJSON *cJSON_Parse(const char *value);
|
||||
/* Render a cJSON entity to text for transfer/storage. Free the char* when finished. */
|
||||
extern char *cJSON_Print(cJSON *item);
|
||||
/* Render a cJSON entity to text for transfer/storage without any formatting. Free the char* when finished. */
|
||||
extern char *cJSON_PrintUnformatted(cJSON *item);
|
||||
/* Render a cJSON entity to text using a buffered strategy. prebuffer is a guess at the final size. guessing well reduces reallocation. fmt=0 gives unformatted, =1 gives formatted */
|
||||
extern char *cJSON_PrintBuffered(cJSON *item,int prebuffer,int fmt);
|
||||
/* Delete a cJSON entity and all subentities. */
|
||||
extern void cJSON_Delete(cJSON *c);
|
||||
|
||||
/* Returns the number of items in an array (or object). */
|
||||
extern int cJSON_GetArraySize(cJSON *array);
|
||||
/* Retrieve item number "item" from array "array". Returns NULL if unsuccessful. */
|
||||
extern cJSON *cJSON_GetArrayItem(cJSON *array,int item);
|
||||
/* Get item "string" from object. Case insensitive. */
|
||||
extern cJSON *cJSON_GetObjectItem(cJSON *object,const char *string);
|
||||
|
||||
/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */
|
||||
extern const char *cJSON_GetErrorPtr(void);
|
||||
|
||||
/* These calls create a cJSON item of the appropriate type. */
|
||||
extern cJSON *cJSON_CreateNull(void);
|
||||
extern cJSON *cJSON_CreateTrue(void);
|
||||
extern cJSON *cJSON_CreateFalse(void);
|
||||
extern cJSON *cJSON_CreateBool(int b);
|
||||
extern cJSON *cJSON_CreateNumber(double num);
|
||||
extern cJSON *cJSON_CreateString(const char *string);
|
||||
extern cJSON *cJSON_CreateArray(void);
|
||||
extern cJSON *cJSON_CreateObject(void);
|
||||
|
||||
/* These utilities create an Array of count items. */
|
||||
extern cJSON *cJSON_CreateIntArray(const int *numbers,int count);
|
||||
extern cJSON *cJSON_CreateFloatArray(const float *numbers,int count);
|
||||
extern cJSON *cJSON_CreateDoubleArray(const double *numbers,int count);
|
||||
extern cJSON *cJSON_CreateStringArray(const char **strings,int count);
|
||||
|
||||
/* Append item to the specified array/object. */
|
||||
extern void cJSON_AddItemToArray(cJSON *array, cJSON *item);
|
||||
extern void cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item);
|
||||
extern void cJSON_AddItemToObjectCS(cJSON *object,const char *string,cJSON *item); /* Use this when string is definitely const (i.e. a literal, or as good as), and will definitely survive the cJSON object */
|
||||
/* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */
|
||||
extern void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item);
|
||||
extern void cJSON_AddItemReferenceToObject(cJSON *object,const char *string,cJSON *item);
|
||||
|
||||
/* Remove/Detatch items from Arrays/Objects. */
|
||||
extern cJSON *cJSON_DetachItemFromArray(cJSON *array,int which);
|
||||
extern void cJSON_DeleteItemFromArray(cJSON *array,int which);
|
||||
extern cJSON *cJSON_DetachItemFromObject(cJSON *object,const char *string);
|
||||
extern void cJSON_DeleteItemFromObject(cJSON *object,const char *string);
|
||||
|
||||
/* Update array items. */
|
||||
extern void cJSON_InsertItemInArray(cJSON *array,int which,cJSON *newitem); /* Shifts pre-existing items to the right. */
|
||||
extern void cJSON_ReplaceItemInArray(cJSON *array,int which,cJSON *newitem);
|
||||
extern void cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem);
|
||||
|
||||
/* Duplicate a cJSON item */
|
||||
extern cJSON *cJSON_Duplicate(cJSON *item,int recurse);
|
||||
/* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will
|
||||
need to be released. With recurse!=0, it will duplicate any children connected to the item.
|
||||
The item->next and ->prev pointers are always zero on return from Duplicate. */
|
||||
|
||||
/* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */
|
||||
extern cJSON *cJSON_ParseWithOpts(const char *value,const char **return_parse_end,int require_null_terminated);
|
||||
|
||||
extern void cJSON_Minify(char *json);
|
||||
|
||||
/* Macros for creating things quickly. */
|
||||
#define cJSON_AddNullToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateNull())
|
||||
#define cJSON_AddTrueToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateTrue())
|
||||
#define cJSON_AddFalseToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateFalse())
|
||||
#define cJSON_AddBoolToObject(object,name,b) cJSON_AddItemToObject(object, name, cJSON_CreateBool(b))
|
||||
#define cJSON_AddNumberToObject(object,name,n) cJSON_AddItemToObject(object, name, cJSON_CreateNumber(n))
|
||||
#define cJSON_AddStringToObject(object,name,s) cJSON_AddItemToObject(object, name, cJSON_CreateString(s))
|
||||
|
||||
/* When assigning an integer value, it needs to be propagated to valuedouble too. */
|
||||
#define cJSON_SetIntValue(object,val) ((object)?(object)->valueint=(object)->valuedouble=(val):(val))
|
||||
#define cJSON_SetNumberValue(object,val) ((object)?(object)->valueint=(object)->valuedouble=(val):(val))
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
@ -1,175 +1,175 @@
|
||||
#include "stdafx.h"
|
||||
#include "util.h"
|
||||
#include <vector>
|
||||
#include "session.h"
|
||||
|
||||
class test_thread : public acl::thread
|
||||
{
|
||||
public:
|
||||
test_thread(acl::redis_client_cluster& cluster,
|
||||
size_t max_threads, int count)
|
||||
: cluster_(cluster), max_threads_(max_threads), count_(count)
|
||||
{
|
||||
sess_ = new acl::redis_session(cluster_, max_threads_);
|
||||
}
|
||||
|
||||
~test_thread()
|
||||
{
|
||||
delete sess_;
|
||||
}
|
||||
|
||||
protected:
|
||||
void* run()
|
||||
{
|
||||
acl::string name;
|
||||
char value[128];
|
||||
|
||||
memset(value, 'X', sizeof(value));
|
||||
value[sizeof(value) - 1] = 0;
|
||||
|
||||
printf(">>> count: %d\r\n", count_);
|
||||
|
||||
struct timeval begin;
|
||||
gettimeofday(&begin, NULL);
|
||||
|
||||
for (int i = 0; i < count_; i++)
|
||||
{
|
||||
name.format("tid_%lu_%d", thread_id(), i);
|
||||
if (sess_->set(name.c_str(), value) == false)
|
||||
{
|
||||
printf("set error, name: %s\r\n", name.c_str());
|
||||
break;
|
||||
}
|
||||
|
||||
const char* ptr = sess_->get(name.c_str());
|
||||
if (ptr == NULL || *ptr == 0)
|
||||
{
|
||||
printf("get error, name: %s\r\n", name.c_str());
|
||||
break;
|
||||
}
|
||||
|
||||
if (strcasecmp(ptr, value) != 0)
|
||||
{
|
||||
printf("invalid result\r\n");
|
||||
break;
|
||||
}
|
||||
|
||||
if (sess_->del(name.c_str()) == false)
|
||||
{
|
||||
printf("del error, name: %s\r\n", name.c_str());
|
||||
break;
|
||||
}
|
||||
|
||||
if (i < 10)
|
||||
printf("test name: %s ok\r\n", name.c_str());
|
||||
if (i % 1000 == 0)
|
||||
{
|
||||
char info[128];
|
||||
acl::safe_snprintf(info, sizeof(info),
|
||||
"benchmark: %d", i);
|
||||
acl::meter_time(__FILE__, __LINE__, info);
|
||||
}
|
||||
}
|
||||
|
||||
struct timeval end;
|
||||
gettimeofday(&end, NULL);
|
||||
|
||||
double inter = util::stamp_sub(&end, &begin);
|
||||
printf("total: %d, spent: %0.2f ms, speed: %0.2f\r\n",
|
||||
count_, inter, (count_ * 1000) /(inter > 0 ? inter : 1));
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
private:
|
||||
acl::redis_client_cluster& cluster_;
|
||||
acl::redis_session* sess_;
|
||||
size_t max_threads_;
|
||||
int count_;
|
||||
};
|
||||
|
||||
bool test_redis_session(const char* addr, int n, int max_threads)
|
||||
{
|
||||
int conn_timeout = 10, rw_timeout = 10;
|
||||
acl::redis_client_cluster cluster(conn_timeout, rw_timeout);
|
||||
cluster.set(addr, max_threads);
|
||||
|
||||
std::vector<test_thread*> threads;
|
||||
for (int i = 0; i < max_threads; i++)
|
||||
{
|
||||
test_thread* thread = new test_thread(cluster, max_threads, n);
|
||||
threads.push_back(thread);
|
||||
thread->set_detachable(false);
|
||||
thread->start();
|
||||
}
|
||||
|
||||
for (std::vector<test_thread*>::iterator it = threads.begin();
|
||||
it != threads.end(); ++it)
|
||||
{
|
||||
(*it)->wait();
|
||||
delete (*it);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void test_session_string(const char* addr)
|
||||
{
|
||||
acl::session_string s1;
|
||||
s1.copy(addr, strlen(addr));
|
||||
s1.todo_ = acl::TODO_DEL;
|
||||
|
||||
printf("s1: addr: %s, todo: %d\r\n", s1.c_str(), s1.todo_);
|
||||
|
||||
acl::session_string s2 = s1;
|
||||
printf("structor copy --> s2: addr: %s, todo: %d\r\n", s2.c_str(), s2.todo_);
|
||||
|
||||
s1.todo_ = acl::TODO_NUL;
|
||||
s2 = s1;
|
||||
printf("assign copy --> s2: addr: %s, todo: %d\r\n", s2.c_str(), s2.todo_);
|
||||
|
||||
s2.todo_ = acl::TODO_SET;
|
||||
acl::session_string s3(s2);
|
||||
printf("structor copy --> s3: addr: %s, todo: %d\r\n", s3.c_str(), s3.todo_);
|
||||
}
|
||||
|
||||
bool test_redis_session_attrs(const char* addr, int n)
|
||||
{
|
||||
std::map<acl::string, acl::session_string> attrs;
|
||||
acl::session_string value, name;
|
||||
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
name.format("name_%d", i);
|
||||
value.format("value_%d", i);
|
||||
|
||||
attrs[name] = value;
|
||||
}
|
||||
|
||||
acl::redis_client_cluster cluster(10, 10);
|
||||
cluster.set(addr, 1);
|
||||
|
||||
acl::redis_session sess(cluster, 1);
|
||||
if (sess.set_attrs(attrs) == false)
|
||||
{
|
||||
printf("set_attrs error\r\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
printf("set_attrs ok\r\n");
|
||||
|
||||
attrs.clear();
|
||||
if (sess.get_attrs(attrs) == false)
|
||||
{
|
||||
printf("get_attrs error\r\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
printf("get_attrs ok, n: %d\r\n", (int) attrs.size());
|
||||
|
||||
std::map<acl::string, acl::session_string>::const_iterator cit;
|
||||
for (cit = attrs.begin(); cit != attrs.end(); ++cit)
|
||||
printf("%s=%s\r\n", cit->first.c_str(), cit->second.c_str());
|
||||
|
||||
return true;
|
||||
}
|
||||
#include "stdafx.h"
|
||||
#include "util.h"
|
||||
#include <vector>
|
||||
#include "session.h"
|
||||
|
||||
class test_thread : public acl::thread
|
||||
{
|
||||
public:
|
||||
test_thread(acl::redis_client_cluster& cluster,
|
||||
size_t max_threads, int count)
|
||||
: cluster_(cluster), max_threads_(max_threads), count_(count)
|
||||
{
|
||||
sess_ = new acl::redis_session(cluster_, max_threads_);
|
||||
}
|
||||
|
||||
~test_thread()
|
||||
{
|
||||
delete sess_;
|
||||
}
|
||||
|
||||
protected:
|
||||
void* run()
|
||||
{
|
||||
acl::string name;
|
||||
char value[128];
|
||||
|
||||
memset(value, 'X', sizeof(value));
|
||||
value[sizeof(value) - 1] = 0;
|
||||
|
||||
printf(">>> count: %d\r\n", count_);
|
||||
|
||||
struct timeval begin;
|
||||
gettimeofday(&begin, NULL);
|
||||
|
||||
for (int i = 0; i < count_; i++)
|
||||
{
|
||||
name.format("tid_%lu_%d", thread_id(), i);
|
||||
if (sess_->set(name.c_str(), value) == false)
|
||||
{
|
||||
printf("set error, name: %s\r\n", name.c_str());
|
||||
break;
|
||||
}
|
||||
|
||||
const char* ptr = sess_->get(name.c_str());
|
||||
if (ptr == NULL || *ptr == 0)
|
||||
{
|
||||
printf("get error, name: %s\r\n", name.c_str());
|
||||
break;
|
||||
}
|
||||
|
||||
if (strcasecmp(ptr, value) != 0)
|
||||
{
|
||||
printf("invalid result\r\n");
|
||||
break;
|
||||
}
|
||||
|
||||
if (sess_->del(name.c_str()) == false)
|
||||
{
|
||||
printf("del error, name: %s\r\n", name.c_str());
|
||||
break;
|
||||
}
|
||||
|
||||
if (i < 10)
|
||||
printf("test name: %s ok\r\n", name.c_str());
|
||||
if (i % 1000 == 0)
|
||||
{
|
||||
char info[128];
|
||||
acl::safe_snprintf(info, sizeof(info),
|
||||
"benchmark: %d", i);
|
||||
acl::meter_time(__FILE__, __LINE__, info);
|
||||
}
|
||||
}
|
||||
|
||||
struct timeval end;
|
||||
gettimeofday(&end, NULL);
|
||||
|
||||
double inter = util::stamp_sub(&end, &begin);
|
||||
printf("total: %d, spent: %0.2f ms, speed: %0.2f\r\n",
|
||||
count_, inter, (count_ * 1000) /(inter > 0 ? inter : 1));
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
private:
|
||||
acl::redis_client_cluster& cluster_;
|
||||
acl::redis_session* sess_;
|
||||
size_t max_threads_;
|
||||
int count_;
|
||||
};
|
||||
|
||||
bool test_redis_session(const char* addr, int n, int max_threads)
|
||||
{
|
||||
int conn_timeout = 10, rw_timeout = 10;
|
||||
acl::redis_client_cluster cluster(conn_timeout, rw_timeout);
|
||||
cluster.set(addr, max_threads);
|
||||
|
||||
std::vector<test_thread*> threads;
|
||||
for (int i = 0; i < max_threads; i++)
|
||||
{
|
||||
test_thread* thread = new test_thread(cluster, max_threads, n);
|
||||
threads.push_back(thread);
|
||||
thread->set_detachable(false);
|
||||
thread->start();
|
||||
}
|
||||
|
||||
for (std::vector<test_thread*>::iterator it = threads.begin();
|
||||
it != threads.end(); ++it)
|
||||
{
|
||||
(*it)->wait();
|
||||
delete (*it);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void test_session_string(const char* addr)
|
||||
{
|
||||
acl::session_string s1;
|
||||
s1.copy(addr, strlen(addr));
|
||||
s1.todo_ = acl::TODO_DEL;
|
||||
|
||||
printf("s1: addr: %s, todo: %d\r\n", s1.c_str(), s1.todo_);
|
||||
|
||||
acl::session_string s2 = s1;
|
||||
printf("structor copy --> s2: addr: %s, todo: %d\r\n", s2.c_str(), s2.todo_);
|
||||
|
||||
s1.todo_ = acl::TODO_NUL;
|
||||
s2 = s1;
|
||||
printf("assign copy --> s2: addr: %s, todo: %d\r\n", s2.c_str(), s2.todo_);
|
||||
|
||||
s2.todo_ = acl::TODO_SET;
|
||||
acl::session_string s3(s2);
|
||||
printf("structor copy --> s3: addr: %s, todo: %d\r\n", s3.c_str(), s3.todo_);
|
||||
}
|
||||
|
||||
bool test_redis_session_attrs(const char* addr, int n)
|
||||
{
|
||||
std::map<acl::string, acl::session_string> attrs;
|
||||
acl::session_string value, name;
|
||||
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
name.format("name_%d", i);
|
||||
value.format("value_%d", i);
|
||||
|
||||
attrs[name] = value;
|
||||
}
|
||||
|
||||
acl::redis_client_cluster cluster(10, 10);
|
||||
cluster.set(addr, 1);
|
||||
|
||||
acl::redis_session sess(cluster, 1);
|
||||
if (sess.set_attrs(attrs) == false)
|
||||
{
|
||||
printf("set_attrs error\r\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
printf("set_attrs ok\r\n");
|
||||
|
||||
attrs.clear();
|
||||
if (sess.get_attrs(attrs) == false)
|
||||
{
|
||||
printf("get_attrs error\r\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
printf("get_attrs ok, n: %d\r\n", (int) attrs.size());
|
||||
|
||||
std::map<acl::string, acl::session_string>::const_iterator cit;
|
||||
for (cit = attrs.begin(); cit != attrs.end(); ++cit)
|
||||
printf("%s=%s\r\n", cit->first.c_str(), cit->second.c_str());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -1,10 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
// in memcache_session.cpp
|
||||
bool test_memcache_session(const char* addr, int n);
|
||||
void test_memcache_session_delay(const char* addr);
|
||||
|
||||
// in redis_session.cpp
|
||||
bool test_redis_session(const char* addr, int n, int max_threads);
|
||||
bool test_redis_session_attrs(const char* addr, int n);
|
||||
void test_session_string(const char* addr);
|
||||
#pragma once
|
||||
|
||||
// in memcache_session.cpp
|
||||
bool test_memcache_session(const char* addr, int n);
|
||||
void test_memcache_session_delay(const char* addr);
|
||||
|
||||
// in redis_session.cpp
|
||||
bool test_redis_session(const char* addr, int n, int max_threads);
|
||||
bool test_redis_session_attrs(const char* addr, int n);
|
||||
void test_session_string(const char* addr);
|
||||
|
Loading…
Reference in New Issue
Block a user