175 lines
4.4 KiB
Go
175 lines
4.4 KiB
Go
package wal
|
|
|
|
import (
|
|
logger "code.google.com/p/log4go"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"os"
|
|
"sort"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
type indexEntry struct {
|
|
FirstRequestNumber uint32 // first request number in the block
|
|
LastRequestNumber uint32 // number of requests in the block
|
|
FirstOffset int64 // the offset of the first request
|
|
LastOffset int64 // the offset of the last request
|
|
}
|
|
|
|
func (self *indexEntry) NumberOfRequests() int {
|
|
return int(self.LastRequestNumber-self.FirstRequestNumber) + 1
|
|
}
|
|
|
|
type index struct {
|
|
f *os.File
|
|
Entries []*indexEntry
|
|
}
|
|
|
|
func newIndex(path string) (*index, error) {
|
|
f, err := os.OpenFile(path, os.O_CREATE|os.O_APPEND|os.O_RDWR, 0644)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
stat, err := f.Stat()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if stat.Size() == 0 {
|
|
// append the version, which is 1 right now
|
|
fmt.Fprintf(f, "%d\n", 1)
|
|
}
|
|
if _, err = f.Seek(0, os.SEEK_SET); err != nil {
|
|
return nil, err
|
|
}
|
|
content, err := ioutil.ReadAll(f)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
lines := strings.Split(string(content), "\n")
|
|
entries := make([]*indexEntry, 0, len(lines))
|
|
// skip the first line, that's the version
|
|
for _, line := range lines[1:] {
|
|
if len(line) == 0 {
|
|
break
|
|
}
|
|
|
|
fields := strings.Split(line, ",")
|
|
firstRequestNumber, _ := strconv.ParseInt(fields[0], 10, 64)
|
|
firstOffset, _ := strconv.ParseInt(fields[1], 10, 64)
|
|
lastRequestNumber, _ := strconv.ParseInt(fields[2], 10, 64)
|
|
lastOffset, _ := strconv.ParseInt(fields[3], 10, 64)
|
|
entries = append(entries, &indexEntry{
|
|
uint32(firstRequestNumber),
|
|
uint32(lastRequestNumber),
|
|
firstOffset,
|
|
lastOffset,
|
|
})
|
|
}
|
|
return &index{f, entries}, nil
|
|
}
|
|
|
|
func (self *index) addEntry(firstRequestNumber, lastRequestNumber uint32, firstOffset, lastOffset int64) {
|
|
if firstRequestNumber > lastRequestNumber {
|
|
panic(fmt.Errorf("invalid index entry: [%d,%d,%d,%d]", firstRequestNumber, lastRequestNumber, firstOffset, lastOffset))
|
|
}
|
|
|
|
logger.Debug("Adding index entry [%d,%d,%d,%d]", firstRequestNumber, lastRequestNumber, firstOffset, lastOffset)
|
|
fmt.Fprintf(self.f, "%d,%d,%d,%d\n", firstRequestNumber, firstOffset, lastRequestNumber, lastOffset)
|
|
entry := &indexEntry{
|
|
FirstRequestNumber: firstRequestNumber,
|
|
FirstOffset: firstOffset,
|
|
LastRequestNumber: lastRequestNumber,
|
|
LastOffset: lastOffset,
|
|
}
|
|
self.Entries = append(self.Entries, entry)
|
|
}
|
|
|
|
func (self *index) syncFile() error {
|
|
return self.f.Sync()
|
|
}
|
|
|
|
func (self *index) close() error {
|
|
return self.f.Close()
|
|
}
|
|
|
|
func (self *index) delete() error {
|
|
return os.Remove(self.f.Name())
|
|
}
|
|
|
|
func (self *index) getLastRequestInfo() (uint32, int64) {
|
|
if len(self.Entries) == 0 {
|
|
return 0, 0
|
|
}
|
|
lastEntry := self.Entries[len(self.Entries)-1]
|
|
return lastEntry.LastRequestNumber, lastEntry.LastOffset
|
|
}
|
|
|
|
func (self *index) getLastOffset() int64 {
|
|
if len(self.Entries) == 0 {
|
|
return 0
|
|
}
|
|
|
|
entry := self.Entries[len(self.Entries)-1]
|
|
return entry.LastOffset
|
|
}
|
|
|
|
func (self *index) getLength() int {
|
|
firstRequest, _ := self.getFirstRequestInfo()
|
|
lastRequest, _ := self.getLastRequestInfo()
|
|
return int(lastRequest - firstRequest)
|
|
}
|
|
|
|
func (self *index) getFirstRequestInfo() (uint32, int64) {
|
|
if len(self.Entries) == 0 {
|
|
return 0, 0
|
|
}
|
|
firstEntry := self.Entries[0]
|
|
return firstEntry.FirstRequestNumber, firstEntry.FirstOffset
|
|
}
|
|
|
|
func (self *index) blockSearch(requestNumber uint32) func(int) bool {
|
|
return func(i int) bool {
|
|
// The returned function must satisfy `f(i) => f(i+1)`, meaning if
|
|
// for index i f returns true, then it must return true for every
|
|
// index greater than i. sort.Search will return the smallest
|
|
// index satisfying f
|
|
return requestNumber <= self.Entries[i].LastRequestNumber
|
|
}
|
|
}
|
|
|
|
func (self *index) requestOffset(requestNumber uint32) int64 {
|
|
numberOfEntries := len(self.Entries)
|
|
|
|
if numberOfEntries == 0 {
|
|
return -1
|
|
}
|
|
|
|
firstEntry := self.Entries[0]
|
|
if requestNumber < firstEntry.FirstRequestNumber {
|
|
return -1
|
|
}
|
|
|
|
lastEntry := self.Entries[numberOfEntries-1]
|
|
if requestNumber > lastEntry.LastRequestNumber {
|
|
return -1
|
|
}
|
|
|
|
index := sort.Search(numberOfEntries, self.blockSearch(requestNumber))
|
|
return self.Entries[index].FirstOffset
|
|
}
|
|
|
|
func (self *index) requestOrLastOffset(requestNumber uint32) int64 {
|
|
numberOfEntries := len(self.Entries)
|
|
if numberOfEntries == 0 {
|
|
return 0
|
|
}
|
|
|
|
if requestNumber > self.Entries[numberOfEntries-1].LastRequestNumber {
|
|
return self.Entries[numberOfEntries-1].LastOffset
|
|
}
|
|
return self.requestOffset(requestNumber)
|
|
}
|