This commit is contained in:
2026-06-10 06:18:45 +03:00
parent 4089ca40c9
commit 9ec86282ad
47 changed files with 3493 additions and 4502 deletions

View File

@@ -1,12 +1,13 @@
package database
import (
"fmt"
"io"
bin "gordenko.dev/dima/bin/little"
qb "gordenko.dev/dima/qb"
"gordenko.dev/dima/qb/atree"
"gordenko.dev/dima/qb/txlog"
"gordenko.dev/dima/qb"
"gordenko.dev/dima/qb/enc"
"gordenko.dev/dima/qb/storage"
)
type ReplayMetric struct {
@@ -17,7 +18,7 @@ type ReplayMetric struct {
databuf []byte // якщо розмір buf == DataPageSize, то databuf буде менше (мінус футер)
tSize int
vSize int
indexLevelTails []txlog.IndexLevelTail // root - last element
indexLevelTails []storage.IndexLevelTail // root - last element
}
func (s *ReplayMetric) ReadFrom(r io.Reader) (err error) {
@@ -57,7 +58,7 @@ func (s *ReplayMetric) ReadFrom(r io.Reader) (err error) {
}
for range levelsCount {
var (
buf = make([]byte, atree.IndexPageSize)
buf = make([]byte, storage.IndexPageSize)
count int
)
count, err = bin.ReadVarSize(r)
@@ -68,7 +69,7 @@ func (s *ReplayMetric) ReadFrom(r io.Reader) (err error) {
if err != nil {
return
}
s.indexLevelTails = append(s.indexLevelTails, txlog.IndexLevelTail{
s.indexLevelTails = append(s.indexLevelTails, storage.IndexLevelTail{
Buffer: buf,
RecordsCount: count,
})
@@ -76,115 +77,256 @@ func (s *ReplayMetric) ReadFrom(r io.Reader) (err error) {
return
}
// func appendNewRecordsToIndexTail(level storage.IndexLevelTail, records []byte) {
// }
// func countReusedIndexPages(changedIndexLevels []storage.ChangedIndexLevel) (count int) {
// return
// }
func (s *ReplayMetric) MeasuresAppend(rec storage.MeasuresAppendRecord) {
timestampsSize := rec.TimestampsOffset - len(rec.Timestamps)
// копіюю нові дані із WAL з урахуванням offset-ів
copy(s.databuf[rec.ValuesOffset:], rec.Values)
copy(s.databuf[len(s.databuf)-timestampsSize:], rec.Timestamps)
//
s.tSize = timestampsSize
s.vSize = rec.ValuesOffset - len(rec.Values)
}
type AppendMeasuresResult struct {
IndexPages []txlog.PageToWrite
DataPages []txlog.PageToWrite
IndexPages []storage.PageToWrite
DataPages []storage.PageToWrite
ReusedIndexPagesCount int
ReusedDataPagesCount int
}
func (s *ReplayMetric) AppendedMeasures(rec txlog.WALRecordAppendMeasures, pagesNeeded bool) (result AppendMeasuresResult) {
func (s *ReplayMetric) MeasuresAppendWithGrow(rec storage.MeasuresAppendWithGrowRecord, isLastPacket bool) (_ AppendMeasuresResult) {
var (
reusedIndexPages int
reusedDataPages int
indexPages []storage.PageToWrite
dataPages []storage.PageToWrite
)
// Додати в index level tails недостаючі дані, або замінити
for levelIdx, change := range rec.ChangedIndexLevels {
var (
head = change.HeadIndexPage
newRecordsCount = len(change.TailRecords) / indexRecordSize
)
// 1. набиваю сторінки для перезапису в index файлі
if isLastPacket {
if head != nil {
indexPages = append(indexPages,
composeHeadIndexPage(levelIdx, s.indexLevelTails[levelIdx], head))
}
for _, x := range change.IndexPages {
indexPages = append(indexPages, storage.PageToWrite{
PageNo: x.PageNo,
Content: x.Content,
})
}
}
// 2. Вношу зміни в поточні індексні хвости
if levelIdx < len(s.indexLevelTails) {
level := s.indexLevelTails[levelIdx]
if change.CompletedIndexPage != nil {
// попередній хвіст індексного рівня перетворився на сторінку,
if head != nil {
// попередній хвіст індексного рівня перетворився на head сторінку,
// отже TailRecords - це новий хвіст
// або копіюю дані, або алокую більший буфер?
copy(level.Buffer, change.TailRecords)
level.RecordsCount = len(change.TailRecords) / indexRecordSize
level.RecordsCount = newRecordsCount
} else {
// TailRecords - це нові дані, які треба додати
pos := level.RecordsCount * indexRecordSize
copy(level.Buffer[pos:], change.TailRecords)
level.RecordsCount += newRecordsCount
}
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,
// додаю новий індексний рівень
buf := make([]byte, storage.IndexPageSize)
copy(buf, change.TailRecords)
//
s.indexLevelTails = append(s.indexLevelTails, storage.IndexLevelTail{
Buffer: buf,
RecordsCount: newRecordsCount,
})
}
// 3. рахую кількість reused індексних сторінок
if head != nil {
if head.Reused {
reusedIndexPages++
}
}
for _, x := range change.IndexPages {
if x.Reused {
reusedIndexPages++
}
}
}
// 4. набиваю сторінки для перезапису в data файлі
if isLastPacket {
dataPages = append(dataPages, composeHeadDataPage(s.databuf, rec.HeadDataPage))
for _, x := range rec.DataPages {
dataPages = append(dataPages, storage.PageToWrite{
PageNo: x.PageNo,
Content: x.Content,
})
}
}
// Перезаписати в buffer timestamps та values. Якщо розмір буферу недостатній -
// спочатку створити більший
// рахую кількість reused дата сторінок
if rec.HeadDataPage.Reused {
reusedDataPages++
}
for _, x := range rec.DataPages {
if x.Reused {
reusedDataPages++
}
}
// HeadDataPage є полюбому, оскільки це транзакція із мінімум однією заповненою
// data сторінкою. Отже TailTimestamps і TailValues - це нові хвости data рівня.
copy(s.databuf, rec.TailValues)
pos := len(s.databuf) - len(rec.TailTimestamps)
copy(s.databuf[pos:], rec.TailTimestamps)
var (
// CompletedDataPage є полюбому, оскільки це Record із мінімум одною заповненою
// data сторінкою. Отже TailTimestamps і TailValues - це нові хвости data рівня.
s.tSize = len(rec.TailTimestamps)
s.vSize = len(rec.TailValues)
requiredSpace = len(rec.TailTimestamps) + len(rec.TailValues)
databuf []byte
)
if requiredSpace < len(s.buf) {
databuf = s.buf
// fix - update sizes?
if len(rec.DataPages) == 0 {
s.lastPageNo = rec.HeadDataPage.PageNo
} 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
s.lastPageNo = rec.DataPages[len(rec.DataPages)-1].PageNo
}
//rec.TailTimestamps
//rec.TailValues
return
return AppendMeasuresResult{
IndexPages: indexPages,
DataPages: dataPages,
ReusedIndexPagesCount: reusedIndexPages,
ReusedDataPagesCount: reusedDataPages,
}
}
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,
// HELPERS
func composeHeadIndexPage(levelIdx int, level storage.IndexLevelTail, head *storage.HeadIndexPage) storage.PageToWrite {
// розраховую pos, з якого буду дописувати хвіст
pos := level.RecordsCount * indexRecordSize
// створюю копію сторінки
page := make([]byte, storage.IndexPageSize)
copy(page, level.Buffer[:pos]) // поточні дані
copy(page[pos:], head.Records)
// запечатати сторінку
calculatedCRC := storage.SealIndexPage(storage.SealIndexPageIn{
Content: page,
RecordsCount: level.RecordsCount + len(head.Records)/indexRecordSize,
ZeroLevel: levelIdx == 0,
})
// перевірка CRC
if calculatedCRC != head.Checksum {
qb.Abort(qb.WALReplayFailed,
fmt.Errorf("calculated CRC %d not equal expected %d of head page %d on index level %d",
calculatedCRC, head.Checksum, head.PageNo, levelIdx))
}
return storage.PageToWrite{
PageNo: head.PageNo,
Content: page,
}
}
func composeHeadDataPage(databuf []byte, head storage.HeadDataPage) storage.PageToWrite {
// створюю копію сторінки
var (
page = make([]byte, storage.DataPageSize)
timestampsSize = head.TimestampsOffset - len(head.Timestamps)
)
// копіюю поточні дані
copy(page, databuf)
// копіюю нові дані із WAL з урахуванням offset-ів
copy(page[head.ValuesOffset:], head.Values)
copy(page[len(page)-timestampsSize:], head.Timestamps)
// запечатати сторінку
calculatedCRC := storage.SealDataPage(storage.SealDataPageIn{
Content: page,
PrevPageNo: head.PrevPageNo,
TimestampsSize: timestampsSize,
ValuesSize: head.ValuesOffset + len(head.Values),
})
// перевірка CRC
if calculatedCRC != head.Checksum {
qb.Abort(qb.WALReplayFailed,
fmt.Errorf("calculated CRC %d not equal expected %d of head data page %d",
calculatedCRC, head.Checksum, head.PageNo))
}
return storage.PageToWrite{
PageNo: head.PageNo,
Content: page,
}
}
func (s *ReplayMetric) ToMetric() *_metric {
var (
values qb.ValueCompressor
requiredSpace = s.tSize + s.vSize
)
bufferSize := calculateDataBufferSize(requiredSpace)
if len(s.buf) > bufferSize {
s.buf = shrinkBuffer(shrinkBufferIn{
Buffer: s.buf,
TimestampsSize: s.tSize,
ValuesSize: s.vSize,
NewBufferSize: bufferSize,
})
}
// дописую дані по зміщенню
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
if s.metricType == qb.Cumulative {
values = enc.NewCumulativeDeltaCompressor(s.fracDigits, s.buf, s.vSize)
} else {
values = enc.NewInstantDeltaCompressor(s.fracDigits, s.buf, s.vSize)
}
return &_metric{
metricType: s.metricType,
fracDigits: s.fracDigits,
lastPageNo: s.lastPageNo,
lastValue: values.LastValue(),
buffer: s.buf,
timestamps: enc.NewTimeDeltaCompressor(s.buf, s.tSize),
values: values,
indexLevelTails: s.indexLevelTails,
}
}
// func (s *ReplayMetric) ApplyHeadIndexPages(changedIndexLevels txlog.ChangedIndexLevel) {
// func (s *ReplayMetric) ApplyHeadDataPage(p storage.HeadDataPage) {
// // буфер має бути розміру сторінки
// 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 storage.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,
// })
// }
//
// }
// }