-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathreplace-dead-node.js
143 lines (126 loc) · 4.01 KB
/
replace-dead-node.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
const Redis = require('ioredis');
var sleep = require('sleep');
var async = require('async');
const { StringDecoder } = require('string_decoder');
var yesno = require('yesno');
var redisClusterConn = {
"host": "127.0.0.1",
"port": 7002
};
var redisCluster = new Redis.Cluster([redisClusterConn]);
var newRedisConn = {
"host": "127.0.0.1",
"port": 7003
};
var newRedis = new Redis(newRedisConn);
async.waterfall([
getClusterInfo,
parseClusterInfo,
forgetBrokenNode,
addNewNode,
assignSlotsToNewNode
], function(err, results) {
console.log('done');
});
function getClusterInfo(callback) {
arbitraryCommand(redisCluster, 'cluster', ['nodes'], function (err, value) {
var clusterInfo = value.toString();
var nodes = redisCluster.nodes();
console.log("cluster info:");
console.log(clusterInfo);
callback(null, {
'clusterInfo': clusterInfo,
'clusterNodes': nodes
});
});
}
function parseClusterInfo(results, callback) {
var lines = results.clusterInfo.split("\n");
for (var i in lines) {
var line = lines[i];
var regex = /([^\s]+).*disconnected\s+([^\s]+)/g;
var match = regex.exec(line);
if (match) {
results['nodeId'] = match[1];
results['missingSlots'] = match[2];
break;
}
}
callback(null, results);
}
function forgetBrokenNode(results, callback) {
if (!results.nodeId)
throw 'no disconnected node found, exiting';
yesno.ask('About to send cluster forget, ok to continue?', true, function(ok) {
if(ok) {
async.map(results.clusterNodes, function(node, callback) {
if (node.status != "end") { // skip over the failed node
arbitraryCommand(node, 'cluster', ['forget', results.nodeId], function (err, value) {
if (err) throw err;
console.log(value.toString());
callback(null, null);
});
} else {
callback(null, null);
}
}, function(err, data) {
if (err) console.log(err);
callback(null, results);
});
} else {
throw 'exiting';
}
});
}
function addNewNode(results, callback) {
yesno.ask('About to add new node, ok to continue?', true, function(ok) {
if(ok) {
arbitraryCommand(newRedis, 'cluster', ['meet', redisClusterConn.host, redisClusterConn.port], function (err, value) {
if (err) throw err;
console.log(value.toString());
// TODO check that new node was successfully added before continuing. for now just pause for a second
sleep.msleep(1000);
callback(null, results);
});
} else {
throw 'exiting';
}
});
}
function assignSlotsToNewNode(results, callback) {
var args = ['addslots'];
var slotsStr = results.missingSlots;
var slotsChunks = slotsStr.split(",");
for (var i in slotsChunks) {
var slotsChunk = slotsChunks[i];
var slotsSplit = slotsChunk.split("-");
if (slotsSplit.length > 1) {
for (var j = slotsSplit[0]; j <= slotsSplit[1]; j++) {
args.push(j + "");
}
} else {
args.push(slotsSplit[0]);
}
}
yesno.ask("Assigning slots " + slotsStr + " to new node, ok to continue?", true, function(ok) {
if (ok) {
arbitraryCommand(newRedis, 'cluster', args, function (err, value) {
if (err) throw err;
console.log(value.toString());
callback(null, results);
});
} else {
throw 'exiting';
}
});
}
function arbitraryCommand(redis, command, args, callback) {
redis.sendCommand(
new Redis.Command(
command,
args,
'utf-8',
callback
)
);
}