parent
8e589e666f
commit
a98dcf1101
9 changed files with 296 additions and 50 deletions
Binary file not shown.
@ -0,0 +1,173 @@ |
||||
package run |
||||
|
||||
import ( |
||||
"pkg/libs/log" |
||||
"strconv" |
||||
|
||||
"redis-shake/common" |
||||
"redis-shake/configure" |
||||
|
||||
"github.com/garyburd/redigo/redis" |
||||
) |
||||
|
||||
const ( |
||||
TencentCluster = "tencent_cluster" |
||||
AliyunCluster = "aliyun_cluster" |
||||
) |
||||
|
||||
type CmdRump struct { |
||||
sourceConn redis.Conn |
||||
targetConn redis.Conn |
||||
|
||||
keyChan chan *KeyNode // keyChan is used to communicated between routine1 and routine2
|
||||
resultChan chan *KeyNode // resultChan is used to communicated between routine2 and routine3
|
||||
} |
||||
|
||||
type KeyNode struct { |
||||
key string |
||||
value string |
||||
pttl int64 |
||||
} |
||||
|
||||
func (cr *CmdRump) GetDetailedInfo() []interface{} { |
||||
return nil |
||||
} |
||||
|
||||
func (cr *CmdRump) Main() { |
||||
// build connection
|
||||
cr.sourceConn = utils.OpenRedisConn(conf.Options.SourceAddress, conf.Options.SourceAuthType, |
||||
conf.Options.SourcePasswordRaw) |
||||
cr.targetConn = utils.OpenRedisConn(conf.Options.TargetAddress, conf.Options.TargetAuthType, |
||||
conf.Options.TargetPasswordRaw) |
||||
|
||||
// init two channels
|
||||
cr.keyChan = make(chan *KeyNode, conf.Options.ScanKeyNumber) |
||||
cr.resultChan = make(chan *KeyNode, conf.Options.ScanKeyNumber) |
||||
|
||||
/* |
||||
* we start 3 routines to run: |
||||
* 1. fetch keys from the source redis |
||||
* 2. write keys into the target redis |
||||
* 3. read result from the target redis |
||||
*/ |
||||
// routine1
|
||||
go cr.fetcher() |
||||
// routine2
|
||||
go cr.writer() |
||||
// routine3
|
||||
cr.receiver() |
||||
} |
||||
|
||||
func (cr *CmdRump) fetcher() { |
||||
length := 1 |
||||
if conf.Options.ScanSpecialCloud == TencentCluster { |
||||
length = len(conf.Options.ScanSpecialCloudTencentUrls) |
||||
} else if conf.Options.ScanSpecialCloud == AliyunCluster { |
||||
length = int(conf.Options.ScanSpecialCloudAliyunNodeNumber) |
||||
} |
||||
|
||||
// iterate all source nodes
|
||||
for i := 0; i < length; i++ { |
||||
var ( |
||||
cursor int64 |
||||
keys []string |
||||
values []interface{} |
||||
err error |
||||
) |
||||
|
||||
// fetch data from on node
|
||||
for { |
||||
switch conf.Options.ScanSpecialCloud { |
||||
case "": |
||||
values, err = redis.Values(cr.sourceConn.Do("SCAN", cursor, "COUNT", |
||||
conf.Options.ScanKeyNumber)) |
||||
case TencentCluster: |
||||
values, err = redis.Values(cr.sourceConn.Do("SCAN", cursor, "COUNT", |
||||
conf.Options.ScanKeyNumber, conf.Options.ScanSpecialCloudTencentUrls[i])) |
||||
case AliyunCluster: |
||||
values, err = redis.Values(cr.sourceConn.Do("ISCAN", i, cursor, "COUNT", |
||||
conf.Options.ScanKeyNumber)) |
||||
} |
||||
if err != nil && err != redis.ErrNil { |
||||
log.Panicf("scan with cursor[%v] failed[%v]", cursor, err) |
||||
} |
||||
|
||||
values, err = redis.Scan(values, &cursor, &keys) |
||||
if err != nil && err != redis.ErrNil { |
||||
log.Panicf("do scan with cursor[%v] failed[%v]", cursor, err) |
||||
} |
||||
|
||||
log.Info("scaned keys: ", len(keys)) |
||||
|
||||
// pipeline dump
|
||||
for _, key := range keys { |
||||
log.Debug("scan key: ", key) |
||||
cr.sourceConn.Send("DUMP", key) |
||||
} |
||||
dumps, err := redis.Strings(cr.sourceConn.Do("")) |
||||
if err != nil && err != redis.ErrNil { |
||||
log.Panicf("do dump with cursor[%v] failed[%v]", cursor, err) |
||||
} |
||||
|
||||
// pipeline ttl
|
||||
for _, key := range keys { |
||||
cr.sourceConn.Send("PTTL", key) |
||||
} |
||||
pttls, err := redis.Int64s(cr.sourceConn.Do("")) |
||||
if err != nil && err != redis.ErrNil { |
||||
log.Panicf("do ttl with cursor[%v] failed[%v]", cursor, err) |
||||
} |
||||
|
||||
for i, k := range keys { |
||||
cr.keyChan <- &KeyNode{k, dumps[i], pttls[i]} |
||||
} |
||||
|
||||
// Last iteration of scan.
|
||||
if cursor == 0 { |
||||
break |
||||
} |
||||
} |
||||
} |
||||
|
||||
close(cr.keyChan) |
||||
} |
||||
|
||||
func (cr *CmdRump) writer() { |
||||
var count uint32 |
||||
for ele := range cr.keyChan { |
||||
if ele.pttl == -1 { // not set ttl
|
||||
ele.pttl = 0 |
||||
} |
||||
if ele.pttl == -2 { |
||||
log.Debugf("skip key %s for expired", ele.key) |
||||
continue |
||||
} |
||||
|
||||
log.Debugf("restore %s", ele.key) |
||||
if conf.Options.Rewrite { |
||||
cr.targetConn.Send("RESTORE", ele.key, ele.pttl, ele.value, "REPLACE") |
||||
} else { |
||||
cr.targetConn.Send("RESTORE", ele.key, ele.pttl, ele.value) |
||||
} |
||||
|
||||
cr.resultChan <- ele |
||||
count++ |
||||
if count == conf.Options.ScanKeyNumber { |
||||
// batch
|
||||
log.Debugf("send keys %d\n", count) |
||||
cr.targetConn.Flush() |
||||
count = 0 |
||||
} |
||||
} |
||||
cr.targetConn.Flush() |
||||
close(cr.resultChan) |
||||
} |
||||
|
||||
func (cr *CmdRump) receiver() { |
||||
for ele := range cr.resultChan { |
||||
if _, err := cr.targetConn.Receive(); err != nil && err != redis.ErrNil { |
||||
log.Panicf("restore key[%v] with pttl[%v] error[%v]", ele.key, strconv.FormatInt(ele.pttl, 10), |
||||
err) |
||||
} |
||||
} |
||||
} |
Loading…
Reference in new issue