improve: 'info xxx' command isn't supported in codis, used 'info' and parse 'xxx'

v4
vinllen 5 years ago
parent 0a0b18fb3d
commit dfa69b120d
  1. 8
      README.md
  2. 42
      src/redis-shake/common/command.go
  3. 2
      src/redis-shake/common/common.go
  4. 22
      src/redis-shake/common/utils.go
  5. 52
      src/redis-shake/common/utils_test.go
  6. 32
      src/redis-shake/filter/filter_test.go

@ -19,7 +19,7 @@ The type can be one of the followings:<br>
* **restore**: Restore RDB file to target redis.
* **dump**: Dump RDB file from souce redis.
* **sync**: Sync data from source redis to target redis by `sync` or `psync` command. Including full synchronization and incremental synchronization.
* **rump**: Sync data from source redis to target redis by `scan` command. Only support full synchronization. Plus, RedisShake also supports fetching data from given keys in the input file when `scan` command is not supported on the source side.
* **rump**: Sync data from source redis to target redis by `scan` command. Only support full synchronization. Plus, RedisShake also supports fetching data from given keys in the input file when `scan` command is not supported on the source side. This mode is usually used when `sync` and `psync` redis commands aren't supported.
Please check out the `conf/redis-shake.conf` to see the detailed parameters description.<br>
@ -41,7 +41,7 @@ Redis-shake offers metrics through restful api and log file.<br>
* restful api: `curl 127.0.0.1:9320/metric`.
* log: the metric info will be printed in the log periodically if enable.
m
* inner routine heap: `curl http://127.0.0.1:9310/debug/pprof/goroutine?debug=2`
# Redis Type
---
@ -80,7 +80,7 @@ You can also build redis-shake yourself according to the following steps, the `g
* cd src/vendor
* govendor sync #please note: must install govendor first and then pull all dependencies: `go get -u github.com/kardianos/govendor`
* cd ../../ && ./build.sh
* ./bin/redis-shake -type=$(type_must_be_sync_dump_restore_or_decode) -conf=conf/redis-shake.conf #please note: user must modify collector.conf first to match needs.
* ./bin/redis-shake -type=$(type_must_be_sync_dump_restore_decode_or_rump) -conf=conf/redis-shake.conf #please note: user must modify collector.conf first to match needs.
# Shake series tool
---
@ -90,7 +90,7 @@ We also provide some tools for synchronization in Shake series.<br>
* [RedisShake](https://github.com/aliyun/RedisShake): redis data synchronization tool.
* [RedisFullCheck](https://github.com/aliyun/RedisFullCheck): redis data synchronization verification tool.
Plus, we have a WeChat group so that users can join and discuss, but the group user number is limited. So please add my WeChat number: `vinllen_xingge` first, and I will add you to this group.<br>
Plus, we have a WeChat group so that users can join and discuss, but the group user number is limited. So please add my WeChat number: `vinllen_xingge` first, and I will add you to this group. (<-微信加群请戳这)<br>
# Thanks
---

@ -7,6 +7,7 @@ import (
"redis-shake/configure"
redigo "github.com/garyburd/redigo/redis"
"strings"
)
type ClusterNodeInfo struct {
@ -21,6 +22,47 @@ type ClusterNodeInfo struct {
Slot string
}
// parse single info field: "info server", "info keyspace"
func ParseRedisInfo(content []byte) map[string]string {
result := make(map[string]string, 10)
lines := bytes.Split(content, []byte("\r\n"))
for i := 0; i < len(lines); i++ {
items := bytes.SplitN(lines[i], []byte(":"), 2)
if len(items) != 2 {
continue
}
result[string(items[0])] = string(items[1])
}
return result
}
// cut segment
func CutRedisInfoSegment(content []byte, field string) []byte {
field1 := strings.ToLower(field)
field2 := "# " + field1
segmentSplitter := []byte{13, 10, 35, 32} // \r\n#
lineSplitter := []byte{13, 10}
segments := bytes.Split(content, segmentSplitter)
for i, segment := range segments {
lines := bytes.Split(segment, lineSplitter)
if len(lines) == 0 {
continue
}
cmd := strings.ToLower(string(lines[0]))
if cmd == field1 || cmd == field2 {
// match
var newSeg []byte
if i != 0 {
newSeg = []byte{35, 32}
}
newSeg = append(newSeg, segment...)
return newSeg
}
}
return nil
}
func ParseKeyspace(content []byte) (map[int32]int64, error) {
if bytes.HasPrefix(content, []byte("# Keyspace")) == false {
return nil, fmt.Errorf("invalid info Keyspace: %s", string(content))

@ -32,6 +32,8 @@ const (
TencentCluster = "tencent_cluster"
AliyunCluster = "aliyun_cluster"
UCloudCluster = "ucloud_cluster"
CoidsErrMsg = "ERR backend server 'server' not found"
)
var (

@ -864,27 +864,21 @@ func NewRDBLoader(reader *bufio.Reader, rbytes *atomic2.Int64, size int) chan *r
return pipe
}
func ParseRedisInfo(content []byte) map[string]string {
result := make(map[string]string, 10)
lines := bytes.Split(content, []byte("\r\n"))
for i := 0; i < len(lines); i++ {
items := bytes.SplitN(lines[i], []byte(":"), 2)
if len(items) != 2 {
continue
}
result[string(items[0])] = string(items[1])
}
return result
}
func GetRedisVersion(target, authType, auth string, tlsEnable bool) (string, error) {
c := OpenRedisConn([]string{target}, authType, auth, false, tlsEnable)
defer c.Close()
infoStr, err := redigo.Bytes(c.Do("info", "server"))
if err != nil {
return "", err
if err.Error() == CoidsErrMsg {
// "info xxx" command is disable in codis, try to use "info" and parse "xxx"
infoStr, err = redigo.Bytes(c.Do("info"))
infoStr = CutRedisInfoSegment(infoStr, "server")
} else {
return "", err
}
}
infoKV := ParseRedisInfo(infoStr)
if value, ok := infoKV["redis_version"]; ok {
return value, nil

File diff suppressed because one or more lines are too long

@ -278,6 +278,38 @@ func TestHandleFilterKeyWithCommand(t *testing.T) {
}
}
func TestHasAtLeastOnePrefix(t *testing.T) {
cases := []struct {
key string
prefixes []string
expectResult bool
}{
{
// no prefix provided
"a",
[]string{},
false,
},
{
// has prefix
"abc",
[]string{"ab"},
true,
},
{
// does NOT have prefix
"abc",
[]string{"edf", "wab"},
false,
},
}
for _, c := range cases {
result := hasAtLeastOnePrefix(c.key, c.prefixes)
assert.Equal(t, c.expectResult, result)
}
}
func convertToByte(args... string) [][]byte {
ret := make([][]byte, 0)
for _, arg := range args {

Loading…
Cancel
Save