influxdb/wal/log.go

316 lines
7.5 KiB
Go
Raw Normal View History

2014-02-11 19:49:04 +00:00
package wal
import (
"fmt"
2014-02-11 21:35:33 +00:00
"io"
2014-02-11 19:49:04 +00:00
"os"
2014-02-17 18:21:01 +00:00
"path"
"strconv"
"strings"
"code.google.com/p/goprotobuf/proto"
logger "code.google.com/p/log4go"
"github.com/influxdb/influxdb/configuration"
"github.com/influxdb/influxdb/protocol"
2014-02-11 19:49:04 +00:00
)
type log struct {
2014-02-14 20:21:03 +00:00
closed bool
fileSize uint64
file *os.File
requestsSinceLastFlush int
2014-02-17 18:21:01 +00:00
config *configuration.Configuration
2014-06-18 15:03:45 +00:00
cachedSuffix uint32
2014-02-11 19:49:04 +00:00
}
2014-02-17 18:21:01 +00:00
func newLog(file *os.File, config *configuration.Configuration) (*log, error) {
2014-02-12 11:47:51 +00:00
info, err := file.Stat()
if err != nil {
return nil, err
}
size := uint64(info.Size())
2014-02-17 18:21:01 +00:00
suffixString := strings.TrimLeft(path.Base(file.Name()), "log.")
2014-06-18 15:03:45 +00:00
suffix, err := strconv.ParseUint(suffixString, 10, 32)
2014-02-17 18:21:01 +00:00
if err != nil {
return nil, err
}
2014-02-12 11:47:51 +00:00
2014-02-11 19:49:04 +00:00
l := &log{
2014-02-17 18:21:01 +00:00
file: file,
fileSize: size,
closed: false,
config: config,
2014-06-18 15:03:45 +00:00
cachedSuffix: uint32(suffix),
2014-02-11 19:49:04 +00:00
}
return l, l.check()
}
func (self *log) check() error {
file, err := self.dupLogFile()
if err != nil {
return err
}
info, err := file.Stat()
if err != nil {
return err
}
size := info.Size()
offset, err := file.Seek(0, os.SEEK_SET)
if err != nil {
return err
}
for {
n, hdr, err := self.getNextHeader(file)
if err != nil {
return err
}
if n == 0 || hdr.length == 0 {
logger.Warn("%s was truncated to %d since the file has a zero size request", self.file.Name(), offset)
return self.file.Truncate(offset)
}
if offset+int64(n)+int64(hdr.length) > size {
// file is incomplete, truncate
logger.Warn("%s was truncated to %d since the file ends prematurely", self.file.Name(), offset)
return self.file.Truncate(offset)
}
bytes := make([]byte, hdr.length)
_, err = file.Read(bytes)
if err != nil {
return err
}
// this request is invalid truncate file
req := &protocol.Request{}
err = req.Decode(bytes)
if err != nil {
logger.Warn("%s was truncated to %d since the end of the file contains invalid data", self.file.Name(), offset)
// truncate file and return
return self.file.Truncate(offset)
}
offset += int64(n) + int64(hdr.length)
}
2014-02-11 19:49:04 +00:00
}
2014-03-03 17:23:06 +00:00
func (self *log) offset() int64 {
offset, _ := self.file.Seek(0, os.SEEK_CUR)
return offset
}
2014-06-18 15:03:45 +00:00
func (self *log) suffix() uint32 {
2014-03-03 17:23:06 +00:00
return self.cachedSuffix
}
2014-02-12 11:47:51 +00:00
// this is for testing only
2014-03-03 17:23:06 +00:00
func (self *log) syncFile() error {
return self.file.Sync()
2014-02-12 11:47:51 +00:00
}
func (self *log) close() error {
2014-03-07 20:21:49 +00:00
logger.Debug("Closing %s", self.file.Name())
2014-02-12 11:47:51 +00:00
return self.file.Close()
}
2014-03-03 17:23:06 +00:00
func (self *log) delete() error {
2014-03-07 20:21:49 +00:00
logger.Debug("Deleting %s", self.file.Name())
2014-03-03 17:23:06 +00:00
return os.Remove(self.file.Name())
2014-02-11 21:35:33 +00:00
}
2014-03-03 17:23:06 +00:00
func (self *log) appendRequest(request *protocol.Request, shardId uint32) error {
bytes, err := request.Encode()
if err != nil {
2014-03-03 17:23:06 +00:00
return err
}
// every request is preceded with the length, shard id and the request number
2014-02-17 19:11:28 +00:00
hdr := &entryHeader{
shardId: shardId,
2014-03-03 17:23:06 +00:00
requestNumber: request.GetRequestNumber(),
length: uint32(len(bytes)),
}
2014-02-17 19:11:28 +00:00
writtenHdrBytes, err := hdr.Write(self.file)
if err != nil {
2014-02-14 17:25:57 +00:00
logger.Error("Error while writing header: %s", err)
2014-03-03 17:23:06 +00:00
return err
}
2014-02-17 19:11:28 +00:00
written, err := self.file.Write(bytes)
if err != nil {
2014-02-14 17:25:57 +00:00
logger.Error("Error while writing request: %s", err)
2014-03-03 17:23:06 +00:00
return err
}
if written < len(bytes) {
err = fmt.Errorf("Couldn't write entire request")
2014-02-14 17:25:57 +00:00
logger.Error("Error while writing request: %s", err)
2014-03-03 17:23:06 +00:00
return err
}
self.fileSize += uint64(writtenHdrBytes + written)
2014-03-03 17:23:06 +00:00
return nil
}
2014-02-12 10:59:42 +00:00
func (self *log) dupLogFile() (*os.File, error) {
2014-03-03 17:23:06 +00:00
return os.OpenFile(self.file.Name(), os.O_RDWR, 0)
2014-02-12 10:59:42 +00:00
}
// replay requests starting at the given requestNumber and for the
// given shard ids. Return all requests if shardIds is empty
2014-03-03 17:23:06 +00:00
func (self *log) dupAndReplayFromOffset(shardIds []uint32, offset int64, rn uint32) (chan *replayRequest, chan struct{}) {
// this channel needs to be buffered in case the last request in the
// log file caused an error in the yield function
stopChan := make(chan struct{}, 1)
2014-02-11 21:35:33 +00:00
replayChan := make(chan *replayRequest, 10)
2014-02-11 21:35:33 +00:00
go func() {
2014-02-12 10:59:42 +00:00
file, err := self.dupLogFile()
2014-02-11 21:35:33 +00:00
if err != nil {
sendOrStop(newErrorReplayRequest(err), replayChan, stopChan)
close(replayChan)
2014-02-11 21:35:33 +00:00
return
}
2014-02-11 21:41:57 +00:00
defer file.Close()
2014-03-03 17:23:06 +00:00
if err = self.skip(file, offset, rn); err != nil {
sendOrStop(newErrorReplayRequest(err), replayChan, stopChan)
close(replayChan)
2014-03-06 20:29:55 +00:00
return
2014-02-11 21:41:57 +00:00
}
2014-02-11 21:35:33 +00:00
shardIdsSet := map[uint32]struct{}{}
for _, shardId := range shardIds {
shardIdsSet[shardId] = struct{}{}
}
self.replayFromFileLocation(file, shardIdsSet, replayChan, stopChan)
2014-02-12 11:47:51 +00:00
}()
return replayChan, stopChan
}
2014-02-11 21:35:33 +00:00
func (self *log) getNextHeader(file *os.File) (int, *entryHeader, error) {
hdr := &entryHeader{}
numberOfBytes, err := hdr.Read(file)
if err == io.EOF {
return 0, nil, nil
}
return numberOfBytes, hdr, err
}
2014-03-03 17:23:06 +00:00
func (self *log) skip(file *os.File, offset int64, rn uint32) error {
if offset == -1 {
_, err := file.Seek(0, os.SEEK_SET)
return err
}
logger.Debug("Replaying from file offset %d", offset)
_, err := file.Seek(int64(offset), os.SEEK_SET)
if err != nil {
return err
}
return self.skipToRequest(file, rn)
}
func (self *log) skipRequest(file *os.File, hdr *entryHeader) (err error) {
_, err = file.Seek(int64(hdr.length), os.SEEK_CUR)
return
}
2014-03-03 17:23:06 +00:00
func (self *log) skipToRequest(file *os.File, requestNumber uint32) error {
for {
n, hdr, err := self.getNextHeader(file)
if n == 0 {
// EOF
return nil
}
if err != nil {
return err
}
2014-03-03 17:23:06 +00:00
if hdr.requestNumber < requestNumber {
if err := self.skipRequest(file, hdr); err != nil {
return err
}
continue
}
// seek back to the beginning of the request header
_, err = file.Seek(int64(-n), os.SEEK_CUR)
return err
}
}
func (self *log) replayFromFileLocation(file *os.File,
shardIdsSet map[uint32]struct{},
replayChan chan *replayRequest,
stopChan chan struct{}) {
2014-03-03 17:23:06 +00:00
offset, err := file.Seek(0, os.SEEK_CUR)
logger.Info("replaying from file location %d", offset)
2014-03-03 17:23:06 +00:00
if err != nil {
sendOrStop(newErrorReplayRequest(err), replayChan, stopChan)
return
}
defer func() { close(replayChan) }()
2014-02-12 11:47:51 +00:00
for {
numberOfBytes, hdr, err := self.getNextHeader(file)
if numberOfBytes == 0 {
break
2014-02-12 11:47:51 +00:00
}
2014-02-11 21:35:33 +00:00
2014-02-12 11:47:51 +00:00
if err != nil {
sendOrStop(newErrorReplayRequest(err), replayChan, stopChan)
2014-02-12 11:47:51 +00:00
return
}
2014-02-11 21:35:33 +00:00
2014-02-12 11:47:51 +00:00
ok := false
if len(shardIdsSet) == 0 {
ok = true
} else {
_, ok = shardIdsSet[hdr.shardId]
}
if !ok {
err = self.skipRequest(file, hdr)
2014-02-11 21:35:33 +00:00
if err != nil {
sendOrStop(newErrorReplayRequest(err), replayChan, stopChan)
2014-02-11 21:35:33 +00:00
return
}
2014-02-12 11:47:51 +00:00
continue
2014-02-11 21:35:33 +00:00
}
2014-02-12 11:47:51 +00:00
bytes := make([]byte, hdr.length)
read, err := file.Read(bytes)
2014-02-12 11:47:51 +00:00
if err != nil {
sendOrStop(newErrorReplayRequest(err), replayChan, stopChan)
2014-02-12 11:47:51 +00:00
return
}
if uint32(read) != hdr.length {
// file ends prematurely, probably a request is being written
logger.Debug("%s ends prematurely. Truncating to %d", file.Name(), offset)
2014-02-12 11:47:51 +00:00
return
}
2014-02-12 11:47:51 +00:00
req := &protocol.Request{}
err = req.Decode(bytes)
if err != nil {
sendOrStop(newErrorReplayRequest(err), replayChan, stopChan)
return
}
req.RequestNumber = proto.Uint32(hdr.requestNumber)
2014-03-03 17:23:06 +00:00
replayRequest := &replayRequest{hdr.requestNumber, req, hdr.shardId, offset, offset + int64(numberOfBytes) + int64(hdr.length), nil}
if sendOrStop(replayRequest, replayChan, stopChan) {
2014-02-12 11:47:51 +00:00
return
}
2014-03-03 17:23:06 +00:00
offset = replayRequest.endOffset
2014-02-12 11:47:51 +00:00
}
2014-02-11 21:35:33 +00:00
}
func sendOrStop(req *replayRequest, replayChan chan *replayRequest, stopChan chan struct{}) bool {
2014-02-27 01:14:07 +00:00
if req.err != nil {
logger.Error("Error in replay: %s", req.err)
}
select {
case replayChan <- req:
2014-02-17 19:11:28 +00:00
case _, ok := <-stopChan:
2014-02-27 01:14:07 +00:00
logger.Debug("Stopping replay")
2014-02-17 19:11:28 +00:00
return ok
}
return false
}