wp
This commit is contained in:
296
atree/atree.go
296
atree/atree.go
@@ -1,14 +1,31 @@
|
||||
package atree
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
"sync"
|
||||
)
|
||||
|
||||
const (
|
||||
filePerm = 0770
|
||||
filePerm = 0666
|
||||
)
|
||||
|
||||
type PeriodsWriter interface {
|
||||
Feed(uint32, float64)
|
||||
FeedNoSend(uint32, float64)
|
||||
Close() error
|
||||
}
|
||||
|
||||
type WorkerMeasureConsumer interface {
|
||||
FeedNoSend(uint32, float64)
|
||||
}
|
||||
|
||||
type AtreeMeasureConsumer interface {
|
||||
Feed(uint32, float64)
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
type _page struct {
|
||||
PageNo uint32
|
||||
Buf []byte
|
||||
@@ -16,66 +33,35 @@ type _page struct {
|
||||
}
|
||||
|
||||
type Atree struct {
|
||||
file *os.File
|
||||
mutex sync.Mutex
|
||||
allocatedPagesQty uint32
|
||||
pages map[uint32]*_page
|
||||
pageWaits map[uint32][]chan readResult
|
||||
pagesToRead []uint32
|
||||
readSignalCh chan struct{}
|
||||
writeSignalCh chan struct{}
|
||||
//writeTasksQueue []WriteTask
|
||||
indexFile *os.File
|
||||
dataFile *os.File
|
||||
mutex sync.Mutex
|
||||
pages map[uint32]*_page
|
||||
pageWaits map[uint32][]chan readResult
|
||||
pagesToRead []uint32
|
||||
readSignalCh chan struct{}
|
||||
}
|
||||
|
||||
type Options struct {
|
||||
Dir string
|
||||
DatabaseName string
|
||||
IndexFile *os.File
|
||||
DataFile *os.File
|
||||
}
|
||||
|
||||
func New(opt Options) (*Atree, error) {
|
||||
// if opt.Dir == "" {
|
||||
// return nil, errors.New("Dir 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")
|
||||
// file *os.File
|
||||
// allocatedPagesQty uint32
|
||||
// )
|
||||
|
||||
// // При создании data файла сразу создается индекс, поэтому корректное
|
||||
// // состояние БД: либо оба файла есть, либо ни одного файла нет.
|
||||
// isDataExist, err := isFileExist(fileName)
|
||||
// if err != nil {
|
||||
// return nil, fmt.Errorf("check data file is exist: %s", err)
|
||||
// }
|
||||
// if isDataExist {
|
||||
// file, allocatedPagesQty, err = openFile(fileName, PageSize)
|
||||
// if err != nil {
|
||||
// return nil, fmt.Errorf("open data file: %s", err)
|
||||
// }
|
||||
// } else {
|
||||
// // нет файла
|
||||
// file, err = os.OpenFile(fileName, os.O_CREATE|os.O_RDWR, filePerm)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
// }
|
||||
|
||||
// tree := &Atree{
|
||||
// file: file,
|
||||
// allocatedPagesQty: allocatedPagesQty,
|
||||
// pages: make(map[uint32]*_page),
|
||||
// pageWaits: make(map[uint32][]chan readResult),
|
||||
// readSignalCh: make(chan struct{}, 1),
|
||||
// writeSignalCh: make(chan struct{}, 1),
|
||||
// }
|
||||
|
||||
// return tree, nil
|
||||
return nil, nil
|
||||
if opt.IndexFile == nil {
|
||||
return nil, errors.New("IndexFile option is required")
|
||||
}
|
||||
if opt.DataFile == nil {
|
||||
return nil, errors.New("DataFile option is required")
|
||||
}
|
||||
s := &Atree{
|
||||
indexFile: opt.IndexFile,
|
||||
dataFile: opt.DataFile,
|
||||
pages: make(map[uint32]*_page),
|
||||
pageWaits: make(map[uint32][]chan readResult),
|
||||
readSignalCh: make(chan struct{}, 1),
|
||||
}
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func (s *Atree) Run() {
|
||||
@@ -236,203 +222,3 @@ func (s *Atree) GetAllPages(rootPageNo uint32) (_ []uint32, err error) {
|
||||
// }
|
||||
return
|
||||
}
|
||||
|
||||
// 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,6 +4,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"gordenko.dev/dima/bin"
|
||||
"gordenko.dev/dima/qb"
|
||||
)
|
||||
|
||||
@@ -118,69 +119,69 @@ func (s *BackwardCursor) Close() {
|
||||
// HELPER
|
||||
|
||||
func (s *BackwardCursor) makeDecompressors() error {
|
||||
// timestampsSize, _ := bin.GetUint16(s.pageData[timestampsSizeIdx:])
|
||||
// valuesSize, _ := bin.GetUint16(s.pageData[valuesSizeIdx:])
|
||||
timestampsSize, _ := bin.GetUint16(s.pageData[timestampsSizeIdx:])
|
||||
valuesSize, _ := bin.GetUint16(s.pageData[valuesSizeIdx:])
|
||||
|
||||
// payloadSize := timestampsSize + valuesSize
|
||||
payloadSize := timestampsSize + valuesSize
|
||||
|
||||
// if payloadSize > dataFooterIdx {
|
||||
// return fmt.Errorf("corrupted data page %d: timestamps + values size %d gt payload size",
|
||||
// s.pageNo, payloadSize)
|
||||
// }
|
||||
if payloadSize > dataFooterIdx {
|
||||
return fmt.Errorf("corrupted data page %d: timestamps + values size %d gt payload size",
|
||||
s.pageNo, payloadSize)
|
||||
}
|
||||
|
||||
// s.timestampDecompressor = enc.NewTimeDeltaDecompressor(
|
||||
// s.pageData[:timestampsSize],
|
||||
// )
|
||||
s.timestampDecompressor = enc.NewTimeDeltaDecompressor(
|
||||
s.pageData[:timestampsSize],
|
||||
)
|
||||
|
||||
// vbuf := s.pageData[timestampsSize : timestampsSize+valuesSize]
|
||||
vbuf := s.pageData[timestampsSize : timestampsSize+valuesSize]
|
||||
|
||||
// switch s.metricType {
|
||||
// case qb.Instant:
|
||||
// s.valueDecompressor = enc.NewInstantDeltaDecompressor(
|
||||
// vbuf, s.fracDigits)
|
||||
switch s.metricType {
|
||||
case qb.Instant:
|
||||
s.valueDecompressor = enc.NewInstantDeltaDecompressor(
|
||||
vbuf, s.fracDigits)
|
||||
|
||||
// case qb.Cumulative:
|
||||
// s.valueDecompressor = enc.NewCumulativeDeltaDecompressor(
|
||||
// vbuf, s.fracDigits)
|
||||
case qb.Cumulative:
|
||||
s.valueDecompressor = enc.NewCumulativeDeltaDecompressor(
|
||||
vbuf, s.fracDigits)
|
||||
|
||||
// default:
|
||||
// return fmt.Errorf("bug: wrong metricType %d", s.metricType)
|
||||
// }
|
||||
default:
|
||||
return fmt.Errorf("bug: wrong metricType %d", s.metricType)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func makeDecompressors(pageData []byte, metricType qb.MetricType, fracDigits byte) (
|
||||
qb.TimestampDecompressor, qb.ValueDecompressor, error,
|
||||
) {
|
||||
// timestampsSize, _ := bin.GetUint16(pageData[timestampsSizeIdx:])
|
||||
// valuesSize, _ := bin.GetUint16(pageData[valuesSizeIdx:])
|
||||
// func makeDecompressors(pageData []byte, metricType qb.MetricType, fracDigits byte) (
|
||||
// qb.TimestampDecompressor, qb.ValueDecompressor, error,
|
||||
// ) {
|
||||
// timestampsSize, _ := bin.GetUint16(pageData[timestampsSizeIdx:])
|
||||
// valuesSize, _ := bin.GetUint16(pageData[valuesSizeIdx:])
|
||||
|
||||
// payloadSize := timestampsSize + valuesSize
|
||||
// payloadSize := timestampsSize + valuesSize
|
||||
|
||||
// if payloadSize > dataFooterIdx {
|
||||
// return nil, nil, fmt.Errorf("corrupted: timestamps + values size %d > payload size",
|
||||
// payloadSize)
|
||||
// }
|
||||
// if payloadSize > dataFooterIdx {
|
||||
// return nil, nil, fmt.Errorf("corrupted: timestamps + values size %d > payload size",
|
||||
// payloadSize)
|
||||
// }
|
||||
|
||||
// timestampDecompressor := enc.NewTimeDeltaDecompressor(
|
||||
// pageData[:timestampsSize],
|
||||
// )
|
||||
// timestampDecompressor := enc.NewTimeDeltaDecompressor(
|
||||
// pageData[:timestampsSize],
|
||||
// )
|
||||
|
||||
// vbuf := pageData[timestampsSize : timestampsSize+valuesSize]
|
||||
// vbuf := pageData[timestampsSize : timestampsSize+valuesSize]
|
||||
|
||||
// var valueDecompressor qb.ValueDecompressor
|
||||
// switch metricType {
|
||||
// case qb.Instant:
|
||||
// valueDecompressor = enc.NewInstantDeltaDecompressor(
|
||||
// vbuf, fracDigits)
|
||||
// var valueDecompressor qb.ValueDecompressor
|
||||
// switch metricType {
|
||||
// case qb.Instant:
|
||||
// valueDecompressor = enc.NewInstantDeltaDecompressor(
|
||||
// vbuf, fracDigits)
|
||||
|
||||
// case qb.Cumulative:
|
||||
// valueDecompressor = enc.NewCumulativeDeltaDecompressor(
|
||||
// vbuf, fracDigits)
|
||||
// case qb.Cumulative:
|
||||
// valueDecompressor = enc.NewCumulativeDeltaDecompressor(
|
||||
// vbuf, fracDigits)
|
||||
|
||||
// default:
|
||||
// return nil, nil, fmt.Errorf("bug: wrong metricType %d", metricType)
|
||||
// }
|
||||
//return timestampDecompressor, valueDecompressor, nil
|
||||
return nil, nil, nil
|
||||
}
|
||||
// default:
|
||||
// return nil, nil, fmt.Errorf("bug: wrong metricType %d", metricType)
|
||||
// }
|
||||
//return timestampDecompressor, valueDecompressor, nil
|
||||
// return nil, nil, nil
|
||||
// }
|
||||
|
||||
348
atree/io.go
348
atree/io.go
@@ -1,24 +1,14 @@
|
||||
package atree
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"math"
|
||||
"os"
|
||||
|
||||
bin "gordenko.dev/dima/bin/little"
|
||||
"gordenko.dev/dima/qb"
|
||||
"gordenko.dev/dima/qb/storage"
|
||||
"gordenko.dev/dima/qb/util"
|
||||
)
|
||||
|
||||
// fix - додати ID, щоб потім перемістити із тимчасового буфера в pages, або звільнити
|
||||
// type AllocatedPage struct {
|
||||
// PageNo uint32
|
||||
// Data []byte
|
||||
// IsReused bool
|
||||
// }
|
||||
|
||||
type readResult struct {
|
||||
Data []byte
|
||||
Err error
|
||||
@@ -47,46 +37,41 @@ func (s *Atree) fetchIndexPage(pageNo uint32) ([]byte, error) {
|
||||
}
|
||||
|
||||
func (s *Atree) fetchDataPage(pageNo uint32) ([]byte, error) {
|
||||
// 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
|
||||
return nil, nil
|
||||
buf, err := s.fetchPage(pageNo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
func (s *Atree) fetchPage(pageNo uint32) ([]byte, error) {
|
||||
// s.mutex.Lock()
|
||||
// p, ok := s.pages[pageNo]
|
||||
// if ok {
|
||||
// p.ReferenceCount++
|
||||
// s.mutex.Unlock()
|
||||
// return p.Buf, nil
|
||||
// }
|
||||
s.mutex.Lock()
|
||||
p, ok := s.pages[pageNo]
|
||||
if ok {
|
||||
p.ReferenceCount++
|
||||
s.mutex.Unlock()
|
||||
return p.Buf, nil
|
||||
}
|
||||
|
||||
// resultCh := make(chan readResult, 1)
|
||||
// s.pageWaits[pageNo] = append(s.pageWaits[pageNo], resultCh)
|
||||
// if len(s.pageWaits[pageNo]) == 1 {
|
||||
// s.pagesToRead = append(s.pagesToRead, pageNo)
|
||||
// s.mutex.Unlock()
|
||||
resultCh := make(chan readResult, 1)
|
||||
s.pageWaits[pageNo] = append(s.pageWaits[pageNo], resultCh)
|
||||
if len(s.pageWaits[pageNo]) == 1 {
|
||||
s.pagesToRead = append(s.pagesToRead, pageNo)
|
||||
s.mutex.Unlock()
|
||||
|
||||
// select {
|
||||
// case s.readSignalCh <- struct{}{}:
|
||||
// default:
|
||||
// }
|
||||
// } else {
|
||||
// s.mutex.Unlock()
|
||||
// }
|
||||
select {
|
||||
case s.readSignalCh <- struct{}{}:
|
||||
default:
|
||||
}
|
||||
} else {
|
||||
s.mutex.Unlock()
|
||||
}
|
||||
|
||||
// result := <-resultCh
|
||||
// if result.Err == nil {
|
||||
// result.Err = s.verifyCRC(result.Data, PageSize)
|
||||
// }
|
||||
// return result.Data, result.Err
|
||||
return nil, nil
|
||||
result := <-resultCh
|
||||
if result.Err == nil {
|
||||
result.Err = s.verifyCRC(result.Data, storage.DataPageSize)
|
||||
}
|
||||
return result.Data, result.Err
|
||||
}
|
||||
|
||||
func (s *Atree) releasePage(pageNo uint32) {
|
||||
@@ -108,98 +93,6 @@ 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()
|
||||
// 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
|
||||
// }
|
||||
|
||||
// // fix - без freelist
|
||||
// func (s *Atree) allocIndexPage() 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
|
||||
// }
|
||||
|
||||
// func (s *Atree) allocDataPage() 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
|
||||
// }
|
||||
|
||||
// DATA PAGES
|
||||
|
||||
// READ
|
||||
@@ -214,155 +107,52 @@ func (s *Atree) pageReader() {
|
||||
}
|
||||
|
||||
func (s *Atree) readPages() {
|
||||
// s.mutex.Lock()
|
||||
// if len(s.pagesToRead) == 0 {
|
||||
// s.mutex.Unlock()
|
||||
// return
|
||||
// }
|
||||
// pagesToRead := s.pagesToRead
|
||||
// s.pagesToRead = nil
|
||||
// s.mutex.Unlock()
|
||||
s.mutex.Lock()
|
||||
if len(s.pagesToRead) == 0 {
|
||||
s.mutex.Unlock()
|
||||
return
|
||||
}
|
||||
pagesToRead := s.pagesToRead
|
||||
s.pagesToRead = nil
|
||||
s.mutex.Unlock()
|
||||
|
||||
// for _, pageNo := range pagesToRead {
|
||||
// buf := make([]byte, PageSize)
|
||||
// off := (pageNo - 1) * PageSize
|
||||
// n, err := s.file.ReadAt(buf, int64(off))
|
||||
// if n != PageSize {
|
||||
// err = fmt.Errorf("read %d instead of %d", n, PageSize)
|
||||
// }
|
||||
for _, pageNo := range pagesToRead {
|
||||
buf := make([]byte, storage.DataPageSize)
|
||||
off := int(pageNo-1) * storage.DataPageSize
|
||||
n, err := s.file.ReadAt(buf, int64(off))
|
||||
if n != storage.DataPageSize {
|
||||
err = fmt.Errorf("read %d instead of %d", n, storage.DataPageSize)
|
||||
}
|
||||
|
||||
// s.mutex.Lock()
|
||||
// resultChannels := s.pageWaits[pageNo]
|
||||
// delete(s.pageWaits, pageNo)
|
||||
s.mutex.Lock()
|
||||
resultChannels := s.pageWaits[pageNo]
|
||||
delete(s.pageWaits, pageNo)
|
||||
|
||||
// if err != nil {
|
||||
// s.mutex.Unlock()
|
||||
// for _, resultCh := range resultChannels {
|
||||
// resultCh <- readResult{
|
||||
// Err: err,
|
||||
// }
|
||||
// }
|
||||
// } else {
|
||||
// s.pages[pageNo] = &_page{
|
||||
// // fix - page type
|
||||
// PageNo: pageNo,
|
||||
// Buf: buf,
|
||||
// ReferenceCount: len(resultChannels),
|
||||
// }
|
||||
// s.mutex.Unlock()
|
||||
// for _, resultCh := range resultChannels {
|
||||
// resultCh <- readResult{
|
||||
// Data: buf,
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
if err != nil {
|
||||
s.mutex.Unlock()
|
||||
for _, resultCh := range resultChannels {
|
||||
resultCh <- readResult{
|
||||
Err: err,
|
||||
}
|
||||
}
|
||||
} else {
|
||||
s.pages[pageNo] = &_page{
|
||||
PageNo: pageNo,
|
||||
Buf: buf,
|
||||
ReferenceCount: len(resultChannels),
|
||||
}
|
||||
s.mutex.Unlock()
|
||||
for _, resultCh := range resultChannels {
|
||||
resultCh <- readResult{
|
||||
Data: buf,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WRITE
|
||||
|
||||
// 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
|
||||
// }
|
||||
|
||||
// func (s *Atree) appendWriteTaskToQueue(task WriteTask) {
|
||||
// s.mutex.Lock()
|
||||
// s.writeTasksQueue = append(s.writeTasksQueue, task)
|
||||
// s.mutex.Unlock()
|
||||
|
||||
// select {
|
||||
// case s.writeSignalCh <- struct{}{}:
|
||||
// default:
|
||||
// }
|
||||
// }
|
||||
|
||||
// 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]))
|
||||
|
||||
// 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
|
||||
|
||||
func isFileExist(fileName string) (bool, error) {
|
||||
_, err := os.Stat(fileName)
|
||||
if err != nil {
|
||||
if errors.Is(err, fs.ErrNotExist) {
|
||||
return false, nil
|
||||
} else {
|
||||
return false, err
|
||||
}
|
||||
} else {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
|
||||
func openFile(fileName string, pageSize int) (_ *os.File, _ uint32, err error) {
|
||||
file, err := os.OpenFile(fileName, os.O_RDWR, filePerm)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
fi, err := file.Stat()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
fileSize := fi.Size()
|
||||
|
||||
if (fileSize % int64(pageSize)) > 0 {
|
||||
err = fmt.Errorf("the file size %d is not a multiple of the page size %d",
|
||||
fileSize, pageSize)
|
||||
return
|
||||
}
|
||||
|
||||
allocatedPagesQty := fileSize / int64(pageSize)
|
||||
if allocatedPagesQty > math.MaxUint32 {
|
||||
err = fmt.Errorf("allocated pages %d is > max pages %d",
|
||||
allocatedPagesQty, math.MaxUint32)
|
||||
return
|
||||
}
|
||||
|
||||
return file, uint32(allocatedPagesQty), nil
|
||||
}
|
||||
|
||||
// func (s *Atree) ApplyREDO(task WriteTask) {
|
||||
// s.appendWriteTaskToQueue(task)
|
||||
// }
|
||||
|
||||
func (s *Atree) verifyCRC(data []byte, pageSize int) error {
|
||||
var (
|
||||
pos = pageSize - 4
|
||||
|
||||
158
atree/writer.go
158
atree/writer.go
@@ -1,158 +0,0 @@
|
||||
package atree
|
||||
|
||||
// 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
|
||||
// // }
|
||||
|
||||
// /*
|
||||
// Формат
|
||||
// 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,
|
||||
// // }
|
||||
// // }
|
||||
@@ -1,15 +0,0 @@
|
||||
package atree
|
||||
|
||||
type PeriodsWriter interface {
|
||||
Feed(uint32, float64)
|
||||
FeedNoSend(uint32, float64)
|
||||
Close() error
|
||||
}
|
||||
|
||||
type WorkerMeasureConsumer interface {
|
||||
FeedNoSend(uint32, float64)
|
||||
}
|
||||
|
||||
type AtreeMeasureConsumer interface {
|
||||
Feed(uint32, float64)
|
||||
}
|
||||
Reference in New Issue
Block a user