func newRDBLoader(reader *bufio.Reader, rbytes *atomic2.Int64, size int) chan *rdb.BinEntry { pipe := make(chan *rdb.BinEntry, size) go func() { defer close(pipe) l := rdb.NewLoader(stats.NewCountReader(reader, rbytes)) if err := l.Header(); err != nil { log.PanicError(err, "parse rdb header error") } for { if entry, err := l.NextBinEntry(); err != nil { log.PanicError(err, "parse rdb entry error") } else { if entry != nil { pipe <- entry } else { if err := l.Footer(); err != nil { log.PanicError(err, "parse rdb checksum error") } return } } } }() return pipe }
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 (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 counter.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 := rpdb.ExpireAtToTTLms(entry.ExpireAt); ok && v > 0 { ttlms = v } else { ttlms = 1 } } if err := c.Rpdb().SlotsRestore(db, key, 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() } } }