tmp
This commit is contained in:
403
atree/atree.go
403
atree/atree.go
@@ -8,7 +8,6 @@ import (
|
||||
"sync"
|
||||
|
||||
"gordenko.dev/dima/qb/bin"
|
||||
"gordenko.dev/dima/qb/util"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -47,17 +46,6 @@ const (
|
||||
DataPagePayloadSize int = dataFooterIdx
|
||||
)
|
||||
|
||||
const (
|
||||
FlagReused byte = 1 // сторінка із FreeList
|
||||
FlagNewRoot byte = 2 // новая страница
|
||||
)
|
||||
|
||||
type PageToWrite struct {
|
||||
PageNo uint32
|
||||
Data []byte
|
||||
IsReused bool
|
||||
}
|
||||
|
||||
type _page struct {
|
||||
PageNo uint32
|
||||
Buf []byte
|
||||
@@ -85,13 +73,9 @@ func New(opt Options) (*Atree, error) {
|
||||
if opt.Dir == "" {
|
||||
return nil, errors.New("Dir option is required")
|
||||
}
|
||||
// if opt.RedoDir == "" {
|
||||
// return nil, errors.New("RedoDir option is required")
|
||||
// }
|
||||
if opt.DatabaseName == "" {
|
||||
return nil, errors.New("DatabaseName option is required")
|
||||
}
|
||||
|
||||
// открываю или создаю dbName.data и dbName.index файлы
|
||||
var (
|
||||
fileName = filepath.Join(opt.Dir, opt.DatabaseName+".db")
|
||||
@@ -205,193 +189,6 @@ func (s *Atree) FindPathToLastPage(rootPageNo uint32) (_ PathToDataPage, err err
|
||||
}
|
||||
}
|
||||
|
||||
// APPEND DATA PAGE
|
||||
|
||||
//MetricID uint32
|
||||
//Timestamp uint32
|
||||
//Value float64
|
||||
//RootPageNo uint32
|
||||
//PrevPageNo uint32
|
||||
|
||||
type AppendDataPageReq struct {
|
||||
Since uint32
|
||||
TimestampsChunks [][]byte
|
||||
TimestampsSize uint16
|
||||
ValuesChunks [][]byte
|
||||
ValuesSize uint16
|
||||
}
|
||||
|
||||
// type ChangedPage struct {
|
||||
// PageNo uint32
|
||||
// Data []byte
|
||||
// IsReused bool
|
||||
// }
|
||||
|
||||
// AppendDataPage - метод не записує дані в data-файл, а лише змінює дані
|
||||
// в page cache та freeList і повертає звіт що змінено.
|
||||
// Цей звіт txlog має записати в transaction log і лише потім можна змінювати data файл.
|
||||
// Є ідея - записати у index файли заглушки 255,255,255,255 замість номерів сторінок і зберегти зміщення.
|
||||
// А потім одним викликом отримати із FreeList список вільних сторінок.
|
||||
// Тому що є проблема із відновленням FreeList після збою, якщо з нього будуть паралельно
|
||||
// забирати та добавляти номери сторінок інші потоки.
|
||||
// Це буде працювати, якщо додавання в txlog і маніпуляції із freeList будуть відбуватись в одному потоці
|
||||
// NotLinkedDataPage - це data сторінка із payload, але без встановленого prevPageNo та без розрахованого CRC32
|
||||
type NotLinkedDataPage struct {
|
||||
Since uint32
|
||||
Data []byte
|
||||
}
|
||||
|
||||
func (s NotLinkedDataPage) SetPrevPageNo(prevPageNo uint32) {
|
||||
bin.PutUint32(s.Data[prevPageIdx:], prevPageNo)
|
||||
bin.PutUint32(s.Data[crc32Idx:], util.CalcChecksum(s.Data[:crc32Idx]))
|
||||
}
|
||||
|
||||
type AppendDataPagesReq struct {
|
||||
LastPageNo uint32
|
||||
Legs []PathLeg
|
||||
DataPages []NotLinkedDataPage
|
||||
}
|
||||
|
||||
type Report struct {
|
||||
NewRootPageNo uint32
|
||||
LastPageNo uint32
|
||||
Pages []PageToWrite
|
||||
}
|
||||
|
||||
// Можливо atree не треба блокувати при читанні. Я можу створити копії index сторінок,
|
||||
// а потім під 1 мутексом замінити в pages.
|
||||
func (s *Atree) AppendDataPages(req AppendDataPagesReq) Report {
|
||||
var (
|
||||
//pagesToRelease []uint32
|
||||
newRootPageNo uint32
|
||||
lastPageNo = req.LastPageNo
|
||||
pages []PageToWrite
|
||||
legs = req.Legs
|
||||
changed = make(map[uint32]PathLeg)
|
||||
)
|
||||
|
||||
for _, p := range req.DataPages {
|
||||
newDataPageNo, isReused := s.allocPageNumber() // alloc only number
|
||||
// pagesToRelease = append(pagesToRelease, newDataPage.PageNo)
|
||||
|
||||
// set prevPageNo
|
||||
p.SetPrevPageNo(lastPageNo)
|
||||
|
||||
pages = append(pages, PageToWrite{
|
||||
PageNo: newDataPageNo,
|
||||
Data: p.Data,
|
||||
IsReused: isReused,
|
||||
})
|
||||
|
||||
// FIX - після додавання index page потрібно модифікувати path,
|
||||
// або будувати щось типу дерева знизу вверх
|
||||
|
||||
if len(legs) > 0 {
|
||||
newPageNo := newDataPageNo
|
||||
lastIdx := len(legs) - 1
|
||||
|
||||
for legIdx := lastIdx; legIdx >= 0; legIdx-- {
|
||||
leg := req.Legs[legIdx]
|
||||
ok := appendPair(leg.Data, p.Since, newPageNo)
|
||||
if ok {
|
||||
// index FIX
|
||||
// потрібно запам'ятати змінені сторінки, але їх можуть змінювати
|
||||
// кілька ітерацій, тому додавати в Pages не можна.
|
||||
// на індексній сторінці достатньо місця. Запис вставлено.
|
||||
changed[leg.PageNo] = leg
|
||||
break
|
||||
}
|
||||
// на індексній сторінці НЕ достатньо місця. Створюю нову.
|
||||
newIndexPage := s.allocPage()
|
||||
//pagesToRelease = append(pagesToRelease, newIndexPage.PageNo)
|
||||
appendPair(newIndexPage.Data, p.Since, newPageNo)
|
||||
// ставлю мітку що всі pageNo на сторінці - це data pageNo
|
||||
// fix - єдина оптимізація від існування isDataPageNumbersIdx - getAllPages не завантажує data pages
|
||||
// if legIdx == lastIdx {
|
||||
// newIndexPage.Data[isDataPageNumbersIdx] = 1
|
||||
// }
|
||||
pages = append(pages, PageToWrite{
|
||||
PageNo: newIndexPage.PageNo,
|
||||
Data: newIndexPage.Data,
|
||||
IsReused: newIndexPage.IsReused,
|
||||
})
|
||||
// замінюю крок в path на новий
|
||||
legs[legIdx] = PathLeg{
|
||||
PageNo: newIndexPage.PageNo,
|
||||
Data: newIndexPage.Data,
|
||||
}
|
||||
//
|
||||
newPageNo = newIndexPage.PageNo
|
||||
|
||||
if legIdx == 0 {
|
||||
newRoot := s.allocPage()
|
||||
//pagesToRelease = append(pagesToRelease, newRoot.PageNo)
|
||||
appendPair(newRoot.Data, getSince(leg.Data), leg.PageNo) // old rootPageNo
|
||||
appendPair(newRoot.Data, p.Since, newIndexPage.PageNo)
|
||||
|
||||
// Фиксирую новый root в REDO логе
|
||||
pages = append(pages, PageToWrite{
|
||||
PageNo: newRoot.PageNo,
|
||||
Data: newRoot.Data,
|
||||
IsReused: newRoot.IsReused,
|
||||
})
|
||||
newRootPageNo = newRoot.PageNo
|
||||
// додаю в початок списку кроків нову root сторінку
|
||||
legs = append([]PathLeg{
|
||||
{
|
||||
PageNo: newRoot.PageNo,
|
||||
Data: newRoot.Data,
|
||||
},
|
||||
}, legs...)
|
||||
break
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// індексну root сторінку створюю одразу для першої data сторінки,
|
||||
// root data сторінки, як в B+Tree не буває.
|
||||
newRoot := s.allocPage()
|
||||
//pagesToRelease = append(pagesToRelease, newRoot.PageNo)
|
||||
//newRoot.Data[isDataPageNumbersIdx] = 1
|
||||
appendPair(newRoot.Data, p.Since, newDataPageNo)
|
||||
|
||||
pages = append(pages, PageToWrite{
|
||||
PageNo: newRoot.PageNo,
|
||||
Data: newRoot.Data,
|
||||
IsReused: newRoot.IsReused,
|
||||
})
|
||||
newRootPageNo = newRoot.PageNo
|
||||
// додаю в початок списку кроків нову root сторінку
|
||||
legs = append([]PathLeg{
|
||||
{
|
||||
PageNo: newRoot.PageNo,
|
||||
Data: newRoot.Data,
|
||||
},
|
||||
}, legs...)
|
||||
}
|
||||
|
||||
// На данний момен схема - наступна. Всі сторінки - data та index - зафіксовані в кеші.
|
||||
// Отже запис на диск пройде максимально швидко. Після цього ReferenceCount кожної
|
||||
// сторінки зменшиться на 1. Оскільки на метрику утримується XLock, сторінки мають
|
||||
// ReferenceCount = 1 (немає інших читачів).
|
||||
// for _, pageNo := range indexPagesToRelease {
|
||||
// s.releasePage(pageNo)
|
||||
// }
|
||||
lastPageNo = newDataPageNo
|
||||
}
|
||||
for _, leg := range changed {
|
||||
// fix recalc checksum
|
||||
pages = append(pages, PageToWrite{
|
||||
PageNo: leg.PageNo,
|
||||
Data: leg.Data,
|
||||
})
|
||||
}
|
||||
return Report{
|
||||
NewRootPageNo: newRootPageNo,
|
||||
LastPageNo: lastPageNo,
|
||||
Pages: pages,
|
||||
}
|
||||
}
|
||||
|
||||
// DELETE
|
||||
|
||||
type Level struct {
|
||||
@@ -469,3 +266,203 @@ func (s *Atree) GetAllPages(rootPageNo uint32) (_ []uint32, err error) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// const (
|
||||
// FlagReused byte = 1 // сторінка із FreeList
|
||||
// FlagNewRoot byte = 2 // новая страница
|
||||
// )
|
||||
|
||||
// type PageToWrite struct {
|
||||
// PageNo uint32
|
||||
// Data []byte
|
||||
// IsReused bool
|
||||
// }
|
||||
|
||||
//type AppendDataPagesReq struct {
|
||||
// LastPageNo uint32
|
||||
// Legs []PathLeg
|
||||
// DataPages []NotLinkedDataPage
|
||||
// }
|
||||
|
||||
// type Report struct {
|
||||
// NewRootPageNo uint32
|
||||
// LastPageNo uint32
|
||||
// Pages []PageToWrite
|
||||
// }
|
||||
|
||||
// // Можливо atree не треба блокувати при читанні. Я можу створити копії index сторінок,
|
||||
// // а потім під 1 мутексом замінити в pages.
|
||||
// func (s *Atree) AppendDataPages(req AppendDataPagesReq) Report {
|
||||
// var (
|
||||
// //pagesToRelease []uint32
|
||||
// newRootPageNo uint32
|
||||
// lastPageNo = req.LastPageNo
|
||||
// pages []PageToWrite
|
||||
// legs = req.Legs
|
||||
// changed = make(map[uint32]PathLeg)
|
||||
// )
|
||||
|
||||
// for _, p := range req.DataPages {
|
||||
// newDataPageNo, isReused := s.allocPageNumber() // alloc only number
|
||||
// // pagesToRelease = append(pagesToRelease, newDataPage.PageNo)
|
||||
|
||||
// // set prevPageNo
|
||||
// p.SetPrevPageNo(lastPageNo)
|
||||
|
||||
// pages = append(pages, PageToWrite{
|
||||
// PageNo: newDataPageNo,
|
||||
// Data: p.Data,
|
||||
// IsReused: isReused,
|
||||
// })
|
||||
|
||||
// // FIX - після додавання index page потрібно модифікувати path,
|
||||
// // або будувати щось типу дерева знизу вверх
|
||||
|
||||
// if len(legs) > 0 {
|
||||
// newPageNo := newDataPageNo
|
||||
// lastIdx := len(legs) - 1
|
||||
|
||||
// for legIdx := lastIdx; legIdx >= 0; legIdx-- {
|
||||
// leg := req.Legs[legIdx]
|
||||
// ok := appendPair(leg.Data, p.Since, newPageNo)
|
||||
// if ok {
|
||||
// // index FIX
|
||||
// // потрібно запам'ятати змінені сторінки, але їх можуть змінювати
|
||||
// // кілька ітерацій, тому додавати в Pages не можна.
|
||||
// // на індексній сторінці достатньо місця. Запис вставлено.
|
||||
// changed[leg.PageNo] = leg
|
||||
// break
|
||||
// }
|
||||
// // на індексній сторінці НЕ достатньо місця. Створюю нову.
|
||||
// newIndexPage := s.allocPage()
|
||||
// //pagesToRelease = append(pagesToRelease, newIndexPage.PageNo)
|
||||
// appendPair(newIndexPage.Data, p.Since, newPageNo)
|
||||
// // ставлю мітку що всі pageNo на сторінці - це data pageNo
|
||||
// // fix - єдина оптимізація від існування isDataPageNumbersIdx - getAllPages не завантажує data pages
|
||||
// // if legIdx == lastIdx {
|
||||
// // newIndexPage.Data[isDataPageNumbersIdx] = 1
|
||||
// // }
|
||||
// pages = append(pages, PageToWrite{
|
||||
// PageNo: newIndexPage.PageNo,
|
||||
// Data: newIndexPage.Data,
|
||||
// IsReused: newIndexPage.IsReused,
|
||||
// })
|
||||
// // замінюю крок в path на новий
|
||||
// legs[legIdx] = PathLeg{
|
||||
// PageNo: newIndexPage.PageNo,
|
||||
// Data: newIndexPage.Data,
|
||||
// }
|
||||
// //
|
||||
// newPageNo = newIndexPage.PageNo
|
||||
|
||||
// if legIdx == 0 {
|
||||
// newRoot := s.allocPage()
|
||||
// //pagesToRelease = append(pagesToRelease, newRoot.PageNo)
|
||||
// appendPair(newRoot.Data, getSince(leg.Data), leg.PageNo) // old rootPageNo
|
||||
// appendPair(newRoot.Data, p.Since, newIndexPage.PageNo)
|
||||
|
||||
// // Фиксирую новый root в REDO логе
|
||||
// pages = append(pages, PageToWrite{
|
||||
// PageNo: newRoot.PageNo,
|
||||
// Data: newRoot.Data,
|
||||
// IsReused: newRoot.IsReused,
|
||||
// })
|
||||
// newRootPageNo = newRoot.PageNo
|
||||
// // додаю в початок списку кроків нову root сторінку
|
||||
// legs = append([]PathLeg{
|
||||
// {
|
||||
// PageNo: newRoot.PageNo,
|
||||
// Data: newRoot.Data,
|
||||
// },
|
||||
// }, legs...)
|
||||
// break
|
||||
// }
|
||||
// }
|
||||
// } else {
|
||||
// // індексну root сторінку створюю одразу для першої data сторінки,
|
||||
// // root data сторінки, як в B+Tree не буває.
|
||||
// newRoot := s.allocPage()
|
||||
// //pagesToRelease = append(pagesToRelease, newRoot.PageNo)
|
||||
// //newRoot.Data[isDataPageNumbersIdx] = 1
|
||||
// appendPair(newRoot.Data, p.Since, newDataPageNo)
|
||||
|
||||
// pages = append(pages, PageToWrite{
|
||||
// PageNo: newRoot.PageNo,
|
||||
// Data: newRoot.Data,
|
||||
// IsReused: newRoot.IsReused,
|
||||
// })
|
||||
// newRootPageNo = newRoot.PageNo
|
||||
// // додаю в початок списку кроків нову root сторінку
|
||||
// legs = append([]PathLeg{
|
||||
// {
|
||||
// PageNo: newRoot.PageNo,
|
||||
// Data: newRoot.Data,
|
||||
// },
|
||||
// }, legs...)
|
||||
// }
|
||||
|
||||
// // На данний момен схема - наступна. Всі сторінки - data та index - зафіксовані в кеші.
|
||||
// // Отже запис на диск пройде максимально швидко. Після цього ReferenceCount кожної
|
||||
// // сторінки зменшиться на 1. Оскільки на метрику утримується XLock, сторінки мають
|
||||
// // ReferenceCount = 1 (немає інших читачів).
|
||||
// // for _, pageNo := range indexPagesToRelease {
|
||||
// // s.releasePage(pageNo)
|
||||
// // }
|
||||
// lastPageNo = newDataPageNo
|
||||
// }
|
||||
// for _, leg := range changed {
|
||||
// // fix recalc checksum
|
||||
// pages = append(pages, PageToWrite{
|
||||
// PageNo: leg.PageNo,
|
||||
// Data: leg.Data,
|
||||
// })
|
||||
// }
|
||||
// return Report{
|
||||
// NewRootPageNo: newRootPageNo,
|
||||
// LastPageNo: lastPageNo,
|
||||
// Pages: pages,
|
||||
// }
|
||||
// }
|
||||
|
||||
// APPEND DATA PAGE
|
||||
|
||||
//MetricID uint32
|
||||
//Timestamp uint32
|
||||
//Value float64
|
||||
//RootPageNo uint32
|
||||
//PrevPageNo uint32
|
||||
|
||||
// type AppendDataPageReq struct {
|
||||
// Since uint32
|
||||
// TimestampsChunks [][]byte
|
||||
// TimestampsSize uint16
|
||||
// ValuesChunks [][]byte
|
||||
// ValuesSize uint16
|
||||
// }
|
||||
|
||||
// type ChangedPage struct {
|
||||
// PageNo uint32
|
||||
// Data []byte
|
||||
// IsReused bool
|
||||
// }
|
||||
|
||||
// AppendDataPage - метод не записує дані в data-файл, а лише змінює дані
|
||||
// в page cache та freeList і повертає звіт що змінено.
|
||||
// Цей звіт txlog має записати в transaction log і лише потім можна змінювати data файл.
|
||||
// Є ідея - записати у index файли заглушки 255,255,255,255 замість номерів сторінок і зберегти зміщення.
|
||||
// А потім одним викликом отримати із FreeList список вільних сторінок.
|
||||
// Тому що є проблема із відновленням FreeList після збою, якщо з нього будуть паралельно
|
||||
// забирати та добавляти номери сторінок інші потоки.
|
||||
// Це буде працювати, якщо додавання в txlog і маніпуляції із freeList будуть відбуватись в одному потоці
|
||||
// NotLinkedDataPage - це data сторінка із payload, але без встановленого prevPageNo та без розрахованого CRC32
|
||||
// type NotLinkedDataPage struct {
|
||||
// Since uint32
|
||||
// Data []byte
|
||||
// }
|
||||
|
||||
// func (s NotLinkedDataPage) SetPrevPageNo(prevPageNo uint32) {
|
||||
// bin.PutUint32(s.Data[prevPageIdx:], prevPageNo)
|
||||
// bin.PutUint32(s.Data[crc32Idx:], util.CalcChecksum(s.Data[:crc32Idx]))
|
||||
// }
|
||||
|
||||
//
|
||||
|
||||
@@ -4,23 +4,23 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
octopus "gordenko.dev/dima/qb"
|
||||
"gordenko.dev/dima/qb"
|
||||
"gordenko.dev/dima/qb/bin"
|
||||
"gordenko.dev/dima/qb/enc"
|
||||
)
|
||||
|
||||
type BackwardCursor struct {
|
||||
metricType octopus.MetricType
|
||||
metricType qb.MetricType
|
||||
fracDigits byte
|
||||
atree *Atree
|
||||
pageNo uint32
|
||||
pageData []byte
|
||||
timestampDecompressor octopus.TimestampDecompressor
|
||||
valueDecompressor octopus.ValueDecompressor
|
||||
timestampDecompressor qb.TimestampDecompressor
|
||||
valueDecompressor qb.ValueDecompressor
|
||||
}
|
||||
|
||||
type BackwardCursorOptions struct {
|
||||
MetricType octopus.MetricType
|
||||
MetricType qb.MetricType
|
||||
FracDigits byte
|
||||
PageNo uint32
|
||||
PageData []byte
|
||||
@@ -29,12 +29,12 @@ type BackwardCursorOptions struct {
|
||||
|
||||
func NewBackwardCursor(opt BackwardCursorOptions) (*BackwardCursor, error) {
|
||||
switch opt.MetricType {
|
||||
case octopus.Instant, octopus.Cumulative:
|
||||
case qb.Instant, qb.Cumulative:
|
||||
// ok
|
||||
default:
|
||||
return nil, fmt.Errorf("MetricType option has wrong value: %d", opt.MetricType)
|
||||
}
|
||||
if opt.FracDigits > octopus.MaxFracDigits {
|
||||
if opt.FracDigits > qb.MaxFracDigits {
|
||||
return nil, errors.New("FracDigits option is required")
|
||||
}
|
||||
if opt.Atree == nil {
|
||||
@@ -137,11 +137,11 @@ func (s *BackwardCursor) makeDecompressors() error {
|
||||
vbuf := s.pageData[timestampsPayloadSize : timestampsPayloadSize+valuesPayloadSize]
|
||||
|
||||
switch s.metricType {
|
||||
case octopus.Instant:
|
||||
case qb.Instant:
|
||||
s.valueDecompressor = enc.NewReverseInstantDeltaDecompressor(
|
||||
vbuf, s.fracDigits)
|
||||
|
||||
case octopus.Cumulative:
|
||||
case qb.Cumulative:
|
||||
s.valueDecompressor = enc.NewReverseCumulativeDeltaDecompressor(
|
||||
vbuf, s.fracDigits)
|
||||
|
||||
@@ -151,8 +151,8 @@ func (s *BackwardCursor) makeDecompressors() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func makeDecompressors(pageData []byte, metricType octopus.MetricType, fracDigits byte) (
|
||||
octopus.TimestampDecompressor, octopus.ValueDecompressor, error,
|
||||
func makeDecompressors(pageData []byte, metricType qb.MetricType, fracDigits byte) (
|
||||
qb.TimestampDecompressor, qb.ValueDecompressor, error,
|
||||
) {
|
||||
timestampsPayloadSize := bin.GetUint16(pageData[timestampsSizeIdx:])
|
||||
valuesPayloadSize := bin.GetUint16(pageData[valuesSizeIdx:])
|
||||
@@ -170,13 +170,13 @@ func makeDecompressors(pageData []byte, metricType octopus.MetricType, fracDigit
|
||||
|
||||
vbuf := pageData[timestampsPayloadSize : timestampsPayloadSize+valuesPayloadSize]
|
||||
|
||||
var valueDecompressor octopus.ValueDecompressor
|
||||
var valueDecompressor qb.ValueDecompressor
|
||||
switch metricType {
|
||||
case octopus.Instant:
|
||||
case qb.Instant:
|
||||
valueDecompressor = enc.NewReverseInstantDeltaDecompressor(
|
||||
vbuf, fracDigits)
|
||||
|
||||
case octopus.Cumulative:
|
||||
case qb.Cumulative:
|
||||
valueDecompressor = enc.NewReverseCumulativeDeltaDecompressor(
|
||||
vbuf, fracDigits)
|
||||
|
||||
|
||||
62
atree/io.go
62
atree/io.go
@@ -13,11 +13,11 @@ import (
|
||||
)
|
||||
|
||||
// fix - додати ID, щоб потім перемістити із тимчасового буфера в pages, або звільнити
|
||||
type AllocatedPage struct {
|
||||
PageNo uint32
|
||||
Data []byte
|
||||
IsReused bool
|
||||
}
|
||||
// type AllocatedPage struct {
|
||||
// PageNo uint32
|
||||
// Data []byte
|
||||
// IsReused bool
|
||||
// }
|
||||
|
||||
type readResult struct {
|
||||
Data []byte
|
||||
@@ -110,33 +110,33 @@ func (s *Atree) allocPageNumber() (uint32, bool) {
|
||||
return 0, true
|
||||
}
|
||||
|
||||
func (s *Atree) allocPage() AllocatedPage {
|
||||
var (
|
||||
allocated = AllocatedPage{
|
||||
Data: make([]byte, PageSize),
|
||||
}
|
||||
)
|
||||
//allocated.PageNo = s.freelist.ReservePage()
|
||||
s.mutex.Lock()
|
||||
// if allocated.PageNo > 0 {
|
||||
// allocated.IsReused = true
|
||||
// } else {
|
||||
// if s.allocatedPagesQty == math.MaxUint32 {
|
||||
// qb.Abort(qb.MaxAtreeSizeExceeded, errors.New("no space in Atree index"))
|
||||
// }
|
||||
// s.allocatedPagesQty++
|
||||
// allocated.PageNo = s.allocatedPagesQty
|
||||
// }
|
||||
// func (s *Atree) allocPage() AllocatedPage {
|
||||
// var (
|
||||
// allocated = AllocatedPage{
|
||||
// Data: make([]byte, PageSize),
|
||||
// }
|
||||
// )
|
||||
// //allocated.PageNo = s.freelist.ReservePage()
|
||||
// s.mutex.Lock()
|
||||
// // if allocated.PageNo > 0 {
|
||||
// // allocated.IsReused = true
|
||||
// // } else {
|
||||
// // if s.allocatedPagesQty == math.MaxUint32 {
|
||||
// // qb.Abort(qb.MaxAtreeSizeExceeded, errors.New("no space in Atree index"))
|
||||
// // }
|
||||
// // s.allocatedPagesQty++
|
||||
// // allocated.PageNo = s.allocatedPagesQty
|
||||
// // }
|
||||
|
||||
// s.pages[allocated.PageNo] = &_page{
|
||||
// // fix pageType
|
||||
// PageNo: allocated.PageNo,
|
||||
// Buf: allocated.Data,
|
||||
// ReferenceCount: 1,
|
||||
// }
|
||||
s.mutex.Unlock()
|
||||
return allocated
|
||||
}
|
||||
// // s.pages[allocated.PageNo] = &_page{
|
||||
// // // fix pageType
|
||||
// // PageNo: allocated.PageNo,
|
||||
// // Buf: allocated.Data,
|
||||
// // ReferenceCount: 1,
|
||||
// // }
|
||||
// s.mutex.Unlock()
|
||||
// return allocated
|
||||
// }
|
||||
|
||||
// // fix - без freelist
|
||||
// func (s *Atree) allocIndexPage() AllocatedPage {
|
||||
|
||||
112
atree/misc.go
112
atree/misc.go
@@ -55,51 +55,51 @@ func BinarySearch(qty int, keyComparator bin.KeyComparator) (elemIdx int, isFoun
|
||||
}
|
||||
}
|
||||
|
||||
type ChunksToNotLinkedDataPageReq struct {
|
||||
// PrevPageNo uint32
|
||||
TimestampsChunks [][]byte
|
||||
TimestampsSize uint16
|
||||
ValuesChunks [][]byte
|
||||
ValuesSize uint16
|
||||
}
|
||||
// type ChunksToNotLinkedDataPageReq struct {
|
||||
// // PrevPageNo uint32
|
||||
// TimestampsChunks [][]byte
|
||||
// TimestampsSize uint16
|
||||
// ValuesChunks [][]byte
|
||||
// ValuesSize uint16
|
||||
// }
|
||||
|
||||
func ChunksToNotLinkedDataPage(buf []byte, req ChunksToNotLinkedDataPageReq) {
|
||||
bin.PutUint16(buf[timestampsSizeIdx:], req.TimestampsSize)
|
||||
bin.PutUint16(buf[valuesSizeIdx:], req.ValuesSize)
|
||||
// func ChunksToNotLinkedDataPage(buf []byte, req ChunksToNotLinkedDataPageReq) {
|
||||
// bin.PutUint16(buf[timestampsSizeIdx:], req.TimestampsSize)
|
||||
// bin.PutUint16(buf[valuesSizeIdx:], req.ValuesSize)
|
||||
|
||||
var (
|
||||
remainingSize = int(req.TimestampsSize)
|
||||
pos = 0
|
||||
)
|
||||
for _, chunk := range req.TimestampsChunks {
|
||||
if remainingSize >= len(chunk) {
|
||||
copy(buf[pos:], chunk)
|
||||
remainingSize -= len(chunk)
|
||||
pos += len(chunk)
|
||||
} else {
|
||||
copy(buf[pos:], chunk[:remainingSize])
|
||||
break
|
||||
}
|
||||
}
|
||||
// var (
|
||||
// remainingSize = int(req.TimestampsSize)
|
||||
// pos = 0
|
||||
// )
|
||||
// for _, chunk := range req.TimestampsChunks {
|
||||
// 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)
|
||||
// remainingSize = int(req.ValuesSize)
|
||||
// pos = int(req.TimestampsSize)
|
||||
|
||||
for _, chunk := range req.ValuesChunks {
|
||||
if remainingSize >= len(chunk) {
|
||||
copy(buf[pos:], chunk)
|
||||
remainingSize -= len(chunk)
|
||||
pos += len(chunk)
|
||||
} else {
|
||||
copy(buf[pos:], chunk[:remainingSize])
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
// for _, chunk := range req.ValuesChunks {
|
||||
// if remainingSize >= len(chunk) {
|
||||
// copy(buf[pos:], chunk)
|
||||
// remainingSize -= len(chunk)
|
||||
// pos += len(chunk)
|
||||
// } else {
|
||||
// copy(buf[pos:], chunk[:remainingSize])
|
||||
// break
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
func setPrevPageNo(buf []byte, pageNo uint32) {
|
||||
bin.PutUint32(buf[prevPageIdx:], pageNo)
|
||||
}
|
||||
// func setPrevPageNo(buf []byte, pageNo uint32) {
|
||||
// bin.PutUint32(buf[prevPageIdx:], pageNo)
|
||||
// }
|
||||
|
||||
func getPrevPageNo(buf []byte) uint32 {
|
||||
return bin.GetUint32(buf[prevPageIdx:])
|
||||
@@ -191,20 +191,20 @@ func listPageNumbersSince(buf []byte, timestamp uint32) (pageNumbers []uint32) {
|
||||
return
|
||||
}
|
||||
|
||||
func getSince(buf []byte) uint32 {
|
||||
return bin.GetUint32(buf[0:])
|
||||
}
|
||||
// func getSince(buf []byte) uint32 {
|
||||
// return bin.GetUint32(buf[0:])
|
||||
// }
|
||||
|
||||
func appendPair(buf []byte, timestamp uint32, pageNo uint32) bool {
|
||||
qty := bin.GetUint16AsInt(buf[indexRecordsQtyIdx:])
|
||||
free := indexFooterIdx - qty*pairSize
|
||||
if free < pairSize {
|
||||
return false
|
||||
}
|
||||
pos := qty * timestampSize
|
||||
bin.PutUint32(buf[pos:], timestamp)
|
||||
pos = indexFooterIdx - (qty+1)*PageNoSize
|
||||
bin.PutUint32(buf[pos:], pageNo)
|
||||
bin.PutIntAsUint16(buf[indexRecordsQtyIdx:], qty+1)
|
||||
return true
|
||||
}
|
||||
// func appendPair(buf []byte, timestamp uint32, pageNo uint32) bool {
|
||||
// qty := bin.GetUint16AsInt(buf[indexRecordsQtyIdx:])
|
||||
// free := indexFooterIdx - qty*pairSize
|
||||
// if free < pairSize {
|
||||
// return false
|
||||
// }
|
||||
// pos := qty * timestampSize
|
||||
// bin.PutUint32(buf[pos:], timestamp)
|
||||
// pos = indexFooterIdx - (qty+1)*PageNoSize
|
||||
// bin.PutUint32(buf[pos:], pageNo)
|
||||
// bin.PutIntAsUint16(buf[indexRecordsQtyIdx:], qty+1)
|
||||
// return true
|
||||
// }
|
||||
|
||||
@@ -1,96 +0,0 @@
|
||||
package redox
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"hash/crc32"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"gordenko.dev/dima/qb/bin"
|
||||
)
|
||||
|
||||
type REDOFile struct {
|
||||
MetricID uint32
|
||||
Timestamp uint32
|
||||
Value float64
|
||||
IsDataPageReused bool
|
||||
DataPage PageToWrite
|
||||
IsRootChanged bool
|
||||
RootPageNo uint32
|
||||
ReusedIndexPages []uint32
|
||||
IndexPages []PageToWrite
|
||||
}
|
||||
|
||||
type ReadREDOFileReq struct {
|
||||
FileName string
|
||||
DataPageSize int
|
||||
IndexPageSize int
|
||||
}
|
||||
|
||||
func ReadREDOFile(req ReadREDOFileReq) (*REDOFile, error) {
|
||||
buf, err := os.ReadFile(req.FileName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(buf) < 25 {
|
||||
return nil, io.EOF
|
||||
}
|
||||
|
||||
var (
|
||||
end = len(buf) - 4
|
||||
payload = buf[:end]
|
||||
checksum = bin.GetUint32(buf[end:])
|
||||
calculatedChecksum = crc32.ChecksumIEEE(payload)
|
||||
)
|
||||
|
||||
// Помилка чексуми означає що файл або недописаний, або пошкодженний
|
||||
if checksum != calculatedChecksum {
|
||||
return nil, fmt.Errorf("written checksum %d not equal calculated checksum %d",
|
||||
checksum, calculatedChecksum)
|
||||
}
|
||||
|
||||
var (
|
||||
redoLog = REDOFile{
|
||||
MetricID: bin.GetUint32(buf[0:]),
|
||||
Timestamp: bin.GetUint32(buf[4:]),
|
||||
Value: bin.GetFloat64(buf[8:]),
|
||||
IsDataPageReused: buf[16] == 1,
|
||||
DataPage: PageToWrite{
|
||||
PageNo: bin.GetUint32(buf[17:]),
|
||||
Data: buf[21 : 21+req.DataPageSize],
|
||||
},
|
||||
}
|
||||
pos = 21 + req.DataPageSize
|
||||
)
|
||||
|
||||
for {
|
||||
if pos == len(payload) {
|
||||
return &redoLog, nil
|
||||
}
|
||||
|
||||
if pos > len(payload) {
|
||||
return nil, io.EOF
|
||||
}
|
||||
|
||||
flags := buf[pos]
|
||||
|
||||
item := PageToWrite{
|
||||
PageNo: bin.GetUint32(buf[pos+1:]),
|
||||
}
|
||||
pos += 5 // flags + pageNo
|
||||
item.Data = buf[pos : pos+req.IndexPageSize]
|
||||
pos += req.IndexPageSize
|
||||
|
||||
redoLog.IndexPages = append(redoLog.IndexPages, item)
|
||||
|
||||
if (flags & FlagReused) == FlagReused {
|
||||
redoLog.ReusedIndexPages = append(redoLog.ReusedIndexPages, item.PageNo)
|
||||
}
|
||||
|
||||
if (flags & FlagNewRoot) == FlagNewRoot {
|
||||
redoLog.IsRootChanged = true
|
||||
redoLog.RootPageNo = item.PageNo
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,207 +0,0 @@
|
||||
package redox
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"hash"
|
||||
"hash/crc32"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"gordenko.dev/dima/qb/bin"
|
||||
)
|
||||
|
||||
const (
|
||||
FlagReused byte = 1 // сторінка із FreeList
|
||||
FlagNewRoot byte = 2 // новая страница
|
||||
)
|
||||
|
||||
type PageToWrite struct {
|
||||
PageNo uint32
|
||||
Data []byte
|
||||
}
|
||||
|
||||
type Writer struct {
|
||||
metricID uint32
|
||||
timestamp uint32
|
||||
value float64
|
||||
tmp []byte
|
||||
fileName string
|
||||
file *os.File
|
||||
hasher hash.Hash32
|
||||
isDataPageReused bool
|
||||
dataPageNo uint32
|
||||
isRootChanged bool
|
||||
newRootPageNo uint32
|
||||
indexPages []uint32
|
||||
reusedIndexPages []uint32
|
||||
indexPagesToWrite []PageToWrite
|
||||
}
|
||||
|
||||
type WriterOptions struct {
|
||||
Dir string
|
||||
MetricID uint32
|
||||
Value float64
|
||||
Timestamp uint32
|
||||
IsDataPageReused bool
|
||||
DataPageNo uint32
|
||||
Page []byte
|
||||
}
|
||||
|
||||
// dataPage можно записати 1 раз. Щоб не заплутувати інтерфейс - передаю data сторінку
|
||||
// через Options. Index сторінок може бути від 1 до N, тому виділяю окремий метод
|
||||
func NewWriter(opt WriterOptions) (*Writer, error) {
|
||||
if opt.Dir == "" {
|
||||
return nil, errors.New("Dir option is required")
|
||||
}
|
||||
if opt.MetricID == 0 {
|
||||
return nil, errors.New("MetricID option is required")
|
||||
}
|
||||
if opt.DataPageNo == 0 {
|
||||
return nil, errors.New("DataPageNo option is required")
|
||||
}
|
||||
// if len(opt.Page) != octopus.DataPageSize {
|
||||
// return nil, fmt.Errorf("bug: wrong data page size %d", len(opt.Page))
|
||||
// }
|
||||
|
||||
s := &Writer{
|
||||
fileName: JoinREDOFileName(opt.Dir, opt.MetricID),
|
||||
metricID: opt.MetricID,
|
||||
timestamp: opt.Timestamp,
|
||||
value: opt.Value,
|
||||
tmp: make([]byte, 21),
|
||||
isDataPageReused: opt.IsDataPageReused,
|
||||
dataPageNo: opt.DataPageNo,
|
||||
hasher: crc32.NewIEEE(),
|
||||
}
|
||||
|
||||
var err error
|
||||
s.file, err = os.OpenFile(s.fileName, os.O_CREATE|os.O_WRONLY, 0770)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = s.init(opt.Page)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return s, nil
|
||||
}
|
||||
|
||||
/*
|
||||
Формат:
|
||||
4b metricID
|
||||
8b value
|
||||
4b timestamp
|
||||
1b flags (reused)
|
||||
4b dataPageNo
|
||||
8KB dataPage
|
||||
*/
|
||||
func (s *Writer) init(dataPage []byte) error {
|
||||
bin.PutUint32(s.tmp[0:], s.metricID)
|
||||
bin.PutUint32(s.tmp[4:], s.timestamp)
|
||||
bin.PutFloat64(s.tmp[8:], s.value)
|
||||
if s.isDataPageReused {
|
||||
s.tmp[16] = 1
|
||||
}
|
||||
bin.PutUint32(s.tmp[17:], s.dataPageNo)
|
||||
|
||||
_, err := s.file.Write(s.tmp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = s.file.Write(dataPage)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s.hasher.Write(s.tmp)
|
||||
s.hasher.Write(dataPage)
|
||||
return nil
|
||||
}
|
||||
|
||||
/*
|
||||
Формат
|
||||
1b index page flags
|
||||
4b indexPageNo
|
||||
Nb indexPage
|
||||
*/
|
||||
func (s *Writer) AppendIndexPage(indexPageNo uint32, indexPage []byte, flags byte) error {
|
||||
s.tmp[0] = flags
|
||||
bin.PutUint32(s.tmp[1:], indexPageNo)
|
||||
_, err := s.file.Write(s.tmp[:5])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = s.file.Write(indexPage)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s.hasher.Write(s.tmp[:5])
|
||||
s.hasher.Write(indexPage)
|
||||
|
||||
s.indexPages = append(s.indexPages, indexPageNo)
|
||||
|
||||
if (flags & FlagReused) == FlagReused {
|
||||
s.reusedIndexPages = append(s.reusedIndexPages, indexPageNo)
|
||||
}
|
||||
|
||||
if (flags & FlagNewRoot) == FlagNewRoot {
|
||||
s.newRootPageNo = indexPageNo
|
||||
s.isRootChanged = true
|
||||
}
|
||||
|
||||
s.indexPagesToWrite = append(s.indexPagesToWrite,
|
||||
PageToWrite{
|
||||
PageNo: indexPageNo,
|
||||
Data: indexPage,
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Writer) IndexPagesToWrite() []PageToWrite {
|
||||
return s.indexPagesToWrite
|
||||
}
|
||||
|
||||
func (s *Writer) Close() (err error) {
|
||||
// финализирую запись
|
||||
bin.PutUint32(s.tmp, s.hasher.Sum32())
|
||||
_, err = s.file.Write(s.tmp[:4])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = s.file.Sync()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return s.file.Close()
|
||||
}
|
||||
|
||||
type Report struct {
|
||||
FileName string
|
||||
IsDataPageReused bool
|
||||
DataPageNo uint32
|
||||
IsRootChanged bool
|
||||
NewRootPageNo uint32
|
||||
ReusedIndexPages []uint32
|
||||
}
|
||||
|
||||
func (s *Writer) GetReport() Report {
|
||||
return Report{
|
||||
FileName: s.fileName,
|
||||
IsDataPageReused: s.isDataPageReused,
|
||||
DataPageNo: s.dataPageNo,
|
||||
//IndexPages: s.indexPages,
|
||||
IsRootChanged: s.isRootChanged,
|
||||
NewRootPageNo: s.newRootPageNo,
|
||||
ReusedIndexPages: s.reusedIndexPages,
|
||||
}
|
||||
}
|
||||
|
||||
// HELPERS
|
||||
|
||||
func JoinREDOFileName(dir string, metricID uint32) string {
|
||||
return filepath.Join(dir, fmt.Sprintf("m%d.redo", metricID))
|
||||
}
|
||||
298
atree/writer.go
298
atree/writer.go
@@ -1,164 +1,158 @@
|
||||
package atree
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"hash"
|
||||
"hash/crc32"
|
||||
)
|
||||
|
||||
type Writer struct {
|
||||
metricID uint32
|
||||
timestamp uint32
|
||||
value float64
|
||||
tmp []byte
|
||||
hasher hash.Hash32
|
||||
isDataPageReused bool
|
||||
dataPageNo uint32
|
||||
isRootChanged bool
|
||||
newRootPageNo uint32
|
||||
indexPages []uint32
|
||||
reusedIndexPages []uint32
|
||||
indexPagesToWrite []PageToWrite
|
||||
}
|
||||
|
||||
type WriterOptions struct {
|
||||
MetricID uint32
|
||||
Value float64
|
||||
Timestamp uint32
|
||||
IsDataPageReused bool
|
||||
DataPageNo uint32
|
||||
Page []byte
|
||||
}
|
||||
|
||||
// dataPage можно записати 1 раз. Щоб не заплутувати інтерфейс - передаю data сторінку
|
||||
// через Options. Index сторінок може бути від 1 до N, тому виділяю окремий метод
|
||||
func NewWriter(opt WriterOptions) (*Writer, error) {
|
||||
if opt.MetricID == 0 {
|
||||
return nil, errors.New("MetricID option is required")
|
||||
}
|
||||
if opt.DataPageNo == 0 {
|
||||
return nil, errors.New("DataPageNo option is required")
|
||||
}
|
||||
// if len(opt.Page) != octopus.DataPageSize {
|
||||
// return nil, fmt.Errorf("bug: wrong data page size %d", len(opt.Page))
|
||||
// }
|
||||
|
||||
s := &Writer{
|
||||
metricID: opt.MetricID,
|
||||
timestamp: opt.Timestamp,
|
||||
value: opt.Value,
|
||||
tmp: make([]byte, 21),
|
||||
isDataPageReused: opt.IsDataPageReused,
|
||||
dataPageNo: opt.DataPageNo,
|
||||
hasher: crc32.NewIEEE(),
|
||||
}
|
||||
|
||||
// var err error
|
||||
// err = s.init(opt.Page)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
return s, nil
|
||||
}
|
||||
|
||||
/*
|
||||
Формат:
|
||||
4b metricID
|
||||
8b value
|
||||
4b timestamp
|
||||
1b flags (reused)
|
||||
4b dataPageNo
|
||||
8KB dataPage
|
||||
*/
|
||||
// func (s *Writer) init(dataPage []byte) error {
|
||||
// bin.PutUint32(s.tmp[0:], s.metricID)
|
||||
// bin.PutUint32(s.tmp[4:], s.timestamp)
|
||||
// bin.PutFloat64(s.tmp[8:], s.value)
|
||||
// if s.isDataPageReused {
|
||||
// s.tmp[16] = 1
|
||||
// }
|
||||
// bin.PutUint32(s.tmp[17:], s.dataPageNo)
|
||||
|
||||
// _, err := s.file.Write(s.tmp)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
|
||||
// _, err = s.file.Write(dataPage)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
|
||||
// s.hasher.Write(s.tmp)
|
||||
// s.hasher.Write(dataPage)
|
||||
// return nil
|
||||
// type Writer struct {
|
||||
// metricID uint32
|
||||
// timestamp uint32
|
||||
// value float64
|
||||
// tmp []byte
|
||||
// hasher hash.Hash32
|
||||
// isDataPageReused bool
|
||||
// dataPageNo uint32
|
||||
// isRootChanged bool
|
||||
// newRootPageNo uint32
|
||||
// indexPages []uint32
|
||||
// reusedIndexPages []uint32
|
||||
// indexPagesToWrite []PageToWrite
|
||||
// }
|
||||
|
||||
/*
|
||||
Формат
|
||||
1b index page flags
|
||||
4b indexPageNo
|
||||
Nb indexPage
|
||||
*/
|
||||
// func (s *Writer) AppendIndexPage(indexPageNo uint32, indexPage []byte, flags byte) error {
|
||||
// s.tmp[0] = flags
|
||||
// bin.PutUint32(s.tmp[1:], indexPageNo)
|
||||
// // _, err := s.file.Write(s.tmp[:5])
|
||||
// // if err != nil {
|
||||
// // return err
|
||||
// // }
|
||||
// // _, err = s.file.Write(indexPage)
|
||||
// // if err != nil {
|
||||
// // return err
|
||||
// type WriterOptions struct {
|
||||
// MetricID uint32
|
||||
// Value float64
|
||||
// Timestamp uint32
|
||||
// IsDataPageReused bool
|
||||
// DataPageNo uint32
|
||||
// Page []byte
|
||||
// }
|
||||
|
||||
// // dataPage можно записати 1 раз. Щоб не заплутувати інтерфейс - передаю data сторінку
|
||||
// // через Options. Index сторінок може бути від 1 до N, тому виділяю окремий метод
|
||||
// func NewWriter(opt WriterOptions) (*Writer, error) {
|
||||
// if opt.MetricID == 0 {
|
||||
// return nil, errors.New("MetricID option is required")
|
||||
// }
|
||||
// if opt.DataPageNo == 0 {
|
||||
// return nil, errors.New("DataPageNo option is required")
|
||||
// }
|
||||
// // if len(opt.Page) != octopus.DataPageSize {
|
||||
// // return nil, fmt.Errorf("bug: wrong data page size %d", len(opt.Page))
|
||||
// // }
|
||||
|
||||
// s.hasher.Write(s.tmp[:5])
|
||||
// s.hasher.Write(indexPage)
|
||||
|
||||
// s.indexPages = append(s.indexPages, indexPageNo)
|
||||
|
||||
// if (flags & FlagReused) == FlagReused {
|
||||
// s.reusedIndexPages = append(s.reusedIndexPages, indexPageNo)
|
||||
// s := &Writer{
|
||||
// metricID: opt.MetricID,
|
||||
// timestamp: opt.Timestamp,
|
||||
// value: opt.Value,
|
||||
// tmp: make([]byte, 21),
|
||||
// isDataPageReused: opt.IsDataPageReused,
|
||||
// dataPageNo: opt.DataPageNo,
|
||||
// hasher: crc32.NewIEEE(),
|
||||
// }
|
||||
|
||||
// if (flags & FlagNewRoot) == FlagNewRoot {
|
||||
// s.newRootPageNo = indexPageNo
|
||||
// s.isRootChanged = true
|
||||
// }
|
||||
|
||||
// s.indexPagesToWrite = append(s.indexPagesToWrite,
|
||||
// PageToWrite{
|
||||
// PageNo: indexPageNo,
|
||||
// Data: indexPage,
|
||||
// })
|
||||
// return nil
|
||||
// // var err error
|
||||
// // err = s.init(opt.Page)
|
||||
// // if err != nil {
|
||||
// // return nil, err
|
||||
// // }
|
||||
// return s, nil
|
||||
// }
|
||||
|
||||
// func (s *Writer) IndexPagesToWrite() []PageToWrite {
|
||||
// return s.indexPagesToWrite
|
||||
// }
|
||||
// /*
|
||||
// Формат:
|
||||
// 4b metricID
|
||||
// 8b value
|
||||
// 4b timestamp
|
||||
// 1b flags (reused)
|
||||
// 4b dataPageNo
|
||||
// 8KB dataPage
|
||||
// */
|
||||
// // func (s *Writer) init(dataPage []byte) error {
|
||||
// // bin.PutUint32(s.tmp[0:], s.metricID)
|
||||
// // bin.PutUint32(s.tmp[4:], s.timestamp)
|
||||
// // bin.PutFloat64(s.tmp[8:], s.value)
|
||||
// // if s.isDataPageReused {
|
||||
// // s.tmp[16] = 1
|
||||
// // }
|
||||
// // bin.PutUint32(s.tmp[17:], s.dataPageNo)
|
||||
|
||||
// func (s *Writer) Close() (err error) {
|
||||
// // финализирую запись
|
||||
// bin.PutUint32(s.tmp, s.hasher.Sum32())
|
||||
// _, err = s.file.Write(s.tmp[:4])
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
// err = s.file.Sync()
|
||||
// if err != nil {
|
||||
// return
|
||||
// }
|
||||
// return s.file.Close()
|
||||
// }
|
||||
// // _, err := s.file.Write(s.tmp)
|
||||
// // if err != nil {
|
||||
// // return err
|
||||
// // }
|
||||
|
||||
// func (s *Writer) GetReport() Report {
|
||||
// return Report{
|
||||
// IsDataPageReused: s.isDataPageReused,
|
||||
// DataPageNo: s.dataPageNo,
|
||||
// //IndexPages: s.indexPages,
|
||||
// IsRootChanged: s.isRootChanged,
|
||||
// NewRootPageNo: s.newRootPageNo,
|
||||
// ReusedIndexPages: s.reusedIndexPages,
|
||||
// }
|
||||
// }
|
||||
// // _, err = s.file.Write(dataPage)
|
||||
// // if err != nil {
|
||||
// // return err
|
||||
// // }
|
||||
|
||||
// // s.hasher.Write(s.tmp)
|
||||
// // s.hasher.Write(dataPage)
|
||||
// // return nil
|
||||
// // }
|
||||
|
||||
// /*
|
||||
// Формат
|
||||
// 1b index page flags
|
||||
// 4b indexPageNo
|
||||
// Nb indexPage
|
||||
// */
|
||||
// // func (s *Writer) AppendIndexPage(indexPageNo uint32, indexPage []byte, flags byte) error {
|
||||
// // s.tmp[0] = flags
|
||||
// // bin.PutUint32(s.tmp[1:], indexPageNo)
|
||||
// // // _, err := s.file.Write(s.tmp[:5])
|
||||
// // // if err != nil {
|
||||
// // // return err
|
||||
// // // }
|
||||
// // // _, err = s.file.Write(indexPage)
|
||||
// // // if err != nil {
|
||||
// // // return err
|
||||
// // // }
|
||||
|
||||
// // s.hasher.Write(s.tmp[:5])
|
||||
// // s.hasher.Write(indexPage)
|
||||
|
||||
// // s.indexPages = append(s.indexPages, indexPageNo)
|
||||
|
||||
// // if (flags & FlagReused) == FlagReused {
|
||||
// // s.reusedIndexPages = append(s.reusedIndexPages, indexPageNo)
|
||||
// // }
|
||||
|
||||
// // if (flags & FlagNewRoot) == FlagNewRoot {
|
||||
// // s.newRootPageNo = indexPageNo
|
||||
// // s.isRootChanged = true
|
||||
// // }
|
||||
|
||||
// // s.indexPagesToWrite = append(s.indexPagesToWrite,
|
||||
// // PageToWrite{
|
||||
// // PageNo: indexPageNo,
|
||||
// // Data: indexPage,
|
||||
// // })
|
||||
// // return nil
|
||||
// // }
|
||||
|
||||
// // func (s *Writer) IndexPagesToWrite() []PageToWrite {
|
||||
// // return s.indexPagesToWrite
|
||||
// // }
|
||||
|
||||
// // func (s *Writer) Close() (err error) {
|
||||
// // // финализирую запись
|
||||
// // bin.PutUint32(s.tmp, s.hasher.Sum32())
|
||||
// // _, err = s.file.Write(s.tmp[:4])
|
||||
// // if err != nil {
|
||||
// // return err
|
||||
// // }
|
||||
// // err = s.file.Sync()
|
||||
// // if err != nil {
|
||||
// // return
|
||||
// // }
|
||||
// // return s.file.Close()
|
||||
// // }
|
||||
|
||||
// // func (s *Writer) GetReport() Report {
|
||||
// // return Report{
|
||||
// // IsDataPageReused: s.isDataPageReused,
|
||||
// // DataPageNo: s.dataPageNo,
|
||||
// // //IndexPages: s.indexPages,
|
||||
// // IsRootChanged: s.isRootChanged,
|
||||
// // NewRootPageNo: s.newRootPageNo,
|
||||
// // ReusedIndexPages: s.reusedIndexPages,
|
||||
// // }
|
||||
// // }
|
||||
|
||||
34
atree/x.go
34
atree/x.go
@@ -1,6 +1,7 @@
|
||||
package atree
|
||||
|
||||
import (
|
||||
"gordenko.dev/dima/qb"
|
||||
"gordenko.dev/dima/qb/bin"
|
||||
"gordenko.dev/dima/qb/util"
|
||||
)
|
||||
@@ -19,7 +20,7 @@ type DataPage struct {
|
||||
ValuesSize int
|
||||
PageNo uint32
|
||||
IsReused bool
|
||||
Checksum uint32
|
||||
//Checksum uint32
|
||||
}
|
||||
|
||||
type IndexPage struct {
|
||||
@@ -27,7 +28,6 @@ type IndexPage struct {
|
||||
Data []byte
|
||||
PageNo uint32
|
||||
IsReused bool
|
||||
Checksum uint32
|
||||
}
|
||||
type IndexLevel struct {
|
||||
// вже записані дані (лише для першої в списку індексної сторінки, беремо із _metric)
|
||||
@@ -57,7 +57,7 @@ type ChunksToDataPageReq struct {
|
||||
}
|
||||
|
||||
// return checksum
|
||||
func ChunksToDataPage(buf []byte, req ChunksToDataPageReq) (checksum uint32) {
|
||||
func ChunksToDataPage(buf []byte, req ChunksToDataPageReq) {
|
||||
var (
|
||||
remainingSize = int(req.TimestampsSize)
|
||||
pos = 0
|
||||
@@ -90,30 +90,29 @@ func ChunksToDataPage(buf []byte, req ChunksToDataPageReq) (checksum uint32) {
|
||||
bin.PutUint16(buf[valuesSizeIdx:], uint16(req.ValuesSize))
|
||||
bin.PutUint32(buf[prevPageIdx:], req.PrevPageNo)
|
||||
|
||||
checksum = util.CalcChecksum(buf[:dataCRC32Idx])
|
||||
checksum := util.CalcChecksum(buf[:dataCRC32Idx])
|
||||
bin.PutUint32(buf[dataCRC32Idx:], checksum)
|
||||
return
|
||||
}
|
||||
|
||||
func DataToIndexPage(buf []byte, data []byte, lastLevel bool) (checksum uint32) {
|
||||
func DataToIndexPage(buf []byte, data []byte, lastLevel bool) {
|
||||
copy(buf, data)
|
||||
bin.PutUint16(buf[indexRecordsQtyIdx:], uint16(len(data)/indexRecordSize))
|
||||
if lastLevel {
|
||||
buf[isLastLevelIdx] = 1
|
||||
}
|
||||
checksum = util.CalcChecksum(buf[:indexCRC32Idx])
|
||||
checksum := util.CalcChecksum(buf[:indexCRC32Idx])
|
||||
bin.PutUint32(buf[indexCRC32Idx:], checksum)
|
||||
return
|
||||
}
|
||||
|
||||
type Pager interface {
|
||||
GetPageNumber() (uint32, bool)
|
||||
}
|
||||
|
||||
// fix - reduce leves
|
||||
func AppendDataPagesToTree(pager Pager, levels []*IndexLevel, dataPages []*DataPage) {
|
||||
func AppendDataPagesToTree(getDataPageNumber func() (uint32, bool, error), getIndexPageNumber func() (uint32, bool, error), levels []*IndexLevel, dataPages []*DataPage) {
|
||||
for i, d := range dataPages[1:] {
|
||||
d.PageNo, d.IsReused = pager.GetPageNumber()
|
||||
pageNo, isReused, err := getDataPageNumber()
|
||||
if err != nil {
|
||||
qb.Abort(qb.FailedGetPageNumber, err)
|
||||
}
|
||||
d.PageNo = pageNo
|
||||
d.IsReused = isReused
|
||||
d.PrevPageNo = dataPages[i-1].PageNo
|
||||
}
|
||||
|
||||
@@ -143,7 +142,12 @@ func AppendDataPagesToTree(pager Pager, levels []*IndexLevel, dataPages []*DataP
|
||||
LowerTimestamp: level.LowerTimestamp,
|
||||
Data: level.Data,
|
||||
}
|
||||
filled.PageNo, filled.IsReused = pager.GetPageNumber()
|
||||
pageNo, isReused, err := getIndexPageNumber()
|
||||
if err != nil {
|
||||
qb.Abort(qb.FailedGetPageNumber, err)
|
||||
}
|
||||
filled.PageNo = pageNo
|
||||
filled.IsReused = isReused
|
||||
|
||||
level.Filled = append(level.Filled, filled)
|
||||
//
|
||||
|
||||
Reference in New Issue
Block a user