This commit is contained in:
2026-05-17 12:58:46 +00:00
parent 5ca083ece9
commit 793df29e11
3 changed files with 379 additions and 76 deletions

View File

@@ -22,21 +22,27 @@ const (
pageTypeIdx = PageSize - 5
// index page
indexRecordsQtyIdx = PageSize - 8
isDataPageNumbersIdx = PageSize - 6
indexCRC32Idx = IndexPageSize - 4
isLastLevelIdx = IndexPageSize - 5
indexRecordsQtyIdx = IndexPageSize - 7
indexRecordSize = 8
// data page
timestampsSizeIdx = PageSize - 13
valuesSizeIdx = PageSize - 11
prevPageIdx = PageSize - 9
dataCRC32Idx = DataPageSize - 4
prevPageIdx = DataPageSize - 8
valuesSizeIdx = DataPageSize - 10
timestampsSizeIdx = DataPageSize - 12
timestampSize = 4
pairSize = timestampSize + PageNoSize
indexFooterIdx = indexRecordsQtyIdx
dataFooterIdx = timestampsSizeIdx
PageSize = 8192
PageNoSize = 4
PageSize = 8192
DataPageSize = 8192
IndexPageSize = 1024
PageNoSize = 4
//
DataPagePayloadSize int = dataFooterIdx
)
@@ -161,7 +167,7 @@ func (s *Atree) findDataPage(rootPageNo uint32, timestamp uint32) (uint32, []byt
s.releasePage(indexPageNo)
// fix
if buf[isDataPageNumbersIdx] == 1 {
if buf[isLastLevelIdx] == 1 {
buf, err := s.fetchDataPage(foundPageNo)
if err != nil {
return 0, nil, fmt.Errorf("fetchDataPage(%d): %s", foundPageNo, err)
@@ -206,7 +212,7 @@ func (s *Atree) FindPathToLastPage(rootPageNo uint32) (_ PathToDataPage, err err
foundPageNo := getLastPageNo(buf)
// fix
if buf[isDataPageNumbersIdx] == 1 {
if buf[isLastLevelIdx] == 1 {
return PathToDataPage{
Legs: legs,
LastPageNo: foundPageNo,

147
atree/x.go Normal file
View File

@@ -0,0 +1,147 @@
package atree
import "gordenko.dev/dima/qb/bin"
const (
payloadSize = 24
recordSize = 4 + 4 // timestamp + pageNo
)
type DataPage struct {
PrevPageNo uint32
LowerTimestamp uint32
Timestamps [][]byte // chunks
TimestampsSize int
Values [][]byte // chunks
ValuesSize int
PageNo uint32
IsReused bool
}
type IndexPage struct {
LowerTimestamp uint32
Data []byte
PageNo uint32
IsReused bool
}
type IndexLevel struct {
// вже записані дані (лише для першої в списку індексної сторінки, беремо із _metric)
// дані потрібні, якщо сторінка буде заповнена і піде на запис в .index файл
Offset int // (заповнені одразу)
LowerTimestamp uint32 // (заповнені одразу)
Data []byte // поточні дані index сторінки (заповнені одразу)
Filled []IndexPage // пусто
}
func appendIndexRecord(data []byte, timestamp uint32, pageNo uint32) []byte {
var tmp = []byte{
0, 0, 0, 0, // timestamp
0, 0, 0, 0, // pageNo
}
bin.PutUint32(tmp, timestamp)
bin.PutUint32(tmp[4:], pageNo)
return append(data, tmp...)
}
type ChunksToDataPageReq struct {
PrevPageNo uint32
Timestamps [][]byte // chunks
TimestampsSize uint16
Values [][]byte // chunks
ValuesSize uint16
}
func ChunksToDataPage(buf []byte, req ChunksToDataPageReq) {
var (
remainingSize = int(req.TimestampsSize)
pos = 0
)
for _, chunk := range req.Timestamps {
if remainingSize >= len(chunk) {
copy(buf[pos:], chunk)
remainingSize -= len(chunk)
pos += len(chunk)
} else {
copy(buf[pos:], chunk[:remainingSize])
break
}
}
remainingSize = int(req.ValuesSize)
pos = int(req.TimestampsSize)
for _, chunk := range req.Values {
if remainingSize >= len(chunk) {
copy(buf[pos:], chunk)
remainingSize -= len(chunk)
pos += len(chunk)
} else {
copy(buf[pos:], chunk[:remainingSize])
break
}
}
bin.PutUint16(buf[timestampsSizeIdx:], req.TimestampsSize)
bin.PutUint16(buf[valuesSizeIdx:], req.ValuesSize)
bin.PutUint32(buf[prevPageIdx:], req.PrevPageNo)
bin.PutUint32(buf[dataCRC32Idx:], calcChecksum(buf[:dataCRC32Idx]))
}
func DataToIndexPage(buf []byte, data []byte, lastLevel bool) {
copy(buf, data)
bin.PutUint16(buf[indexRecordsQtyIdx:], uint16(len(data)/indexRecordSize))
if lastLevel {
buf[isLastLevelIdx] = 1
}
bin.PutUint32(buf[indexCRC32Idx:], calcChecksum(buf[:indexCRC32Idx]))
}
type Pager interface {
GetPageNumber() (uint32, bool)
}
func AppendDataPagesToTree(pager Pager, levels []*IndexLevel, dataPages []*DataPage) {
for i, d := range dataPages[1:] {
d.PageNo, d.IsReused = pager.GetPageNumber()
d.PrevPageNo = dataPages[i-1].PageNo
}
for _, d := range dataPages {
var (
upTimestamp = d.LowerTimestamp
upPageNo = d.PageNo
levelIdx = 0
)
for {
if levelIdx == len(levels) {
// encode prev page record + up*
levels = append(levels, &IndexLevel{
LowerTimestamp: upTimestamp,
Data: appendIndexRecord(nil, upTimestamp, upPageNo),
})
break
}
level := levels[levelIdx]
if len(level.Data)+recordSize <= payloadSize {
level.Data = appendIndexRecord(level.Data, upTimestamp, upPageNo)
break
}
// в буфері немає місця для додавання нової пари, отже це
// заповнена сторінка
filled := IndexPage{
LowerTimestamp: level.LowerTimestamp,
Data: level.Data,
}
filled.PageNo, filled.IsReused = pager.GetPageNumber()
level.Filled = append(level.Filled, filled)
//
level.LowerTimestamp = upTimestamp
level.Data = appendIndexRecord(nil, upTimestamp, upPageNo)
//
upPageNo = filled.PageNo
upTimestamp = filled.LowerTimestamp
levelIdx++
}
}
}