func checkslotsmgrt(t *testing.T, r *bufio.Reader, w *bufio.Writer, c chan error, expect ...interface{}) { if len(expect) != 0 { req1, err := redis.Decode(r) checkerror(t, err, true) cmd1, args1, err := redis.ParseArgs(req1) checkerror(t, err, cmd1 == "select" && len(args1) == 1) checkerror(t, redis.Encode(w, redis.NewString("OK")), true) checkerror(t, w.Flush(), true) req2, err := redis.Decode(r) cmd2, args2, err := redis.ParseArgs(req2) checkerror(t, err, cmd2 == "slotsrestore" && len(args2) == len(expect)) m := make(map[string]*struct { key, value string ttlms uint64 }) for i := 0; i < len(expect)/3; i++ { v := &struct { key, value string ttlms uint64 }{key: expect[i*3].(string), value: expect[i*3+2].(string)} v.ttlms, err = ParseUint(expect[i*3+1]) checkerror(t, err, true) m[v.key] = v } for i := 0; i < len(expect)/3; i++ { key := args2[i*3] ttlms := args2[i*3+1] value := args2[i*3+2] v := m[string(key)] checkerror(t, nil, v != nil) checkerror(t, nil, string(key) == v.key) b, err := rdb.DecodeDump(value) checkerror(t, err, string(b.(rdb.String)) == v.value) x, err := strconv.Atoi(string(ttlms)) checkerror(t, err, true) if v.ttlms == 0 { checkerror(t, nil, x == 0) } else { checkerror(t, nil, x != 0) checkerror(t, nil, math.Abs(float64(x)-float64(v.ttlms)) < 500) } } checkerror(t, redis.Encode(w, redis.NewString("OK")), true) checkerror(t, w.Flush(), true) } select { case err := <-c: checkerror(t, err, true) case <-time.After(time.Second): checkerror(t, nil, false) } }
// SLOTSRESTORE key ttlms value [key ttlms value ...] func (b *Rpdb) SlotsRestore(db uint32, args ...interface{}) error { if len(args) == 0 || len(args)%3 != 0 { return errArguments("len(args) = %d, expect != 0 && mod 3 = 0", len(args)) } objs := make([]*rdb.ObjEntry, len(args)/3) for i := 0; i < len(objs); i++ { var key, value []byte var ttlms int64 for j, ref := range []interface{}{&key, &ttlms, &value} { if err := parseArgument(args[i*3+j], ref); err != nil { return errArguments("parse args[%d] failed, %s", i*3+j, err) } } expireat := uint64(0) if ttlms != 0 { if v, ok := TTLmsToExpireAt(ttlms); ok && v > 0 { expireat = v } else { return errArguments("parse args[%d] ttlms = %d", i*3+1, ttlms) } } obj, err := rdb.DecodeDump(value) if err != nil { return errArguments("decode args[%d] failed, %s", i*3+2, err) } objs[i] = &rdb.ObjEntry{ DB: db, Key: key, ExpireAt: expireat, Value: obj, } } if err := b.acquire(); err != nil { return err } defer b.release() ms := &markSet{} bt := store.NewBatch() for i := len(objs) - 1; i >= 0; i-- { e := objs[i] if ms.Has(e.Key) { log.Debugf("[%d] restore batch, db = %d, key = %v, ignore", i, e.DB, e.Key) continue } else { log.Debugf("[%d] restore batch, db = %d, key = %v", i, e.DB, e.Key) } if err := b.restore(bt, e.DB, e.Key, e.ExpireAt, e.Value); err != nil { log.DebugErrorf(err, "restore object failed, db = %d, key = %v", e.DB, e.Key) return err } ms.Set(e.Key) } fw := &Forward{DB: db, Op: "SlotsRestore", Args: args} return b.commit(bt, fw) }
// RESTORE key ttlms value func (b *Rpdb) Restore(db uint32, args ...interface{}) error { if len(args) != 3 { return errArguments("len(args) = %d, expect = 3", len(args)) } var key, value []byte var ttlms int64 for i, ref := range []interface{}{&key, &ttlms, &value} { if err := parseArgument(args[i], ref); err != nil { return errArguments("parse args[%d] failed, %s", i, err) } } expireat := uint64(0) if ttlms != 0 { if v, ok := TTLmsToExpireAt(ttlms); ok && v > 0 { expireat = v } else { return errArguments("parse ttlms = %d", ttlms) } } obj, err := rdb.DecodeDump(value) if err != nil { return err } if err := b.acquire(); err != nil { return err } defer b.release() fw := &Forward{DB: db, Op: "Restore", Args: args} bt := store.NewBatch() if err := b.restore(bt, db, key, expireat, obj); err != nil { return err } return b.commit(bt, fw) }
func TestBgsaveTo(t *testing.T) { c := client(t) k := random(t) checkok(t, c, "reset") const max = 100 for i := 0; i < max; i++ { checkok(t, c, "set", k+strconv.Itoa(i), i) } path := "/tmp/testdb-dump.rdb" checkok(t, c, "bgsaveto", path) f, err := os.Open(path) checkerror(t, err, true) defer f.Close() l := rdb.NewLoader(f) checkerror(t, l.Header(), true) m := make(map[string][]byte) for { e, err := l.NextBinEntry() checkerror(t, err, true) if e == nil { break } checkerror(t, nil, e.DB == 0) checkerror(t, nil, e.ExpireAt == 0) m[string(e.Key)] = e.Value } checkerror(t, l.Footer(), true) for i := 0; i < max; i++ { b := m[k+strconv.Itoa(i)] o, err := rdb.DecodeDump(b) checkerror(t, err, true) x, ok := o.(rdb.String) checkerror(t, nil, ok) checkerror(t, nil, string(x) == string(rpdb.FormatInt(int64(i)))) } }
func (cmd *cmdDecode) decoderMain(ipipe <-chan *rdb.BinEntry, opipe chan<- string) { toText := func(p []byte) string { var b bytes.Buffer for _, c := range p { switch { case c >= '#' && c <= '~': b.WriteByte(c) default: b.WriteByte('.') } } return b.String() } toBase64 := func(p []byte) string { return base64.StdEncoding.EncodeToString(p) } toJson := func(o interface{}) string { b, err := json.Marshal(o) if err != nil { log.PanicError(err, "encode to json failed") } return string(b) } for e := range ipipe { o, err := rdb.DecodeDump(e.Value) if err != nil { log.PanicError(err, "decode failed") } var b bytes.Buffer switch obj := o.(type) { default: log.Panicf("unknown object %v", o) case rdb.String: o := &struct { DB uint32 `json:"db"` Type string `json:"type"` ExpireAt uint64 `json:"expireat"` Key string `json:"key"` Key64 string `json:"key64"` Value64 string `json:"value64"` }{ e.DB, "string", e.ExpireAt, toText(e.Key), toBase64(e.Key), toBase64(obj), } fmt.Fprintf(&b, "%s\n", toJson(o)) case rdb.List: for i, ele := range obj { o := &struct { DB uint32 `json:"db"` Type string `json:"type"` ExpireAt uint64 `json:"expireat"` Key string `json:"key"` Key64 string `json:"key64"` Index int `json:"index"` Value64 string `json:"value64"` }{ e.DB, "list", e.ExpireAt, toText(e.Key), toBase64(e.Key), i, toBase64(ele), } fmt.Fprintf(&b, "%s\n", toJson(o)) } case rdb.Hash: for _, ele := range obj { o := &struct { DB uint32 `json:"db"` Type string `json:"type"` ExpireAt uint64 `json:"expireat"` Key string `json:"key"` Key64 string `json:"key64"` Field string `json:"field"` Field64 string `json:"field64"` Value64 string `json:"value64"` }{ e.DB, "hash", e.ExpireAt, toText(e.Key), toBase64(e.Key), toText(ele.Field), toBase64(ele.Field), toBase64(ele.Value), } fmt.Fprintf(&b, "%s\n", toJson(o)) } case rdb.Set: for _, mem := range obj { o := &struct { DB uint32 `json:"db"` Type string `json:"type"` ExpireAt uint64 `json:"expireat"` Key string `json:"key"` Key64 string `json:"key64"` Member string `json:"member"` Member64 string `json:"member64"` }{ e.DB, "set", e.ExpireAt, toText(e.Key), toBase64(e.Key), toText(mem), toBase64(mem), } fmt.Fprintf(&b, "%s\n", toJson(o)) } case rdb.ZSet: for _, ele := range obj { o := &struct { DB uint32 `json:"db"` Type string `json:"type"` ExpireAt uint64 `json:"expireat"` Key string `json:"key"` Key64 string `json:"key64"` Member string `json:"member"` Member64 string `json:"member64"` Score float64 `json:"score"` }{ e.DB, "zset", e.ExpireAt, toText(e.Key), toBase64(e.Key), toText(ele.Member), toBase64(ele.Member), ele.Score, } fmt.Fprintf(&b, "%s\n", toJson(o)) } } cmd.nentry.Incr() opipe <- b.String() } }