2026-06-10 06:18:45 +03:00
|
|
|
package storage
|
2026-02-10 14:02:11 +00:00
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"bufio"
|
|
|
|
|
"bytes"
|
|
|
|
|
"fmt"
|
|
|
|
|
"io"
|
|
|
|
|
|
2026-06-09 08:16:16 +03:00
|
|
|
bin "gordenko.dev/dima/bin/little"
|
|
|
|
|
"gordenko.dev/dima/qb/util"
|
2026-02-10 14:02:11 +00:00
|
|
|
)
|
|
|
|
|
|
2026-06-09 08:16:16 +03:00
|
|
|
const readBufferSize = 8 * 1024 * 1024
|
|
|
|
|
|
2026-06-10 06:18:45 +03:00
|
|
|
type WALReader struct {
|
2026-06-14 23:12:03 +03:00
|
|
|
//file *os.File
|
|
|
|
|
reader *bufio.Reader
|
|
|
|
|
next []any
|
|
|
|
|
done bool
|
2026-02-10 14:02:11 +00:00
|
|
|
}
|
|
|
|
|
|
2026-06-14 23:12:03 +03:00
|
|
|
// type WALReaderOptions struct {
|
|
|
|
|
// File *os.File
|
|
|
|
|
// BufferSize int
|
|
|
|
|
// }
|
2026-02-10 14:02:11 +00:00
|
|
|
|
2026-06-14 23:12:03 +03:00
|
|
|
func NewWALReader(src io.Reader) (*WALReader, error) {
|
|
|
|
|
// if opt.FileName == "" {
|
|
|
|
|
// return nil, errors.New("FileName option is required")
|
|
|
|
|
// }
|
|
|
|
|
// if opt.BufferSize <= 0 {
|
|
|
|
|
// return nil, errors.New("BufferSize option is required")
|
|
|
|
|
// }
|
2026-02-10 14:02:11 +00:00
|
|
|
|
2026-06-10 06:18:45 +03:00
|
|
|
return &WALReader{
|
2026-06-14 23:12:03 +03:00
|
|
|
//file: file,
|
|
|
|
|
reader: bufio.NewReaderSize(src, readBufferSize),
|
2026-02-10 14:02:11 +00:00
|
|
|
}, nil
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-14 23:12:03 +03:00
|
|
|
// func (s *WALReader) Close() {
|
|
|
|
|
// s.file.Close()
|
|
|
|
|
// }
|
2026-02-10 14:02:11 +00:00
|
|
|
|
2026-06-14 23:12:03 +03:00
|
|
|
// (packet records, isLastPacket, error)
|
|
|
|
|
func (s *WALReader) NextPacket() (_ []any, _ bool, err error) {
|
|
|
|
|
if s.done {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
var current []any
|
|
|
|
|
if s.next != nil {
|
|
|
|
|
current = s.next
|
|
|
|
|
} else {
|
|
|
|
|
current, err = s.readPacket()
|
2026-06-09 08:16:16 +03:00
|
|
|
if err != nil {
|
2026-06-15 01:20:30 +03:00
|
|
|
err = fmt.Errorf("readPacket: %s", err)
|
2026-06-14 23:12:03 +03:00
|
|
|
return
|
2026-06-09 08:16:16 +03:00
|
|
|
}
|
2026-06-14 23:12:03 +03:00
|
|
|
if current == nil {
|
|
|
|
|
return
|
2026-06-09 08:16:16 +03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
s.next, err = s.readPacket()
|
2026-02-10 14:02:11 +00:00
|
|
|
if err != nil {
|
2026-06-15 01:20:30 +03:00
|
|
|
err = fmt.Errorf("readPacket: %s", err)
|
2026-06-14 23:12:03 +03:00
|
|
|
return
|
2026-06-09 08:16:16 +03:00
|
|
|
}
|
2026-06-14 23:12:03 +03:00
|
|
|
return current, s.next == nil, nil
|
2026-06-09 08:16:16 +03:00
|
|
|
}
|
|
|
|
|
|
2026-06-10 06:18:45 +03:00
|
|
|
func (s *WALReader) readPacket() (_ []any, err error) {
|
2026-06-14 23:12:03 +03:00
|
|
|
bodySize, err := bin.ReadVarSize(s.reader) // fix add n
|
2026-06-09 08:16:16 +03:00
|
|
|
if err != nil {
|
|
|
|
|
if err == io.EOF {
|
2026-06-14 23:12:03 +03:00
|
|
|
s.done = true
|
2026-06-09 08:16:16 +03:00
|
|
|
return nil, nil
|
2026-02-10 14:02:11 +00:00
|
|
|
}
|
2026-06-14 23:12:03 +03:00
|
|
|
return
|
2026-02-10 14:02:11 +00:00
|
|
|
}
|
2026-06-14 23:12:03 +03:00
|
|
|
body, err := bin.ReadN(s.reader, int(bodySize))
|
2026-06-09 08:16:16 +03:00
|
|
|
if err != nil {
|
|
|
|
|
return
|
|
|
|
|
}
|
2026-06-14 23:12:03 +03:00
|
|
|
writtenCRC, err := bin.ReadUint32(s.reader)
|
2026-02-10 14:02:11 +00:00
|
|
|
if err != nil {
|
2026-06-09 08:16:16 +03:00
|
|
|
return
|
2026-02-10 14:02:11 +00:00
|
|
|
}
|
2026-06-14 23:12:03 +03:00
|
|
|
calculatedCRC := util.CalculateCRC32(body)
|
|
|
|
|
if calculatedCRC != writtenCRC {
|
2026-06-15 01:20:30 +03:00
|
|
|
//fmt.Printf("bodySize: %d\n", bodySize)
|
|
|
|
|
//fmt.Printf("body: % x\n", body)
|
2026-06-14 23:12:03 +03:00
|
|
|
return nil, fmt.Errorf("written CRC %d != calculated CRC %d",
|
|
|
|
|
writtenCRC, calculatedCRC)
|
2026-02-10 14:02:11 +00:00
|
|
|
}
|
2026-06-15 01:20:30 +03:00
|
|
|
//fmt.Println("SAME CRC")
|
2026-06-14 23:12:03 +03:00
|
|
|
return s.parseRecords(body)
|
2026-02-10 14:02:11 +00:00
|
|
|
}
|
|
|
|
|
|
2026-06-14 23:12:03 +03:00
|
|
|
func (s *WALReader) parseRecords(body []byte) (_ []any, err error) {
|
2026-02-10 14:02:11 +00:00
|
|
|
var (
|
|
|
|
|
src = bytes.NewBuffer(body)
|
|
|
|
|
records []any
|
|
|
|
|
)
|
|
|
|
|
for {
|
2026-06-14 23:12:03 +03:00
|
|
|
var recordType byte
|
|
|
|
|
recordType, err = src.ReadByte()
|
2026-02-10 14:02:11 +00:00
|
|
|
if err != nil {
|
|
|
|
|
if err == io.EOF {
|
|
|
|
|
return records, nil
|
|
|
|
|
}
|
2026-06-14 23:12:03 +03:00
|
|
|
return
|
2026-02-10 14:02:11 +00:00
|
|
|
}
|
|
|
|
|
switch recordType {
|
2026-06-10 06:18:45 +03:00
|
|
|
case CodeMetricAdd:
|
|
|
|
|
rec := new(MetricAddRecord)
|
2026-05-10 00:59:47 +00:00
|
|
|
if err = rec.Parse(src); err != nil {
|
2026-06-14 23:12:03 +03:00
|
|
|
return
|
2026-02-10 14:02:11 +00:00
|
|
|
}
|
2026-06-14 23:12:03 +03:00
|
|
|
records = append(records, *rec)
|
2026-02-10 14:02:11 +00:00
|
|
|
|
2026-06-10 06:18:45 +03:00
|
|
|
case CodeMetricDelete:
|
|
|
|
|
rec := new(MetricDeleteRecord)
|
2026-05-10 00:59:47 +00:00
|
|
|
if err = rec.Parse(src); err != nil {
|
2026-06-14 23:12:03 +03:00
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
records = append(records, *rec)
|
|
|
|
|
|
|
|
|
|
case CodeMeasuresAppend:
|
|
|
|
|
rec := new(MeasuresAppendRecord)
|
|
|
|
|
if err = rec.Parse(src); err != nil {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
records = append(records, *rec)
|
|
|
|
|
|
|
|
|
|
case CodeMeasuresAppendWithGrow:
|
|
|
|
|
rec := new(MeasuresAppendWithGrowRecord)
|
|
|
|
|
if err = rec.Parse(src); err != nil {
|
|
|
|
|
return
|
2026-02-10 14:02:11 +00:00
|
|
|
}
|
2026-06-14 23:12:03 +03:00
|
|
|
records = append(records, *rec)
|
2026-02-10 14:02:11 +00:00
|
|
|
|
2026-06-10 06:18:45 +03:00
|
|
|
case CodeMeasuresDelete:
|
2026-06-13 08:01:42 +03:00
|
|
|
rec := new(MeasuresDeleteRecord)
|
2026-05-10 00:59:47 +00:00
|
|
|
if err = rec.Parse(src); err != nil {
|
2026-06-14 23:12:03 +03:00
|
|
|
return
|
2026-02-10 14:02:11 +00:00
|
|
|
}
|
2026-06-14 23:12:03 +03:00
|
|
|
records = append(records, *rec)
|
2026-02-10 14:02:11 +00:00
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
return nil, fmt.Errorf("unknown record type code: %d", recordType)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// HELPERS
|
|
|
|
|
|
2026-06-14 07:57:01 +03:00
|
|
|
// func (s *WALReader) 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
|
|
|
|
|
// }
|