func (h *Handler) doSyncFromMater(c *conn, counter *atomic2.Int64) error { c.authenticated = true lastACKTime := time.Now() for { readTotalSize := counter.Get() if _, err := c.handleRequest(h); err != nil { return errors.Trace(err) } if h.syncOffset.Get() != -1 { h.syncOffset.Add(counter.Get() - readTotalSize) n := time.Now() if n.Sub(lastACKTime) > time.Second { lastACKTime = n // this command has no reply if err := c.sendCommand("REPLCONF", "ACK", h.syncOffset.Get()); err != nil { log.Errorf("send REPLCONF ACK %d err - %s", h.syncOffset.Get(), err) } } } } return nil }
func (cmd *cmdDump) DumpRDBFile(reader *bufio.Reader, writer *bufio.Writer, nsize int64) { var nread atomic2.Int64 wait := make(chan struct{}) go func() { defer close(wait) p := make([]byte, WriterBufferSize) for nsize != nread.Get() { cnt := iocopy(reader, writer, p, int(nsize-nread.Get())) nread.Add(int64(cnt)) } flushWriter(writer) }() for done := false; !done; { select { case <-wait: done = true case <-time.After(time.Second): } n := nread.Get() p := 100 * n / nsize log.Infof("total = %d - %12d [%3d%%]\n", nsize, n, p) } log.Info("dump: rdb done") }
func (s *testRedisRdbSuite) TestEncodeRdb(c *gocheck.C) { objs := make([]struct { db uint32 expireat uint64 key []byte obj interface{} typ string }, 128) var b bytes.Buffer enc := NewEncoder(&b) c.Assert(enc.EncodeHeader(), gocheck.IsNil) for i := 0; i < len(objs); i++ { db := uint32(i + 32) expireat := uint64(i) key := []byte(strconv.Itoa(i)) var obj interface{} var typ string switch i % 5 { case 0: sss := strconv.Itoa(i) obj = sss typ = "string" c.Assert(enc.EncodeObject(db, key, expireat, s.toString(sss)), gocheck.IsNil) case 1: list := []string{} for j := 0; j < 32; j++ { list = append(list, fmt.Sprintf("l%d_%d", i, rand.Int())) } obj = list typ = "list" c.Assert(enc.EncodeObject(db, key, expireat, s.toList(list...)), gocheck.IsNil) case 2: hash := make(map[string]string) for j := 0; j < 32; j++ { hash[strconv.Itoa(j)] = fmt.Sprintf("h%d_%d", i, rand.Int()) } obj = hash typ = "hash" c.Assert(enc.EncodeObject(db, key, expireat, s.toHash(hash)), gocheck.IsNil) case 3: zset := make(map[string]float64) for j := 0; j < 32; j++ { zset[strconv.Itoa(j)] = rand.Float64() } obj = zset typ = "zset" c.Assert(enc.EncodeObject(db, key, expireat, s.toZSet(zset)), gocheck.IsNil) case 4: set := []string{} for j := 0; j < 32; j++ { set = append(set, fmt.Sprintf("s%d_%d", i, rand.Int())) } obj = set typ = "set" c.Assert(enc.EncodeObject(db, key, expireat, s.toSet(set...)), gocheck.IsNil) } objs[i].db = db objs[i].expireat = expireat objs[i].key = key objs[i].obj = obj objs[i].typ = typ } c.Assert(enc.EncodeFooter(), gocheck.IsNil) rdb := b.Bytes() var cc atomic2.Int64 l := NewLoader(ioutils.NewCountReader(bytes.NewReader(rdb), &cc)) c.Assert(l.Header(), gocheck.IsNil) var i int = 0 for { e, err := l.NextBinEntry() c.Assert(err, gocheck.IsNil) if e == nil { break } c.Assert(objs[i].db, gocheck.Equals, e.DB) c.Assert(objs[i].expireat, gocheck.Equals, e.ExpireAt) c.Assert(objs[i].key, gocheck.DeepEquals, e.Key) o, err := DecodeDump(e.Value) c.Assert(err, gocheck.IsNil) switch objs[i].typ { case "string": s.checkString(c, o, objs[i].obj.(string)) case "list": s.checkList(c, o, objs[i].obj.([]string)) case "hash": s.checkHash(c, o, objs[i].obj.(map[string]string)) case "zset": s.checkZSet(c, o, objs[i].obj.(map[string]float64)) case "set": s.checkSet(c, o, objs[i].obj.([]string)) } i++ } c.Assert(i, gocheck.Equals, len(objs)) c.Assert(l.Footer(), gocheck.IsNil) c.Assert(cc.Get(), gocheck.DeepEquals, int64(len(rdb))) }
func (cmd *cmdRestore) RestoreCommand(reader *bufio.Reader, slave string, nsize int64) { var forward, nbypass atomic2.Int64 c := openNetConn(slave) defer c.Close() writer := bufio.NewWriterSize(c, WriterBufferSize) defer flushWriter(writer) discard := bufio.NewReaderSize(c, ReaderBufferSize) go func() { var bypass bool = false for { resp := redis.MustDecode(reader) if cmd, args, err := redis.ParseArgs(resp); err != nil { log.PanicError(err, "parse command arguments failed") } else if cmd != "ping" { if cmd == "select" { if len(args) != 1 { log.Panicf("select command len(args) = %d", len(args)) } s := string(args[0]) n, err := parseInt(s, MinDB, MaxDB) if err != nil { log.PanicErrorf(err, "parse db = %s failed", s) } bypass = !acceptDB(uint32(n)) } if bypass { nbypass.Incr() continue } } redis.MustEncode(writer, resp) flushWriter(writer) forward.Incr() redis.MustDecode(discard) } }() for { lastForward := forward.Get() lastByPass := nbypass.Get() time.Sleep(time.Second) log.Infof("restore: +forward=%-6d +bypass=%-6d\n", forward.Get()-lastForward, nbypass.Get()-lastByPass) } }
func (cmd *cmdSync) SyncCommand(reader *bufio.Reader, slave string) { var forward, nbypass atomic2.Int64 c := openNetConn(slave) defer c.Close() writer := bufio.NewWriterSize(c, WriterBufferSize) defer flushWriter(writer) go func() { p := make([]byte, ReaderBufferSize) for { cnt := iocopy(c, ioutil.Discard, p, len(p)) cmd.nrecv.Add(int64(cnt)) } }() var mu sync.Mutex go func() { for { time.Sleep(time.Second) mu.Lock() flushWriter(writer) mu.Unlock() } }() go func() { var bypass bool = false for { resp := redis.MustDecode(reader) if cmd, args, err := redis.ParseArgs(resp); err != nil { log.PanicError(err, "parse command arguments failed") } else if cmd != "ping" { if cmd == "select" { if len(args) != 1 { log.Panicf("select command len(args) = %d", len(args)) } s := string(args[0]) n, err := parseInt(s, MinDB, MaxDB) if err != nil { log.PanicErrorf(err, "parse db = %s failed", s) } bypass = !acceptDB(uint32(n)) } if bypass { nbypass.Incr() continue } } mu.Lock() redis.MustEncode(writer, resp) mu.Unlock() forward.Incr() } }() for { lastForward := forward.Get() lastByPass := nbypass.Get() lastRead := cmd.nread.Get() lastRecv := cmd.nrecv.Get() time.Sleep(time.Second) log.Infof("sync: +forward=%-6d +bypass=%-6d +read=%-9d +recv=%-9d\n", forward.Get()-lastForward, nbypass.Get()-lastByPass, cmd.nread.Get()-lastRead, cmd.nrecv.Get()-lastRecv) } }
func (h *Handler) doSyncRDB(c *conn, size int64) error { defer h.counters.syncRdbRemains.Set(0) h.counters.syncRdbRemains.Set(size) r := ioutils.NewCountReader(c.r, nil) l := rdb.NewLoader(r) if err := l.Header(); err != nil { return err } ncpu := runtime.GOMAXPROCS(0) errs := make(chan error, ncpu) var lock sync.Mutex var flag atomic2.Int64 loadNextEntry := func() (*rdb.BinEntry, error) { lock.Lock() defer lock.Unlock() if flag.Get() != 0 { return nil, nil } entry, err := l.NextBinEntry() if err != nil || entry == nil { flag.Set(1) return nil, err } return entry, nil } for i := 0; i < ncpu; i++ { go func() { defer flag.Set(1) for { entry, err := loadNextEntry() if err != nil || entry == nil { errs <- err return } db, key, value := entry.DB, entry.Key, entry.Value ttlms := int64(0) if entry.ExpireAt != 0 { if v, ok := store.ExpireAtToTTLms(int64(entry.ExpireAt)); ok && v > 0 { ttlms = v } else { ttlms = 1 } } if err := c.Store().SlotsRestore(db, [][]byte{key, store.FormatInt(ttlms), value}); err != nil { errs <- err return } } }() } for { select { case <-time.After(time.Second): h.counters.syncRdbRemains.Set(size - r.Count()) case err := <-errs: for i := 1; i < cap(errs); i++ { e := <-errs if err == nil && e != nil { err = e } } if err != nil { return err } return l.Footer() } } }
func TestEncodeRdb(t *testing.T) { objs := make([]struct { db uint32 expireat uint64 key []byte obj interface{} typ string }, 128) var b bytes.Buffer enc := NewEncoder(&b) assert.ErrorIsNil(t, enc.EncodeHeader()) for i := 0; i < len(objs); i++ { db := uint32(i + 32) expireat := uint64(i) key := []byte(strconv.Itoa(i)) var obj interface{} var typ string switch i % 5 { case 0: s := strconv.Itoa(i) obj = s typ = "string" assert.ErrorIsNil(t, enc.EncodeObject(db, key, expireat, toString(s))) case 1: list := []string{} for j := 0; j < 32; j++ { list = append(list, fmt.Sprintf("l%d_%d", i, rand.Int())) } obj = list typ = "list" assert.ErrorIsNil(t, enc.EncodeObject(db, key, expireat, toList(list...))) case 2: hash := make(map[string]string) for j := 0; j < 32; j++ { hash[strconv.Itoa(j)] = fmt.Sprintf("h%d_%d", i, rand.Int()) } obj = hash typ = "hash" assert.ErrorIsNil(t, enc.EncodeObject(db, key, expireat, toHash(hash))) case 3: zset := make(map[string]float64) for j := 0; j < 32; j++ { zset[strconv.Itoa(j)] = rand.Float64() } obj = zset typ = "zset" assert.ErrorIsNil(t, enc.EncodeObject(db, key, expireat, toZSet(zset))) case 4: set := []string{} for j := 0; j < 32; j++ { set = append(set, fmt.Sprintf("s%d_%d", i, rand.Int())) } obj = set typ = "set" assert.ErrorIsNil(t, enc.EncodeObject(db, key, expireat, toSet(set...))) } objs[i].db = db objs[i].expireat = expireat objs[i].key = key objs[i].obj = obj objs[i].typ = typ } assert.ErrorIsNil(t, enc.EncodeFooter()) rdb := b.Bytes() var c atomic2.Int64 l := NewLoader(ioutils.NewCountReader(bytes.NewReader(rdb), &c)) assert.ErrorIsNil(t, l.Header()) var i int = 0 for { e, err := l.NextBinEntry() assert.ErrorIsNil(t, err) if e == nil { break } assert.Must(t, objs[i].db == e.DB) assert.Must(t, objs[i].expireat == e.ExpireAt) assert.Must(t, bytes.Equal(objs[i].key, e.Key)) o, err := DecodeDump(e.Value) assert.ErrorIsNil(t, err) switch objs[i].typ { case "string": checkString(t, o, objs[i].obj.(string)) case "list": checkList(t, o, objs[i].obj.([]string)) case "hash": checkHash(t, o, objs[i].obj.(map[string]string)) case "zset": checkZSet(t, o, objs[i].obj.(map[string]float64)) case "set": checkSet(t, o, objs[i].obj.([]string)) } i++ } assert.Must(t, i == len(objs)) assert.ErrorIsNil(t, l.Footer()) assert.Must(t, c.Get() == int64(len(rdb))) }