tile38/internal/server/aofmigrate.go

160 lines
3.4 KiB
Go
Raw Normal View History

package server
2016-03-30 22:58:34 +03:00
import (
2016-03-31 00:53:29 +03:00
"bufio"
2016-03-30 22:58:34 +03:00
"encoding/binary"
"errors"
"io"
"os"
"path"
2016-03-31 00:53:29 +03:00
"time"
"github.com/tidwall/resp"
"github.com/tidwall/tile38/internal/log"
2016-03-30 22:58:34 +03:00
)
var errCorruptedAOF = errors.New("corrupted aof file")
2016-04-03 05:16:36 +03:00
// LegacyAOFReader represents the older AOF file reader.
2016-03-30 22:58:34 +03:00
type LegacyAOFReader struct {
r io.Reader // reader
rerr error // read error
chunk []byte // chunk buffer
buf []byte // main buffer
l int // length of valid data in buffer
p int // pointer
}
2016-04-03 05:16:36 +03:00
// ReadCommand reads an old command.
2016-03-30 22:58:34 +03:00
func (rd *LegacyAOFReader) ReadCommand() ([]byte, error) {
if rd.l >= 4 {
sz1 := int(binary.LittleEndian.Uint32(rd.buf[rd.p:]))
if rd.l >= sz1+9 {
// we have enough data for a record
sz2 := int(binary.LittleEndian.Uint32(rd.buf[rd.p+4+sz1:]))
if sz2 != sz1 || rd.buf[rd.p+4+sz1+4] != 0 {
return nil, errCorruptedAOF
}
buf := rd.buf[rd.p+4 : rd.p+4+sz1]
rd.p += sz1 + 9
rd.l -= sz1 + 9
return buf, nil
}
}
// need more data
if rd.rerr != nil {
if rd.rerr == io.EOF {
rd.rerr = nil // we want to return EOF, but we want to be able to try again
if rd.l != 0 {
return nil, io.ErrUnexpectedEOF
}
return nil, io.EOF
}
return nil, rd.rerr
}
if rd.p != 0 {
// move p to the beginning
copy(rd.buf, rd.buf[rd.p:rd.p+rd.l])
rd.p = 0
}
var n int
n, rd.rerr = rd.r.Read(rd.chunk)
if n > 0 {
cbuf := rd.chunk[:n]
if len(rd.buf)-rd.l < n {
if len(rd.buf) == 0 {
rd.buf = make([]byte, len(cbuf))
copy(rd.buf, cbuf)
} else {
copy(rd.buf[rd.l:], cbuf[:len(rd.buf)-rd.l])
rd.buf = append(rd.buf, cbuf[len(rd.buf)-rd.l:]...)
}
} else {
copy(rd.buf[rd.l:], cbuf)
}
rd.l += n
}
return rd.ReadCommand()
}
2016-04-03 05:16:36 +03:00
// NewLegacyAOFReader creates a new LegacyAOFReader.
2016-03-30 22:58:34 +03:00
func NewLegacyAOFReader(r io.Reader) *LegacyAOFReader {
rd := &LegacyAOFReader{r: r, chunk: make([]byte, 0xFFFF)}
return rd
}
func (s *Server) migrateAOF() error {
_, err := os.Stat(path.Join(s.dir, "appendonly.aof"))
2016-03-30 22:58:34 +03:00
if err == nil {
return nil
}
if !os.IsNotExist(err) {
return err
}
_, err = os.Stat(path.Join(s.dir, "aof"))
2016-03-30 22:58:34 +03:00
if err != nil {
if os.IsNotExist(err) {
return nil
}
return err
}
2016-03-31 00:53:29 +03:00
log.Warn("Migrating aof to new format")
newf, err := os.Create(path.Join(s.dir, "migrate.aof"))
2016-03-30 22:58:34 +03:00
if err != nil {
return err
}
defer newf.Close()
oldf, err := os.Open(path.Join(s.dir, "aof"))
2016-03-30 22:58:34 +03:00
if err != nil {
return err
}
defer oldf.Close()
2016-03-31 00:53:29 +03:00
start := time.Now()
count := 0
wr := bufio.NewWriter(newf)
rd := NewLegacyAOFReader(oldf)
2016-03-30 22:58:34 +03:00
for {
2016-03-31 00:53:29 +03:00
cmdb, err := rd.ReadCommand()
2016-03-30 22:58:34 +03:00
if err != nil {
if err == io.EOF {
break
}
return err
}
2016-03-31 00:53:29 +03:00
line := string(cmdb)
var tok string
values := make([]resp.Value, 0, 64)
for line != "" {
line, tok = token(line)
if len(tok) > 0 && tok[0] == '{' {
if line != "" {
tok = tok + " " + line
line = ""
}
}
values = append(values, resp.StringValue(tok))
}
data, err := resp.ArrayValue(values).MarshalRESP()
if err != nil {
return err
}
if _, err := wr.Write(data); err != nil {
return err
}
if wr.Buffered() > 1024*1024 {
if err := wr.Flush(); err != nil {
return err
}
}
count++
}
if err := wr.Flush(); err != nil {
return err
}
oldf.Close()
newf.Close()
log.Debugf("%d items: %.0f/sec", count, float64(count)/(float64(time.Since(start))/float64(time.Second)))
return os.Rename(path.Join(s.dir, "migrate.aof"), path.Join(s.dir, "appendonly.aof"))
2016-03-30 22:58:34 +03:00
}