This commit is contained in:
2026-06-15 05:32:15 +03:00
parent 80309aab99
commit ca125c99d2
16 changed files with 685 additions and 1350 deletions

View File

@@ -1,14 +1,31 @@
package atree package atree
import ( import (
"errors"
"os" "os"
"sync" "sync"
) )
const ( 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 { type _page struct {
PageNo uint32 PageNo uint32
Buf []byte Buf []byte
@@ -16,66 +33,35 @@ type _page struct {
} }
type Atree struct { type Atree struct {
file *os.File indexFile *os.File
mutex sync.Mutex dataFile *os.File
allocatedPagesQty uint32 mutex sync.Mutex
pages map[uint32]*_page pages map[uint32]*_page
pageWaits map[uint32][]chan readResult pageWaits map[uint32][]chan readResult
pagesToRead []uint32 pagesToRead []uint32
readSignalCh chan struct{} readSignalCh chan struct{}
writeSignalCh chan struct{}
//writeTasksQueue []WriteTask
} }
type Options struct { type Options struct {
Dir string IndexFile *os.File
DatabaseName string DataFile *os.File
} }
func New(opt Options) (*Atree, error) { func New(opt Options) (*Atree, error) {
// if opt.Dir == "" { if opt.IndexFile == nil {
// return nil, errors.New("Dir option is required") return nil, errors.New("IndexFile option is required")
// } }
// if opt.DatabaseName == "" { if opt.DataFile == nil {
// return nil, errors.New("DatabaseName option is required") return nil, errors.New("DataFile option is required")
// } }
// // открываю или создаю dbName.data и dbName.index файлы s := &Atree{
// var ( indexFile: opt.IndexFile,
// fileName = filepath.Join(opt.Dir, opt.DatabaseName+".db") dataFile: opt.DataFile,
// file *os.File pages: make(map[uint32]*_page),
// allocatedPagesQty uint32 pageWaits: make(map[uint32][]chan readResult),
// ) readSignalCh: make(chan struct{}, 1),
}
// // При создании data файла сразу создается индекс, поэтому корректное return s, nil
// // состояние БД: либо оба файла есть, либо ни одного файла нет.
// 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
} }
func (s *Atree) Run() { func (s *Atree) Run() {
@@ -236,203 +222,3 @@ func (s *Atree) GetAllPages(rootPageNo uint32) (_ []uint32, err error) {
// } // }
return 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]))
// }
//

View File

@@ -4,6 +4,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"gordenko.dev/dima/bin"
"gordenko.dev/dima/qb" "gordenko.dev/dima/qb"
) )
@@ -118,69 +119,69 @@ func (s *BackwardCursor) Close() {
// HELPER // HELPER
func (s *BackwardCursor) makeDecompressors() error { func (s *BackwardCursor) makeDecompressors() error {
// timestampsSize, _ := bin.GetUint16(s.pageData[timestampsSizeIdx:]) timestampsSize, _ := bin.GetUint16(s.pageData[timestampsSizeIdx:])
// valuesSize, _ := bin.GetUint16(s.pageData[valuesSizeIdx:]) valuesSize, _ := bin.GetUint16(s.pageData[valuesSizeIdx:])
// payloadSize := timestampsSize + valuesSize payloadSize := timestampsSize + valuesSize
// if payloadSize > dataFooterIdx { if payloadSize > dataFooterIdx {
// return fmt.Errorf("corrupted data page %d: timestamps + values size %d gt payload size", return fmt.Errorf("corrupted data page %d: timestamps + values size %d gt payload size",
// s.pageNo, payloadSize) s.pageNo, payloadSize)
// } }
// s.timestampDecompressor = enc.NewTimeDeltaDecompressor( s.timestampDecompressor = enc.NewTimeDeltaDecompressor(
// s.pageData[:timestampsSize], s.pageData[:timestampsSize],
// ) )
// vbuf := s.pageData[timestampsSize : timestampsSize+valuesSize] vbuf := s.pageData[timestampsSize : timestampsSize+valuesSize]
// switch s.metricType { switch s.metricType {
// case qb.Instant: case qb.Instant:
// s.valueDecompressor = enc.NewInstantDeltaDecompressor( s.valueDecompressor = enc.NewInstantDeltaDecompressor(
// vbuf, s.fracDigits) vbuf, s.fracDigits)
// case qb.Cumulative: case qb.Cumulative:
// s.valueDecompressor = enc.NewCumulativeDeltaDecompressor( s.valueDecompressor = enc.NewCumulativeDeltaDecompressor(
// vbuf, s.fracDigits) vbuf, s.fracDigits)
// default: default:
// return fmt.Errorf("bug: wrong metricType %d", s.metricType) return fmt.Errorf("bug: wrong metricType %d", s.metricType)
// } }
return nil return nil
} }
func makeDecompressors(pageData []byte, metricType qb.MetricType, fracDigits byte) ( // func makeDecompressors(pageData []byte, metricType qb.MetricType, fracDigits byte) (
qb.TimestampDecompressor, qb.ValueDecompressor, error, // qb.TimestampDecompressor, qb.ValueDecompressor, error,
) { // ) {
// timestampsSize, _ := bin.GetUint16(pageData[timestampsSizeIdx:]) // timestampsSize, _ := bin.GetUint16(pageData[timestampsSizeIdx:])
// valuesSize, _ := bin.GetUint16(pageData[valuesSizeIdx:]) // valuesSize, _ := bin.GetUint16(pageData[valuesSizeIdx:])
// payloadSize := timestampsSize + valuesSize // payloadSize := timestampsSize + valuesSize
// if payloadSize > dataFooterIdx { // if payloadSize > dataFooterIdx {
// return nil, nil, fmt.Errorf("corrupted: timestamps + values size %d > payload size", // return nil, nil, fmt.Errorf("corrupted: timestamps + values size %d > payload size",
// payloadSize) // payloadSize)
// } // }
// timestampDecompressor := enc.NewTimeDeltaDecompressor( // timestampDecompressor := enc.NewTimeDeltaDecompressor(
// pageData[:timestampsSize], // pageData[:timestampsSize],
// ) // )
// vbuf := pageData[timestampsSize : timestampsSize+valuesSize] // vbuf := pageData[timestampsSize : timestampsSize+valuesSize]
// var valueDecompressor qb.ValueDecompressor // var valueDecompressor qb.ValueDecompressor
// switch metricType { // switch metricType {
// case qb.Instant: // case qb.Instant:
// valueDecompressor = enc.NewInstantDeltaDecompressor( // valueDecompressor = enc.NewInstantDeltaDecompressor(
// vbuf, fracDigits) // vbuf, fracDigits)
// case qb.Cumulative: // case qb.Cumulative:
// valueDecompressor = enc.NewCumulativeDeltaDecompressor( // valueDecompressor = enc.NewCumulativeDeltaDecompressor(
// vbuf, fracDigits) // vbuf, fracDigits)
// default: // default:
// return nil, nil, fmt.Errorf("bug: wrong metricType %d", metricType) // return nil, nil, fmt.Errorf("bug: wrong metricType %d", metricType)
// } // }
//return timestampDecompressor, valueDecompressor, nil //return timestampDecompressor, valueDecompressor, nil
return nil, nil, nil // return nil, nil, nil
} // }

View File

@@ -1,24 +1,14 @@
package atree package atree
import ( import (
"errors"
"fmt" "fmt"
"io/fs"
"math"
"os"
bin "gordenko.dev/dima/bin/little" bin "gordenko.dev/dima/bin/little"
"gordenko.dev/dima/qb" "gordenko.dev/dima/qb"
"gordenko.dev/dima/qb/storage"
"gordenko.dev/dima/qb/util" "gordenko.dev/dima/qb/util"
) )
// fix - додати ID, щоб потім перемістити із тимчасового буфера в pages, або звільнити
// type AllocatedPage struct {
// PageNo uint32
// Data []byte
// IsReused bool
// }
type readResult struct { type readResult struct {
Data []byte Data []byte
Err error Err error
@@ -47,46 +37,41 @@ func (s *Atree) fetchIndexPage(pageNo uint32) ([]byte, error) {
} }
func (s *Atree) fetchDataPage(pageNo uint32) ([]byte, error) { func (s *Atree) fetchDataPage(pageNo uint32) ([]byte, error) {
// buf, err := s.fetchPage(pageNo) buf, err := s.fetchPage(pageNo)
// if err != nil { if err != nil {
// return nil, err return nil, err
// } }
// if buf[pageTypeIdx] != PageTypeIndex { return buf, nil
// return nil, fmt.Errorf("wrong pageType %d instead of %d", buf[pageTypeIdx], PageTypeIndex)
// }
// return buf, nil
return nil, nil
} }
func (s *Atree) fetchPage(pageNo uint32) ([]byte, error) { func (s *Atree) fetchPage(pageNo uint32) ([]byte, error) {
// s.mutex.Lock() s.mutex.Lock()
// p, ok := s.pages[pageNo] p, ok := s.pages[pageNo]
// if ok { if ok {
// p.ReferenceCount++ p.ReferenceCount++
// s.mutex.Unlock() s.mutex.Unlock()
// return p.Buf, nil return p.Buf, nil
// } }
// resultCh := make(chan readResult, 1) resultCh := make(chan readResult, 1)
// s.pageWaits[pageNo] = append(s.pageWaits[pageNo], resultCh) s.pageWaits[pageNo] = append(s.pageWaits[pageNo], resultCh)
// if len(s.pageWaits[pageNo]) == 1 { if len(s.pageWaits[pageNo]) == 1 {
// s.pagesToRead = append(s.pagesToRead, pageNo) s.pagesToRead = append(s.pagesToRead, pageNo)
// s.mutex.Unlock() s.mutex.Unlock()
// select { select {
// case s.readSignalCh <- struct{}{}: case s.readSignalCh <- struct{}{}:
// default: default:
// } }
// } else { } else {
// s.mutex.Unlock() s.mutex.Unlock()
// } }
// result := <-resultCh result := <-resultCh
// if result.Err == nil { if result.Err == nil {
// result.Err = s.verifyCRC(result.Data, PageSize) result.Err = s.verifyCRC(result.Data, storage.DataPageSize)
// } }
// return result.Data, result.Err return result.Data, result.Err
return nil, nil
} }
func (s *Atree) releasePage(pageNo uint32) { 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 // DATA PAGES
// READ // READ
@@ -214,155 +107,52 @@ func (s *Atree) pageReader() {
} }
func (s *Atree) readPages() { func (s *Atree) readPages() {
// s.mutex.Lock() s.mutex.Lock()
// if len(s.pagesToRead) == 0 { if len(s.pagesToRead) == 0 {
// s.mutex.Unlock() s.mutex.Unlock()
// return return
// } }
// pagesToRead := s.pagesToRead pagesToRead := s.pagesToRead
// s.pagesToRead = nil s.pagesToRead = nil
// s.mutex.Unlock() s.mutex.Unlock()
// for _, pageNo := range pagesToRead { for _, pageNo := range pagesToRead {
// buf := make([]byte, PageSize) buf := make([]byte, storage.DataPageSize)
// off := (pageNo - 1) * PageSize off := int(pageNo-1) * storage.DataPageSize
// n, err := s.file.ReadAt(buf, int64(off)) n, err := s.file.ReadAt(buf, int64(off))
// if n != PageSize { if n != storage.DataPageSize {
// err = fmt.Errorf("read %d instead of %d", n, PageSize) err = fmt.Errorf("read %d instead of %d", n, storage.DataPageSize)
// } }
// s.mutex.Lock() s.mutex.Lock()
// resultChannels := s.pageWaits[pageNo] resultChannels := s.pageWaits[pageNo]
// delete(s.pageWaits, pageNo) delete(s.pageWaits, pageNo)
// if err != nil { if err != nil {
// s.mutex.Unlock() s.mutex.Unlock()
// for _, resultCh := range resultChannels { for _, resultCh := range resultChannels {
// resultCh <- readResult{ resultCh <- readResult{
// Err: err, Err: err,
// } }
// } }
// } else { } else {
// s.pages[pageNo] = &_page{ s.pages[pageNo] = &_page{
// // fix - page type PageNo: pageNo,
// PageNo: pageNo, Buf: buf,
// Buf: buf, ReferenceCount: len(resultChannels),
// ReferenceCount: len(resultChannels), }
// } s.mutex.Unlock()
// s.mutex.Unlock() for _, resultCh := range resultChannels {
// for _, resultCh := range resultChannels { resultCh <- readResult{
// resultCh <- readResult{ Data: buf,
// Data: buf, }
// } }
// } }
// } }
// }
} }
// WRITE // 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 { func (s *Atree) verifyCRC(data []byte, pageSize int) error {
var ( var (
pos = pageSize - 4 pos = pageSize - 4

View File

@@ -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,
// // }
// // }

View File

@@ -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)
}

View File

@@ -389,10 +389,8 @@ func (s *Connection) readCumulativeMeasures() (_ []proto.CumulativeMeasure, err
if err != nil { if err != nil {
return nil, fmt.Errorf("read response code: %s", err) return nil, fmt.Errorf("read response code: %s", err)
} }
fmt.Println("code", code)
switch code { switch code {
case proto.RespPartOfValue: case proto.RespPartOfValue:
fmt.Println("RespPartOfValue")
var count int var count int
count, err = bin.ReadUint32AsInt(s.src) count, err = bin.ReadUint32AsInt(s.src)
if err != nil { if err != nil {
@@ -415,10 +413,8 @@ func (s *Connection) readCumulativeMeasures() (_ []proto.CumulativeMeasure, err
result = append(result, measure) result = append(result, measure)
} }
case proto.RespEndOfValue: case proto.RespEndOfValue:
fmt.Println("RespEndOfValue")
return result, nil return result, nil
case proto.RespError: case proto.RespError:
fmt.Println("RespError")
return nil, s.onError() return nil, s.onError()
default: default:
return nil, fmt.Errorf("unknown reponse code %d", code) return nil, fmt.Errorf("unknown reponse code %d", code)

View File

@@ -278,39 +278,39 @@ func (s *Database) GetMetric(conn io.Writer, req proto.GetMetricReq) error {
} }
func (s *Database) DeleteMetric(req proto.DeleteMetricReq) uint16 { func (s *Database) DeleteMetric(req proto.DeleteMetricReq) uint16 {
resultCh := make(chan worker.DeleteMetricResult, 1) // resultCh := make(chan worker.DeleteMetricResult, 1)
s.workerInbox.Push(worker.DeleteMetricReq{ // s.workerInbox.Push(worker.DeleteMetricReq{
MetricID: req.MetricID, // MetricID: req.MetricID,
ResultCh: resultCh, // ResultCh: resultCh,
}) // })
result := <-resultCh // result := <-resultCh
switch result.ResultCode { // switch result.ResultCode {
case worker.Succeed: // case worker.Succeed:
// var ( // // var (
// //freePageNumbers []uint32 // // //freePageNumbers []uint32
// ) // // )
// if result.RootPageNo > 0 { // // if result.RootPageNo > 0 {
// var err error // // var err error
// freePageNumbers, err = s.atree.GetAllPages(result.RootPageNo) // // freePageNumbers, err = s.atree.GetAllPages(result.RootPageNo)
// if err != nil { // // if err != nil {
// qb.Abort(qb.FailedAtreeRequest, err) // // qb.Abort(qb.FailedAtreeRequest, err)
// } // // }
// } // // }
// s.storage.Append(storage.DeletedMetric{ // // s.storage.Append(storage.DeletedMetric{
// MetricID: req.MetricID, // // MetricID: req.MetricID,
// //FreePageNumbers: freePageNumbers, FIX // // //FreePageNumbers: freePageNumbers, FIX
// }) // // })
//<-waitCh // //<-waitCh
case worker.NoMetric: // case worker.NoMetric:
return proto.ErrNoMetric // return proto.ErrNoMetric
default: // default:
qb.Abort(qb.WrongResultCodeBug, ErrWrongResultCodeBug) // qb.Abort(qb.WrongResultCodeBug, ErrWrongResultCodeBug)
} // }
return 0 return 0
} }
@@ -363,7 +363,7 @@ func (s *Database) AppendMeasures(conn io.Writer, req proto.AppendMeasuresReq) e
} }
func (s *Database) DeleteMeasures(req proto.DeleteMeasuresReq) uint16 { func (s *Database) DeleteMeasures(req proto.DeleteMeasuresReq) uint16 {
resultCh := make(chan worker.DeleteMeasuresResult, 1) resultCh := make(chan byte, 1)
s.workerInbox.Push(worker.DeleteMeasuresReq{ s.workerInbox.Push(worker.DeleteMeasuresReq{
MetricID: req.MetricID, MetricID: req.MetricID,
@@ -373,36 +373,38 @@ func (s *Database) DeleteMeasures(req proto.DeleteMeasuresReq) uint16 {
result := <-resultCh result := <-resultCh
switch result.ResultCode { _ = result
case worker.NoMeasuresToDelete:
// ok
//case worker.DeleteFromAtreeNotNeeded: // switch result.ResultCode {
// регистрирую удаление в TransactionLog // case worker.NoMeasuresToDelete:
// s.storage.Append(storage.MeasuresDeleteRecord{ // // ok
// MetricID: req.MetricID,
// })
//<-waitCh
//case worker.DeleteFromAtreeRequired: // //case worker.DeleteFromAtreeNotNeeded:
// собираю номера всех data и index страниц метрики (типа запись REDO лога).
// pageNumbers, err := s.atree.GetAllPages(req.MetricID)
// if err != nil {
// qb.Abort(qb.FailedAtreeRequest, err)
// }
// // регистрирую удаление в TransactionLog // // регистрирую удаление в TransactionLog
// s.storage.Append(storage.DeletedMeasures{ // // s.storage.Append(storage.MeasuresDeleteRecord{
// MetricID: req.MetricID, // // MetricID: req.MetricID,
// FreePageNumbers: pageNumbers, // // })
// }) // //<-waitCh
//<-waitCh
case worker.NoMetric: // //case worker.DeleteFromAtreeRequired:
return proto.ErrNoMetric // // собираю номера всех data и index страниц метрики (типа запись REDO лога).
// // pageNumbers, err := s.atree.GetAllPages(req.MetricID)
// // if err != nil {
// // qb.Abort(qb.FailedAtreeRequest, err)
// // }
// // // регистрирую удаление в TransactionLog
// // s.storage.Append(storage.DeletedMeasures{
// // MetricID: req.MetricID,
// // FreePageNumbers: pageNumbers,
// // })
// //<-waitCh
default: // case worker.NoMetric:
qb.Abort(qb.WrongResultCodeBug, ErrWrongResultCodeBug) // return proto.ErrNoMetric
}
// default:
// qb.Abort(qb.WrongResultCodeBug, ErrWrongResultCodeBug)
// }
return 0 return 0
} }
@@ -616,17 +618,17 @@ func (s *Database) fullScan(req fullScanReq) error {
req.ResponseWriter.Close() req.ResponseWriter.Close()
case worker.UntilFound: case worker.UntilFound:
// err := s.atree.ContinueFullScan(atree.ContinueFullScanReq{ err := s.atree.ContinueFullScan(atree.ContinueFullScanReq{
// FracDigits: result.FracDigits, FracDigits: result.FracDigits,
// ResponseWriter: req.ResponseWriter, ResponseWriter: req.ResponseWriter,
// LastPageNo: result.LastPageNo, LastPageNo: result.LastPageNo,
// }) })
// s.worker.AddJobToQueue(req.MetricID) //s.worker.AddJobToQueue(req.MetricID) // FIX release rlock
// if err != nil { if err != nil {
// reply(req.Conn, proto.ErrUnexpected) reply(req.Conn, proto.ErrUnexpected)
// } else { } else {
// req.ResponseWriter.Close() req.ResponseWriter.Close()
// } }
case worker.NoMetric: case worker.NoMetric:
reply(req.Conn, proto.ErrNoMetric) reply(req.Conn, proto.ErrNoMetric)

View File

@@ -10,19 +10,13 @@ import (
"time" "time"
"gordenko.dev/dima/qb" "gordenko.dev/dima/qb"
"gordenko.dev/dima/qb/atree"
"gordenko.dev/dima/qb/inbox" "gordenko.dev/dima/qb/inbox"
"gordenko.dev/dima/qb/recovery" "gordenko.dev/dima/qb/recovery"
"gordenko.dev/dima/qb/storage" "gordenko.dev/dima/qb/storage"
"gordenko.dev/dima/qb/worker" "gordenko.dev/dima/qb/worker"
) )
// type metricLockEntry struct {
// XLock bool
// RLocks int
// WaitQueue []any
// }
//metricLockEntries map[uint32]*metricLockEntry
type Database struct { type Database struct {
mutex sync.Mutex mutex sync.Mutex
dir string dir string
@@ -30,6 +24,7 @@ type Database struct {
workerInbox *inbox.Inbox workerInbox *inbox.Inbox
worker *worker.Worker worker *worker.Worker
storage *storage.Writer storage *storage.Writer
atree *atree.Atree
tcpPort int tcpPort int
logfile *os.File logfile *os.File
logger *log.Logger logger *log.Logger
@@ -122,8 +117,6 @@ func New(opt Options) (_ *Database, err error) {
return nil, fmt.Errorf("storage.NewWriter: %s", err) return nil, fmt.Errorf("storage.NewWriter: %s", err)
} }
fmt.Println("storage created")
s.worker = worker.New(worker.Options{ s.worker = worker.New(worker.Options{
Inbox: s.workerInbox, Inbox: s.workerInbox,
StorageInbox: storageInbox, StorageInbox: storageInbox,
@@ -133,7 +126,14 @@ func New(opt Options) (_ *Database, err error) {
ExitCh: opt.ExitCh, ExitCh: opt.ExitCh,
WaitGroup: opt.WaitGroup, WaitGroup: opt.WaitGroup,
}) })
fmt.Println("worker created")
s.atree, err = atree.New(atree.Options{
DataFile: dataFile,
IndexFile: indexFile,
})
if err != nil {
return nil, fmt.Errorf("atree.New: %s", err)
}
return s, nil return s, nil
} }
@@ -146,13 +146,6 @@ func (s *Database) ListenAndServe() (err error) {
//s.waitGroup.Add(1) //s.waitGroup.Add(1)
go s.storage.Run() go s.storage.Run()
// s.atree, err = atree.New(atree.Options{
// Dir: s.dir,
// DatabaseName: s.databaseName,
// })
// if err != nil {
// return fmt.Errorf("atree.New: %s", err)
// }
// s.atree.Run() // s.atree.Run()
s.waitGroup.Add(1) s.waitGroup.Add(1)

View File

@@ -304,6 +304,12 @@ func (s *TimeDeltaCompressor) CreateDecompressor() qb.TimestampDecompressor {
return d return d
} }
func (s *TimeDeltaCompressor) FirstTimestamp() uint32 {
pos := len(s.buf) - 4
timestamp, _ := bin.GetUint32(s.buf[pos:])
return timestamp
}
func (s *TimeDeltaCompressor) LastTimestamp() uint32 { func (s *TimeDeltaCompressor) LastTimestamp() uint32 {
return s.lastUnixtime return s.lastUnixtime
} }

View File

@@ -9,7 +9,6 @@ import (
"time" "time"
"gopkg.in/ini.v1" "gopkg.in/ini.v1"
"gordenko.dev/dima/qb"
"gordenko.dev/dima/qb/client" "gordenko.dev/dima/qb/client"
"gordenko.dev/dima/qb/proto" "gordenko.dev/dima/qb/proto"
) )
@@ -292,46 +291,46 @@ func sendRequests(conn *client.Connection) {
// ADD CUMULATIVE METRIC // ADD CUMULATIVE METRIC
err = conn.AddMetric(proto.AddMetricReq{ // err = conn.AddMetric(proto.AddMetricReq{
MetricID: cumulativeMetricID, // MetricID: cumulativeMetricID,
MetricType: qb.Cumulative, // MetricType: qb.Cumulative,
FracDigits: fracDigits, // FracDigits: fracDigits,
}) // })
if err != nil { // if err != nil {
log.Fatalf("conn.AddMetric: %s\n", err) // log.Fatalf("conn.AddMetric: %s\n", err)
} else { // } else {
fmt.Printf("\nCumulative metric %d added\n", cumulativeMetricID) // fmt.Printf("\nCumulative metric %d added\n", cumulativeMetricID)
} // }
// GET CUMULATIVE METRIC // // GET CUMULATIVE METRIC
cMetric, err := conn.GetMetric(cumulativeMetricID) // cMetric, err := conn.GetMetric(cumulativeMetricID)
if err != nil { // if err != nil {
log.Fatalf("conn.GetMetric: %s\n", err) // log.Fatalf("conn.GetMetric: %s\n", err)
} else { // } else {
fmt.Printf(` // fmt.Printf(`
GetMetric: // GetMetric:
metricID: %d // metricID: %d
metricType: %s // metricType: %s
fracDigits: %d // fracDigits: %d
`, // `,
cMetric.MetricID, metricTypeToName[cMetric.MetricType], cMetric.FracDigits) // cMetric.MetricID, metricTypeToName[cMetric.MetricType], cMetric.FracDigits)
} // }
// APPEND MEASURES // // APPEND MEASURES
cumulativeMeasures := GenerateCumulativeMeasures(62) // cumulativeMeasures := GenerateCumulativeMeasures(62)
result, err := conn.AppendMeasures(proto.AppendMeasuresReq{ // result, err := conn.AppendMeasures(proto.AppendMeasuresReq{
MetricID: cumulativeMetricID, // MetricID: cumulativeMetricID,
Measures: cumulativeMeasures[:1000], // Measures: cumulativeMeasures[:1000],
}) // })
if err != nil { // if err != nil {
log.Fatalf("conn.AppendMeasures: %s\n", err) // log.Fatalf("conn.AppendMeasures: %s\n", err)
} else { // } else {
fmt.Printf("\nAppended %d measures for the metric %d: count=%d, errorCode=%d\n", // fmt.Printf("\nAppended %d measures for the metric %d: count=%d, errorCode=%d\n",
len(cumulativeMeasures), cumulativeMetricID, result.WrittenCount, result.ErrorCode) // len(cumulativeMeasures), cumulativeMetricID, result.WrittenCount, result.ErrorCode)
} // }
// currentValues, err := conn.ListCurrentValues([]uint32{ // currentValues, err := conn.ListCurrentValues([]uint32{
// cumulativeMetricID, // cumulativeMetricID,
@@ -374,8 +373,8 @@ func sendRequests(conn *client.Connection) {
log.Fatalf("conn.ListAllCumulativeMeasures: %s\n", err) log.Fatalf("conn.ListAllCumulativeMeasures: %s\n", err)
} else { } else {
fmt.Printf("\nListAllCumulativeMeasures (last 15 items):\n") fmt.Printf("\nListAllCumulativeMeasures (last 15 items):\n")
fmt.Printf("%#v\n", cumulativeList) //fmt.Printf("%#v\n", cumulativeList)
for _, item := range cumulativeList { for _, item := range cumulativeList[:15] {
fmt.Printf(" %s => %.2f\n", formatTime(item.Timestamp), item.Value) fmt.Printf(" %s => %.2f\n", formatTime(item.Timestamp), item.Value)
} }
} }

Binary file not shown.

1
qb.go
View File

@@ -41,6 +41,7 @@ type TimestampCompressor interface {
// Offset() int // Offset() int
ReplaceBuffer([]byte) ReplaceBuffer([]byte)
WriteCommitedTo(io.Writer) error WriteCommitedTo(io.Writer) error
FirstTimestamp() uint32
LastTimestamp() uint32 LastTimestamp() uint32
ReplaceSinceWithUntil() uint32 ReplaceSinceWithUntil() uint32
} }

92
storage/misc.go Normal file
View File

@@ -0,0 +1,92 @@
package storage
import (
"fmt"
bin "gordenko.dev/dima/bin/little"
"gordenko.dev/dima/qb"
"gordenko.dev/dima/qb/enc"
)
// func (s *BackwardCursor) makeDecompressors() error {
// timestampsSize, _ := bin.GetUint16(s.pageData[timestampsSizeIdx:])
// valuesSize, _ := bin.GetUint16(s.pageData[valuesSizeIdx:])
// payloadSize := timestampsSize + valuesSize
// 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],
// )
// vbuf := s.pageData[timestampsSize : timestampsSize+valuesSize]
// switch s.metricType {
// case qb.Instant:
// s.valueDecompressor = enc.NewInstantDeltaDecompressor(
// vbuf, s.fracDigits)
// case qb.Cumulative:
// s.valueDecompressor = enc.NewCumulativeDeltaDecompressor(
// vbuf, s.fracDigits)
// 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,
) {
valuesSize, _ := bin.GetUint16(pageData[valuesSizeIdx:])
payloadSize := timestampsSize + valuesSize
if payloadSize > dataFooterIdx {
return nil, nil, fmt.Errorf("corrupted: timestamps + values size %d > payload size",
payloadSize)
}
timestampDecompressor := enc.NewTimeDeltaDecompressor(
pageData[:timestampsSize],
)
vbuf := pageData[timestampsSize : timestampsSize+valuesSize]
var valueDecompressor qb.ValueDecompressor
switch metricType {
case qb.Instant:
valueDecompressor = enc.NewInstantDeltaDecompressor(
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
}
func CreateTimeDeltaDecompressor(page []byte) qb.TimestampDecompressor {
size, _ := bin.GetUint16(page[timestampsSizeIdx:])
pos := DataPagePayloadSize - int(size)
d := enc.NewTimeDeltaDecompressor()
d.RestoreFromEnd(page[pos:DataPagePayloadSize])
return d
}
func CreateValueDeltaDecompressor(page []byte, metricType qb.MetricType, fracDigits byte) qb.ValueDecompressor {
size, _ := bin.GetUint16(page[valuesSizeIdx:])
d := enc.NewValueDeltaDecompressor(metricType, fracDigits)
d.RestoreFromEnd(page[:size])
return d
}

View File

@@ -77,23 +77,20 @@ type Writer struct {
isExited bool isExited bool
exitCh chan struct{} exitCh chan struct{}
waitGroup *sync.WaitGroup waitGroup *sync.WaitGroup
//signalCh chan struct{}
} }
type WriterOptions struct { type WriterOptions struct {
Inbox *inbox.Inbox Inbox *inbox.Inbox
WorkerInbox *inbox.Inbox WorkerInbox *inbox.Inbox
Dir string Dir string
DatabaseName string DatabaseName string
//SnapshotNumber int // номер журнала
WAL string WAL string
DataFreeList *freelist.FreeList DataFreeList *freelist.FreeList
IndexFreeList *freelist.FreeList IndexFreeList *freelist.FreeList
DataFile *os.File DataFile *os.File
IndexFile *os.File IndexFile *os.File
//Atree *atree.Atree ExitCh chan struct{}
ExitCh chan struct{} WaitGroup *sync.WaitGroup
WaitGroup *sync.WaitGroup
} }
func NewWriter(opt WriterOptions) (*Writer, error) { func NewWriter(opt WriterOptions) (*Writer, error) {
@@ -245,17 +242,26 @@ func (s *Writer) packAndWrite() (err error) {
fmt.Println("synced to wal") fmt.Println("synced to wal")
// 4. Пишу в atree сторінки // 4. Пишу в atree сторінки
err = WriteDataPages(s.dataFile, prepared.WriteToData) if len(prepared.WriteToData) > 0 {
if err != nil { err = WriteDataPages(s.dataFile, prepared.WriteToData)
return if err != nil {
return
}
if err = s.dataFile.Sync(); err != nil {
return
}
fmt.Println("written to data")
} }
fmt.Println("written to data") if len(prepared.WriteToIndex) > 0 {
err = WriteIndexPages(s.indexFile, prepared.WriteToIndex) err = WriteIndexPages(s.indexFile, prepared.WriteToIndex)
if err != nil { if err != nil {
return return
}
if err = s.indexFile.Sync(); err != nil {
return
}
fmt.Println("written to index")
} }
fmt.Println("written to index")
// 6. відправляю input - worker-у // 6. відправляю input - worker-у
var ( var (
@@ -413,62 +419,3 @@ func WriteIndexPages(file *os.File, pages []PageToWrite) (err error) {
// TimestampsBuf []byte // TimestampsBuf []byte
// ValuesBuf []byte // ValuesBuf []byte
// } // }
// Якщо (Pages) > 0 - незаповнену сторінку кодую в стисненому вигляді.
// Якщо (Pages) = 0 - кодую просто пари timestamp / value
// func (s *Writer) packWriteAppended(w *bytes.Buffer, req AppendedPagesReq) {
// // завантажений path. Отже просто додаємо в дерево data сторінки, створюємо нові індексні
// // без звернення до диску.
// report := s.atree.AppendDataPages(atree.AppendDataPagesReq{
// LastPageNo: req.LastPageNo,
// Legs: req.Legs,
// DataPages: req.Pages,
// })
// m := AppendedPages{
// MetricID: req.MetricID,
// Timestamp: req.Timestamp,
// Value: req.Value,
// NewRootPageNo: report.NewRootPageNo,
// LastPageNo: report.LastPageNo,
// Pages: report.Pages,
// TimestampsChunks: req.TimestampsChunks,
// TimestampsSize: int(req.TimestampsSize),
// ValuesChunks: req.ValuesChunks,
// ValuesSize: int(req.ValuesSize),
// }
// m.Pack(w)
// }
// helpers
// type Metric struct {
// MetricID uint32
// MetricType qb.MetricType
// FracDigits byte
// LastPageNo uint32
// Since uint32
// SinceValue float64
// Until uint32
// UntilValue float64
// Timestamps []byte // payload FIX
// Values []byte // payload FIX add lastDelta + h ?
// }
// func writeChunks(dst io.Writer, chunks [][]byte, size int) (err error) {
// remaining := size
// for _, buf := range chunks {
// if remaining < len(buf) {
// buf = buf[:remaining]
// }
// _, err = dst.Write(buf)
// if err != nil {
// return
// }
// remaining -= len(buf)
// if remaining == 0 {
// break
// }
// }
// return
// }

View File

@@ -33,11 +33,75 @@ type Metric struct {
values qb.ValueCompressor values qb.ValueCompressor
xLock bool xLock bool
rLocks int rLocks int
WaitQueue []any waitQueue []any
indexLevelTails []storage.IndexLevelTail // root - last element indexLevelTails []storage.IndexLevelTail // root - last element
capturedState *CapturedState capturedState *CapturedState
} }
// індекси
// Metric encode format:
// metricID - 4b
// metricType - 1b
// fracDigits - 1b
// lastPageNo - 4b
// timestamps size - 2b
// values size - 2b
// timestams payload - Nb
// values payload - Nb
// index levels count - varsize
// [
// records qty - varsize
// records - Nb
// ]
func (s *Metric) WriteTo(w io.Writer) (err error) {
_, err = w.Write([]byte{
byte(s.metricType),
s.fracDigits,
})
if err != nil {
return
}
err = bin.WriteUint32(w, s.lastPageNo)
if err != nil {
return
}
err = bin.WriteUint16(w, uint16(s.timestamps.CommitedSize()))
if err != nil {
return
}
err = bin.WriteUint16(w, uint16(s.values.CommitedSize()))
if err != nil {
return
}
// timestamps payload
err = s.timestamps.WriteCommitedTo(w)
if err != nil {
return
}
// values payload
err = s.values.WriteCommitedTo(w)
if err != nil {
return
}
// indexes
_, err = bin.WriteVarSize(w, len(s.indexLevelTails))
if err != nil {
return
}
for _, level := range s.indexLevelTails {
_, err = bin.WriteVarSize(w, level.RecordsCount)
if err != nil {
return
}
_, err = w.Write(level.Buffer[:level.RecordsCount*storage.IndexRecordSize])
if err != nil {
return
}
}
return
}
func (s *Metric) MetricType() qb.MetricType { func (s *Metric) MetricType() qb.MetricType {
return s.metricType return s.metricType
} }
@@ -60,26 +124,12 @@ func (s *Metric) LastTimestamp() uint32 {
return s.timestamps.LastTimestamp() return s.timestamps.LastTimestamp()
} }
func (s *Metric) OnMeasuresDeleteCommited(rec storage.MeasuresDeleteCommited) { // REQUESTS
//s.timestamps.Reset()
//s.values.Reset()
s.xLock = false
// s.Timestamps.Renew()
// s.Values.Renew()
// s.LastPageNo = 0
// s.Since = 0
// s.SinceValue = 0
// s.Until = 0
s.indexLevelTails = nil
s.lastPageNo = 0
s.lastValue = 0
}
func (s *Metric) AppendMeasures(req AppendMeasuresReq, tmp []byte, storageInbox *inbox.Inbox) { func (s *Metric) AppendMeasures(req AppendMeasuresReq, tmp []byte, storageInbox *inbox.Inbox) {
if s.xLock || s.capturedState != nil {
if s.capturedState != nil { s.waitQueue = append(s.waitQueue, req)
s.WaitQueue = append(s.WaitQueue, req) return
} }
var ( var (
timestamps = s.timestamps timestamps = s.timestamps
@@ -188,7 +238,6 @@ func (s *Metric) AppendMeasures(req AppendMeasuresReq, tmp []byte, storageInbox
// скопіювати. Причому можна скопіювати зрізи chunks // скопіювати. Причому можна скопіювати зрізи chunks
if len(pages) > 0 { if len(pages) > 0 {
fmt.Println("push pages")
// пишу в storage довгим шляхом через redo файл і запис в data файл // пишу в storage довгим шляхом через redo файл і запис в data файл
storageInbox.Push(storage.MeasuresAppendWithGrow{ storageInbox.Push(storage.MeasuresAppendWithGrow{
MetricID: req.MetricID, MetricID: req.MetricID,
@@ -206,7 +255,6 @@ func (s *Metric) AppendMeasures(req AppendMeasuresReq, tmp []byte, storageInbox
ResultCh: req.ResultCh, ResultCh: req.ResultCh,
}) })
} else { } else {
fmt.Println("push simple")
// короткий шлях - запис лише в storage // короткий шлях - запис лише в storage
storageInbox.Push(storage.MeasuresAppend{ storageInbox.Push(storage.MeasuresAppend{
MetricID: req.MetricID, MetricID: req.MetricID,
@@ -221,6 +269,143 @@ func (s *Metric) AppendMeasures(req AppendMeasuresReq, tmp []byte, storageInbox
} }
} }
func (s *Metric) DeleteMeasures(req DeleteMeasuresReq) {
if s.xLock || s.capturedState != nil {
s.waitQueue = append(s.waitQueue, req)
return
}
since := s.timestamps.FirstTimestamp()
until := s.timestamps.LastTimestamp()
if since == 0 || (req.Since > 0 && until < req.Since) {
req.ResultCh <- NoMeasuresToDelete
}
// if s.RootPageNo > 0 {
// req.ResultCh <- tryDeleteMeasuresResult{
// ResultCode: DeleteFromAtreeRequired,
// RootPageNo: metric.RootPageNo,
// }
// } else {
// req.ResultCh <- tryDeleteMeasuresResult{
// ResultCode: DeleteFromAtreeNotNeeded,
// }
// }
}
func (s *Metric) StartRangeScan(req RangeScanReq) {
if s.xLock {
s.waitQueue = append(s.waitQueue, req)
return
}
// if s.timestamps.CommitedSize() == 0 {
// req.ResultCh <- RangeScanResult{
// ResultCode: QueryDone,
// }
// return
// }
// if req.Since > s.timestamps.LastTimestamp() {
// req.ResultCh <- RangeScanResult{
// ResultCode: QueryDone,
// }
// return
// }
// if req.Until < s.Since {
// if s.RootPageNo > 0 {
// req.ResultCh <- RangeScanResult{
// ResultCode: UntilNotFound,
// RootPageNo: s.RootPageNo,
// FracDigits: s.fracDigits,
// }
// s.rLocks++
// return
// } else {
// req.ResultCh <- RangeScanResult{
// ResultCode: QueryDone,
// }
// return
// }
// }
timestampDecompressor := s.timestamps.CreateDecompressor()
valueDecompressor := s.values.CreateDecompressor(s.metricType, s.fracDigits)
for {
timestamp, done := timestampDecompressor.NextValue()
if done {
break
}
value, done := valueDecompressor.NextValue()
if done {
qb.Abort(qb.HasTimestampNoValueBug, ErrNoValueBug)
}
if timestamp <= req.Until {
req.ResponseWriter.FeedNoSend(timestamp, value)
if timestamp < req.Since {
req.ResultCh <- RangeScanResult{
ResultCode: QueryDone,
}
return
}
}
}
if s.lastPageNo > 0 {
req.ResultCh <- RangeScanResult{
ResultCode: UntilFound,
LastPageNo: s.lastPageNo,
FracDigits: s.fracDigits,
}
s.rLocks++
} else {
req.ResultCh <- RangeScanResult{
ResultCode: QueryDone,
}
}
}
func (s *Metric) StartFullScan(req FullScanReq) {
if s.xLock {
s.waitQueue = append(s.waitQueue, req)
return
}
if s.timestamps.CommitedSize() == 0 {
req.ResultCh <- FullScanResult{
ResultCode: QueryDone,
}
return
}
timestampDecompressor := s.timestamps.CreateDecompressor()
valueDecompressor := s.values.CreateDecompressor(s.metricType, s.fracDigits)
for {
timestamp, done := timestampDecompressor.NextValue()
if done {
break
}
//fmt.Println("ts:", timestamp)
value, done := valueDecompressor.NextValue()
if done {
qb.Abort(qb.HasTimestampNoValueBug, ErrNoValueBug)
}
//fmt.Println("value:", value)
req.ResponseWriter.FeedNoSend(timestamp, value)
}
if s.lastPageNo > 0 {
req.ResultCh <- FullScanResult{
ResultCode: UntilFound,
LastPageNo: s.lastPageNo,
FracDigits: s.fracDigits,
}
s.rLocks++
} else {
req.ResultCh <- FullScanResult{
ResultCode: QueryDone,
}
}
}
// COMMITS
func (s *Metric) OnMeasuresAppendCommited(rec storage.MeasuresAppendCommited) { func (s *Metric) OnMeasuresAppendCommited(rec storage.MeasuresAppendCommited) {
// Видаляю state. Оригінальні Timestamps і Values вже мають останню версію // Видаляю state. Оригінальні Timestamps і Values вже мають останню версію
s.capturedState = nil s.capturedState = nil
@@ -252,175 +437,18 @@ func (s *Metric) OnMeasuresAppendWithGrowCommited(rec storage.MeasuresAppendWith
} }
} }
// READ func (s *Metric) OnMeasuresDeleteCommited(rec storage.MeasuresDeleteCommited) {
//s.timestamps.Reset()
//s.values.Reset()
s.xLock = false
// s.Timestamps.Renew()
// s.Values.Renew()
func (s *Metric) StartRangeScan(req RangeScanReq) { // s.LastPageNo = 0
// if s.Since == 0 { // s.Since = 0
// req.ResultCh <- rangeScanResult{ // s.SinceValue = 0
// ResultCode: QueryDone, // s.Until = 0
// } s.indexLevelTails = nil
// return s.lastPageNo = 0
// } s.lastValue = 0
// if req.Since > s.Until {
// req.ResultCh <- rangeScanResult{
// ResultCode: QueryDone,
// }
// return
// }
// if req.Until < s.Since {
// if s.RootPageNo > 0 {
// req.ResultCh <- rangeScanResult{
// ResultCode: UntilNotFound,
// RootPageNo: s.RootPageNo,
// FracDigits: s.FracDigits,
// }
// s.RLocks++
// return
// } else {
// req.ResultCh <- rangeScanResult{
// ResultCode: QueryDone,
// }
// return
// }
// }
// timestampDecompressor := s.timestamps.CreateDecompressor()
// valueDecompressor := s.values.CreateDecompressor(s.metricType, s.fracDigits)
// for {
// timestamp, done := timestampDecompressor.NextValue()
// if done {
// break
// }
// value, done := valueDecompressor.NextValue()
// if done {
// qb.Abort(qb.HasTimestampNoValueBug, ErrNoValueBug)
// }
// if timestamp <= req.Until {
// req.ResponseWriter.FeedNoSend(timestamp, value)
// if timestamp < req.Since {
// req.ResultCh <- rangeScanResult{
// ResultCode: QueryDone,
// }
// return
// }
// }
// }
// if s.lastPageNo > 0 {
// req.ResultCh <- rangeScanResult{
// ResultCode: UntilFound,
// LastPageNo: s.lastPageNo,
// FracDigits: s.fracDigits,
// }
// s.RLocks++
// } else {
// req.ResultCh <- rangeScanResult{
// ResultCode: QueryDone,
// }
// }
}
func (s *Metric) StartFullScan(req FullScanReq) {
// if s.Since == 0 {
// req.ResultCh <- fullScanResult{
// ResultCode: QueryDone,
// }
// return
// }
timestampDecompressor := s.timestamps.CreateDecompressor()
valueDecompressor := s.values.CreateDecompressor(s.metricType, s.fracDigits)
for {
timestamp, done := timestampDecompressor.NextValue()
if done {
break
}
//fmt.Println("ts:", timestamp)
value, done := valueDecompressor.NextValue()
if done {
qb.Abort(qb.HasTimestampNoValueBug, ErrNoValueBug)
}
//fmt.Println("value:", value)
req.ResponseWriter.FeedNoSend(timestamp, value)
}
// if s.lastPageNo > 0 {
// req.ResultCh <- FullScanResult{
// ResultCode: UntilFound,
// LastPageNo: s.lastPageNo,
// FracDigits: s.fracDigits,
// }
// s.rLocks++
// } else {
req.ResultCh <- FullScanResult{
ResultCode: QueryDone,
}
//}
}
// індекси
// Metric encode format:
// metricID - 4b
// metricType - 1b
// fracDigits - 1b
// lastPageNo - 4b
// timestamps size - 2b
// values size - 2b
// timestams payload - Nb
// values payload - Nb
// index levels count - varsize
// [
// records qty - varsize
// records - Nb
// ]
func (s *Metric) WriteTo(w io.Writer) (err error) {
_, err = w.Write([]byte{
byte(s.metricType),
s.fracDigits,
})
if err != nil {
return
}
err = bin.WriteUint32(w, s.lastPageNo)
if err != nil {
return
}
err = bin.WriteUint16(w, uint16(s.timestamps.CommitedSize()))
if err != nil {
return
}
err = bin.WriteUint16(w, uint16(s.values.CommitedSize()))
if err != nil {
return
}
// timestamps payload
err = s.timestamps.WriteCommitedTo(w)
if err != nil {
return
}
// values payload
err = s.values.WriteCommitedTo(w)
if err != nil {
return
}
// indexes
_, err = bin.WriteVarSize(w, len(s.indexLevelTails))
if err != nil {
return
}
for _, level := range s.indexLevelTails {
_, err = bin.WriteVarSize(w, level.RecordsCount)
if err != nil {
return
}
_, err = w.Write(level.Buffer[:level.RecordsCount*storage.IndexRecordSize])
if err != nil {
return
}
}
return
} }

View File

@@ -147,31 +147,22 @@ func (s *Worker) doWork() {
switch req := untyped.(type) { switch req := untyped.(type) {
case AppendMeasuresReq: case AppendMeasuresReq:
s.AppendMeasures(req) s.AppendMeasures(req)
case storage.Changes: case storage.Changes:
s.applyCommits(req) // all metrics only s.applyCommits(req) // all metrics only
case ListCurrentValuesReq: case ListCurrentValuesReq:
s.ListCurrentValues(req) // all metrics only s.ListCurrentValues(req) // all metrics only
case RangeScanReq: case RangeScanReq:
s.RangeScan(req) s.RangeScan(req)
case FullScanReq: case FullScanReq:
s.FullScan(req) s.FullScan(req)
case AddMetricReq: case AddMetricReq:
s.AddMetric(req) s.AddMetric(req)
case DeleteMetricReq: case DeleteMetricReq:
s.DeleteMetric(req) s.DeleteMetric(req)
case DeleteMeasuresReq: case DeleteMeasuresReq:
s.DeleteMeasures(req) s.DeleteMeasures(req)
case GetMetricReq: case GetMetricReq:
s.GetMetric(req) s.GetMetric(req)
default: default:
qb.Abort(qb.UnknownWorkerQueueItemBug, qb.Abort(qb.UnknownWorkerQueueItemBug,
fmt.Errorf("bug: unknown worker queue item type %T", req)) fmt.Errorf("bug: unknown worker queue item type %T", req))
@@ -181,30 +172,23 @@ func (s *Worker) doWork() {
// суть у тому що треба запускати запити, пока не зустріну XLock // суть у тому що треба запускати запити, пока не зустріну XLock
func (s *Worker) processMetricQueue(metricID uint32, metric *Metric, tmp []byte) { func (s *Worker) processMetricQueue(metricID uint32, metric *Metric, tmp []byte) {
if len(metric.WaitQueue) == 0 { if len(metric.waitQueue) == 0 {
return return
} }
for _, untyped := range metric.waitQueue {
for _, untyped := range metric.WaitQueue {
switch req := untyped.(type) { switch req := untyped.(type) {
case RangeScanReq: case RangeScanReq:
metric.StartRangeScan(req) metric.StartRangeScan(req)
case FullScanReq: case FullScanReq:
metric.StartFullScan(req) metric.StartFullScan(req)
case GetMetricReq: case GetMetricReq:
s.GetMetric(req) s.GetMetric(req)
case AppendMeasuresReq: case AppendMeasuresReq:
metric.AppendMeasures(req, tmp, s.storageInbox) metric.AppendMeasures(req, tmp, s.storageInbox)
case DeleteMetricReq: case DeleteMetricReq:
s.startDeleteMetric(metric, req) s.DeleteMetric(req)
case DeleteMeasuresReq: case DeleteMeasuresReq:
s.startDeleteMeasures(metric, req) metric.DeleteMeasures(req)
default: default:
qb.Abort(qb.UnknownMetricWaitQueueItemBug, qb.Abort(qb.UnknownMetricWaitQueueItemBug,
fmt.Errorf("bug: unknown metric wait queue item type %T", req)) fmt.Errorf("bug: unknown metric wait queue item type %T", req))
@@ -222,7 +206,6 @@ type AddMetricReq struct {
func (s *Worker) AddMetric(req AddMetricReq) { func (s *Worker) AddMetric(req AddMetricReq) {
_, ok := s.metrics[req.MetricID] _, ok := s.metrics[req.MetricID]
if ok { if ok {
fmt.Println("add metric duplicate")
req.ResultCh <- MetricDuplicate req.ResultCh <- MetricDuplicate
return return
} }
@@ -246,27 +229,6 @@ func (s *Worker) AddMetric(req AddMetricReq) {
}) })
} }
func (s *Worker) processTryAddMetricReqsImmediatelyAfterDelete(reqs []AddMetricReq) {
if len(reqs) == 0 {
return
}
var (
req = reqs[0]
waitQueue []any
)
if len(reqs) > 1 {
for _, req := range reqs[1:] {
waitQueue = append(waitQueue, req)
}
}
// FIX
// s.metricLockEntries[req.MetricID] = &metricLockEntry{
// XLock: true,
// WaitQueue: waitQueue,
// }
req.ResultCh <- Succeed
}
type GetMetricResult struct { type GetMetricResult struct {
MetricType qb.MetricType MetricType qb.MetricType
FracDigits byte FracDigits byte
@@ -293,116 +255,43 @@ func (s *Worker) GetMetric(req GetMetricReq) {
} }
} }
type DeleteMetricResult struct {
ResultCode byte
RootPageNo uint32
}
type DeleteMetricReq struct { type DeleteMetricReq struct {
MetricID uint32 MetricID uint32
ResultCh chan DeleteMetricResult ResultCh chan byte
} }
func (s *Worker) DeleteMetric(req DeleteMetricReq) { func (s *Worker) DeleteMetric(req DeleteMetricReq) {
// FIX metric, ok := s.metrics[req.MetricID]
// metric, ok := s.metrics[req.MetricID] if !ok {
// if !ok { req.ResultCh <- NoMetric
// req.ResultCh <- tryDeleteMetricResult{ return
// ResultCode: NoMetric, }
// } if metric.xLock {
// return metric.waitQueue = append(metric.waitQueue, req)
// } } else {
// collect all pages, than
// lockEntry, ok := s.metricLockEntries[req.MetricID] s.storageInbox.Push(storage.MetricDelete{
// if ok { MetricID: req.MetricID,
// lockEntry.WaitQueue = append(lockEntry.WaitQueue, req) FreeIndexPages: nil,
// } else { FreeDataPages: nil,
// s.startDeleteMetric(metric, req) ResultCh: req.ResultCh,
// } })
} }
func (s *Worker) startDeleteMetric(metric *Metric, req DeleteMetricReq) {
// FIX
// s.metricLockEntries[req.MetricID] = &metricLockEntry{
// XLock: true,
// }
// req.ResultCh <- tryDeleteMetricResult{
// ResultCode: Succeed,
// RootPageNo: metric.RootPageNo,
// }
}
type DeleteMeasuresResult struct {
ResultCode byte
RootPageNo uint32
} }
type DeleteMeasuresReq struct { type DeleteMeasuresReq struct {
MetricID uint32 MetricID uint32
Since uint32 Since uint32
ResultCh chan DeleteMeasuresResult ResultCh chan byte
} }
func (s *Worker) DeleteMeasures(req DeleteMeasuresReq) { func (s *Worker) DeleteMeasures(req DeleteMeasuresReq) {
// FIX metric, ok := s.metrics[req.MetricID]
// metric, ok := s.metrics[req.MetricID]
// if !ok {
// req.ResultCh <- tryDeleteMeasuresResult{
// ResultCode: NoMetric,
// }
// return
// }
// if metric.Since == 0 || (req.Since > 0 && metric.Until < req.Since) {
// req.ResultCh <- tryDeleteMeasuresResult{
// ResultCode: NoMeasuresToDelete,
// }
// }
// lockEntry, ok := s.metricLockEntries[req.MetricID]
// if ok {
// lockEntry.WaitQueue = append(lockEntry.WaitQueue, req)
// } else {
// s.startDeleteMeasures(metric, req)
// }
}
func (s *Worker) startDeleteMeasures(metric *Metric, req DeleteMeasuresReq) {
// FIX
// s.metricLockEntries[req.MetricID] = &metricLockEntry{
// XLock: true,
// }
// if metric.RootPageNo > 0 {
// req.ResultCh <- tryDeleteMeasuresResult{
// ResultCode: DeleteFromAtreeRequired,
// RootPageNo: metric.RootPageNo,
// }
// } else {
// req.ResultCh <- tryDeleteMeasuresResult{
// ResultCode: DeleteFromAtreeNotNeeded,
// }
// }
}
func (s *Worker) onMeasuresAppendCommited(rec storage.MeasuresAppendCommited) {
metric, ok := s.metrics[rec.MetricID]
if !ok { if !ok {
qb.Abort(qb.NoMetricBug, req.ResultCh <- NoMetric
fmt.Errorf("onMeasuresAppendCommited: metric %d not found", return
rec.MetricID))
} }
metric.OnMeasuresAppendCommited(rec) metric.DeleteMeasures(req)
}
func (s *Worker) onMeasuresAppendWithGrowCommited(rec storage.MeasuresAppendWithGrowCommited) {
metric, ok := s.metrics[rec.MetricID]
if !ok {
qb.Abort(qb.NoMetricBug,
fmt.Errorf("finAppendMeasures: metric %d not found",
rec.MetricID))
}
metric.OnMeasuresAppendWithGrowCommited(rec)
} }
type AppendMeasuresReq struct { type AppendMeasuresReq struct {
@@ -420,10 +309,10 @@ func (s *Worker) AppendMeasures(req AppendMeasuresReq) {
return return
} }
if metric.xLock { if metric.xLock {
metric.WaitQueue = append(metric.WaitQueue, req) metric.waitQueue = append(metric.waitQueue, req)
return } else {
metric.AppendMeasures(req, s.tmp, s.storageInbox)
} }
metric.AppendMeasures(req, s.tmp, s.storageInbox)
} }
type RangeScanResult struct { type RangeScanResult struct {
@@ -456,13 +345,11 @@ func (s *Worker) RangeScan(req RangeScanReq) {
} }
return return
} }
if metric.xLock { if metric.xLock {
metric.WaitQueue = append(metric.WaitQueue, req) metric.waitQueue = append(metric.waitQueue, req)
return } else {
metric.StartRangeScan(req)
} }
metric.StartRangeScan(req)
} }
type FullScanResult struct { type FullScanResult struct {
@@ -492,12 +379,11 @@ func (s *Worker) FullScan(req FullScanReq) {
} }
return return
} }
if metric.xLock { if metric.xLock {
metric.WaitQueue = append(metric.WaitQueue, req) metric.waitQueue = append(metric.waitQueue, req)
return } else {
metric.StartFullScan(req)
} }
metric.StartFullScan(req)
} }
type ListCurrentValuesReq struct { type ListCurrentValuesReq struct {
@@ -566,65 +452,43 @@ func (s *Worker) onMetricAddCommited(rec storage.MetricAddCommited) {
metric, ok := s.metrics[rec.MetricID] metric, ok := s.metrics[rec.MetricID]
if !ok { if !ok {
qb.Abort(qb.MetricAddedBug, qb.Abort(qb.MetricAddedBug,
fmt.Errorf("metric %d not found after commit", rec.MetricID)) fmt.Errorf("onMetricAddCommited: metric %d not found", rec.MetricID))
} }
metric.xLock = false metric.xLock = false
rec.ResultCh <- Succeed // new rec.ResultCh <- Succeed
} }
func (s *Worker) onMetricDeleteCommited(rec storage.MetricDeleteCommited) { func (s *Worker) onMetricDeleteCommited(rec storage.MetricDeleteCommited) {
metric, ok := s.metrics[rec.MetricID] metric, ok := s.metrics[rec.MetricID]
if !ok { if !ok {
qb.Abort(qb.NoMetricBug, qb.Abort(qb.NoMetricBug,
fmt.Errorf("deleteMetric: metric %d not found", fmt.Errorf("onMetricDeleteCommited: metric %d not found", rec.MetricID))
rec.MetricID))
} }
if !metric.xLock { if len(metric.waitQueue) > 0 {
qb.Abort(qb.NoXLockBug, for _, untyped := range metric.waitQueue {
fmt.Errorf("deleteMetric: xlock not set for the metric %d",
rec.MetricID))
}
var addMetricReqs []AddMetricReq
if len(metric.WaitQueue) > 0 {
for _, untyped := range metric.WaitQueue {
switch req := untyped.(type) { switch req := untyped.(type) {
// case tryAppendMeasureReq: case AppendMeasuresReq:
// req.ResultCh <- tryAppendMeasureResult{ req.ResultCh <- storage.MeasuresAppendResult{
// MetricID: req.MetricID, ResultCode: NoMetric,
// ResultCode: NoMetric, WrittenCount: 0,
// } }
case RangeScanReq: case RangeScanReq:
req.ResultCh <- RangeScanResult{ req.ResultCh <- RangeScanResult{
ResultCode: NoMetric, ResultCode: NoMetric,
} }
case FullScanReq: case FullScanReq:
req.ResultCh <- FullScanResult{ req.ResultCh <- FullScanResult{
ResultCode: NoMetric, ResultCode: NoMetric,
} }
case AddMetricReq:
addMetricReqs = append(addMetricReqs, req)
case DeleteMetricReq: case DeleteMetricReq:
req.ResultCh <- DeleteMetricResult{ req.ResultCh <- NoMetric
ResultCode: NoMetric,
}
case DeleteMeasuresReq: case DeleteMeasuresReq:
req.ResultCh <- DeleteMeasuresResult{ req.ResultCh <- NoMetric
ResultCode: NoMetric,
}
case GetMetricReq: case GetMetricReq:
req.ResultCh <- GetMetricResult{ req.ResultCh <- GetMetricResult{
ResultCode: NoMetric, ResultCode: NoMetric,
} }
default: default:
qb.Abort(qb.UnknownMetricWaitQueueItemBug, qb.Abort(qb.UnknownMetricWaitQueueItemBug,
fmt.Errorf("bug: unknown metric wait queue item type %T", req)) fmt.Errorf("bug: unknown metric wait queue item type %T", req))
@@ -632,36 +496,39 @@ func (s *Worker) onMetricDeleteCommited(rec storage.MetricDeleteCommited) {
} }
} }
delete(s.metrics, rec.MetricID) delete(s.metrics, rec.MetricID)
//
rec.ResultCh <- Succeed
}
// ADD in storage func (s *Worker) onMeasuresAppendCommited(rec storage.MeasuresAppendCommited) {
// if len(rec.FreePageNumbers) > 0 { metric, ok := s.metrics[rec.MetricID]
// s.freeList.AddPages(rec.FreePageNumbers) if !ok {
// } qb.Abort(qb.NoMetricBug,
fmt.Errorf("onMeasuresAppendCommited: metric %d not found",
if len(addMetricReqs) > 0 { rec.MetricID))
s.processTryAddMetricReqsImmediatelyAfterDelete(addMetricReqs)
} }
metric.OnMeasuresAppendCommited(rec)
}
func (s *Worker) onMeasuresAppendWithGrowCommited(rec storage.MeasuresAppendWithGrowCommited) {
metric, ok := s.metrics[rec.MetricID]
if !ok {
qb.Abort(qb.NoMetricBug,
fmt.Errorf("onMeasuresAppendWithGrowCommited: metric %d not found",
rec.MetricID))
}
metric.OnMeasuresAppendWithGrowCommited(rec)
} }
func (s *Worker) onMeasuresDeleteCommited(rec storage.MeasuresDeleteCommited) { func (s *Worker) onMeasuresDeleteCommited(rec storage.MeasuresDeleteCommited) {
metric, ok := s.metrics[rec.MetricID] metric, ok := s.metrics[rec.MetricID]
if !ok { if !ok {
qb.Abort(qb.NoMetricBug, qb.Abort(qb.NoMetricBug,
fmt.Errorf("deleteMeasures: metric %d not found", fmt.Errorf("onMeasuresDeleteCommited: metric %d not found", rec.MetricID))
rec.MetricID))
}
if !metric.xLock {
qb.Abort(qb.NoXLockBug,
fmt.Errorf("deleteMeasures: xlock not set for the metric %d",
rec.MetricID))
} }
metric.OnMeasuresDeleteCommited(rec) metric.OnMeasuresDeleteCommited(rec)
metric.xLock = false metric.xLock = false
// FIX add in storage
// if len(rec.FreePageNumbers) > 0 {
// s.freeList.AddPages(rec.FreePageNumbers)
// }
//s.doAfterReleaseXLock(rec.MetricID, metric) //s.doAfterReleaseXLock(rec.MetricID, metric)
} }