306 lines
6.7 KiB
Go
306 lines
6.7 KiB
Go
|
|
package txlog
|
||
|
|
|
||
|
|
import (
|
||
|
|
"bytes"
|
||
|
|
|
||
|
|
bin "gordenko.dev/dima/bin/little"
|
||
|
|
)
|
||
|
|
|
||
|
|
type CompletedDataPage struct {
|
||
|
|
PageNo uint32
|
||
|
|
Reused bool
|
||
|
|
PrevPageNo uint32
|
||
|
|
Checksum uint32
|
||
|
|
TimestampsOffset int // offset on 1st page
|
||
|
|
Timestamps []byte
|
||
|
|
ValuesOffset int // offset on 1st page
|
||
|
|
Values []byte
|
||
|
|
}
|
||
|
|
|
||
|
|
type CompletedIndexPage struct {
|
||
|
|
PageNo uint32
|
||
|
|
Reused bool
|
||
|
|
Checksum uint32
|
||
|
|
Records []byte // offset не потрібен, оскільки Records додаються в кінець
|
||
|
|
}
|
||
|
|
|
||
|
|
type ChangedIndexLevel struct {
|
||
|
|
CompletedIndexPage *CompletedIndexPage
|
||
|
|
IndexPages []SealedIndexPage
|
||
|
|
TailRecords []byte // payload only
|
||
|
|
}
|
||
|
|
|
||
|
|
type WALRecordAppendMeasures struct {
|
||
|
|
MetricID uint32
|
||
|
|
CompletedDataPage CompletedDataPage
|
||
|
|
DataPages []SealedDataPage
|
||
|
|
ChangedIndexLevels []ChangedIndexLevel
|
||
|
|
TailTimestamps []byte // data level tail
|
||
|
|
TailValues []byte // data level tail
|
||
|
|
}
|
||
|
|
|
||
|
|
/*
|
||
|
|
Format appended measures:
|
||
|
|
1b - tx type
|
||
|
|
4b - metricID
|
||
|
|
4b - pageNo
|
||
|
|
1b - reused
|
||
|
|
4b - prevPageNo
|
||
|
|
4b - page crc32
|
||
|
|
Nb - (varsize) timestamps offset
|
||
|
|
Nb - (varsize) timestamps tail size on 1st filled data page
|
||
|
|
Nb - timestamps tail on 1st filled data page
|
||
|
|
Nb - (varsize) values offset
|
||
|
|
Nb - (varsize) values tail size on 1st filled data page
|
||
|
|
Nb - values tail on 1st filled data page
|
||
|
|
|
||
|
|
Nb - (varsize) filled data pages qty
|
||
|
|
[
|
||
|
|
|
||
|
|
4b - pageNo
|
||
|
|
1b - reused
|
||
|
|
Nb - (data page size) page content
|
||
|
|
|
||
|
|
]
|
||
|
|
Nb - (varsize) timestamps payload size on tail
|
||
|
|
Nb - timestamps payload
|
||
|
|
Nb - (varsize) values payload size on tail
|
||
|
|
Nb - values payload
|
||
|
|
Nb - (varsize) qty of index levels
|
||
|
|
[
|
||
|
|
|
||
|
|
// NOTE!
|
||
|
|
// - idx = 0 - zeroLevel
|
||
|
|
// - skipped = maxRecordsOnIndexPage - records count
|
||
|
|
4b - pageNo of 1st index page
|
||
|
|
1b - reused
|
||
|
|
4b - page crc32
|
||
|
|
Nb - (varsize) records tail count on 1st filled index page
|
||
|
|
Nb - records tail on 1st filled index page
|
||
|
|
|
||
|
|
|
||
|
|
Nb - (varsize) qty of level filled pages
|
||
|
|
[
|
||
|
|
4b - pageNo
|
||
|
|
1b - reused
|
||
|
|
Nb - (index page size) page content
|
||
|
|
]
|
||
|
|
Nb - (varsize) size of records level tail
|
||
|
|
Nb - level tail records
|
||
|
|
|
||
|
|
]
|
||
|
|
*/
|
||
|
|
func (s WALRecordAppendMeasures) WriteTo(w *bytes.Buffer) {
|
||
|
|
w.WriteByte(CodeAppendedMeasures)
|
||
|
|
bin.WriteUint32(w, s.MetricID)
|
||
|
|
// completed data page
|
||
|
|
completed := s.CompletedDataPage
|
||
|
|
bin.WriteUint32(w, completed.PageNo)
|
||
|
|
bin.WriteBool(w, completed.Reused)
|
||
|
|
bin.WriteUint32(w, completed.PrevPageNo)
|
||
|
|
bin.WriteUint32(w, completed.Checksum)
|
||
|
|
bin.WriteVarSize(w, completed.TimestampsOffset)
|
||
|
|
bin.WriteVarSize(w, len(completed.Timestamps))
|
||
|
|
w.Write(completed.Timestamps)
|
||
|
|
bin.WriteVarSize(w, completed.ValuesOffset)
|
||
|
|
bin.WriteVarSize(w, len(completed.Values))
|
||
|
|
w.Write(completed.Values)
|
||
|
|
// data pages
|
||
|
|
bin.WriteVarSize(w, len(s.DataPages))
|
||
|
|
for _, p := range s.DataPages {
|
||
|
|
bin.WriteUint32(w, p.PageNo)
|
||
|
|
bin.WriteBool(w, p.Reused)
|
||
|
|
bin.WriteVarSize(w, len(p.Content))
|
||
|
|
w.Write(p.Content)
|
||
|
|
}
|
||
|
|
// data tail
|
||
|
|
bin.WriteVarSize(w, len(s.TailTimestamps))
|
||
|
|
w.Write(s.TailTimestamps)
|
||
|
|
bin.WriteVarSize(w, len(s.TailValues))
|
||
|
|
w.Write(s.TailValues)
|
||
|
|
// changed levels
|
||
|
|
bin.WriteVarSize(w, len(s.ChangedIndexLevels))
|
||
|
|
for _, level := range s.ChangedIndexLevels {
|
||
|
|
// completed index page
|
||
|
|
completed := level.CompletedIndexPage
|
||
|
|
bin.WriteUint32(w, completed.PageNo)
|
||
|
|
bin.WriteBool(w, completed.Reused)
|
||
|
|
bin.WriteUint32(w, completed.Checksum)
|
||
|
|
bin.WriteVarSize(w, len(completed.Records)/indexRecordSize)
|
||
|
|
w.Write(completed.Records)
|
||
|
|
// full index pages
|
||
|
|
for _, p := range level.IndexPages {
|
||
|
|
bin.WriteUint32(w, p.PageNo)
|
||
|
|
bin.WriteBool(w, p.Reused)
|
||
|
|
bin.WriteVarSize(w, len(p.Content))
|
||
|
|
w.Write(p.Content)
|
||
|
|
}
|
||
|
|
// level tail
|
||
|
|
bin.WriteVarSize(w, len(level.TailRecords)/indexRecordSize)
|
||
|
|
w.Write(level.TailRecords)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func (s *WALRecordAppendMeasures) Parse(r *bytes.Buffer) (err error) {
|
||
|
|
var (
|
||
|
|
size int
|
||
|
|
recordsCount int
|
||
|
|
)
|
||
|
|
s.MetricID, err = bin.ReadUint32(r)
|
||
|
|
if err != nil {
|
||
|
|
return
|
||
|
|
}
|
||
|
|
s.CompletedDataPage.PageNo, err = bin.ReadUint32(r)
|
||
|
|
if err != nil {
|
||
|
|
return
|
||
|
|
}
|
||
|
|
s.CompletedDataPage.Reused, err = bin.ReadBool(r)
|
||
|
|
if err != nil {
|
||
|
|
return
|
||
|
|
}
|
||
|
|
s.CompletedDataPage.PrevPageNo, err = bin.ReadUint32(r)
|
||
|
|
if err != nil {
|
||
|
|
return
|
||
|
|
}
|
||
|
|
s.CompletedDataPage.Checksum, err = bin.ReadUint32(r)
|
||
|
|
if err != nil {
|
||
|
|
return
|
||
|
|
}
|
||
|
|
s.CompletedDataPage.TimestampsOffset, err = bin.ReadVarSize(r)
|
||
|
|
if err != nil {
|
||
|
|
return
|
||
|
|
}
|
||
|
|
size, err = bin.ReadVarSize(r)
|
||
|
|
if err != nil {
|
||
|
|
return
|
||
|
|
}
|
||
|
|
s.CompletedDataPage.Timestamps, err = bin.ReadN(r, size)
|
||
|
|
if err != nil {
|
||
|
|
return
|
||
|
|
}
|
||
|
|
size, err = bin.ReadVarSize(r)
|
||
|
|
if err != nil {
|
||
|
|
return
|
||
|
|
}
|
||
|
|
s.CompletedDataPage.Values, err = bin.ReadN(r, size)
|
||
|
|
if err != nil {
|
||
|
|
return
|
||
|
|
}
|
||
|
|
// data pages
|
||
|
|
dataPagesQty, err := bin.ReadVarSize(r)
|
||
|
|
if err != nil {
|
||
|
|
return
|
||
|
|
}
|
||
|
|
for range dataPagesQty {
|
||
|
|
var (
|
||
|
|
p SealedDataPage
|
||
|
|
)
|
||
|
|
p.PageNo, err = bin.ReadUint32(r)
|
||
|
|
if err != nil {
|
||
|
|
return
|
||
|
|
}
|
||
|
|
p.Reused, err = bin.ReadBool(r)
|
||
|
|
if err != nil {
|
||
|
|
return
|
||
|
|
}
|
||
|
|
size, err = bin.ReadVarSize(r)
|
||
|
|
if err != nil {
|
||
|
|
return
|
||
|
|
}
|
||
|
|
p.Content, err = bin.ReadN(r, size)
|
||
|
|
if err != nil {
|
||
|
|
return
|
||
|
|
}
|
||
|
|
s.DataPages = append(s.DataPages, p)
|
||
|
|
}
|
||
|
|
// data tail
|
||
|
|
size, err = bin.ReadVarSize(r)
|
||
|
|
if err != nil {
|
||
|
|
return
|
||
|
|
}
|
||
|
|
s.TailTimestamps, err = bin.ReadN(r, size)
|
||
|
|
if err != nil {
|
||
|
|
return
|
||
|
|
}
|
||
|
|
size, err = bin.ReadVarSize(r)
|
||
|
|
if err != nil {
|
||
|
|
return
|
||
|
|
}
|
||
|
|
s.TailValues, err = bin.ReadN(r, size)
|
||
|
|
if err != nil {
|
||
|
|
return
|
||
|
|
}
|
||
|
|
// changed levels
|
||
|
|
changedIndexLevelsCount, err := bin.ReadVarSize(r)
|
||
|
|
if err != nil {
|
||
|
|
return
|
||
|
|
}
|
||
|
|
for range changedIndexLevelsCount {
|
||
|
|
var (
|
||
|
|
indexPagesCount int
|
||
|
|
level ChangedIndexLevel
|
||
|
|
)
|
||
|
|
// completed index page
|
||
|
|
level.CompletedIndexPage.PageNo, err = bin.ReadUint32(r)
|
||
|
|
if err != nil {
|
||
|
|
return
|
||
|
|
}
|
||
|
|
level.CompletedIndexPage.Reused, err = bin.ReadBool(r)
|
||
|
|
if err != nil {
|
||
|
|
return
|
||
|
|
}
|
||
|
|
level.CompletedIndexPage.Checksum, err = bin.ReadUint32(r)
|
||
|
|
if err != nil {
|
||
|
|
return
|
||
|
|
}
|
||
|
|
recordsCount, err = bin.ReadVarSize(r)
|
||
|
|
if err != nil {
|
||
|
|
return
|
||
|
|
}
|
||
|
|
level.CompletedIndexPage.Records, err = bin.ReadN(r, recordsCount*indexRecordSize)
|
||
|
|
if err != nil {
|
||
|
|
return
|
||
|
|
}
|
||
|
|
//
|
||
|
|
indexPagesCount, err = bin.ReadVarSize(r)
|
||
|
|
if err != nil {
|
||
|
|
return
|
||
|
|
}
|
||
|
|
for range indexPagesCount {
|
||
|
|
var p SealedIndexPage
|
||
|
|
|
||
|
|
p.PageNo, err = bin.ReadUint32(r)
|
||
|
|
if err != nil {
|
||
|
|
return
|
||
|
|
}
|
||
|
|
p.Reused, err = bin.ReadBool(r)
|
||
|
|
if err != nil {
|
||
|
|
return
|
||
|
|
}
|
||
|
|
p.Checksum, err = bin.ReadUint32(r)
|
||
|
|
if err != nil {
|
||
|
|
return
|
||
|
|
}
|
||
|
|
size, err = bin.ReadVarSize(r)
|
||
|
|
if err != nil {
|
||
|
|
return
|
||
|
|
}
|
||
|
|
p.Content, err = bin.ReadN(r, size)
|
||
|
|
if err != nil {
|
||
|
|
return
|
||
|
|
}
|
||
|
|
level.IndexPages = append(level.IndexPages, p)
|
||
|
|
}
|
||
|
|
recordsCount, err = bin.ReadVarSize(r)
|
||
|
|
if err != nil {
|
||
|
|
return
|
||
|
|
}
|
||
|
|
level.TailRecords, err = bin.ReadN(r, recordsCount*indexRecordSize)
|
||
|
|
if err != nil {
|
||
|
|
return
|
||
|
|
}
|
||
|
|
s.ChangedIndexLevels = append(s.ChangedIndexLevels, level)
|
||
|
|
}
|
||
|
|
return
|
||
|
|
}
|