Files
qb/database/replay_metric.go
2026-06-09 16:42:17 +00:00

191 lines
5.3 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package database
import (
"io"
bin "gordenko.dev/dima/bin/little"
qb "gordenko.dev/dima/qb"
"gordenko.dev/dima/qb/atree"
"gordenko.dev/dima/qb/txlog"
)
type ReplayMetric struct {
metricType qb.MetricType
fracDigits byte
lastPageNo uint32
buf []byte
databuf []byte // якщо розмір buf == DataPageSize, то databuf буде менше (мінус футер)
tSize int
vSize int
indexLevelTails []txlog.IndexLevelTail // root - last element
}
func (s *ReplayMetric) ReadFrom(r io.Reader) (err error) {
metricType, err := bin.ReadByte(r)
if err != nil {
return
}
s.metricType = qb.MetricType(metricType)
s.fracDigits, err = bin.ReadByte(r)
if err != nil {
return
}
s.lastPageNo, err = bin.ReadUint32(r)
if err != nil {
return
}
s.tSize, err = bin.ReadUint16AsInt(r)
if err != nil {
return
}
s.vSize, err = bin.ReadUint16AsInt(r)
if err != nil {
return
}
err = bin.ReadNInto(r, s.databuf[len(s.databuf)-s.tSize:])
if err != nil {
return
}
err = bin.ReadNInto(r, s.databuf[:s.vSize])
if err != nil {
return
}
//
levelsCount, err := bin.ReadVarSize(r)
if err != nil {
return
}
for range levelsCount {
var (
buf = make([]byte, atree.IndexPageSize)
count int
)
count, err = bin.ReadVarSize(r)
if err != nil {
return
}
err = bin.ReadNInto(r, buf[:count*indexRecordSize])
if err != nil {
return
}
s.indexLevelTails = append(s.indexLevelTails, txlog.IndexLevelTail{
Buffer: buf,
RecordsCount: count,
})
}
return
}
type AppendMeasuresResult struct {
IndexPages []txlog.PageToWrite
DataPages []txlog.PageToWrite
ReusedIndexPagesCount int
ReusedDataPagesCount int
}
func (s *ReplayMetric) AppendedMeasures(rec txlog.WALRecordAppendMeasures, pagesNeeded bool) (result AppendMeasuresResult) {
// Додати в index level tails недостаючі дані, або замінити
for levelIdx, change := range rec.ChangedIndexLevels {
if levelIdx < len(s.indexLevelTails) {
level := s.indexLevelTails[levelIdx]
if change.CompletedIndexPage != nil {
// попередній хвіст індексного рівня перетворився на сторінку,
// отже TailRecords - це новий хвіст
// або копіюю дані, або алокую більший буфер?
copy(level.Buffer, change.TailRecords)
level.RecordsCount = len(change.TailRecords) / indexRecordSize
} else {
// TailRecords - це нові дані, які треба додати
pos := level.RecordsCount * indexRecordSize
copy(level.Buffer[pos:], change.TailRecords)
}
s.indexLevelTails[levelIdx] = level
} else {
var (
recordsCount = len(change.TailRecords) / indexRecordSize
buffer = make([]byte, atree.IndexPageSize)
)
copy(buffer, change.TailRecords)
s.indexLevelTails = append(s.indexLevelTails, txlog.IndexLevelTail{
Buffer: buffer,
RecordsCount: recordsCount,
})
}
}
// Перезаписати в buffer timestamps та values. Якщо розмір буферу недостатній -
// спочатку створити більший
var (
// CompletedDataPage є полюбому, оскільки це Record із мінімум одною заповненою
// data сторінкою. Отже TailTimestamps і TailValues - це нові хвости data рівня.
requiredSpace = len(rec.TailTimestamps) + len(rec.TailValues)
databuf []byte
)
if requiredSpace < len(s.buf) {
databuf = s.buf
// fix - update sizes?
} else {
var buf []byte
buf, databuf = allocateBuffers(requiredSpace)
copy(databuf, rec.TailValues)
pos := len(databuf) - len(rec.TailTimestamps)
copy(databuf[pos:], rec.TailTimestamps)
// fix - replace and remeber sizes?
s.buf = buf
}
//rec.TailTimestamps
//rec.TailValues
return
}
func (s *ReplayMetric) ApplyHeadDataPage(p txlog.CompletedDataPage) {
// буфер має бути розміру сторінки
if len(s.buf) != atree.DataPageSize {
s.buf, s.databuf = growBuffers(growBuffersIn{
databuf: s.databuf,
tSize: s.tSize,
vSize: s.vSize,
requiredSpace: atree.DataPageSize,
})
}
// дописую дані по зміщенню
pos := len(s.databuf) - p.TimestampsOffset - len(p.Timestamps)
copy(s.databuf[pos:], p.Timestamps)
copy(s.databuf[p.ValuesOffset:], p.Values)
//
s.tSize = p.TimestampsOffset + len(p.Timestamps)
s.tSize = p.ValuesOffset + len(p.Values)
// fix - заполнить страницу
// fix - check checksum
}
// func (s *ReplayMetric) ApplyHeadIndexPages(changedIndexLevels txlog.ChangedIndexLevel) {
// for levelIdx, changedLevel := range changedIndexLevels {
// p := changedLevel.CompletedIndexPage
// if p != nil {
// level := s.indexLevelTails[levelIdx]
// copy(level.Buffer[level.RecordsCount*indexRecordSize:], p.Records)
// // fix - заполнить страницу
// // fix - check checksum
// s.indexPages = append(s.indexPages, txlog.PageToWrite{
// PageNo: p.PageNo,
// Content: level.Buffer,
// })
// }
// for _, x := range changedLevel.IndexPages {
// s.indexPages = append(s.indexPages, txlog.PageToWrite{
// PageNo: x.PageNo,
// Content: x.Content,
// })
// }
// }
// }