1st
This commit is contained in:
346
txlog/reader.go
Normal file
346
txlog/reader.go
Normal file
@@ -0,0 +1,346 @@
|
||||
package txlog
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"hash/crc32"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
diploma "gordenko.dev/dima/qb"
|
||||
"gordenko.dev/dima/qb/bin"
|
||||
"gordenko.dev/dima/qb/proto"
|
||||
)
|
||||
|
||||
type Reader struct {
|
||||
file *os.File
|
||||
reader *bufio.Reader
|
||||
}
|
||||
|
||||
type ReaderOptions struct {
|
||||
FileName string
|
||||
BufferSize int
|
||||
}
|
||||
|
||||
func NewReader(opt ReaderOptions) (*Reader, error) {
|
||||
if opt.FileName == "" {
|
||||
return nil, errors.New("FileName option is required")
|
||||
}
|
||||
if opt.BufferSize <= 0 {
|
||||
return nil, errors.New("BufferSize option is required")
|
||||
}
|
||||
|
||||
file, err := os.Open(opt.FileName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Reader{
|
||||
file: file,
|
||||
reader: bufio.NewReaderSize(file, 1024*1024),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Reader) Close() {
|
||||
s.file.Close()
|
||||
}
|
||||
|
||||
func (s *Reader) ReadPacket() (uint32, []any, bool, error) {
|
||||
prefix := make([]byte, packetPrefixSize)
|
||||
n, err := s.reader.Read(prefix)
|
||||
if err != nil {
|
||||
if err == io.EOF && n == 0 {
|
||||
return 0, nil, true, nil
|
||||
} else {
|
||||
return 0, nil, false, fmt.Errorf("read packet prefix: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
length := bin.GetUint32(prefix[lengthIdx:])
|
||||
storedCRC := bin.GetUint32(prefix[checksumIdx:])
|
||||
lsn := bin.GetUint32(prefix[lsnIdx:])
|
||||
|
||||
body, err := bin.ReadN(s.reader, int(length))
|
||||
if err != nil {
|
||||
return 0, nil, false, fmt.Errorf("read packet body: %s", err)
|
||||
}
|
||||
|
||||
hasher := crc32.NewIEEE()
|
||||
hasher.Write(prefix[lsnIdx:])
|
||||
hasher.Write(body)
|
||||
|
||||
calculatedCRC := hasher.Sum32()
|
||||
|
||||
if calculatedCRC != storedCRC {
|
||||
return 0, nil, false, fmt.Errorf("stored CRC %d != calculated CRC %d",
|
||||
storedCRC, calculatedCRC)
|
||||
}
|
||||
|
||||
records, err := s.parseRecords(body)
|
||||
if err != nil {
|
||||
return 0, nil, false, err
|
||||
}
|
||||
return lsn, records, false, nil
|
||||
}
|
||||
|
||||
func (s *Reader) parseRecords(body []byte) ([]any, error) {
|
||||
var (
|
||||
src = bytes.NewBuffer(body)
|
||||
records []any
|
||||
)
|
||||
|
||||
for {
|
||||
recordType, err := src.ReadByte()
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
return records, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch recordType {
|
||||
case CodeAddedMetric:
|
||||
var rec AddedMetric
|
||||
rec, err = s.readAddedMetric(src)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
records = append(records, rec)
|
||||
|
||||
case CodeDeletedMetric:
|
||||
var rec DeletedMetric
|
||||
rec, err = s.readDeletedMetric(src)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
records = append(records, rec)
|
||||
|
||||
case CodeAppendedMeasure:
|
||||
var rec AppendedMeasure
|
||||
rec, err = s.readAppendedMeasure(src)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
records = append(records, rec)
|
||||
|
||||
case CodeAppendedMeasures:
|
||||
var rec AppendedMeasures
|
||||
rec, err = s.readAppendedMeasures(src)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
records = append(records, rec)
|
||||
|
||||
case CodeAppendedMeasureWithOverflow:
|
||||
var rec AppendedMeasureWithOverflow
|
||||
rec, err = s.readAppendedMeasureWithOverflow(src)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
records = append(records, rec)
|
||||
|
||||
case CodeDeletedMeasures:
|
||||
var rec DeletedMeasures
|
||||
rec, err = s.readDeletedMeasures(src)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
records = append(records, rec)
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown record type code: %d", recordType)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Reader) readAddedMetric(src *bytes.Buffer) (_ AddedMetric, err error) {
|
||||
arr, err := bin.ReadN(src, 6)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return AddedMetric{
|
||||
MetricID: bin.GetUint32(arr),
|
||||
MetricType: diploma.MetricType(arr[4]),
|
||||
FracDigits: int(arr[5]),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Reader) readDeletedMetric(src *bytes.Buffer) (_ DeletedMetric, err error) {
|
||||
var rec DeletedMetric
|
||||
rec.MetricID, err = bin.ReadUint32(src)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
// free data pages
|
||||
dataQty, _, err := bin.ReadVarUint64(src)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
for range dataQty {
|
||||
var pageNo uint32
|
||||
pageNo, err = bin.ReadUint32(src)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
rec.FreeDataPages = append(rec.FreeDataPages, pageNo)
|
||||
}
|
||||
// free index pages
|
||||
indexQty, _, err := bin.ReadVarUint64(src)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
for range indexQty {
|
||||
var pageNo uint32
|
||||
pageNo, err = bin.ReadUint32(src)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
rec.FreeIndexPages = append(rec.FreeIndexPages, pageNo)
|
||||
}
|
||||
return rec, nil
|
||||
}
|
||||
|
||||
func (s *Reader) readAppendedMeasure(src *bytes.Buffer) (_ AppendedMeasure, err error) {
|
||||
arr, err := bin.ReadN(src, 16)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return AppendedMeasure{
|
||||
MetricID: bin.GetUint32(arr[0:]),
|
||||
Timestamp: bin.GetUint32(arr[4:]),
|
||||
Value: bin.GetFloat64(arr[8:]),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Reader) readAppendedMeasures(src *bytes.Buffer) (_ AppendedMeasures, err error) {
|
||||
var rec AppendedMeasures
|
||||
rec.MetricID, err = bin.ReadUint32(src)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
qty, err := bin.ReadUint16(src)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
for range qty {
|
||||
var measure proto.Measure
|
||||
measure.Timestamp, err = bin.ReadUint32(src)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
measure.Value, err = bin.ReadFloat64(src)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
rec.Measures = append(rec.Measures, measure)
|
||||
}
|
||||
return rec, nil
|
||||
}
|
||||
|
||||
func (s *Reader) readAppendedMeasureWithOverflow(src *bytes.Buffer) (_ AppendedMeasureWithOverflow, err error) {
|
||||
var (
|
||||
b byte
|
||||
rec AppendedMeasureWithOverflow
|
||||
)
|
||||
rec.MetricID, err = bin.ReadUint32(src)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
rec.Timestamp, err = bin.ReadUint32(src)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
rec.Value, err = bin.ReadFloat64(src)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
b, err = src.ReadByte()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
rec.IsDataPageReused = b == 1
|
||||
|
||||
rec.DataPageNo, err = bin.ReadUint32(src)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
b, err = src.ReadByte()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if b == 1 {
|
||||
rec.IsRootChanged = true
|
||||
rec.RootPageNo, err = bin.ReadUint32(src)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
// index pages
|
||||
indexQty, err := src.ReadByte()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
for range indexQty {
|
||||
var pageNo uint32
|
||||
pageNo, err = bin.ReadUint32(src)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
rec.ReusedIndexPages = append(rec.ReusedIndexPages, pageNo)
|
||||
}
|
||||
return rec, nil
|
||||
}
|
||||
|
||||
func (s *Reader) readDeletedMeasures(src *bytes.Buffer) (_ DeletedMeasures, err error) {
|
||||
var (
|
||||
rec DeletedMeasures
|
||||
)
|
||||
rec.MetricID, err = bin.ReadUint32(src)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
// free data pages
|
||||
rec.FreeDataPages, err = s.readFreePageNumbers(src)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
// free index pages
|
||||
rec.FreeIndexPages, err = s.readFreePageNumbers(src)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return rec, nil
|
||||
}
|
||||
|
||||
// HELPERS
|
||||
|
||||
func (s *Reader) readFreePageNumbers(src *bytes.Buffer) ([]uint32, error) {
|
||||
var freePages []uint32
|
||||
qty, _, err := bin.ReadVarUint64(src)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for range qty {
|
||||
var pageNo uint32
|
||||
pageNo, err = bin.ReadUint32(src)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
freePages = append(freePages, pageNo)
|
||||
}
|
||||
return freePages, nil
|
||||
}
|
||||
|
||||
func (s *Reader) Seek(offset int64) error {
|
||||
ret, err := s.file.Seek(offset, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if ret != offset {
|
||||
return fmt.Errorf("ret %d != offset %d", ret, offset)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user