mirror of https://github.com/tidwall/tile38.git
read backward multibulk
This commit is contained in:
parent
9ece493a8e
commit
4a15a20e27
|
@ -2,19 +2,17 @@ package controller
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/md5"
|
"crypto/md5"
|
||||||
"encoding/binary"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/tidwall/resp"
|
||||||
"github.com/tidwall/tile38/controller/log"
|
"github.com/tidwall/tile38/controller/log"
|
||||||
"github.com/tidwall/tile38/core"
|
"github.com/tidwall/tile38/core"
|
||||||
)
|
)
|
||||||
|
|
||||||
const backwardsBufferSize = 50000
|
|
||||||
|
|
||||||
// checksum performs a simple md5 checksum on the aof file
|
// checksum performs a simple md5 checksum on the aof file
|
||||||
func (c *Controller) checksum(pos, size int64) (sum string, err error) {
|
func (c *Controller) checksum(pos, size int64) (sum string, err error) {
|
||||||
if pos+size > int64(c.aofsz) {
|
if pos+size > int64(c.aofsz) {
|
||||||
|
@ -63,6 +61,10 @@ func connAOFMD5(conn *Conn, pos, size int64) (sum string, err error) {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
if v.Error() != nil {
|
if v.Error() != nil {
|
||||||
|
errmsg := v.Error().Error()
|
||||||
|
if errmsg == "ERR EOF" || errmsg == "EOF" {
|
||||||
|
return "", io.EOF
|
||||||
|
}
|
||||||
return "", v.Error()
|
return "", v.Error()
|
||||||
}
|
}
|
||||||
sum = v.String()
|
sum = v.String()
|
||||||
|
@ -90,6 +92,50 @@ func (c *Controller) matchChecksums(conn *Conn, pos, size int64) (match bool, er
|
||||||
return csum == sum, nil
|
return csum == sum, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// getEndOfLastValuePositionInFile is a very slow operation because it reads the file
|
||||||
|
// backwards on byte at a time. Eek. It seek+read, seek+read, etc.
|
||||||
|
func getEndOfLastValuePositionInFile(fname string, startPos int64) (int64, error) {
|
||||||
|
pos := startPos
|
||||||
|
f, err := os.Open(fname)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
readByte := func() (byte, error) {
|
||||||
|
if pos <= 0 {
|
||||||
|
return 0, io.EOF
|
||||||
|
}
|
||||||
|
pos--
|
||||||
|
if _, err := f.Seek(pos, 0); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
b := make([]byte, 1)
|
||||||
|
if n, err := f.Read(b); err != nil {
|
||||||
|
return 0, err
|
||||||
|
} else if n != 1 {
|
||||||
|
return 0, errors.New("invalid read")
|
||||||
|
}
|
||||||
|
return b[0], nil
|
||||||
|
}
|
||||||
|
for {
|
||||||
|
c, err := readByte()
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
if c == '*' {
|
||||||
|
if _, err := f.Seek(pos, 0); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
rd := resp.NewReader(f)
|
||||||
|
_, telnet, n, err := rd.ReadMultiBulk()
|
||||||
|
if err != nil || telnet {
|
||||||
|
continue // keep reading backwards
|
||||||
|
}
|
||||||
|
return pos + int64(n), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// followCheckSome is not a full checksum. It just "checks some" data.
|
// followCheckSome is not a full checksum. It just "checks some" data.
|
||||||
// We will do some various checksums on the leader until we find the correct position to start at.
|
// We will do some various checksums on the leader until we find the correct position to start at.
|
||||||
func (c *Controller) followCheckSome(addr string, followc uint64) (pos int64, err error) {
|
func (c *Controller) followCheckSome(addr string, followc uint64) (pos int64, err error) {
|
||||||
|
@ -118,6 +164,7 @@ func (c *Controller) followCheckSome(addr string, followc uint64) (pos int64, er
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if match {
|
if match {
|
||||||
min += checksumsz // bump up the min
|
min += checksumsz // bump up the min
|
||||||
for {
|
for {
|
||||||
|
@ -152,63 +199,10 @@ func (c *Controller) followCheckSome(addr string, followc uint64) (pos int64, er
|
||||||
|
|
||||||
// we want to truncate at a command location
|
// we want to truncate at a command location
|
||||||
// search for nearest command
|
// search for nearest command
|
||||||
f, err := os.Open(c.f.Name())
|
pos, err = getEndOfLastValuePositionInFile(c.f.Name(), fullpos)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
defer f.Close()
|
|
||||||
if _, err := f.Seek(pos, 0); err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
// need to read backwards looking for null byte
|
|
||||||
const bufsz = backwardsBufferSize
|
|
||||||
buf := make([]byte, bufsz)
|
|
||||||
outer:
|
|
||||||
for {
|
|
||||||
if pos < int64(len(buf)) {
|
|
||||||
pos = 0
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if _, err := f.Seek(pos-bufsz, 0); err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
if _, err := io.ReadFull(f, buf); err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
for i := len(buf) - 1; i >= 0; i-- {
|
|
||||||
if buf[i] == 0 {
|
|
||||||
tpos := pos - bufsz + int64(i) - 4
|
|
||||||
if tpos < 0 {
|
|
||||||
pos = 0
|
|
||||||
break outer // at beginning of file
|
|
||||||
}
|
|
||||||
if _, err := f.Seek(tpos, 0); err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
szb := make([]byte, 4)
|
|
||||||
if _, err := io.ReadFull(f, szb); err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
sz2 := int64(binary.LittleEndian.Uint32(szb))
|
|
||||||
tpos = tpos - sz2 - 4
|
|
||||||
if tpos < 0 {
|
|
||||||
continue // keep scanning
|
|
||||||
}
|
|
||||||
if _, err := f.Seek(tpos, 0); err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
if _, err := io.ReadFull(f, szb); err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
sz1 := int64(binary.LittleEndian.Uint32(szb))
|
|
||||||
if sz1 == sz2 {
|
|
||||||
pos = pos - bufsz + int64(i) + 1
|
|
||||||
break outer // we found our match
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pos -= bufsz
|
|
||||||
}
|
|
||||||
if pos == fullpos {
|
if pos == fullpos {
|
||||||
if core.ShowDebugMessages {
|
if core.ShowDebugMessages {
|
||||||
log.Debug("follow: aof fully intact")
|
log.Debug("follow: aof fully intact")
|
||||||
|
@ -217,7 +211,6 @@ outer:
|
||||||
}
|
}
|
||||||
log.Warnf("truncating aof to %d", pos)
|
log.Warnf("truncating aof to %d", pos)
|
||||||
// any errror below are fatal.
|
// any errror below are fatal.
|
||||||
f.Close()
|
|
||||||
c.f.Close()
|
c.f.Close()
|
||||||
if err := os.Truncate(fname, pos); err != nil {
|
if err := os.Truncate(fname, pos); err != nil {
|
||||||
log.Fatalf("could not truncate aof, possible data loss. %s", err.Error())
|
log.Fatalf("could not truncate aof, possible data loss. %s", err.Error())
|
||||||
|
|
Loading…
Reference in New Issue