wp
This commit is contained in:
412
atree/atree.go
412
atree/atree.go
@@ -8,7 +8,6 @@ import (
|
||||
"path/filepath"
|
||||
"sync"
|
||||
|
||||
diploma "gordenko.dev/dima/qb"
|
||||
"gordenko.dev/dima/qb/bin"
|
||||
)
|
||||
|
||||
@@ -19,8 +18,8 @@ const (
|
||||
filePerm = 0770
|
||||
|
||||
// common
|
||||
crc32Idx = PageSize - 4
|
||||
pageType = PageSize - 5
|
||||
crc32Idx = PageSize - 4
|
||||
pageTypeIdx = PageSize - 5
|
||||
|
||||
// index page
|
||||
indexRecordsQtyIdx = PageSize - 8
|
||||
@@ -73,16 +72,16 @@ type _page struct {
|
||||
}
|
||||
|
||||
type Atree struct {
|
||||
freelist FreeList
|
||||
file *os.File
|
||||
mutex sync.Mutex
|
||||
freelist FreeList
|
||||
allocatedPagesQty uint32
|
||||
pages map[uint32]*_page
|
||||
pageWaits map[uint32][]chan readResult
|
||||
pagesToRead []uint32
|
||||
readSignalCh chan struct{}
|
||||
writeSignalCh chan struct{}
|
||||
writeTasksQueue []WriteTask
|
||||
//writeTasksQueue []WriteTask
|
||||
}
|
||||
|
||||
type Options struct {
|
||||
@@ -131,7 +130,7 @@ func New(opt Options) (*Atree, error) {
|
||||
}
|
||||
|
||||
tree := &Atree{
|
||||
//freelist: opt.FreeList,
|
||||
freelist: opt.FreeList,
|
||||
file: file,
|
||||
allocatedPagesQty: allocatedPagesQty,
|
||||
pages: make(map[uint32]*_page),
|
||||
@@ -144,7 +143,7 @@ func New(opt Options) (*Atree, error) {
|
||||
}
|
||||
|
||||
func (s *Atree) Run() {
|
||||
go s.pageWriter()
|
||||
//go s.pageWriter()
|
||||
go s.pageReader()
|
||||
}
|
||||
|
||||
@@ -161,6 +160,7 @@ func (s *Atree) findDataPage(rootPageNo uint32, timestamp uint32) (uint32, []byt
|
||||
foundPageNo := findPageNo(buf, timestamp)
|
||||
s.releasePage(indexPageNo)
|
||||
|
||||
// fix
|
||||
if buf[isDataPageNumbersIdx] == 1 {
|
||||
buf, err := s.fetchDataPage(foundPageNo)
|
||||
if err != nil {
|
||||
@@ -173,20 +173,20 @@ func (s *Atree) findDataPage(rootPageNo uint32, timestamp uint32) (uint32, []byt
|
||||
}
|
||||
}
|
||||
|
||||
type pathLeg struct {
|
||||
type PathLeg struct {
|
||||
PageNo uint32
|
||||
Data []byte
|
||||
}
|
||||
|
||||
type pathToDataPage struct {
|
||||
Legs []pathLeg
|
||||
type PathToDataPage struct {
|
||||
Legs []PathLeg
|
||||
LastPageNo uint32
|
||||
}
|
||||
|
||||
func (s *Atree) findPathToLastPage(rootPageNo uint32) (_ pathToDataPage, err error) {
|
||||
func (s *Atree) FindPathToLastPage(rootPageNo uint32) (_ PathToDataPage, err error) {
|
||||
var (
|
||||
pageNo = rootPageNo
|
||||
legs []pathLeg
|
||||
legs []PathLeg
|
||||
)
|
||||
|
||||
for {
|
||||
@@ -197,7 +197,7 @@ func (s *Atree) findPathToLastPage(rootPageNo uint32) (_ pathToDataPage, err err
|
||||
return
|
||||
}
|
||||
|
||||
legs = append(legs, pathLeg{
|
||||
legs = append(legs, PathLeg{
|
||||
PageNo: pageNo,
|
||||
Data: buf,
|
||||
// childIdx не нужен
|
||||
@@ -205,8 +205,9 @@ func (s *Atree) findPathToLastPage(rootPageNo uint32) (_ pathToDataPage, err err
|
||||
|
||||
foundPageNo := getLastPageNo(buf)
|
||||
|
||||
// fix
|
||||
if buf[isDataPageNumbersIdx] == 1 {
|
||||
return pathToDataPage{
|
||||
return PathToDataPage{
|
||||
Legs: legs,
|
||||
LastPageNo: foundPageNo,
|
||||
}, nil
|
||||
@@ -218,26 +219,19 @@ 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 {
|
||||
MetricID uint32
|
||||
Timestamp uint32
|
||||
Value float64
|
||||
Since uint32
|
||||
RootPageNo uint32
|
||||
PrevPageNo uint32
|
||||
TimestampsChunks [][]byte
|
||||
TimestampsSize uint16
|
||||
ValuesChunks [][]byte
|
||||
ValuesSize uint16
|
||||
}
|
||||
type Report struct {
|
||||
//IsDataPageReused bool
|
||||
//DataPageNo uint32
|
||||
IsRootChanged bool
|
||||
NewRootPageNo uint32
|
||||
//ReusedIndexPages []uint32
|
||||
Pages []PageToWrite
|
||||
}
|
||||
|
||||
// type ChangedPage struct {
|
||||
// PageNo uint32
|
||||
@@ -253,186 +247,165 @@ type Report struct {
|
||||
// Тому що є проблема із відновленням FreeList після збою, якщо з нього будуть паралельно
|
||||
// забирати та добавляти номери сторінок інші потоки.
|
||||
// Це буде працювати, якщо додавання в txlog і маніпуляції із freeList будуть відбуватись в одному потоці
|
||||
func (s *Atree) AppendDataPage(req AppendDataPageReq) (_ Report, err error) {
|
||||
// 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:], 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
|
||||
report Report
|
||||
//pagesToRelease []uint32
|
||||
newRootPageNo uint32
|
||||
lastPageNo = req.LastPageNo
|
||||
pages []PageToWrite
|
||||
legs = req.Legs
|
||||
changed = make(map[uint32]PathLeg)
|
||||
)
|
||||
|
||||
newDataPage := s.allocPage()
|
||||
pagesToRelease = append(pagesToRelease, newDataPage.PageNo)
|
||||
for _, p := range req.DataPages {
|
||||
newDataPageNo, isReused := s.allocPageNumber() // alloc only number
|
||||
// pagesToRelease = append(pagesToRelease, newDataPage.PageNo)
|
||||
|
||||
chunksToDataPage(newDataPage.Data, chunksToDataPageReq{
|
||||
PrevPageNo: req.PrevPageNo,
|
||||
TimestampsChunks: req.TimestampsChunks,
|
||||
TimestampsSize: req.TimestampsSize,
|
||||
ValuesChunks: req.ValuesChunks,
|
||||
ValuesSize: req.ValuesSize,
|
||||
})
|
||||
// set prevPageNo
|
||||
p.SetPrevPageNo(lastPageNo)
|
||||
|
||||
report.Pages = append(report.Pages, PageToWrite{
|
||||
PageNo: newDataPage.PageNo,
|
||||
Data: newDataPage.Data,
|
||||
IsReused: newDataPage.IsReused,
|
||||
})
|
||||
|
||||
// redoWriter, err := NewWriter(WriterOptions{
|
||||
// MetricID: req.MetricID,
|
||||
// Timestamp: req.Timestamp,
|
||||
// Value: req.Value,
|
||||
// IsDataPageReused: newDataPage.IsReused,
|
||||
// DataPageNo: newDataPage.PageNo,
|
||||
// Page: newDataPage.Data,
|
||||
// })
|
||||
// if err != nil {
|
||||
// return
|
||||
// }
|
||||
|
||||
if req.RootPageNo > 0 {
|
||||
var path pathToDataPage
|
||||
path, err = s.findPathToLastPage(req.RootPageNo)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
for _, leg := range path.Legs {
|
||||
pagesToRelease = append(pagesToRelease, leg.PageNo)
|
||||
}
|
||||
|
||||
if path.LastPageNo != req.PrevPageNo {
|
||||
diploma.Abort(
|
||||
diploma.WrongPrevPageNo,
|
||||
fmt.Errorf("bug: last pageNo %d in tree != prev pageNo %d in _metric",
|
||||
path.LastPageNo, req.PrevPageNo),
|
||||
)
|
||||
}
|
||||
|
||||
newPageNo := newDataPage.PageNo
|
||||
lastIdx := len(path.Legs) - 1
|
||||
|
||||
for legIdx := lastIdx; legIdx >= 0; legIdx-- {
|
||||
leg := path.Legs[legIdx]
|
||||
|
||||
ok := appendPair(leg.Data, req.Since, newPageNo)
|
||||
if ok {
|
||||
// index
|
||||
report.Pages = append(report.Pages, PageToWrite{
|
||||
PageNo: leg.PageNo,
|
||||
Data: leg.Data,
|
||||
})
|
||||
// err = redoWriter.AppendIndexPage(leg.PageNo, leg.Data, 0)
|
||||
// if err != nil {
|
||||
// return
|
||||
// }
|
||||
break
|
||||
}
|
||||
|
||||
newIndexPage := s.allocPage()
|
||||
pagesToRelease = append(pagesToRelease, newIndexPage.PageNo)
|
||||
appendPair(newIndexPage.Data, req.Since, newPageNo)
|
||||
// ставлю мітку що всі pageNo на сторінці - це data pageNo
|
||||
if legIdx == lastIdx {
|
||||
newIndexPage.Data[isDataPageNumbersIdx] = 1
|
||||
}
|
||||
|
||||
// flags = 0
|
||||
// if newIndexPage.IsReused {
|
||||
// flags |= FlagReused
|
||||
// }
|
||||
report.Pages = append(report.Pages, PageToWrite{
|
||||
PageNo: newIndexPage.PageNo,
|
||||
Data: newIndexPage.Data,
|
||||
IsReused: newIndexPage.IsReused,
|
||||
})
|
||||
// err = redoWriter.AppendIndexPage(newIndexPage.PageNo, newIndexPage.Data, flags)
|
||||
// if err != nil {
|
||||
// return
|
||||
// }
|
||||
//
|
||||
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, req.Since, newIndexPage.PageNo)
|
||||
|
||||
// Фиксирую новый root в REDO логе
|
||||
|
||||
report.Pages = append(report.Pages, PageToWrite{
|
||||
PageNo: newRoot.PageNo,
|
||||
Data: newRoot.Data,
|
||||
IsReused: newRoot.IsReused,
|
||||
})
|
||||
report.NewRootPageNo = newRoot.PageNo
|
||||
// flags = FlagNewRoot
|
||||
// if newRoot.IsReused {
|
||||
// flags |= FlagReused
|
||||
// }
|
||||
// err = redoWriter.AppendIndexPage(newRoot.PageNo, newRoot.Data, flags)
|
||||
// if err != nil {
|
||||
// return
|
||||
// }
|
||||
break
|
||||
}
|
||||
}
|
||||
} else {
|
||||
newRoot := s.allocPage()
|
||||
pagesToRelease = append(pagesToRelease, newRoot.PageNo)
|
||||
newRoot.Data[isDataPageNumbersIdx] = 1
|
||||
appendPair(newRoot.Data, req.Since, newDataPage.PageNo)
|
||||
|
||||
report.Pages = append(report.Pages, PageToWrite{
|
||||
PageNo: newRoot.PageNo,
|
||||
Data: newRoot.Data,
|
||||
IsReused: newRoot.IsReused,
|
||||
pages = append(pages, PageToWrite{
|
||||
PageNo: newDataPageNo,
|
||||
Data: p.Data,
|
||||
IsReused: isReused,
|
||||
})
|
||||
report.NewRootPageNo = newRoot.PageNo
|
||||
// flags = FlagNewRoot
|
||||
// if newRoot.IsReused {
|
||||
// flags |= FlagReused
|
||||
// }
|
||||
// err = redoWriter.AppendIndexPage(newRoot.PageNo, newRoot.Data, flags)
|
||||
// if err != nil {
|
||||
// return
|
||||
|
||||
// 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,
|
||||
}
|
||||
|
||||
// err = redoWriter.Close()
|
||||
// if err != nil {
|
||||
// return
|
||||
// }
|
||||
|
||||
// На данний момен схема - наступна. Всі сторінки - data та index - зафіксовані в кеші.
|
||||
// Отже запис на диск пройде максимально швидко. Після цього ReferenceCount кожної
|
||||
// сторінки зменшиться на 1. Оскільки на метрику утримується XLock, сторінки мають
|
||||
// ReferenceCount = 1 (немає інших читачів).
|
||||
// waitCh := make(chan struct{})
|
||||
|
||||
// task := WriteTask{
|
||||
// WaitCh: waitCh,
|
||||
// Pages: report.Pages,
|
||||
// }
|
||||
|
||||
// s.appendWriteTaskToQueue(task)
|
||||
|
||||
// <-waitCh
|
||||
|
||||
// for _, pageNo := range dataPagesToRelease {
|
||||
// s.releasePage(pageNo)
|
||||
// }
|
||||
// for _, pageNo := range indexPagesToRelease {
|
||||
// s.releasePage(pageNo)
|
||||
// }
|
||||
return report, nil
|
||||
}
|
||||
|
||||
// DELETE
|
||||
|
||||
type PageLists struct {
|
||||
DataPages []uint32
|
||||
IndexPages []uint32
|
||||
}
|
||||
|
||||
type Level struct {
|
||||
PageNo uint32
|
||||
PageData []byte
|
||||
@@ -440,31 +413,29 @@ type Level struct {
|
||||
ChildQty int
|
||||
}
|
||||
|
||||
func (s *Atree) GetAllPages(rootPageNo uint32) (_ PageLists, err error) {
|
||||
func (s *Atree) GetAllPages(rootPageNo uint32) (_ []uint32, err error) {
|
||||
var (
|
||||
dataPages []uint32
|
||||
indexPages []uint32
|
||||
levels []*Level
|
||||
pageNumbers []uint32
|
||||
levels []*Level
|
||||
)
|
||||
|
||||
buf, err := s.fetchIndexPage(rootPageNo)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("fetchIndexPage(%d): %s", rootPageNo, err)
|
||||
return
|
||||
return nil, fmt.Errorf("fetchIndexPage(%d): %s", rootPageNo, err)
|
||||
}
|
||||
indexPages = append(indexPages, rootPageNo)
|
||||
pageNumbers = append(pageNumbers, rootPageNo)
|
||||
|
||||
if buf[isDataPageNumbersIdx] == 1 {
|
||||
pageNumbers := listPageNumbers(buf)
|
||||
dataPages = append(dataPages, pageNumbers...)
|
||||
// if buf[isDataPageNumbersIdx] == 1 {
|
||||
// pageNumbers := listPageNumbers(buf)
|
||||
// dataPages = append(dataPages, pageNumbers...)
|
||||
|
||||
s.releasePage(rootPageNo)
|
||||
// s.releasePage(rootPageNo)
|
||||
|
||||
return PageLists{
|
||||
DataPages: dataPages,
|
||||
IndexPages: indexPages,
|
||||
}, nil
|
||||
}
|
||||
// return PageLists{
|
||||
// DataPages: dataPages,
|
||||
// IndexPages: indexPages,
|
||||
// }, nil
|
||||
// }
|
||||
|
||||
levels = append(levels, &Level{
|
||||
PageNo: rootPageNo,
|
||||
@@ -475,10 +446,7 @@ func (s *Atree) GetAllPages(rootPageNo uint32) (_ PageLists, err error) {
|
||||
|
||||
for {
|
||||
if len(levels) == 0 {
|
||||
return PageLists{
|
||||
DataPages: dataPages,
|
||||
IndexPages: indexPages,
|
||||
}, nil
|
||||
return pageNumbers, nil
|
||||
}
|
||||
|
||||
lastIdx := len(levels) - 1
|
||||
@@ -489,17 +457,15 @@ func (s *Atree) GetAllPages(rootPageNo uint32) (_ PageLists, err error) {
|
||||
level.Idx++
|
||||
|
||||
var buf []byte
|
||||
buf, err = s.fetchIndexPage(pageNo)
|
||||
buf, err = s.fetchPage(pageNo)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("fetchIndexPage(%d): %s", pageNo, err)
|
||||
return
|
||||
return nil, fmt.Errorf("fetchPage(%d): %s", pageNo, err)
|
||||
}
|
||||
indexPages = append(indexPages, pageNo)
|
||||
|
||||
if buf[isDataPageNumbersIdx] == 1 {
|
||||
pageNumbers := listPageNumbers(buf)
|
||||
dataPages = append(dataPages, pageNumbers...)
|
||||
pageNumbers = append(pageNumbers, pageNo)
|
||||
|
||||
if buf[pageTypeIdx] == PageTypeData {
|
||||
//pageNumbers := listPageNumbers(buf)
|
||||
//dataPages = append(dataPages, pageNumbers...)
|
||||
s.releasePage(pageNo)
|
||||
} else {
|
||||
levels = append(levels, &Level{
|
||||
|
||||
152
atree/io.go
152
atree/io.go
@@ -34,20 +34,31 @@ func (s *Atree) DeletePages(pageNumbers []uint32) {
|
||||
}
|
||||
|
||||
func (s *Atree) fetchIndexPage(pageNo uint32) ([]byte, error) {
|
||||
return s.fetchPage(pageNo, PageTypeIndex)
|
||||
buf, err := s.fetchPage(pageNo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if buf[pageTypeIdx] != PageTypeIndex {
|
||||
return nil, fmt.Errorf("wrong pageType %d instead of %d", buf[pageTypeIdx], PageTypeIndex)
|
||||
}
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
func (s *Atree) fetchDataPage(pageNo uint32) ([]byte, error) {
|
||||
return s.fetchPage(pageNo, PageTypeData)
|
||||
buf, err := s.fetchPage(pageNo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if buf[pageTypeIdx] != PageTypeIndex {
|
||||
return nil, fmt.Errorf("wrong pageType %d instead of %d", buf[pageTypeIdx], PageTypeIndex)
|
||||
}
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
func (s *Atree) fetchPage(pageNo uint32, pageTypeIdx byte) ([]byte, error) {
|
||||
func (s *Atree) fetchPage(pageNo uint32) ([]byte, error) {
|
||||
s.mutex.Lock()
|
||||
p, ok := s.pages[pageNo]
|
||||
if ok {
|
||||
if p.Buf[pageTypeIdx] != pageTypeIdx {
|
||||
return nil, fmt.Errorf("wrong pageType %d instead of %d", p.Buf[pageTypeIdx], pageTypeIdx)
|
||||
}
|
||||
p.ReferenceCount++
|
||||
s.mutex.Unlock()
|
||||
return p.Buf, nil
|
||||
@@ -93,23 +104,28 @@ func (s *Atree) releasePage(pageNo uint32) {
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Atree) allocPageNumber() (uint32, bool) {
|
||||
// fix
|
||||
return 0, true
|
||||
}
|
||||
|
||||
func (s *Atree) allocPage() AllocatedPage {
|
||||
var (
|
||||
allocated = AllocatedPage{
|
||||
Data: make([]byte, PageSize),
|
||||
}
|
||||
)
|
||||
allocated.PageNo = s.freelist.ReservePage()
|
||||
//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
|
||||
}
|
||||
// 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
|
||||
@@ -241,61 +257,61 @@ func (s *Atree) readPages() {
|
||||
|
||||
// WRITE
|
||||
|
||||
func (s *Atree) pageWriter() {
|
||||
for {
|
||||
select {
|
||||
case <-s.writeSignalCh:
|
||||
err := s.writeTasks()
|
||||
if err != nil {
|
||||
qb.Abort(qb.WriteToAtreeFailed, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// func (s *Atree) pageWriter() {
|
||||
// for {
|
||||
// select {
|
||||
// case <-s.writeSignalCh:
|
||||
// err := s.writeTasks()
|
||||
// if err != nil {
|
||||
// qb.Abort(qb.WriteToAtreeFailed, err)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
type WriteTask struct {
|
||||
WaitCh chan struct{}
|
||||
Pages []PageToWrite
|
||||
}
|
||||
// type WriteTask struct {
|
||||
// WaitCh chan struct{}
|
||||
// Pages []PageToWrite
|
||||
// }
|
||||
|
||||
func (s *Atree) appendWriteTaskToQueue(task WriteTask) {
|
||||
s.mutex.Lock()
|
||||
s.writeTasksQueue = append(s.writeTasksQueue, task)
|
||||
s.mutex.Unlock()
|
||||
// func (s *Atree) appendWriteTaskToQueue(task WriteTask) {
|
||||
// s.mutex.Lock()
|
||||
// s.writeTasksQueue = append(s.writeTasksQueue, task)
|
||||
// s.mutex.Unlock()
|
||||
|
||||
select {
|
||||
case s.writeSignalCh <- struct{}{}:
|
||||
default:
|
||||
}
|
||||
}
|
||||
// select {
|
||||
// case s.writeSignalCh <- struct{}{}:
|
||||
// default:
|
||||
// }
|
||||
// }
|
||||
|
||||
func (s *Atree) writeTasks() error {
|
||||
s.mutex.Lock()
|
||||
tasks := s.writeTasksQueue
|
||||
s.writeTasksQueue = nil
|
||||
s.mutex.Unlock()
|
||||
// func (s *Atree) writeTasks() error {
|
||||
// s.mutex.Lock()
|
||||
// tasks := s.writeTasksQueue
|
||||
// s.writeTasksQueue = nil
|
||||
// s.mutex.Unlock()
|
||||
|
||||
for _, task := range tasks {
|
||||
for _, p := range task.Pages {
|
||||
if len(p.Data) != PageSize {
|
||||
return fmt.Errorf("wrong page %d size: %d",
|
||||
p.PageNo, len(p.Data))
|
||||
}
|
||||
bin.PutUint32(p.Data[crc32Idx:], calcChecksum(p.Data[:crc32Idx]))
|
||||
// for _, task := range tasks {
|
||||
// for _, p := range task.Pages {
|
||||
// if len(p.Data) != PageSize {
|
||||
// return fmt.Errorf("wrong page %d size: %d",
|
||||
// p.PageNo, len(p.Data))
|
||||
// }
|
||||
// bin.PutUint32(p.Data[crc32Idx:], calcChecksum(p.Data[:crc32Idx]))
|
||||
|
||||
off := (p.PageNo - 1) * PageSize
|
||||
n, err := s.file.WriteAt(p.Data, int64(off))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if n != len(p.Data) {
|
||||
return fmt.Errorf("write %d instead of %d", n, len(p.Data))
|
||||
}
|
||||
}
|
||||
close(task.WaitCh)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
// off := (p.PageNo - 1) * PageSize
|
||||
// n, err := s.file.WriteAt(p.Data, int64(off))
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
// if n != len(p.Data) {
|
||||
// return fmt.Errorf("write %d instead of %d", n, len(p.Data))
|
||||
// }
|
||||
// }
|
||||
// close(task.WaitCh)
|
||||
// }
|
||||
// return nil
|
||||
// }
|
||||
|
||||
// IO
|
||||
|
||||
@@ -339,9 +355,9 @@ func openFile(fileName string, pageSize int) (_ *os.File, _ uint32, err error) {
|
||||
return file, uint32(allocatedPagesQty), nil
|
||||
}
|
||||
|
||||
func (s *Atree) ApplyREDO(task WriteTask) {
|
||||
s.appendWriteTaskToQueue(task)
|
||||
}
|
||||
// func (s *Atree) ApplyREDO(task WriteTask) {
|
||||
// s.appendWriteTaskToQueue(task)
|
||||
// }
|
||||
|
||||
func (s *Atree) verifyCRC(data []byte, pageSize int) error {
|
||||
var (
|
||||
|
||||
@@ -55,16 +55,15 @@ func BinarySearch(qty int, keyComparator bin.KeyComparator) (elemIdx int, isFoun
|
||||
}
|
||||
}
|
||||
|
||||
type chunksToDataPageReq struct {
|
||||
PrevPageNo uint32
|
||||
type ChunksToNotLinkedDataPageReq struct {
|
||||
// PrevPageNo uint32
|
||||
TimestampsChunks [][]byte
|
||||
TimestampsSize uint16
|
||||
ValuesChunks [][]byte
|
||||
ValuesSize uint16
|
||||
}
|
||||
|
||||
func chunksToDataPage(buf []byte, req chunksToDataPageReq) {
|
||||
bin.PutUint32(buf[prevPageIdx:], req.PrevPageNo)
|
||||
func ChunksToNotLinkedDataPage(buf []byte, req ChunksToNotLinkedDataPageReq) {
|
||||
bin.PutUint16(buf[timestampsSizeIdx:], req.TimestampsSize)
|
||||
bin.PutUint16(buf[valuesSizeIdx:], req.ValuesSize)
|
||||
|
||||
@@ -96,7 +95,6 @@ func chunksToDataPage(buf []byte, req chunksToDataPageReq) {
|
||||
break
|
||||
}
|
||||
}
|
||||
bin.PutUint32(buf[crc32Idx:], calcChecksum(buf[:crc32Idx]))
|
||||
}
|
||||
|
||||
func setPrevPageNo(buf []byte, pageNo uint32) {
|
||||
|
||||
Reference in New Issue
Block a user