acl/app/redis_tools/redis_builder/redis_migrate.cpp
2022-04-16 13:47:03 +08:00

257 lines
6.5 KiB
C++

#include "stdafx.h"
#include "redis_util.h"
#include "redis_migrate.h"
redis_migrate::redis_migrate(std::vector<acl::redis_node*>& masters,
const char* passwd)
: masters_(masters)
{
if (passwd && *passwd)
passwd_ = passwd;
}
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_)) {
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_)) {
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());
from_conn.set_password(passwd_);
acl::redis from_redis(&from_conn);
acl::redis_client to_conn(to.get_addr());
to_conn.set_password(passwd_);
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)) {
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)) {
return false;
}
// set the slot in migrating status for the source node
if (!from.cluster_setslot_migrating(slot, to_id_.c_str())) {
return false;
}
// set the slot in importing status for the target node
if (!to.cluster_setslot_importing(slot, from_id_.c_str())) {
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())) {
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)
{
if (passwd_.empty()) {
if (from.migrate(key, to_addr, 0, 15000, NULL)) {
return true;
}
} else {
acl::string options;
options.format("AUTH %s", passwd_.c_str());
if (from.migrate(key, to_addr, 0, 15000, options)) {
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;
}
if (passwd_.empty()) {
if (from.migrate(key, to_addr, 0, 15000, "REPLACE")) {
return true;
}
} else {
acl::string options;
options.format("REPLACE AUTH %s", passwd_.c_str());
if (from.migrate(key, to_addr, 0, 15000, options)) {
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());
client.set_password(passwd_);
redis.set_client(&client);
redis.clear();
if (!redis.cluster_setslot_node(slot, id)) {
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;
}