This commit is contained in:
2026-05-10 00:59:47 +00:00
parent 271046231c
commit 9b38586ed8
30 changed files with 3560 additions and 1823 deletions

View File

@@ -8,7 +8,6 @@ import (
"path/filepath" "path/filepath"
"sync" "sync"
diploma "gordenko.dev/dima/qb"
"gordenko.dev/dima/qb/bin" "gordenko.dev/dima/qb/bin"
) )
@@ -20,7 +19,7 @@ const (
// common // common
crc32Idx = PageSize - 4 crc32Idx = PageSize - 4
pageType = PageSize - 5 pageTypeIdx = PageSize - 5
// index page // index page
indexRecordsQtyIdx = PageSize - 8 indexRecordsQtyIdx = PageSize - 8
@@ -73,16 +72,16 @@ type _page struct {
} }
type Atree struct { type Atree struct {
freelist FreeList
file *os.File file *os.File
mutex sync.Mutex mutex sync.Mutex
freelist FreeList
allocatedPagesQty uint32 allocatedPagesQty uint32
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{} writeSignalCh chan struct{}
writeTasksQueue []WriteTask //writeTasksQueue []WriteTask
} }
type Options struct { type Options struct {
@@ -131,7 +130,7 @@ func New(opt Options) (*Atree, error) {
} }
tree := &Atree{ tree := &Atree{
//freelist: opt.FreeList, freelist: opt.FreeList,
file: file, file: file,
allocatedPagesQty: allocatedPagesQty, allocatedPagesQty: allocatedPagesQty,
pages: make(map[uint32]*_page), pages: make(map[uint32]*_page),
@@ -144,7 +143,7 @@ func New(opt Options) (*Atree, error) {
} }
func (s *Atree) Run() { func (s *Atree) Run() {
go s.pageWriter() //go s.pageWriter()
go s.pageReader() go s.pageReader()
} }
@@ -161,6 +160,7 @@ func (s *Atree) findDataPage(rootPageNo uint32, timestamp uint32) (uint32, []byt
foundPageNo := findPageNo(buf, timestamp) foundPageNo := findPageNo(buf, timestamp)
s.releasePage(indexPageNo) s.releasePage(indexPageNo)
// fix
if buf[isDataPageNumbersIdx] == 1 { if buf[isDataPageNumbersIdx] == 1 {
buf, err := s.fetchDataPage(foundPageNo) buf, err := s.fetchDataPage(foundPageNo)
if err != nil { if err != nil {
@@ -173,20 +173,20 @@ func (s *Atree) findDataPage(rootPageNo uint32, timestamp uint32) (uint32, []byt
} }
} }
type pathLeg struct { type PathLeg struct {
PageNo uint32 PageNo uint32
Data []byte Data []byte
} }
type pathToDataPage struct { type PathToDataPage struct {
Legs []pathLeg Legs []PathLeg
LastPageNo uint32 LastPageNo uint32
} }
func (s *Atree) findPathToLastPage(rootPageNo uint32) (_ pathToDataPage, err error) { func (s *Atree) FindPathToLastPage(rootPageNo uint32) (_ PathToDataPage, err error) {
var ( var (
pageNo = rootPageNo pageNo = rootPageNo
legs []pathLeg legs []PathLeg
) )
for { for {
@@ -197,7 +197,7 @@ func (s *Atree) findPathToLastPage(rootPageNo uint32) (_ pathToDataPage, err err
return return
} }
legs = append(legs, pathLeg{ legs = append(legs, PathLeg{
PageNo: pageNo, PageNo: pageNo,
Data: buf, Data: buf,
// childIdx не нужен // childIdx не нужен
@@ -205,8 +205,9 @@ func (s *Atree) findPathToLastPage(rootPageNo uint32) (_ pathToDataPage, err err
foundPageNo := getLastPageNo(buf) foundPageNo := getLastPageNo(buf)
// fix
if buf[isDataPageNumbersIdx] == 1 { if buf[isDataPageNumbersIdx] == 1 {
return pathToDataPage{ return PathToDataPage{
Legs: legs, Legs: legs,
LastPageNo: foundPageNo, LastPageNo: foundPageNo,
}, nil }, nil
@@ -218,26 +219,19 @@ func (s *Atree) findPathToLastPage(rootPageNo uint32) (_ pathToDataPage, err err
// APPEND DATA PAGE // APPEND DATA PAGE
//MetricID uint32
//Timestamp uint32
//Value float64
//RootPageNo uint32
//PrevPageNo uint32
type AppendDataPageReq struct { type AppendDataPageReq struct {
MetricID uint32
Timestamp uint32
Value float64
Since uint32 Since uint32
RootPageNo uint32
PrevPageNo uint32
TimestampsChunks [][]byte TimestampsChunks [][]byte
TimestampsSize uint16 TimestampsSize uint16
ValuesChunks [][]byte ValuesChunks [][]byte
ValuesSize uint16 ValuesSize uint16
} }
type Report struct {
//IsDataPageReused bool
//DataPageNo uint32
IsRootChanged bool
NewRootPageNo uint32
//ReusedIndexPages []uint32
Pages []PageToWrite
}
// type ChangedPage struct { // type ChangedPage struct {
// PageNo uint32 // PageNo uint32
@@ -253,186 +247,165 @@ type Report struct {
// Тому що є проблема із відновленням FreeList після збою, якщо з нього будуть паралельно // Тому що є проблема із відновленням FreeList після збою, якщо з нього будуть паралельно
// забирати та добавляти номери сторінок інші потоки. // забирати та добавляти номери сторінок інші потоки.
// Це буде працювати, якщо додавання в txlog і маніпуляції із freeList будуть відбуватись в одному потоці // Це буде працювати, якщо додавання в txlog і маніпуляції із freeList будуть відбуватись в одному потоці
func (s *Atree) AppendDataPage(req AppendDataPageReq) (_ Report, err error) { // NotLinkedDataPage - це data сторінка із payload, але без встановленого prevPageNo та без розрахованого CRC32
type NotLinkedDataPage struct {
Since uint32
Data []byte
}
func (s NotLinkedDataPage) SetPrevPageNo(prevPageNo uint32) {
bin.PutUint32(s.Data[prevPageIdx:], prevPageNo)
bin.PutUint32(s.Data[crc32Idx:], calcChecksum(s.Data[:crc32Idx]))
}
type AppendDataPagesReq struct {
LastPageNo uint32
Legs []PathLeg
DataPages []NotLinkedDataPage
}
type Report struct {
NewRootPageNo uint32
LastPageNo uint32
Pages []PageToWrite
}
// Можливо atree не треба блокувати при читанні. Я можу створити копії index сторінок,
// а потім під 1 мутексом замінити в pages.
func (s *Atree) AppendDataPages(req AppendDataPagesReq) Report {
var ( var (
pagesToRelease []uint32 //pagesToRelease []uint32
report Report newRootPageNo uint32
lastPageNo = req.LastPageNo
pages []PageToWrite
legs = req.Legs
changed = make(map[uint32]PathLeg)
) )
newDataPage := s.allocPage() for _, p := range req.DataPages {
pagesToRelease = append(pagesToRelease, newDataPage.PageNo) newDataPageNo, isReused := s.allocPageNumber() // alloc only number
// pagesToRelease = append(pagesToRelease, newDataPage.PageNo)
chunksToDataPage(newDataPage.Data, chunksToDataPageReq{ // set prevPageNo
PrevPageNo: req.PrevPageNo, p.SetPrevPageNo(lastPageNo)
TimestampsChunks: req.TimestampsChunks,
TimestampsSize: req.TimestampsSize, pages = append(pages, PageToWrite{
ValuesChunks: req.ValuesChunks, PageNo: newDataPageNo,
ValuesSize: req.ValuesSize, Data: p.Data,
IsReused: isReused,
}) })
report.Pages = append(report.Pages, PageToWrite{ // FIX - після додавання index page потрібно модифікувати path,
PageNo: newDataPage.PageNo, // або будувати щось типу дерева знизу вверх
Data: newDataPage.Data,
IsReused: newDataPage.IsReused,
})
// redoWriter, err := NewWriter(WriterOptions{ if len(legs) > 0 {
// MetricID: req.MetricID, newPageNo := newDataPageNo
// Timestamp: req.Timestamp, lastIdx := len(legs) - 1
// Value: req.Value,
// IsDataPageReused: newDataPage.IsReused,
// DataPageNo: newDataPage.PageNo,
// Page: newDataPage.Data,
// })
// if err != nil {
// return
// }
if req.RootPageNo > 0 {
var path pathToDataPage
path, err = s.findPathToLastPage(req.RootPageNo)
if err != nil {
return
}
for _, leg := range path.Legs {
pagesToRelease = append(pagesToRelease, leg.PageNo)
}
if path.LastPageNo != req.PrevPageNo {
diploma.Abort(
diploma.WrongPrevPageNo,
fmt.Errorf("bug: last pageNo %d in tree != prev pageNo %d in _metric",
path.LastPageNo, req.PrevPageNo),
)
}
newPageNo := newDataPage.PageNo
lastIdx := len(path.Legs) - 1
for legIdx := lastIdx; legIdx >= 0; legIdx-- { for legIdx := lastIdx; legIdx >= 0; legIdx-- {
leg := path.Legs[legIdx] leg := req.Legs[legIdx]
ok := appendPair(leg.Data, p.Since, newPageNo)
ok := appendPair(leg.Data, req.Since, newPageNo)
if ok { if ok {
// index // index FIX
report.Pages = append(report.Pages, PageToWrite{ // потрібно запам'ятати змінені сторінки, але їх можуть змінювати
PageNo: leg.PageNo, // кілька ітерацій, тому додавати в Pages не можна.
Data: leg.Data, // на індексній сторінці достатньо місця. Запис вставлено.
}) changed[leg.PageNo] = leg
// err = redoWriter.AppendIndexPage(leg.PageNo, leg.Data, 0)
// if err != nil {
// return
// }
break break
} }
// на індексній сторінці НЕ достатньо місця. Створюю нову.
newIndexPage := s.allocPage() newIndexPage := s.allocPage()
pagesToRelease = append(pagesToRelease, newIndexPage.PageNo) //pagesToRelease = append(pagesToRelease, newIndexPage.PageNo)
appendPair(newIndexPage.Data, req.Since, newPageNo) appendPair(newIndexPage.Data, p.Since, newPageNo)
// ставлю мітку що всі pageNo на сторінці - це data pageNo // ставлю мітку що всі pageNo на сторінці - це data pageNo
if legIdx == lastIdx { // fix - єдина оптимізація від існування isDataPageNumbersIdx - getAllPages не завантажує data pages
newIndexPage.Data[isDataPageNumbersIdx] = 1 // if legIdx == lastIdx {
} // newIndexPage.Data[isDataPageNumbersIdx] = 1
// flags = 0
// if newIndexPage.IsReused {
// flags |= FlagReused
// } // }
report.Pages = append(report.Pages, PageToWrite{ pages = append(pages, PageToWrite{
PageNo: newIndexPage.PageNo, PageNo: newIndexPage.PageNo,
Data: newIndexPage.Data, Data: newIndexPage.Data,
IsReused: newIndexPage.IsReused, IsReused: newIndexPage.IsReused,
}) })
// err = redoWriter.AppendIndexPage(newIndexPage.PageNo, newIndexPage.Data, flags) // замінюю крок в path на новий
// if err != nil { legs[legIdx] = PathLeg{
// return PageNo: newIndexPage.PageNo,
// } Data: newIndexPage.Data,
}
// //
newPageNo = newIndexPage.PageNo newPageNo = newIndexPage.PageNo
if legIdx == 0 { if legIdx == 0 {
newRoot := s.allocPage() newRoot := s.allocPage()
pagesToRelease = append(pagesToRelease, newRoot.PageNo) //pagesToRelease = append(pagesToRelease, newRoot.PageNo)
appendPair(newRoot.Data, getSince(leg.Data), leg.PageNo) // old rootPageNo appendPair(newRoot.Data, getSince(leg.Data), leg.PageNo) // old rootPageNo
appendPair(newRoot.Data, req.Since, newIndexPage.PageNo) appendPair(newRoot.Data, p.Since, newIndexPage.PageNo)
// Фиксирую новый root в REDO логе // Фиксирую новый root в REDO логе
pages = append(pages, PageToWrite{
report.Pages = append(report.Pages, PageToWrite{
PageNo: newRoot.PageNo, PageNo: newRoot.PageNo,
Data: newRoot.Data, Data: newRoot.Data,
IsReused: newRoot.IsReused, IsReused: newRoot.IsReused,
}) })
report.NewRootPageNo = newRoot.PageNo newRootPageNo = newRoot.PageNo
// flags = FlagNewRoot // додаю в початок списку кроків нову root сторінку
// if newRoot.IsReused { legs = append([]PathLeg{
// flags |= FlagReused {
// } PageNo: newRoot.PageNo,
// err = redoWriter.AppendIndexPage(newRoot.PageNo, newRoot.Data, flags) Data: newRoot.Data,
// if err != nil { },
// return }, legs...)
// }
break break
} }
} }
} else { } else {
// індексну root сторінку створюю одразу для першої data сторінки,
// root data сторінки, як в B+Tree не буває.
newRoot := s.allocPage() newRoot := s.allocPage()
pagesToRelease = append(pagesToRelease, newRoot.PageNo) //pagesToRelease = append(pagesToRelease, newRoot.PageNo)
newRoot.Data[isDataPageNumbersIdx] = 1 //newRoot.Data[isDataPageNumbersIdx] = 1
appendPair(newRoot.Data, req.Since, newDataPage.PageNo) appendPair(newRoot.Data, p.Since, newDataPageNo)
report.Pages = append(report.Pages, PageToWrite{ pages = append(pages, PageToWrite{
PageNo: newRoot.PageNo, PageNo: newRoot.PageNo,
Data: newRoot.Data, Data: newRoot.Data,
IsReused: newRoot.IsReused, IsReused: newRoot.IsReused,
}) })
report.NewRootPageNo = newRoot.PageNo newRootPageNo = newRoot.PageNo
// flags = FlagNewRoot // додаю в початок списку кроків нову root сторінку
// if newRoot.IsReused { legs = append([]PathLeg{
// flags |= FlagReused {
// } PageNo: newRoot.PageNo,
// err = redoWriter.AppendIndexPage(newRoot.PageNo, newRoot.Data, flags) Data: newRoot.Data,
// if err != nil { },
// return }, legs...)
// }
} }
// err = redoWriter.Close()
// if err != nil {
// return
// }
// На данний момен схема - наступна. Всі сторінки - data та index - зафіксовані в кеші. // На данний момен схема - наступна. Всі сторінки - data та index - зафіксовані в кеші.
// Отже запис на диск пройде максимально швидко. Після цього ReferenceCount кожної // Отже запис на диск пройде максимально швидко. Після цього ReferenceCount кожної
// сторінки зменшиться на 1. Оскільки на метрику утримується XLock, сторінки мають // сторінки зменшиться на 1. Оскільки на метрику утримується XLock, сторінки мають
// ReferenceCount = 1 (немає інших читачів). // ReferenceCount = 1 (немає інших читачів).
// waitCh := make(chan struct{})
// task := WriteTask{
// WaitCh: waitCh,
// Pages: report.Pages,
// }
// s.appendWriteTaskToQueue(task)
// <-waitCh
// for _, pageNo := range dataPagesToRelease {
// s.releasePage(pageNo)
// }
// for _, pageNo := range indexPagesToRelease { // for _, pageNo := range indexPagesToRelease {
// s.releasePage(pageNo) // s.releasePage(pageNo)
// } // }
return report, nil lastPageNo = newDataPageNo
}
for _, leg := range changed {
// fix recalc checksum
pages = append(pages, PageToWrite{
PageNo: leg.PageNo,
Data: leg.Data,
})
}
return Report{
NewRootPageNo: newRootPageNo,
LastPageNo: lastPageNo,
Pages: pages,
}
} }
// DELETE // DELETE
type PageLists struct {
DataPages []uint32
IndexPages []uint32
}
type Level struct { type Level struct {
PageNo uint32 PageNo uint32
PageData []byte PageData []byte
@@ -440,31 +413,29 @@ type Level struct {
ChildQty int ChildQty int
} }
func (s *Atree) GetAllPages(rootPageNo uint32) (_ PageLists, err error) { func (s *Atree) GetAllPages(rootPageNo uint32) (_ []uint32, err error) {
var ( var (
dataPages []uint32 pageNumbers []uint32
indexPages []uint32
levels []*Level levels []*Level
) )
buf, err := s.fetchIndexPage(rootPageNo) buf, err := s.fetchIndexPage(rootPageNo)
if err != nil { if err != nil {
err = fmt.Errorf("fetchIndexPage(%d): %s", rootPageNo, err) return nil, fmt.Errorf("fetchIndexPage(%d): %s", rootPageNo, err)
return
} }
indexPages = append(indexPages, rootPageNo) pageNumbers = append(pageNumbers, rootPageNo)
if buf[isDataPageNumbersIdx] == 1 { // if buf[isDataPageNumbersIdx] == 1 {
pageNumbers := listPageNumbers(buf) // pageNumbers := listPageNumbers(buf)
dataPages = append(dataPages, pageNumbers...) // dataPages = append(dataPages, pageNumbers...)
s.releasePage(rootPageNo) // s.releasePage(rootPageNo)
return PageLists{ // return PageLists{
DataPages: dataPages, // DataPages: dataPages,
IndexPages: indexPages, // IndexPages: indexPages,
}, nil // }, nil
} // }
levels = append(levels, &Level{ levels = append(levels, &Level{
PageNo: rootPageNo, PageNo: rootPageNo,
@@ -475,10 +446,7 @@ func (s *Atree) GetAllPages(rootPageNo uint32) (_ PageLists, err error) {
for { for {
if len(levels) == 0 { if len(levels) == 0 {
return PageLists{ return pageNumbers, nil
DataPages: dataPages,
IndexPages: indexPages,
}, nil
} }
lastIdx := len(levels) - 1 lastIdx := len(levels) - 1
@@ -489,17 +457,15 @@ func (s *Atree) GetAllPages(rootPageNo uint32) (_ PageLists, err error) {
level.Idx++ level.Idx++
var buf []byte var buf []byte
buf, err = s.fetchIndexPage(pageNo) buf, err = s.fetchPage(pageNo)
if err != nil { if err != nil {
err = fmt.Errorf("fetchIndexPage(%d): %s", pageNo, err) return nil, fmt.Errorf("fetchPage(%d): %s", pageNo, err)
return
} }
indexPages = append(indexPages, pageNo) pageNumbers = append(pageNumbers, pageNo)
if buf[isDataPageNumbersIdx] == 1 {
pageNumbers := listPageNumbers(buf)
dataPages = append(dataPages, pageNumbers...)
if buf[pageTypeIdx] == PageTypeData {
//pageNumbers := listPageNumbers(buf)
//dataPages = append(dataPages, pageNumbers...)
s.releasePage(pageNo) s.releasePage(pageNo)
} else { } else {
levels = append(levels, &Level{ levels = append(levels, &Level{

View File

@@ -34,20 +34,31 @@ func (s *Atree) DeletePages(pageNumbers []uint32) {
} }
func (s *Atree) fetchIndexPage(pageNo uint32) ([]byte, error) { func (s *Atree) fetchIndexPage(pageNo uint32) ([]byte, error) {
return s.fetchPage(pageNo, PageTypeIndex) buf, err := s.fetchPage(pageNo)
if err != nil {
return nil, err
}
if buf[pageTypeIdx] != PageTypeIndex {
return nil, fmt.Errorf("wrong pageType %d instead of %d", buf[pageTypeIdx], PageTypeIndex)
}
return buf, nil
} }
func (s *Atree) fetchDataPage(pageNo uint32) ([]byte, error) { func (s *Atree) fetchDataPage(pageNo uint32) ([]byte, error) {
return s.fetchPage(pageNo, PageTypeData) buf, err := s.fetchPage(pageNo)
if err != nil {
return nil, err
}
if buf[pageTypeIdx] != PageTypeIndex {
return nil, fmt.Errorf("wrong pageType %d instead of %d", buf[pageTypeIdx], PageTypeIndex)
}
return buf, nil
} }
func (s *Atree) fetchPage(pageNo uint32, pageTypeIdx byte) ([]byte, error) { func (s *Atree) fetchPage(pageNo uint32) ([]byte, error) {
s.mutex.Lock() s.mutex.Lock()
p, ok := s.pages[pageNo] p, ok := s.pages[pageNo]
if ok { if ok {
if p.Buf[pageTypeIdx] != pageTypeIdx {
return nil, fmt.Errorf("wrong pageType %d instead of %d", p.Buf[pageTypeIdx], pageTypeIdx)
}
p.ReferenceCount++ p.ReferenceCount++
s.mutex.Unlock() s.mutex.Unlock()
return p.Buf, nil return p.Buf, nil
@@ -93,23 +104,28 @@ func (s *Atree) releasePage(pageNo uint32) {
} }
} }
func (s *Atree) allocPageNumber() (uint32, bool) {
// fix
return 0, true
}
func (s *Atree) allocPage() AllocatedPage { func (s *Atree) allocPage() AllocatedPage {
var ( var (
allocated = AllocatedPage{ allocated = AllocatedPage{
Data: make([]byte, PageSize), Data: make([]byte, PageSize),
} }
) )
allocated.PageNo = s.freelist.ReservePage() //allocated.PageNo = s.freelist.ReservePage()
s.mutex.Lock() s.mutex.Lock()
if allocated.PageNo > 0 { // if allocated.PageNo > 0 {
allocated.IsReused = true // allocated.IsReused = true
} else { // } else {
if s.allocatedPagesQty == math.MaxUint32 { // if s.allocatedPagesQty == math.MaxUint32 {
qb.Abort(qb.MaxAtreeSizeExceeded, errors.New("no space in Atree index")) // qb.Abort(qb.MaxAtreeSizeExceeded, errors.New("no space in Atree index"))
} // }
s.allocatedPagesQty++ // s.allocatedPagesQty++
allocated.PageNo = s.allocatedPagesQty // allocated.PageNo = s.allocatedPagesQty
} // }
// s.pages[allocated.PageNo] = &_page{ // s.pages[allocated.PageNo] = &_page{
// // fix pageType // // fix pageType
@@ -241,61 +257,61 @@ func (s *Atree) readPages() {
// WRITE // WRITE
func (s *Atree) pageWriter() { // func (s *Atree) pageWriter() {
for { // for {
select { // select {
case <-s.writeSignalCh: // case <-s.writeSignalCh:
err := s.writeTasks() // err := s.writeTasks()
if err != nil { // if err != nil {
qb.Abort(qb.WriteToAtreeFailed, err) // qb.Abort(qb.WriteToAtreeFailed, err)
} // }
} // }
} // }
} // }
type WriteTask struct { // type WriteTask struct {
WaitCh chan struct{} // WaitCh chan struct{}
Pages []PageToWrite // Pages []PageToWrite
} // }
func (s *Atree) appendWriteTaskToQueue(task WriteTask) { // func (s *Atree) appendWriteTaskToQueue(task WriteTask) {
s.mutex.Lock() // s.mutex.Lock()
s.writeTasksQueue = append(s.writeTasksQueue, task) // s.writeTasksQueue = append(s.writeTasksQueue, task)
s.mutex.Unlock() // s.mutex.Unlock()
select { // select {
case s.writeSignalCh <- struct{}{}: // case s.writeSignalCh <- struct{}{}:
default: // default:
} // }
} // }
func (s *Atree) writeTasks() error { // func (s *Atree) writeTasks() error {
s.mutex.Lock() // s.mutex.Lock()
tasks := s.writeTasksQueue // tasks := s.writeTasksQueue
s.writeTasksQueue = nil // s.writeTasksQueue = nil
s.mutex.Unlock() // s.mutex.Unlock()
for _, task := range tasks { // for _, task := range tasks {
for _, p := range task.Pages { // for _, p := range task.Pages {
if len(p.Data) != PageSize { // if len(p.Data) != PageSize {
return fmt.Errorf("wrong page %d size: %d", // return fmt.Errorf("wrong page %d size: %d",
p.PageNo, len(p.Data)) // p.PageNo, len(p.Data))
} // }
bin.PutUint32(p.Data[crc32Idx:], calcChecksum(p.Data[:crc32Idx])) // bin.PutUint32(p.Data[crc32Idx:], calcChecksum(p.Data[:crc32Idx]))
off := (p.PageNo - 1) * PageSize // off := (p.PageNo - 1) * PageSize
n, err := s.file.WriteAt(p.Data, int64(off)) // n, err := s.file.WriteAt(p.Data, int64(off))
if err != nil { // if err != nil {
return err // return err
} // }
if n != len(p.Data) { // if n != len(p.Data) {
return fmt.Errorf("write %d instead of %d", n, len(p.Data)) // return fmt.Errorf("write %d instead of %d", n, len(p.Data))
} // }
} // }
close(task.WaitCh) // close(task.WaitCh)
} // }
return nil // return nil
} // }
// IO // IO
@@ -339,9 +355,9 @@ func openFile(fileName string, pageSize int) (_ *os.File, _ uint32, err error) {
return file, uint32(allocatedPagesQty), nil return file, uint32(allocatedPagesQty), nil
} }
func (s *Atree) ApplyREDO(task WriteTask) { // func (s *Atree) ApplyREDO(task WriteTask) {
s.appendWriteTaskToQueue(task) // s.appendWriteTaskToQueue(task)
} // }
func (s *Atree) verifyCRC(data []byte, pageSize int) error { func (s *Atree) verifyCRC(data []byte, pageSize int) error {
var ( var (

View File

@@ -55,16 +55,15 @@ func BinarySearch(qty int, keyComparator bin.KeyComparator) (elemIdx int, isFoun
} }
} }
type chunksToDataPageReq struct { type ChunksToNotLinkedDataPageReq struct {
PrevPageNo uint32 // PrevPageNo uint32
TimestampsChunks [][]byte TimestampsChunks [][]byte
TimestampsSize uint16 TimestampsSize uint16
ValuesChunks [][]byte ValuesChunks [][]byte
ValuesSize uint16 ValuesSize uint16
} }
func chunksToDataPage(buf []byte, req chunksToDataPageReq) { func ChunksToNotLinkedDataPage(buf []byte, req ChunksToNotLinkedDataPageReq) {
bin.PutUint32(buf[prevPageIdx:], req.PrevPageNo)
bin.PutUint16(buf[timestampsSizeIdx:], req.TimestampsSize) bin.PutUint16(buf[timestampsSizeIdx:], req.TimestampsSize)
bin.PutUint16(buf[valuesSizeIdx:], req.ValuesSize) bin.PutUint16(buf[valuesSizeIdx:], req.ValuesSize)
@@ -96,7 +95,6 @@ func chunksToDataPage(buf []byte, req chunksToDataPageReq) {
break break
} }
} }
bin.PutUint32(buf[crc32Idx:], calcChecksum(buf[:crc32Idx]))
} }
func setPrevPageNo(buf []byte, pageNo uint32) { func setPrevPageNo(buf []byte, pageNo uint32) {

View File

@@ -1,3 +1,5 @@
package chunkenc package chunkenc
const eps = 0.000001 const eps = 0.000001
const flagLiteral = 128

298
chunkenc/chunkenc_test.go Normal file
View File

@@ -0,0 +1,298 @@
package chunkenc
import (
"fmt"
"testing"
"gordenko.dev/dima/qb/conbuf"
)
func TestCumdelta(t *testing.T) {
var (
fracDigits byte = 0
buf = conbuf.New(nil)
value float64
done bool
)
c := NewReverseCumulativeDeltaCompressor(buf, 0, fracDigits)
// c.Append(1.55)
// c.Append(23)
// c.Append(23)
// c.Append(23)
// c.Append(23.5)
c.Append(130)
c.Append(191)
c.Append(248)
c.Append(305)
//fmt.Printf("pos: %d\n", c.pos)
//fmt.Printf("%d\n", buf.Chunks()[0])
d := NewReverseCumulativeDeltaDecompressor(buf, c.Size(), fracDigits)
for range 8 {
value, done = d.NextValue()
fmt.Println(value, done)
}
}
func TestInsdelta(t *testing.T) {
var (
fracDigits byte = 2
buf = conbuf.New(nil)
value float64
done bool
)
c := NewReverseInstantDeltaCompressor(buf, 0, fracDigits)
c.Append(-1.55)
c.Append(23)
//fmt.Printf("pos: %d\n", c.pos)
//fmt.Printf("%d\n", buf.Chunks()[0])
c.Append(23)
c.Append(23)
c.Append(-23.5)
//fmt.Printf("pos: %d\n", c.pos)
//fmt.Printf("%d\n", buf.Chunks()[0])
d := NewReverseInstantDeltaDecompressor(buf, c.Size(), fracDigits)
for range 8 {
value, done = d.NextValue()
fmt.Println(value, done)
}
}
func TestTimeDelta(t *testing.T) {
var (
buf = conbuf.New(nil)
value uint32
done bool
)
c := NewReverseTimeDeltaCompressor(buf, 0)
c.Append(10)
//c.Append(131)
//fmt.Printf("pos: %d\n", c.pos)
//fmt.Printf("%d\n", buf.Chunks()[0])
c.Append(70)
c.Append(130)
c.Append(191)
c.Append(248)
c.Append(305)
c.Append(330)
c.Append(390)
c.Append(450)
// fmt.Printf("pos: %d\n", c.pos)
// fmt.Printf("%d\n", buf.Chunks()[0])
//c.Sync()
bound := c.GetState()
fmt.Println("AFTER SYNC")
fmt.Printf("pos: %d\n", c.pos)
chunks := buf.Chunks()
if len(chunks) > 0 {
fmt.Printf("%d\n", chunks[0])
}
d := NewReverseTimeDeltaDecompressor(buf, c.Size())
d.RestoreFromBound(bound)
//d.RestoreFromEnd()
for range 12 {
value, done = d.NextValue()
fmt.Println(value, done)
}
}
func TestCumdeltaBound(t *testing.T) {
var (
fracDigits byte = 0
buf = conbuf.New(nil)
value float64
done bool
)
c := NewReverseCumulativeDeltaCompressor(buf, 0, fracDigits)
// c.Append(1.55)
// c.Append(23)
// c.Append(23)
// c.Append(23)
// c.Append(23.5)
c.Append(10)
c.Append(70)
c.Append(130)
// fmt.Printf("pos: %d\n", c.pos)
// fmt.Printf("%d\n", buf.Chunks()[0])
c.Append(191)
// fmt.Printf("pos: %d\n", c.pos)
// fmt.Printf("%d\n", buf.Chunks()[0])
c.Append(191)
fmt.Printf("pos: %d\n", c.pos)
fmt.Printf("%d\n", buf.Chunks()[0])
//////////////////////////
bound := c.GetState()
fmt.Printf("bound: pos=%d, h=%d\n", bound.Pos, bound.H)
c.Append(248)
c.Append(305)
fmt.Printf("pos: %d\n", c.pos)
fmt.Printf("%d\n", buf.Chunks()[0])
d := NewReverseCumulativeDeltaDecompressor(buf, c.Size(), fracDigits)
d.RestoreFromEnd()
for range 8 {
value, done = d.NextValue()
fmt.Println(value, done)
}
boundDecompressor := NewReverseCumulativeDeltaDecompressor(buf, c.Size(), fracDigits)
boundDecompressor.RestoreFromBound(bound)
fmt.Println("from bound:")
for range 8 {
value, done = boundDecompressor.NextValue()
fmt.Println(value, done)
}
}
func TestInsdeltaBound(t *testing.T) {
var (
fracDigits byte = 0
buf = conbuf.New(nil)
value float64
done bool
)
c := NewReverseInstantDeltaCompressor(buf, 0, fracDigits)
// c.Append(1.55)
// c.Append(23)
// c.Append(23)
// c.Append(23)
// c.Append(23.5)
c.Append(10)
c.Append(70)
c.Append(130)
// fmt.Printf("pos: %d\n", c.pos)
// fmt.Printf("%d\n", buf.Chunks()[0])
c.Append(191)
// fmt.Printf("pos: %d\n", c.pos)
// fmt.Printf("%d\n", buf.Chunks()[0])
c.Append(191)
fmt.Printf("pos: %d\n", c.pos)
fmt.Printf("%d\n", buf.Chunks()[0])
//////////////////////////
bound := c.GetState()
fmt.Printf("bound: pos=%d, h=%d\n", bound.Pos, bound.H)
c.Append(248)
c.Append(305)
fmt.Printf("pos: %d\n", c.pos)
fmt.Printf("%d\n", buf.Chunks()[0])
d := NewReverseInstantDeltaDecompressor(buf, c.Size(), fracDigits)
d.RestoreFromEnd()
for range 8 {
value, done = d.NextValue()
fmt.Println(value, done)
}
boundDecompressor := NewReverseInstantDeltaDecompressor(buf, c.Size(), fracDigits)
boundDecompressor.RestoreFromBound(bound)
fmt.Println("from bound:")
for range 8 {
value, done = boundDecompressor.NextValue()
fmt.Println(value, done)
}
}
func TestInsdeltaBound2(t *testing.T) {
var (
fracDigits byte = 0
buf = conbuf.New(nil)
value float64
done bool
)
c := NewReverseInstantDeltaCompressor(buf, 0, fracDigits)
// c.Append(1.55)
// c.Append(23)
// c.Append(23)
// c.Append(23)
// c.Append(23.5)
c.Append(305)
c.Append(248)
c.Append(191)
// fmt.Printf("pos: %d\n", c.pos)
// fmt.Printf("%d\n", buf.Chunks()[0])
c.Append(191)
// fmt.Printf("pos: %d\n", c.pos)
// fmt.Printf("%d\n", buf.Chunks()[0])
c.Append(130)
fmt.Printf("pos: %d\n", c.pos)
fmt.Printf("%d\n", buf.Chunks()[0])
//////////////////////////
bound := c.GetState()
fmt.Printf("bound: pos=%d, h=%d\n", bound.Pos, bound.H)
c.Append(70)
c.Append(10)
fmt.Printf("pos: %d\n", c.pos)
fmt.Printf("%d\n", buf.Chunks()[0])
d := NewReverseInstantDeltaDecompressor(buf, c.Size(), fracDigits)
d.RestoreFromEnd()
for range 8 {
value, done = d.NextValue()
fmt.Println(value, done)
}
boundDecompressor := NewReverseInstantDeltaDecompressor(buf, c.Size(), fracDigits)
boundDecompressor.RestoreFromBound(bound)
fmt.Println("from bound:")
for range 8 {
value, done = boundDecompressor.NextValue()
fmt.Println(value, done)
}
}

View File

@@ -2,22 +2,49 @@ package chunkenc
import ( import (
"fmt" "fmt"
"log"
"math" "math"
"gordenko.dev/dima/qb/bin"
"gordenko.dev/dima/qb/conbuf" "gordenko.dev/dima/qb/conbuf"
) )
// REVERSE // COMPRESSOR REVERSE
/*
Формат:
base value (var u64)
delta (var u64)
q (qty of previous deltas; msb=0 - series, msb=1 - non series, low 7 bits - qty 1..128)
delta
delta
delta
q
Дельта рахується від base value.
Декодування у зворотньому порядку.
Після base value слідують run або literal блоки.
Run блок - це delta + header byte в кінці.
Literal блок - це від одної до N дельт + header byte в кінці.
Спочатку створюється literal блок.
Якщо для останної дельти додається дублікат, literal блок модифікується -
лічильник зменшується до 1. А остання дельта переміщюється в новий run блок.
Причому лічильник 0 - означає 2 елементи. Приклад:
До:
v1 v2 v3 h-byte(literal, 3) <- v3
Після:
v1 v2 h-byte(literal, 2) v3 h-byte(run, 2)
*/
type ReverseCumulativeDeltaCompressor struct { type ReverseCumulativeDeltaCompressor struct {
buf *conbuf.ContinuousBuffer buf *conbuf.ContinuousBuffer
coef float64 coef float64
pos int pos int
firstValue float64 baseValue float64
lastDelta uint64 lastDelta uint64
length uint16 lastDeltaSize int
numIdx int h byte
state *CumulativeDeltaBound
} }
func NewReverseCumulativeDeltaCompressor(buf *conbuf.ContinuousBuffer, size int, fracDigits byte) *ReverseCumulativeDeltaCompressor { func NewReverseCumulativeDeltaCompressor(buf *conbuf.ContinuousBuffer, size int, fracDigits byte) *ReverseCumulativeDeltaCompressor {
@@ -27,236 +54,166 @@ func NewReverseCumulativeDeltaCompressor(buf *conbuf.ContinuousBuffer, size int,
} }
s := &ReverseCumulativeDeltaCompressor{ s := &ReverseCumulativeDeltaCompressor{
buf: buf, buf: buf,
pos: size, pos: size, // перший вільний байт
coef: coef, coef: coef,
} }
if size > 0 { if size > 0 {
s.restoreState() u64, _, err := s.buf.GetVarUint64(0)
if err != nil {
log.Fatalf("bug: get base value: %s", err)
}
s.baseValue = float64(u64) / s.coef
s.h = s.buf.GetByte(s.pos - 1)
s.lastDelta, s.lastDeltaSize, err = s.buf.ReverseGetVarUint64(s.pos - 2)
if err != nil {
log.Fatalf("bug: get last delta: %s", err)
}
} }
return s return s
} }
func (s *ReverseCumulativeDeltaCompressor) restoreState() {
u64, n, err := s.buf.GetVarUint64(0)
if err != nil {
panic(fmt.Sprintf("bug: get first value: %s", err))
}
s.firstValue = float64(u64) / s.coef
if s.pos > n {
pos := s.pos - 1
idxOf8 := uint(8 - s.buf.GetByte(pos))
pos--
s8 := s.buf.GetByte(pos)
pos--
var n int
s.lastDelta, n, err = s.buf.ReverseGetVarUint64(pos)
if err != nil {
panic(fmt.Sprintf("bug: get last delta: %s", err))
}
pos -= n
s.numIdx = pos + 1
var flag byte = 1 << idxOf8
if (s8 & flag) == flag {
s.length, _ = s.buf.DecodeRunLength(pos)
}
}
}
func (s *ReverseCumulativeDeltaCompressor) Size() int { func (s *ReverseCumulativeDeltaCompressor) Size() int {
return s.pos return s.pos
} }
func (s *ReverseCumulativeDeltaCompressor) CalcRequiredSpace(value float64) int { //func (s *ReverseCumulativeDeltaCompressor) CalcRequiredSpace(value float64) int {
if s.pos == 0 { //}
n := bin.CalcVarUint64Length(uint64(value * s.coef))
return n + 3
}
delta := uint64((value-s.firstValue)*s.coef + eps)
if delta == s.lastDelta {
if s.length == 0 {
return 1
} else {
newLength := s.length + 1
if newLength < 130 {
return 0
} else if newLength == 130 {
return 1
} else {
if newLength < 32769 {
return 0
} else {
n := bin.CalcVarUint64Length(delta)
n += 2
s8q := s.buf.GetByte(s.pos - 1)
if s8q == 8 {
n -= 1
} else {
n -= 2
}
return n
}
}
}
} else {
n := bin.CalcVarUint64Length(delta)
n += 2
s8q := s.buf.GetByte(s.pos - 1)
if s8q == 8 {
n -= 1
} else {
n -= 2
}
return n
}
}
func (s *ReverseCumulativeDeltaCompressor) Append(value float64) { func (s *ReverseCumulativeDeltaCompressor) Append(value float64) {
if s.pos == 0 { if s.pos == 0 {
n := s.buf.PutVarUint64(s.pos, uint64(value*s.coef)) // base value
s.pos += n s.pos += s.buf.PutVarUint64(s.pos, uint64(value*s.coef))
s.firstValue = value s.baseValue = value
s.encodeNewDelta(0, 0, 1) s.appendNewLiteral(0)
} else { } else {
delta := uint64((value-s.firstValue)*s.coef + eps) delta := uint64((value-s.baseValue)*s.coef + eps)
if delta == s.lastDelta { if delta == s.lastDelta {
if s.length == 0 { if s.h < 128 {
s.length = 2 // run блок - отже треба збільшити лічильник
s.shiftOnePosToRight() if s.h < 127 {
s.buf.SetByte(s.numIdx-1, 0) // increase counter
s8q := s.buf.GetByte(s.pos - 1) s.h++
s.buf.SetFlag(s.pos-2, 1<<(8-s8q)) s.buf.SetByte(s.pos-1, s.h)
} else { } else {
s.length++ // не можу збільшити - буде переповнення. Додаю новий literal блок
if s.length < 130 { // counter overflow
s.buf.SetByte(s.numIdx-1, byte(s.length-2)) s.appendNewLiteral(delta)
} else if s.length == 130 {
s.shiftOnePosToRight()
s.encode2bLength()
} else {
if s.length < 32769 {
s.encode2bLength()
} else {
s.appendNewDelta(delta)
}
}
} }
} else { } else {
s.appendNewDelta(delta) // literal блок.
// Якщо в ньому лише одне значення - перетворюю його на run блок.
// Інакше забираю останнє значення із literal блока і додаю новий run блок.
q := s.h & 127
if q == 0 { // 1 кодується як 0
s.convertLiteralToRun()
} else {
// забираю останнє значення із literal блоку щоб зробити run блок
s.convertLastFromLiteralToRun()
}
}
} else {
if s.h < 127 { // fix 128?
// end of run
s.appendNewLiteral(delta)
} else {
if s.h < 255 {
// encode value from pos - 1, then append h byte
s.appendDeltaToLiteral(delta)
} else {
// overflowed - encode new
s.appendNewLiteral(delta)
}
}
} }
} }
} }
func (s *ReverseCumulativeDeltaCompressor) appendNewDelta(delta uint64) { func (s *ReverseCumulativeDeltaCompressor) convertLastFromLiteralToRun() {
s.length = 0 // Зменшую кількість елементів в literal блоці
s.h--
s8 := s.buf.GetByte(s.pos - 2) s.pos -= 1 + s.lastDeltaSize
s8q := s.buf.GetByte(s.pos - 1) s.buf.SetByte(s.pos, s.h) // закриваю literal блок
s.pos++
if s8q == 8 { s.lastDeltaSize = s.buf.ReversePutVarUint64(s.pos, s.lastDelta)
s.pos -= 1 s.pos += s.lastDeltaSize
s8 = 0 s.h = 0 // run блок, довжини 2
s8q = 1 s.buf.SetByte(s.pos, s.h)
} else { s.pos++
s.pos -= 2
s8q++
} }
s.encodeNewDelta(delta, s8, s8q) func (s *ReverseCumulativeDeltaCompressor) convertLiteralToRun() {
// Знімаю flagLiteral, а лічильник 0 дорівнює 2 елементам в серії.
s.h = 0
s.buf.SetByte(s.pos-1, s.h)
} }
func (s *ReverseCumulativeDeltaCompressor) encodeNewDelta(delta uint64, s8 byte, s8q byte) { func (s *ReverseCumulativeDeltaCompressor) appendDeltaToLiteral(delta uint64) {
s.h++ // збільшую к-сть дельт
s.lastDelta = delta s.lastDelta = delta
s.numIdx = s.pos s.pos--
n := s.buf.ReversePutVarUint64(s.pos, s.lastDelta) s.lastDeltaSize = s.buf.ReversePutVarUint64(s.pos, delta)
s.pos += n s.pos += s.lastDeltaSize
s.buf.SetByte(s.pos, s8) s.buf.SetByte(s.pos, s.h)
s.pos++
s.buf.SetByte(s.pos, s8q)
s.pos++ s.pos++
} }
func (s *ReverseCumulativeDeltaCompressor) shiftOnePosToRight() { func (s *ReverseCumulativeDeltaCompressor) appendNewLiteral(delta uint64) {
s.buf.ShiftOnePosToRight(s.numIdx, s.pos) s.h = flagLiteral
s.lastDelta = delta
s.lastDeltaSize = s.buf.ReversePutVarUint64(s.pos, delta)
s.pos += s.lastDeltaSize
s.buf.SetByte(s.pos, flagLiteral) // literal, length = 1
s.pos++ s.pos++
s.numIdx++
}
func (s *ReverseCumulativeDeltaCompressor) encode2bLength() {
num := s.length - 2
s.buf.SetByte(s.numIdx-1, byte(num&127)|128)
s.buf.SetByte(s.numIdx-2, byte(num>>7))
} }
func (s *ReverseCumulativeDeltaCompressor) DeleteLast() { func (s *ReverseCumulativeDeltaCompressor) DeleteLast() {
var (
s8q = s.buf.GetByte(s.pos - 1)
s8 = s.buf.GetByte(s.pos - 2)
flag byte = 1 << uint(8-s8q)
)
if s.length > 0 {
if s.length == 2 {
s.length = 0
s.buf.UnsetFlag(s.pos-2, flag)
s.buf.ShiftOnePosToLeft(s.numIdx, s.pos)
s.numIdx--
s.pos--
} else if s.length < 130 {
s.length--
s.buf.SetByte(s.numIdx-1, byte(s.length)-2)
} else if s.length == 130 {
s.length--
s.buf.ShiftOnePosToLeft(s.numIdx, s.pos)
s.numIdx--
s.pos--
s.buf.SetByte(s.numIdx-1, byte(s.length)-2)
} else {
s.length--
s.encode2bLength()
} }
} else {
if s8q > 1 { type CumulativeDeltaBound struct {
s8q-- Pos int
flag = 1 << uint(8-s8q) H byte
s.pos = s.numIdx + 2 LastDelta uint64
s.buf.SetByte(s.pos-2, s8) Chunks [][]byte
s.buf.SetByte(s.pos-1, s8q)
} else {
s.pos = s.numIdx + 1
s.buf.SetByte(s.pos-1, 8)
s8 = s.buf.GetByte(s.pos - 2)
flag = 1
} }
var (
pos = s.pos - 3 // delta h
n int func (s *ReverseCumulativeDeltaCompressor) Lock() {
err error s.state = &CumulativeDeltaBound{
) Pos: s.pos - 1 - s.lastDeltaSize,
s.lastDelta, n, err = s.buf.ReverseGetVarUint64(pos) H: s.h,
if err != nil { LastDelta: s.lastDelta,
panic(err) Chunks: s.buf.Chunks(),
}
s.numIdx = pos - n
if (s8 & flag) == flag {
s.length, _ = s.buf.DecodeRunLength(s.numIdx - 1)
} }
} }
func (s *ReverseCumulativeDeltaCompressor) CreateDecompressor(fracDigits byte) *ReverseCumulativeDeltaDecompressor {
if s.state == nil {
d := NewReverseCumulativeDeltaDecompressor(s.buf, s.Size(), fracDigits)
d.RestoreFromEnd()
return d
} }
d := NewReverseCumulativeDeltaDecompressor(s.buf, s.Size(), fracDigits)
d.RestoreFromBound(*s.state)
return d
}
func (s *ReverseCumulativeDeltaCompressor) Unlock() {
s.state = nil
}
// DECOMPRESSOR
type ReverseCumulativeDeltaDecompressor struct { type ReverseCumulativeDeltaDecompressor struct {
buf *conbuf.ContinuousBuffer buf *conbuf.ContinuousBuffer
coef float64
pos int pos int
bound int bound int
firstValue float64 baseValue float64
lastValue float64 lastValue float64
length uint16 isRun bool
coef float64 pending int
idxOf8 uint done bool
s8 byte
step byte
} }
func NewReverseCumulativeDeltaDecompressor(buf *conbuf.ContinuousBuffer, size int, fracDigits byte) *ReverseCumulativeDeltaDecompressor { func NewReverseCumulativeDeltaDecompressor(buf *conbuf.ContinuousBuffer, size int, fracDigits byte) *ReverseCumulativeDeltaDecompressor {
@@ -264,67 +221,91 @@ func NewReverseCumulativeDeltaDecompressor(buf *conbuf.ContinuousBuffer, size in
if fracDigits > 0 { if fracDigits > 0 {
coef = math.Pow(10, float64(fracDigits)) coef = math.Pow(10, float64(fracDigits))
} }
u64, n, err := buf.GetVarUint64(0)
if err != nil {
log.Fatalf("bug: get base value: %s", err)
}
//fmt.Printf("baseValue: %.2f, bound: %d, pos: %d\n", float64(u64)/coef, n, size)
return &ReverseCumulativeDeltaDecompressor{ return &ReverseCumulativeDeltaDecompressor{
buf: buf, buf: buf,
coef: coef, coef: coef,
pos: size, pos: size - 1, // last elem
baseValue: float64(u64) / coef,
bound: n,
} }
} }
func (s *ReverseCumulativeDeltaDecompressor) RestoreFromEnd() {
if s.pos > s.bound {
// читаю заголовок наступної серії
s.readHeader()
s.readValue()
} else {
s.done = true
}
}
func (s *ReverseCumulativeDeltaDecompressor) RestoreFromBound(bound CumulativeDeltaBound) {
s.pos = bound.Pos - 1
s.lastValue = s.baseValue + float64(bound.LastDelta)
s.decodeHeaderByte(bound.H)
fmt.Printf("restore from bound: isRun=%t, pending=%d\n", s.isRun, s.pending)
}
func (s *ReverseCumulativeDeltaDecompressor) NextValue() (value float64, done bool) { func (s *ReverseCumulativeDeltaDecompressor) NextValue() (value float64, done bool) {
if s.step > 0 { //fmt.Printf("NextValue(): bound: %d, pos: %d, pending: %d\n", s.bound, s.pos, s.pending)
if s.length > 0 { if s.done {
s.length--
return s.lastValue, false
}
if s.pos < s.bound {
return 0, true return 0, true
} }
if s.idxOf8 == 0 { // повертаю значення, що було прочитано в методі RestoreFromBound/RestoreFromEnd
s.s8 = s.buf.GetByte(s.pos) value = s.lastValue
s.pos-- s.pending--
if s.pending > 0 {
// якщо в серії залишаються елементи
if !s.isRun {
s.readValue()
} }
s.readVar() } else if s.pos > s.bound {
if s.length > 0 { // в серії більше немає елементів, отже перевіряє чи є ще дані в буфері.
s.length-- // дані є - читаю заголовок наступної серії
s.readHeader()
s.readValue()
} else {
s.done = true
} }
return s.lastValue, false // серія завершена - перевіряю чи є ще серії
} return value, false
u64, n, err := s.buf.GetVarUint64(0)
if err != nil {
panic(err)
}
s.firstValue = float64(u64) / s.coef
s.bound = n
s.pos--
s.idxOf8 = uint(8 - s.buf.GetByte(s.pos))
s.pos--
s.s8 = s.buf.GetByte(s.pos)
s.pos--
s.readVar()
if s.length > 0 {
s.length--
}
s.step = 1
return s.lastValue, false
} }
func (s *ReverseCumulativeDeltaDecompressor) readVar() { func (s *ReverseCumulativeDeltaDecompressor) readHeader() {
h := s.buf.GetByte(s.pos)
s.pos--
s.decodeHeaderByte(h)
// fmt.Println("h:", h)
// fmt.Println("isRun:", s.isRun)
// fmt.Println("pending:", s.pending)
}
func (s *ReverseCumulativeDeltaDecompressor) readValue() {
u64, n, err := s.buf.ReverseGetVarUint64(s.pos) u64, n, err := s.buf.ReverseGetVarUint64(s.pos)
if err != nil { if err != nil {
panic(err) log.Fatalln(err)
} }
// fmt.Println()
// fmt.Println("read from pos:", s.pos)
// fmt.Println("read delta:", u64)
// fmt.Println("read delta n:", n)
// fmt.Println()
s.pos -= n s.pos -= n
s.lastValue = s.firstValue + float64(u64)/s.coef s.lastValue = s.baseValue + float64(u64)/s.coef
}
var flag byte = 1 << s.idxOf8 func (s *ReverseCumulativeDeltaDecompressor) decodeHeaderByte(h byte) {
if (s.s8 & flag) == flag { s.isRun = h < 128
s.length, n = s.buf.DecodeRunLength(s.pos) if s.isRun {
s.pos -= n s.pending = int(h&127) + 2
}
if s.idxOf8 == 7 {
s.idxOf8 = 0
} else { } else {
s.idxOf8++ s.pending = int(h&127) + 1
} }
} }
//

View File

@@ -2,22 +2,20 @@ package chunkenc
import ( import (
"fmt" "fmt"
"log"
"math" "math"
"gordenko.dev/dima/qb/bin"
"gordenko.dev/dima/qb/conbuf" "gordenko.dev/dima/qb/conbuf"
) )
// REVERSE
type ReverseInstantDeltaCompressor struct { type ReverseInstantDeltaCompressor struct {
buf *conbuf.ContinuousBuffer buf *conbuf.ContinuousBuffer
coef float64 coef float64
pos int pos int
firstValue float64 baseValue float64
lastDelta int64 lastDelta int64
length uint16 lastDeltaSize int
numIdx int h byte
} }
func NewReverseInstantDeltaCompressor(buf *conbuf.ContinuousBuffer, size int, fracDigits byte) *ReverseInstantDeltaCompressor { func NewReverseInstantDeltaCompressor(buf *conbuf.ContinuousBuffer, size int, fracDigits byte) *ReverseInstantDeltaCompressor {
@@ -31,246 +29,146 @@ func NewReverseInstantDeltaCompressor(buf *conbuf.ContinuousBuffer, size int, fr
coef: coef, coef: coef,
} }
if size > 0 { if size > 0 {
s.restoreState() i64, _, err := s.buf.GetVarInt64(0)
if err != nil {
log.Fatalf("bug: get base value: %s", err)
}
s.baseValue = float64(i64) / s.coef
s.h = s.buf.GetByte(s.pos - 1)
s.lastDelta, s.lastDeltaSize, err = s.buf.ReverseGetVarInt64(s.pos - 2)
if err != nil {
log.Fatalf("bug: get last delta: %s", err)
}
} }
return s return s
} }
func (s *ReverseInstantDeltaCompressor) restoreState() {
i64, n, err := s.buf.GetVarInt64(0)
if err != nil {
panic(fmt.Sprintf("bug: get first value: %s", err))
}
s.firstValue = float64(i64) / s.coef
if s.pos > n {
pos := s.pos - 1
idxOf8 := uint(8 - s.buf.GetByte(pos))
pos--
s8 := s.buf.GetByte(pos)
pos--
var n int
s.lastDelta, n, err = s.buf.ReverseGetVarInt64(pos)
if err != nil {
panic(fmt.Sprintf("bug: get last delta: %s", err))
}
pos -= n
s.numIdx = pos + 1
var flag byte = 1 << idxOf8
if (s8 & flag) == flag {
s.length, _ = s.buf.DecodeRunLength(pos)
}
}
}
func (s *ReverseInstantDeltaCompressor) Size() int { func (s *ReverseInstantDeltaCompressor) Size() int {
return s.pos return s.pos
} }
func (s *ReverseInstantDeltaCompressor) CalcRequiredSpace(value float64) int {
if s.pos == 0 {
n := bin.CalcVarInt64Length(int64(value * s.coef))
return n + 3
}
tmp := (value - s.firstValue) * s.coef
if tmp > 0 {
tmp += eps
} else {
tmp -= eps
}
delta := int64(tmp)
if delta == s.lastDelta {
if s.length == 0 {
return 1
} else {
newLength := s.length + 1
if newLength < 130 {
return 0
} else if newLength == 130 {
return 1
} else {
if newLength < 32769 {
return 0
} else {
n := bin.CalcVarInt64Length(delta)
n += 2
s8q := s.buf.GetByte(s.pos - 1)
if s8q == 8 {
n -= 1
} else {
n -= 2
}
return n
}
}
}
} else {
n := bin.CalcVarInt64Length(delta)
n += 2
s8q := s.buf.GetByte(s.pos - 1)
if s8q == 8 {
n -= 1
} else {
n -= 2
}
return n
}
}
// В начале буфера кодирую базовое значение.
func (s *ReverseInstantDeltaCompressor) Append(value float64) { func (s *ReverseInstantDeltaCompressor) Append(value float64) {
if s.pos == 0 { if s.pos == 0 {
// base value
n := s.buf.PutVarInt64(s.pos, int64(value*s.coef)) n := s.buf.PutVarInt64(s.pos, int64(value*s.coef))
s.pos += n s.pos += n
s.firstValue = value s.baseValue = value
s.encodeNewDelta(0, 0, 1) s.appendNewLiteral(0)
} else { } else {
tmp := (value - s.firstValue) * s.coef tmp := (value - s.baseValue) * s.coef
if tmp > 0 { if tmp > 0 {
tmp += eps tmp += eps
} else { } else {
tmp -= eps tmp -= eps
} }
delta := int64(tmp) delta := int64(tmp)
if delta == s.lastDelta { if delta == s.lastDelta {
if s.length == 0 { if s.h < 128 {
s.length = 2 // run блок - отже треба збільшити лічильник
s.shiftOnePosToRight() if s.h < 127 {
s.buf.SetByte(s.numIdx-1, 0) // increase counter
s8q := s.buf.GetByte(s.pos - 1) s.h++
s.buf.SetFlag(s.pos-2, 1<<(8-s8q)) s.buf.SetByte(s.pos-1, s.h)
} else { } else {
s.length++ // не можу збільшити - буде переповнення. Додаю новий literal блок
if s.length < 130 { // counter overflow
s.buf.SetByte(s.numIdx-1, byte(s.length-2)) // fix encode delta
} else if s.length == 130 { s.appendNewLiteral(delta)
s.shiftOnePosToRight()
s.encode2bLength()
} else {
if s.length < 32769 {
s.encode2bLength()
} else {
s.appendNewDelta(delta)
}
}
} }
} else { } else {
s.appendNewDelta(delta) // literal блок.
// Якщо в ньому лише одне значення - перетворюю його на run блок.
// Інакше забираю останнє значення із literal блока і додаю новий run блок.
q := s.h & 127
if q == 0 { // 1 кодується як 0
s.convertLiteralToRun()
} else {
s.convertLastFromLiteralToRun()
}
}
} else {
if s.h < 127 {
// end of run
s.appendNewLiteral(delta)
} else {
if s.h < 255 {
// encode value from pos - 1, then append h byte
s.appendDeltaToLiteral(delta)
} else {
// overflowed - encode new
s.appendNewLiteral(delta)
}
}
} }
} }
} }
func (s *ReverseInstantDeltaCompressor) appendNewDelta(delta int64) { func (s *ReverseInstantDeltaCompressor) convertLastFromLiteralToRun() {
s.length = 0 // Зменшую кількість елементів в literal блоці
s.h--
s8 := s.buf.GetByte(s.pos - 2) s.pos -= 1 + s.lastDeltaSize
s8q := s.buf.GetByte(s.pos - 1) s.buf.SetByte(s.pos, s.h) // закриваю literal блок
s.pos++
if s8q == 8 { s.lastDeltaSize = s.buf.ReversePutVarInt64(s.pos, s.lastDelta)
s.pos -= 1 s.pos += s.lastDeltaSize
s8 = 0 s.h = 0 // run блок, довжини 2
s8q = 1 s.buf.SetByte(s.pos, s.h)
} else { s.pos++
s.pos -= 2
s8q++
}
s.encodeNewDelta(delta, s8, s8q)
} }
func (s *ReverseInstantDeltaCompressor) encodeNewDelta(delta int64, s8 byte, s8q byte) { func (s *ReverseInstantDeltaCompressor) convertLiteralToRun() {
// Знімаю flagLiteral, а лічильник 0 дорівнює 2 елементам в серії.
s.h = 0
s.buf.SetByte(s.pos-1, s.h)
}
func (s *ReverseInstantDeltaCompressor) appendDeltaToLiteral(delta int64) {
s.h++ // збільшую к-сть дельт
s.lastDelta = delta s.lastDelta = delta
s.numIdx = s.pos
n := s.buf.ReversePutVarInt64(s.pos, s.lastDelta)
s.pos += n
s.buf.SetByte(s.pos, s8)
s.pos++
s.buf.SetByte(s.pos, s8q)
s.pos++
}
func (s *ReverseInstantDeltaCompressor) shiftOnePosToRight() {
s.buf.ShiftOnePosToRight(s.numIdx, s.pos)
s.pos++
s.numIdx++
}
func (s *ReverseInstantDeltaCompressor) encode2bLength() {
num := s.length - 2
s.buf.SetByte(s.numIdx-1, byte(num&127)|128)
s.buf.SetByte(s.numIdx-2, byte(num>>7))
}
func (s *ReverseInstantDeltaCompressor) DeleteLast() {
var (
s8q = s.buf.GetByte(s.pos - 1)
s8 = s.buf.GetByte(s.pos - 2)
flag byte = 1 << uint(8-s8q)
)
if s.length > 0 {
if s.length == 2 {
s.length = 0
s.buf.UnsetFlag(s.pos-2, flag)
s.buf.ShiftOnePosToLeft(s.numIdx, s.pos)
s.numIdx--
s.pos-- s.pos--
} else if s.length < 130 { s.lastDeltaSize = s.buf.ReversePutVarInt64(s.pos, delta)
s.length-- s.pos += s.lastDeltaSize
s.buf.SetByte(s.numIdx-1, byte(s.length)-2) s.buf.SetByte(s.pos, s.h)
} else if s.length == 130 { s.pos++
s.length--
s.buf.ShiftOnePosToLeft(s.numIdx, s.pos)
s.numIdx--
s.pos--
s.buf.SetByte(s.numIdx-1, byte(s.length)-2)
} else {
s.length--
s.encode2bLength()
} }
} else {
if s8q > 1 { func (s *ReverseInstantDeltaCompressor) appendNewLiteral(delta int64) {
s8q-- s.h = flagLiteral
flag = 1 << uint(8-s8q) s.lastDelta = delta
s.pos = s.numIdx + 2 s.lastDeltaSize = s.buf.ReversePutVarInt64(s.pos, delta)
s.buf.SetByte(s.pos-2, s8) s.pos += s.lastDeltaSize
s.buf.SetByte(s.pos-1, s8q) s.buf.SetByte(s.pos, flagLiteral) // literal, length = 1
} else { s.pos++
s.pos = s.numIdx + 1
s.buf.SetByte(s.pos-1, 8)
s8 = s.buf.GetByte(s.pos - 2)
flag = 1
} }
var (
pos = s.pos - 3 func (s *ReverseInstantDeltaCompressor) DeleteLast() {}
n int
err error type InstantDeltaBound struct {
) Pos int
s.lastDelta, n, err = s.buf.ReverseGetVarInt64(pos) H byte
if err != nil { LastDelta int64
panic(err)
}
s.numIdx = pos - n
if (s8 & flag) == flag {
s.length, _ = s.buf.DecodeRunLength(s.numIdx - 1)
} }
func (s *ReverseInstantDeltaCompressor) GetState() InstantDeltaBound {
return InstantDeltaBound{
Pos: s.pos - 1 - s.lastDeltaSize,
H: s.h,
LastDelta: s.lastDelta,
} }
} }
// DECOMPRESSOR
type ReverseInstantDeltaDecompressor struct { type ReverseInstantDeltaDecompressor struct {
step byte
buf *conbuf.ContinuousBuffer buf *conbuf.ContinuousBuffer
coef float64
pos int pos int
bound int bound int
firstValue float64 baseValue float64
lastValue float64 lastValue float64
length uint16 isRun bool
coef float64 pending int
idxOf8 uint done bool
s8 byte
} }
func NewReverseInstantDeltaDecompressor(buf *conbuf.ContinuousBuffer, size int, fracDigits byte) *ReverseInstantDeltaDecompressor { func NewReverseInstantDeltaDecompressor(buf *conbuf.ContinuousBuffer, size int, fracDigits byte) *ReverseInstantDeltaDecompressor {
@@ -278,68 +176,169 @@ func NewReverseInstantDeltaDecompressor(buf *conbuf.ContinuousBuffer, size int,
if fracDigits > 0 { if fracDigits > 0 {
coef = math.Pow(10, float64(fracDigits)) coef = math.Pow(10, float64(fracDigits))
} }
i64, n, err := buf.GetVarInt64(0)
if err != nil {
log.Fatalf("bug: get base value: %s", err)
}
//fmt.Printf("baseValue: %.2f, bound: %d, pos: %d\n", float64(u64)/coef, n, size)
return &ReverseInstantDeltaDecompressor{ return &ReverseInstantDeltaDecompressor{
buf: buf, buf: buf,
coef: coef, coef: coef,
pos: size, pos: size - 1, // last elem
baseValue: float64(i64) / coef,
bound: n,
} }
} }
func (s *ReverseInstantDeltaDecompressor) RestoreFromEnd() {
if s.pos > s.bound {
// читаю заголовок наступної серії
s.readHeader()
s.readValue()
} else {
s.done = true
}
}
func (s *ReverseInstantDeltaDecompressor) RestoreFromBound(bound InstantDeltaBound) {
s.pos = bound.Pos - 1
s.lastValue = s.baseValue + float64(bound.LastDelta)
s.decodeHeaderByte(bound.H)
fmt.Printf("restore from bound: isRun=%t, pending=%d\n", s.isRun, s.pending)
}
func (s *ReverseInstantDeltaDecompressor) NextValue() (value float64, done bool) { func (s *ReverseInstantDeltaDecompressor) NextValue() (value float64, done bool) {
if s.step > 0 { //fmt.Printf("NextValue(): bound: %d, pos: %d, pending: %d\n", s.bound, s.pos, s.pending)
if s.length > 0 { if s.done {
s.length--
return s.lastValue, false
}
if s.pos < s.bound {
return 0, true return 0, true
} }
// повертаю значення, що було прочитано в методі RestoreFromBound/RestoreFromEnd
if s.idxOf8 == 0 { value = s.lastValue
s.s8 = s.buf.GetByte(s.pos) s.pending--
s.pos-- if s.pending > 0 {
// якщо в серії залишаються елементи
if !s.isRun {
s.readValue()
} }
s.readVar() } else if s.pos > s.bound {
if s.length > 0 { // в серії більше немає елементів, отже перевіряє чи є ще дані в буфері.
s.length-- // дані є - читаю заголовок наступної серії
s.readHeader()
s.readValue()
} else {
s.done = true
} }
return s.lastValue, false // серія завершена - перевіряю чи є ще серії
} return value, false
i64, n, err := s.buf.GetVarInt64(0)
if err != nil {
panic(err)
}
s.firstValue = float64(i64) / s.coef
s.bound = n
s.pos--
s.idxOf8 = uint(8 - s.buf.GetByte(s.pos))
s.pos--
s.s8 = s.buf.GetByte(s.pos)
s.pos--
s.readVar()
if s.length > 0 {
s.length--
}
s.step = 1
return s.lastValue, false
} }
func (s *ReverseInstantDeltaDecompressor) readVar() { func (s *ReverseInstantDeltaDecompressor) readHeader() {
h := s.buf.GetByte(s.pos)
s.pos--
s.decodeHeaderByte(h)
// fmt.Println("h:", h)
// fmt.Println("isRun:", s.isRun)
// fmt.Println("pending:", s.pending)
}
func (s *ReverseInstantDeltaDecompressor) readValue() {
i64, n, err := s.buf.ReverseGetVarInt64(s.pos) i64, n, err := s.buf.ReverseGetVarInt64(s.pos)
if err != nil { if err != nil {
panic(err) log.Fatalln(err)
} }
// fmt.Println()
// fmt.Println("read from pos:", s.pos)
// fmt.Println("read delta:", u64)
// fmt.Println("read delta n:", n)
// fmt.Println()
s.pos -= n s.pos -= n
s.lastValue = s.firstValue + float64(i64)/s.coef s.lastValue = s.baseValue + float64(i64)/s.coef
}
var flag byte = 1 << s.idxOf8 func (s *ReverseInstantDeltaDecompressor) decodeHeaderByte(h byte) {
if (s.s8 & flag) == flag { s.isRun = h < 128
s.length, n = s.buf.DecodeRunLength(s.pos) if s.isRun {
s.pos -= n s.pending = int(h&127) + 2
}
if s.idxOf8 == 7 {
s.idxOf8 = 0
} else { } else {
s.idxOf8++ s.pending = int(h&127) + 1
} }
} }
// DECOMPRESSOR
// type ReverseInstantDeltaDecompressor struct {
// buf *conbuf.ContinuousBuffer
// pos int
// bound int
// baseValue float64
// lastValue float64
// coef float64
// isRun bool
// pending int
// }
// func NewReverseInstantDeltaDecompressor(buf *conbuf.ContinuousBuffer, size int, fracDigits byte) *ReverseInstantDeltaDecompressor {
// var coef float64 = 1
// if fracDigits > 0 {
// coef = math.Pow(10, float64(fracDigits))
// }
// u64, n, err := buf.GetVarInt64(0)
// if err != nil {
// log.Fatalf("bug: get base value: %s", err)
// }
// //fmt.Printf("baseValue: %.2f, bound: %d, pos: %d\n", float64(u64)/coef, n, size)
// return &ReverseInstantDeltaDecompressor{
// buf: buf,
// coef: coef,
// pos: size - 1, // last elem
// baseValue: float64(u64) / coef,
// bound: n,
// }
// }
// func (s *ReverseInstantDeltaDecompressor) NextValue() (value float64, done bool) {
// //fmt.Printf("NextValue(): bound: %d, pos: %d, pending: %d\n", s.bound, s.pos, s.pending)
// if s.pending > 0 {
// // якщо в серії залишаються елементи
// if !s.isRun {
// s.readValue()
// }
// s.pending--
// return s.lastValue, false
// }
// if s.pos > s.bound {
// // читаю заголовок наступної серії
// s.readHeader()
// s.readValue()
// s.pending--
// return s.lastValue, false
// }
// // серія завершена - перевіряю чи є ще серії
// return 0, true
// }
// func (s *ReverseInstantDeltaDecompressor) readHeader() {
// h := s.buf.GetByte(s.pos)
// s.pos--
// s.isRun = h < 128
// if s.isRun {
// s.pending = int(h&127) + 2
// } else {
// s.pending = int(h&127) + 1
// }
// // fmt.Println("h:", h)
// // fmt.Println("isRun:", s.isRun)
// // fmt.Println("pending:", s.pending)
// }
// func (s *ReverseInstantDeltaDecompressor) readValue() {
// i64, n, err := s.buf.ReverseGetVarInt64(s.pos)
// if err != nil {
// log.Fatalln(err)
// }
// // fmt.Println()
// // fmt.Println("read from pos:", s.pos)
// // fmt.Println("read delta:", u64)
// // fmt.Println("read delta n:", n)
// // fmt.Println()
// s.pos -= n
// s.lastValue = s.baseValue + float64(i64)/s.coef
// }

288
chunkenc/time_delta.go Normal file
View File

@@ -0,0 +1,288 @@
package chunkenc
import (
"fmt"
"log"
"gordenko.dev/dima/pretty"
"gordenko.dev/dima/qb/conbuf"
)
type ReverseTimeDeltaCompressor struct {
buf *conbuf.ContinuousBuffer
pos int
lastUnixtime uint32
lastDelta uint32
lastDeltaSize int
h byte
}
func NewReverseTimeDeltaCompressor(buf *conbuf.ContinuousBuffer, size int) *ReverseTimeDeltaCompressor {
s := &ReverseTimeDeltaCompressor{
buf: buf,
pos: size, // перший вільний байт
}
if size > 0 {
u64, n, err := s.buf.ReverseGetVarUint64(s.pos - 1)
if err != nil {
log.Fatalf("bug: get last unixtime: %s", err)
}
s.lastUnixtime = uint32(u64)
s.pos -= n
if s.pos > 0 {
s.h = s.buf.GetByte(s.pos - 1)
u64, s.lastDeltaSize, err = s.buf.ReverseGetVarUint64(s.pos - 2)
if err != nil {
log.Fatalf("bug: get last delta: %s", err)
}
s.lastDelta = uint32(u64)
}
}
return s
}
func (s *ReverseTimeDeltaCompressor) Size() int {
return s.pos
}
//func (s *ReverseTimeDeltaCompressor) CalcRequiredSpace(value float64) int {
//}
func (s *ReverseTimeDeltaCompressor) Append(unixtime uint32) {
if s.lastUnixtime > 0 {
// base value
//s.pos += s.buf.PutVarUint64(s.pos, uint64(value*s.coef))
//s.baseValue = value
//s.appendNewLiteral(0)
//} else {
delta := unixtime - s.lastUnixtime
if delta == s.lastDelta {
if s.h < 128 {
// run блок - отже треба збільшити лічильник
if s.h < 127 {
// increase counter
s.h++
s.buf.SetByte(s.pos-1, s.h)
} else {
// не можу збільшити - буде переповнення. Додаю новий literal блок
// counter overflow
s.appendNewLiteral(delta)
}
} else {
// literal блок.
// Якщо в ньому лише одне значення - перетворюю його на run блок.
// Інакше забираю останнє значення із literal блока і додаю новий run блок.
q := s.h & 127
if q == 0 { // 1 кодується як 0
s.convertLiteralToRun()
} else {
// забираю останнє значення із literal блоку щоб зробити run блок
s.convertLastFromLiteralToRun()
}
}
} else {
if s.h < 127 { // fix 128?
// end of run
s.appendNewLiteral(delta)
} else {
if s.h < 255 {
// encode value from pos - 1, then append h byte
s.appendDeltaToLiteral(delta)
} else {
// overflowed - encode new
s.appendNewLiteral(delta)
}
}
}
}
s.lastUnixtime = unixtime
}
func (s *ReverseTimeDeltaCompressor) convertLastFromLiteralToRun() {
// Зменшую кількість елементів в literal блоці
s.h--
s.pos -= 1 + s.lastDeltaSize
s.buf.SetByte(s.pos, s.h) // закриваю literal блок
s.pos++
s.lastDeltaSize = s.buf.ReversePutVarUint64(s.pos, uint64(s.lastDelta))
s.pos += s.lastDeltaSize
s.h = 0 // run блок, довжини 2
s.buf.SetByte(s.pos, s.h)
s.pos++
}
func (s *ReverseTimeDeltaCompressor) convertLiteralToRun() {
// Знімаю flagLiteral, а лічильник 0 дорівнює 2 елементам в серії.
s.h = 0
s.buf.SetByte(s.pos-1, s.h)
}
func (s *ReverseTimeDeltaCompressor) appendDeltaToLiteral(delta uint32) {
s.h++ // збільшую к-сть дельт
s.lastDelta = delta
s.pos--
s.lastDeltaSize = s.buf.ReversePutVarUint64(s.pos, uint64(delta))
s.pos += s.lastDeltaSize
s.buf.SetByte(s.pos, s.h)
s.pos++
}
func (s *ReverseTimeDeltaCompressor) appendNewLiteral(delta uint32) {
//fmt.Println("appendNewLiteral", delta)
s.h = flagLiteral
s.lastDelta = delta
s.lastDeltaSize = s.buf.ReversePutVarUint64(s.pos, uint64(delta))
s.pos += s.lastDeltaSize
s.buf.SetByte(s.pos, flagLiteral) // literal, length = 1
s.pos++
}
func (s *ReverseTimeDeltaCompressor) DeleteLast() {
}
func (s *ReverseTimeDeltaCompressor) Sync() {
s.pos += s.buf.ReversePutVarUint64(s.pos, uint64(s.lastUnixtime))
}
type TimeDeltaBound struct {
Pos int
H byte
LastUnixtime uint32
LastDelta uint32
}
// delta h
func (s *ReverseTimeDeltaCompressor) GetState() TimeDeltaBound {
bound := TimeDeltaBound{
LastUnixtime: s.lastUnixtime,
}
if s.pos > 0 {
bound.Pos = s.pos - 1 - s.lastDeltaSize
bound.H = s.h
bound.LastDelta = s.lastDelta
}
return bound
}
// DECOMPRESSOR
type ReverseTimeDeltaDecompressor struct {
buf *conbuf.ContinuousBuffer
pos int
lastDelta uint32
lastUnixtime uint32
isRegular bool
isRun bool
pending int
done bool
}
func NewReverseTimeDeltaDecompressor(buf *conbuf.ContinuousBuffer, size int) *ReverseTimeDeltaDecompressor {
return &ReverseTimeDeltaDecompressor{
buf: buf,
pos: size,
}
}
func (s *ReverseTimeDeltaDecompressor) RestoreFromEnd() {
fmt.Println("RestoreFromEnd", s.pos)
if s.pos > 0 {
s.pos-- // перший байт даних
u64, n, err := s.buf.ReverseGetVarUint64(s.pos)
if err != nil {
log.Fatalf("bug: get last unixtime: %s", err)
}
s.lastUnixtime = uint32(u64)
s.pos -= n
fmt.Println("restored", s.lastUnixtime, s.pos)
if s.pos > 0 {
s.readHeader()
s.readDelta()
}
} else {
s.done = true
}
}
func (s *ReverseTimeDeltaDecompressor) RestoreFromBound(bound TimeDeltaBound) {
pretty.Println(bound)
s.lastUnixtime = bound.LastUnixtime
if bound.LastDelta > 0 {
s.pos = bound.Pos - 1
s.lastDelta = bound.LastDelta
s.decodeHeaderByte(bound.H)
} else {
s.pos = -1
}
fmt.Printf("restore from bound: pos=%d, isRun=%t, pending=%d\n", s.pos, s.isRun, s.pending)
}
func (s *ReverseTimeDeltaDecompressor) NextValue() (value uint32, done bool) {
//fmt.Printf("NextValue(): pos: %d, isRegular: %t, pending: %d\n", s.pos, s.isRegular, s.pending)
if s.done {
return 0, true
}
// повертаю значення, що було прочитано в методі RestoreFromBound/RestoreFromEnd
value = s.lastUnixtime
if s.isRegular {
s.pending--
if s.pending > 0 {
// якщо в серії залишаються елементи
if !s.isRun {
s.readDelta()
}
s.lastUnixtime -= s.lastDelta
} else if s.pos > 0 {
// в серії більше немає елементів, отже перевіряє чи є ще дані в буфері.
// дані є - читаю заголовок наступної серії
s.readHeader()
s.readDelta()
s.lastUnixtime -= s.lastDelta
} else {
s.done = true
}
} else {
if s.lastDelta > 0 {
s.isRegular = true
s.lastUnixtime -= s.lastDelta
} else {
// був закодований лише last unixtime
s.done = true
}
}
return value, false
}
func (s *ReverseTimeDeltaDecompressor) readHeader() {
h := s.buf.GetByte(s.pos)
s.pos--
s.decodeHeaderByte(h)
// fmt.Println("h:", h)
// fmt.Println("isRun:", s.isRun)
// fmt.Println("pending:", s.pending)
}
func (s *ReverseTimeDeltaDecompressor) decodeHeaderByte(h byte) {
s.isRun = h < 128
if s.isRun {
s.pending = int(h&127) + 2
} else {
s.pending = int(h&127) + 1
}
}
func (s *ReverseTimeDeltaDecompressor) readDelta() {
u64, n, err := s.buf.ReverseGetVarUint64(s.pos)
if err != nil {
log.Fatalln(err)
}
// fmt.Println()
// fmt.Println("read from pos:", s.pos)
// fmt.Println("read delta:", u64)
// fmt.Println("read delta n:", n)
// fmt.Println()
s.pos -= n
s.lastDelta = uint32(u64)
}

View File

@@ -1,374 +1,270 @@
package chunkenc package chunkenc
import ( // // REVERSE
"fmt"
"gordenko.dev/dima/qb/bin" // // const (
"gordenko.dev/dima/qb/conbuf" // // lastUnixtimeIdx = 0
) // // baseDeltaIdx = 4
// // )
// REVERSE // type ReverseTimeDeltaOfDeltaCompressor struct {
// buf *conbuf.ContinuousBuffer
// pos int
// lastDelta uint32
// lastUnixtime uint32
// lastDeltaOfDelta int64
// lastDeltaOfDeltaSize int
// h byte
// }
const ( // func NewReverseTimeDeltaOfDeltaCompressor(buf *conbuf.ContinuousBuffer, size int) *ReverseTimeDeltaOfDeltaCompressor {
lastUnixtimeIdx = 0 // s := &ReverseTimeDeltaOfDeltaCompressor{
baseDeltaIdx = 4 // buf: buf,
) // pos: size,
// }
// if size > 0 {
// // s.lastUnixtime = s.buf.GetUint32(lastUnixtimeIdx)
// // u64, _, err := s.buf.GetVarUint64(baseDeltaIdx)
// // if err != nil {
// // log.Fatalf("bug: get base delta: %s", err)
// // }
// // s.baseDelta = uint32(u64)
// // s.h = s.buf.GetByte(s.pos - 1)
// // s.lastDeltaOfDelta, s.lastDeltaOfDeltaSize, err = s.buf.ReverseGetVarInt64(s.pos - 2)
// // if err != nil {
// // log.Fatalf("bug: get last delta of delta: %s", err)
// // }
// } else {
// //s.baseDelta = baseDelta
// }
// return s
// }
type ReverseTimeDeltaOfDeltaCompressor struct { // func (s *ReverseTimeDeltaOfDeltaCompressor) Size() int {
buf *conbuf.ContinuousBuffer // return s.pos
pos int // }
baseDelta uint32
lastUnixtime uint32
lastDeltaOfDelta int64
length uint16
numIdx int
}
func NewReverseTimeDeltaOfDeltaCompressor(buf *conbuf.ContinuousBuffer, size int) *ReverseTimeDeltaOfDeltaCompressor { // func (s *ReverseTimeDeltaOfDeltaCompressor) Append(unixtime uint32) {
s := &ReverseTimeDeltaOfDeltaCompressor{ // if s.lastUnixtime > 0 {
buf: buf, // if s.pos == 0 {
pos: size, // // 2-й unixtime
} // s.lastDelta = unixtime - s.lastUnixtime
if size > 0 { // s.appendNewLiteral(0)
s.restoreState() // } else {
} // // 3-й unixtime і наступні
return s // lastDelta := unixtime - s.lastUnixtime
} // deltaOfDelta := int64(lastDelta) - int64(s.lastDelta)
// if deltaOfDelta == s.lastDeltaOfDelta {
// if s.h < 128 {
// // run блок - отже треба збільшити лічильник
// if s.h < 127 {
// // increase counter
// s.h++
// s.buf.SetByte(s.pos-1, s.h)
// } else {
// // не можу збільшити - буде переповнення. Додаю новий literal блок
// // counter overflow
// // fix encode delta
// s.appendNewLiteral(deltaOfDelta)
// }
// } else {
// // literal блок.
// // Якщо в ньому лише одне значення - перетворюю його на run блок.
// // Інакше забираю останнє значення із literal блока і додаю новий run блок.
// q := s.h & 127
// if q == 0 { // 1 кодується як 0
// s.convertLiteralToRun()
// } else {
// s.convertLastFromLiteralToRun(deltaOfDelta)
// }
// }
// } else {
// s.lastDelta = lastDelta
// if s.h < 127 {
// // end of run
// s.appendNewLiteral(deltaOfDelta)
// } else {
// if s.h < 255 {
// // encode value from pos - 1, then append h byte
// s.appendDeltaToLiteral(deltaOfDelta)
// } else {
// // overflowed - encode new
// s.appendNewLiteral(deltaOfDelta)
// }
// }
// }
// }
// }
// // для першого unixtime - лише оновлюємо lastUnixtime, а для наступних -
// // теж оновлюємо, але після запису серій в buf.
// s.lastUnixtime = unixtime
// }
func (s *ReverseTimeDeltaOfDeltaCompressor) restoreState() { // func (s *ReverseTimeDeltaOfDeltaCompressor) convertLastFromLiteralToRun(deltaOfDelta int64) {
s.lastUnixtime = s.buf.GetUint32(lastUnixtimeIdx) // // Зменшую кількість елементів в literal блоці
if s.pos > 4 { // s.h--
u64, _, err := s.buf.GetVarUint64(baseDeltaIdx) // s.pos -= 1 + s.lastDeltaOfDeltaSize
if err != nil { // s.buf.SetByte(s.pos, s.h) // закриваю literal блок
panic(fmt.Sprintf("bug: get base delta: %s", err)) // s.pos++
} // s.lastDeltaOfDeltaSize = s.buf.ReversePutVarInt64(s.pos, deltaOfDelta)
s.baseDelta = uint32(u64) // s.pos += s.lastDeltaOfDeltaSize
// s.h = 0 // run блок, довжини 2
// s.buf.SetByte(s.pos, s.h)
// s.pos++
// }
pos := s.pos - 1 // func (s *ReverseTimeDeltaOfDeltaCompressor) convertLiteralToRun() {
idxOf8 := uint(8 - s.buf.GetByte(pos)) // // Знімаю flagLiteral, а лічильник 0 дорівнює 2 елементам в серії.
pos-- // s.h = 0
s8 := s.buf.GetByte(pos) // s.buf.SetByte(s.pos-1, s.h)
pos-- // }
var n int // func (s *ReverseTimeDeltaOfDeltaCompressor) appendDeltaToLiteral(deltaOfDelta int64) {
s.lastDeltaOfDelta, n, err = s.buf.ReverseGetVarInt64(pos) // s.h++ // збільшую к-сть дельт
if err != nil { // s.lastDeltaOfDelta = deltaOfDelta
panic(err) // s.pos--
} // s.lastDeltaOfDeltaSize = s.buf.ReversePutVarInt64(s.pos, deltaOfDelta)
pos -= n // s.pos += s.lastDeltaOfDeltaSize
s.numIdx = pos + 1 // s.buf.SetByte(s.pos, s.h)
var flag byte = 1 << idxOf8 // s.pos++
if (s8 & flag) == flag { // }
s.length, _ = s.buf.DecodeRunLength(pos)
}
}
}
func (s *ReverseTimeDeltaOfDeltaCompressor) Size() int { // func (s *ReverseTimeDeltaOfDeltaCompressor) appendNewLiteral(deltaOfDelta int64) {
return s.pos // s.h = flagLiteral
} // s.lastDeltaOfDelta = deltaOfDelta
// s.lastDeltaOfDeltaSize = s.buf.ReversePutVarInt64(s.pos, deltaOfDelta)
// s.pos += s.lastDeltaOfDeltaSize
// s.buf.SetByte(s.pos, flagLiteral) // literal, length = 1
// s.pos++
// }
func (s *ReverseTimeDeltaOfDeltaCompressor) CalcRequiredSpace(unixtime uint32) int { // func (s *ReverseTimeDeltaOfDeltaCompressor) Sync() {
if s.pos == 0 { // // дописую в кінець lastDelta та lastUnixtime
return 4 // if s.pos > 0 {
} // n := s.buf.ReversePutVarUint64(s.pos, uint64(s.lastDelta))
// s.pos += n
// }
// if s.lastUnixtime > 0 {
// s.buf.PutUint32(s.pos, s.lastUnixtime)
// s.pos += 4
// }
// }
if s.baseDelta == 0 { // func (s *ReverseTimeDeltaOfDeltaCompressor) DeleteLast() {
baseDelta := unixtime - s.lastUnixtime
n := bin.CalcVarUint64Length(uint64(baseDelta))
return n + 3
}
deltaOfDelta := int64(unixtime-s.lastUnixtime) - int64(s.baseDelta) // }
if deltaOfDelta == s.lastDeltaOfDelta {
if s.length == 0 {
return 1
} else {
newLength := s.length + 1
if newLength < 130 {
return 0
} else if newLength == 130 {
return 1
} else {
if newLength < 32769 {
return 0
} else {
n := bin.CalcVarInt64Length(deltaOfDelta)
n += 2
s8q := s.buf.GetByte(s.pos - 1)
if s8q == 8 {
n -= 1
} else {
n -= 2
}
return n
}
}
}
} else {
n := bin.CalcVarInt64Length(deltaOfDelta)
n += 2
s8q := s.buf.GetByte(s.pos - 1)
if s8q == 8 {
n -= 1
} else {
n -= 2
}
return n
}
}
func (s *ReverseTimeDeltaOfDeltaCompressor) Append(unixtime uint32) { // type TimeDeltaOfDeltaBound struct {
if s.pos == 0 { // LastUnixtime uint32
s.lastUnixtime = unixtime // LastDelta uint32
s.buf.PutUint32(lastUnixtimeIdx, unixtime) // Pos int
s.pos += 4 // H byte
return // LastDeltaOfDelta int64
} // }
if s.baseDelta == 0 { // func (s *ReverseTimeDeltaOfDeltaCompressor) GetState() TimeDeltaOfDeltaBound {
s.baseDelta = unixtime - s.lastUnixtime // return TimeDeltaOfDeltaBound{
s.lastDeltaOfDelta = 0 // LastUnixtime: s.lastUnixtime,
s.lastUnixtime = unixtime // LastDelta: s.lastDelta,
s.buf.PutUint32(lastUnixtimeIdx, unixtime) // Pos: s.pos,
// H: s.h,
// LastDeltaOfDelta: s.lastDeltaOfDelta,
// }
// }
n := s.buf.PutVarUint64(s.pos, uint64(s.baseDelta)) // // DECOMPRESSOR
s.pos += n
s.encodeNewDeltaOfDelta(0, 0, 1)
return
}
deltaOfDelta := int64(unixtime-s.lastUnixtime) - int64(s.baseDelta) // type ReverseTimeDeltaOfDeltaDecompressor struct {
s.lastUnixtime = unixtime // buf *conbuf.ContinuousBuffer
s.buf.PutUint32(lastUnixtimeIdx, unixtime) // size int
// pos int
// lastUnixtime uint32
// lastDelta uint32
// //lastDeltaOfDelta int64
// isRun bool
// pending int
// }
if deltaOfDelta == s.lastDeltaOfDelta { // func NewReverseTimeDeltaOfDeltaDecompressor(buf *conbuf.ContinuousBuffer, size int) *ReverseTimeDeltaOfDeltaDecompressor {
if s.length == 0 { // return &ReverseTimeDeltaOfDeltaDecompressor{
s.length = 2 // buf: buf,
s.shiftOnePosToRight() // size: size,
s.buf.SetByte(s.numIdx-1, 0) // pos: size, // last elem
s8q := s.buf.GetByte(s.pos - 1) // }
s.buf.SetFlag(s.pos-2, 1<<(8-s8q)) // }
} else {
s.length++
if s.length < 130 {
s.buf.SetByte(s.numIdx-1, byte(s.length-2))
} else if s.length == 130 {
s.shiftOnePosToRight()
s.encode2bLength()
} else {
if s.length < 32769 {
s.encode2bLength()
} else {
s.appendNewDeltaOfDelta(deltaOfDelta)
}
}
}
} else {
s.appendNewDeltaOfDelta(deltaOfDelta)
}
}
func (s *ReverseTimeDeltaOfDeltaCompressor) appendNewDeltaOfDelta(deltaOfDelta int64) { // func (s *ReverseTimeDeltaOfDeltaDecompressor) NextValue() (value uint32, done bool) {
s.length = 0 // fmt.Printf("\nNextValue(): pos: %d, size: %d, pending: %d\n", s.pos, s.size, s.pending)
s8 := s.buf.GetByte(s.pos - 2) // if s.pos < s.size {
s8q := s.buf.GetByte(s.pos - 1) // if s.pending > 0 {
// //fmt.Println("pending", )
// // якщо в серії залишаються елементи
// fmt.Println("s.lastUnixtime -= s.lastDelta", s.lastUnixtime, s.lastDelta)
// s.lastUnixtime -= s.lastDelta
// if !s.isRun {
// s.readValue()
// }
// s.pending--
// return s.lastUnixtime, false
// }
// // FIX - читаю lastUnixtime
// if s.pos > 0 {
// s.lastUnixtime -= s.lastDelta
// // читаю заголовок наступної серії
// s.readHeader()
// s.readValue()
// s.pending--
// return s.lastUnixtime, false
// }
// // серія завершена - перевіряю чи є ще серії
// return 0, true
// } else {
// s.pos -= 4
// s.lastUnixtime = s.buf.GetUint32(s.pos)
// fmt.Println("lastUnix", s.lastUnixtime)
// if s.pos > 0 {
// s.pos--
// u64, n, err := s.buf.ReverseGetVarUint64(s.pos)
// if err != nil {
// log.Fatalln(err)
// }
// s.pos -= n
// s.lastDelta = uint32(u64)
// fmt.Println("lastDelta", s.lastDelta)
// }
// return s.lastUnixtime, false
// }
// }
if s8q == 8 { // func (s *ReverseTimeDeltaOfDeltaDecompressor) readHeader() {
s.pos -= 1 // h := s.buf.GetByte(s.pos)
s8 = 0 // s.pos--
s8q = 1 // s.isRun = h < 128
} else { // if s.isRun {
s.pos -= 2 // s.pending = int(h&127) + 2
s8q++ // } else {
} // s.pending = int(h&127) + 1
// }
s.encodeNewDeltaOfDelta(deltaOfDelta, s8, s8q) // fmt.Println("h:", h)
} // fmt.Println("isRun:", s.isRun)
// fmt.Println("pending:", s.pending)
func (s *ReverseTimeDeltaOfDeltaCompressor) encodeNewDeltaOfDelta(deltaOfDelta int64, s8 byte, s8q byte) { // }
s.lastDeltaOfDelta = deltaOfDelta // func (s *ReverseTimeDeltaOfDeltaDecompressor) readValue() {
s.numIdx = s.pos // //s.lastUnixtime -= s.lastDelta
n := s.buf.ReversePutVarInt64(s.pos, deltaOfDelta) // // var (
s.pos += n // // err error
s.buf.SetByte(s.pos, s8) // // n int
s.pos++ // // )
s.buf.SetByte(s.pos, s8q) // lastDeltaOfDelta, n, err := s.buf.ReverseGetVarInt64(s.pos)
s.pos++ // if err != nil {
} // log.Fatalln(err)
// }
func (s *ReverseTimeDeltaOfDeltaCompressor) shiftOnePosToRight() { // fmt.Println()
s.buf.ShiftOnePosToRight(s.numIdx, s.pos) // fmt.Println("read Value from pos:", s.pos)
s.pos++ // fmt.Println("delta of delta:", lastDeltaOfDelta)
s.numIdx++ // //fmt.Println("delta of delta n:", n)
} // s.pos -= n
// s.lastDelta = uint32(int64(s.lastDelta) - lastDeltaOfDelta)
func (s *ReverseTimeDeltaOfDeltaCompressor) encode2bLength() { // fmt.Println("last delta:", s.lastDelta)
num := s.length - 2 // fmt.Println()
s.buf.SetByte(s.numIdx-1, byte(num&127)|128) // }
s.buf.SetByte(s.numIdx-2, byte(num>>7))
}
func (s *ReverseTimeDeltaOfDeltaCompressor) DeleteLast() {
var (
s8q = s.buf.GetByte(s.pos - 1)
s8 = s.buf.GetByte(s.pos - 2)
flag byte = 1 << uint(8-s8q)
)
if s.length > 0 {
if s.length == 2 {
s.length = 0
s.buf.UnsetFlag(s.pos-2, flag)
s.buf.ShiftOnePosToLeft(s.numIdx, s.pos)
s.numIdx--
s.pos--
} else if s.length < 130 {
s.length--
s.buf.SetByte(s.numIdx-1, byte(s.length)-2)
} else if s.length == 130 {
s.length--
s.buf.ShiftOnePosToLeft(s.numIdx, s.pos)
s.numIdx--
s.pos--
s.buf.SetByte(s.numIdx-1, byte(s.length)-2)
} else {
s.length--
s.encode2bLength()
}
} else {
if s8q > 1 {
s8q--
flag = 1 << uint(8-s8q)
s.pos = s.numIdx + 2
s.buf.SetByte(s.pos-2, s8)
s.buf.SetByte(s.pos-1, s8q)
} else {
s.pos = s.numIdx + 1
s.buf.SetByte(s.pos-1, 8)
s8 = s.buf.GetByte(s.pos - 2)
flag = 1
}
var (
pos = s.pos - 3
n int
err error
)
s.lastDeltaOfDelta, n, err = s.buf.ReverseGetVarInt64(pos)
if err != nil {
panic(err)
}
s.numIdx = pos - n
if (s8 & flag) == flag {
s.length, _ = s.buf.DecodeRunLength(s.numIdx - 1)
}
}
delta := int64(s.baseDelta) + s.lastDeltaOfDelta
s.lastUnixtime = uint32(int64(s.lastUnixtime) - delta)
s.buf.PutUint32(lastUnixtimeIdx, s.lastUnixtime)
}
type ReverseTimeDeltaOfDeltaDecompressor struct {
step byte
buf *conbuf.ContinuousBuffer
pos int
bound int
lastUnixtime uint32
baseDelta uint32
lastDeltaOfDelta int64
length uint16
idxOf8 uint
s8 byte
}
func NewReverseTimeDeltaOfDeltaDecompressor(buf *conbuf.ContinuousBuffer, size int) *ReverseTimeDeltaOfDeltaDecompressor {
return &ReverseTimeDeltaOfDeltaDecompressor{
buf: buf,
pos: size,
}
}
func (s *ReverseTimeDeltaOfDeltaDecompressor) NextValue() (value uint32, done bool) {
if s.step == 0 {
if s.pos == 0 {
return 0, true
}
s.lastUnixtime = s.buf.GetUint32(lastUnixtimeIdx)
s.step = 1
return s.lastUnixtime, false
}
if s.step == 1 {
if s.pos == baseDeltaIdx {
return 0, true
}
u64, n, err := s.buf.GetVarUint64(baseDeltaIdx)
if err != nil {
panic("EOF")
}
s.bound = baseDeltaIdx + n
s.baseDelta = uint32(u64)
s.pos--
s.idxOf8 = uint(8 - s.buf.GetByte(s.pos))
s.pos--
s.s8 = s.buf.GetByte(s.pos)
s.pos--
s.readVar()
if s.length > 0 {
s.length--
}
s.step = 2
return s.lastUnixtime, false
}
if s.length > 0 {
s.length--
delta := int64(s.baseDelta) + s.lastDeltaOfDelta
s.lastUnixtime = uint32(int64(s.lastUnixtime) - delta)
return s.lastUnixtime, false
}
if s.pos < s.bound {
return 0, true
}
if s.idxOf8 == 0 {
s.s8 = s.buf.GetByte(s.pos)
s.pos--
}
s.readVar()
if s.length > 0 {
s.length--
}
return s.lastUnixtime, false
}
func (s *ReverseTimeDeltaOfDeltaDecompressor) readVar() {
var (
n int
err error
)
s.lastDeltaOfDelta, n, err = s.buf.ReverseGetVarInt64(s.pos)
if err != nil {
panic(err)
}
s.pos -= n
delta := int64(s.baseDelta) + s.lastDeltaOfDelta
s.lastUnixtime = uint32(int64(s.lastUnixtime) - delta)
var flag byte = 1 << s.idxOf8
if (s.s8 & flag) == flag {
s.length, n = s.buf.DecodeRunLength(s.pos)
s.pos -= n
}
if s.idxOf8 == 7 {
s.idxOf8 = 0
} else {
s.idxOf8++
}
}

3
chunkenc2/chunckenc.go Normal file
View File

@@ -0,0 +1,3 @@
package chunkenc2
const eps = 0.000001

330
chunkenc2/cumdelta.go Normal file
View File

@@ -0,0 +1,330 @@
package chunkenc2
import (
"fmt"
"math"
"gordenko.dev/dima/qb/bin"
"gordenko.dev/dima/qb/conbuf"
)
// REVERSE
type ReverseCumulativeDeltaCompressor struct {
buf *conbuf.ContinuousBuffer
coef float64
pos int
firstValue float64
lastDelta uint64
length uint16
numIdx int
}
func NewReverseCumulativeDeltaCompressor(buf *conbuf.ContinuousBuffer, size int, fracDigits byte) *ReverseCumulativeDeltaCompressor {
var coef float64 = 1
if fracDigits > 0 {
coef = math.Pow(10, float64(fracDigits))
}
s := &ReverseCumulativeDeltaCompressor{
buf: buf,
pos: size,
coef: coef,
}
if size > 0 {
s.restoreState()
}
return s
}
func (s *ReverseCumulativeDeltaCompressor) restoreState() {
u64, n, err := s.buf.GetVarUint64(0)
if err != nil {
panic(fmt.Sprintf("bug: get first value: %s", err))
}
s.firstValue = float64(u64) / s.coef
if s.pos > n {
pos := s.pos - 1
idxOf8 := uint(8 - s.buf.GetByte(pos))
pos--
s8 := s.buf.GetByte(pos)
pos--
var n int
s.lastDelta, n, err = s.buf.ReverseGetVarUint64(pos)
if err != nil {
panic(fmt.Sprintf("bug: get last delta: %s", err))
}
pos -= n
s.numIdx = pos + 1
var flag byte = 1 << idxOf8
if (s8 & flag) == flag {
s.length, _ = s.buf.DecodeRunLength(pos)
}
}
}
func (s *ReverseCumulativeDeltaCompressor) Size() int {
return s.pos
}
func (s *ReverseCumulativeDeltaCompressor) CalcRequiredSpace(value float64) int {
if s.pos == 0 {
n := bin.CalcVarUint64Length(uint64(value * s.coef))
return n + 3
}
delta := uint64((value-s.firstValue)*s.coef + eps)
if delta == s.lastDelta {
if s.length == 0 {
return 1
} else {
newLength := s.length + 1
if newLength < 130 {
return 0
} else if newLength == 130 {
return 1
} else {
if newLength < 32769 {
return 0
} else {
n := bin.CalcVarUint64Length(delta)
n += 2
s8q := s.buf.GetByte(s.pos - 1)
if s8q == 8 {
n -= 1
} else {
n -= 2
}
return n
}
}
}
} else {
n := bin.CalcVarUint64Length(delta)
n += 2
s8q := s.buf.GetByte(s.pos - 1)
if s8q == 8 {
n -= 1
} else {
n -= 2
}
return n
}
}
func (s *ReverseCumulativeDeltaCompressor) Append(value float64) {
if s.pos == 0 {
n := s.buf.PutVarUint64(s.pos, uint64(value*s.coef))
s.pos += n
s.firstValue = value
s.encodeNewDelta(0, 0, 1)
} else {
delta := uint64((value-s.firstValue)*s.coef + eps)
if delta == s.lastDelta {
if s.length == 0 {
s.length = 2
s.shiftOnePosToRight()
s.buf.SetByte(s.numIdx-1, 0)
s8q := s.buf.GetByte(s.pos - 1)
s.buf.SetFlag(s.pos-2, 1<<(8-s8q))
} else {
s.length++
if s.length < 130 {
s.buf.SetByte(s.numIdx-1, byte(s.length-2))
} else if s.length == 130 {
s.shiftOnePosToRight()
s.encode2bLength()
} else {
if s.length < 32769 {
s.encode2bLength()
} else {
s.appendNewDelta(delta)
}
}
}
} else {
s.appendNewDelta(delta)
}
}
}
func (s *ReverseCumulativeDeltaCompressor) appendNewDelta(delta uint64) {
s.length = 0
s8 := s.buf.GetByte(s.pos - 2)
s8q := s.buf.GetByte(s.pos - 1)
if s8q == 8 {
s.pos -= 1
s8 = 0
s8q = 1
} else {
s.pos -= 2
s8q++
}
s.encodeNewDelta(delta, s8, s8q)
}
func (s *ReverseCumulativeDeltaCompressor) encodeNewDelta(delta uint64, s8 byte, s8q byte) {
s.lastDelta = delta
s.numIdx = s.pos
n := s.buf.ReversePutVarUint64(s.pos, s.lastDelta)
s.pos += n
s.buf.SetByte(s.pos, s8)
s.pos++
s.buf.SetByte(s.pos, s8q)
s.pos++
}
func (s *ReverseCumulativeDeltaCompressor) shiftOnePosToRight() {
s.buf.ShiftOnePosToRight(s.numIdx, s.pos)
s.pos++
s.numIdx++
}
func (s *ReverseCumulativeDeltaCompressor) encode2bLength() {
num := s.length - 2
s.buf.SetByte(s.numIdx-1, byte(num&127)|128)
s.buf.SetByte(s.numIdx-2, byte(num>>7))
}
func (s *ReverseCumulativeDeltaCompressor) DeleteLast() {
var (
s8q = s.buf.GetByte(s.pos - 1)
s8 = s.buf.GetByte(s.pos - 2)
flag byte = 1 << uint(8-s8q)
)
if s.length > 0 {
if s.length == 2 {
s.length = 0
s.buf.UnsetFlag(s.pos-2, flag)
s.buf.ShiftOnePosToLeft(s.numIdx, s.pos)
s.numIdx--
s.pos--
} else if s.length < 130 {
s.length--
s.buf.SetByte(s.numIdx-1, byte(s.length)-2)
} else if s.length == 130 {
s.length--
s.buf.ShiftOnePosToLeft(s.numIdx, s.pos)
s.numIdx--
s.pos--
s.buf.SetByte(s.numIdx-1, byte(s.length)-2)
} else {
s.length--
s.encode2bLength()
}
} else {
if s8q > 1 {
s8q--
flag = 1 << uint(8-s8q)
s.pos = s.numIdx + 2
s.buf.SetByte(s.pos-2, s8)
s.buf.SetByte(s.pos-1, s8q)
} else {
s.pos = s.numIdx + 1
s.buf.SetByte(s.pos-1, 8)
s8 = s.buf.GetByte(s.pos - 2)
flag = 1
}
var (
pos = s.pos - 3
n int
err error
)
s.lastDelta, n, err = s.buf.ReverseGetVarUint64(pos)
if err != nil {
panic(err)
}
s.numIdx = pos - n
if (s8 & flag) == flag {
s.length, _ = s.buf.DecodeRunLength(s.numIdx - 1)
}
}
}
type ReverseCumulativeDeltaDecompressor struct {
buf *conbuf.ContinuousBuffer
pos int
bound int
firstValue float64
lastValue float64
length uint16
coef float64
idxOf8 uint
s8 byte
step byte
}
func NewReverseCumulativeDeltaDecompressor(buf *conbuf.ContinuousBuffer, size int, fracDigits byte) *ReverseCumulativeDeltaDecompressor {
var coef float64 = 1
if fracDigits > 0 {
coef = math.Pow(10, float64(fracDigits))
}
return &ReverseCumulativeDeltaDecompressor{
buf: buf,
coef: coef,
pos: size,
}
}
func (s *ReverseCumulativeDeltaDecompressor) NextValue() (value float64, done bool) {
if s.step > 0 {
if s.length > 0 {
s.length--
return s.lastValue, false
}
if s.pos < s.bound {
return 0, true
}
if s.idxOf8 == 0 {
s.s8 = s.buf.GetByte(s.pos)
s.pos--
}
s.readVar()
if s.length > 0 {
s.length--
}
return s.lastValue, false
}
u64, n, err := s.buf.GetVarUint64(0)
if err != nil {
panic(err)
}
s.firstValue = float64(u64) / s.coef
s.bound = n
s.pos--
s.idxOf8 = uint(8 - s.buf.GetByte(s.pos))
s.pos--
s.s8 = s.buf.GetByte(s.pos)
s.pos--
s.readVar()
if s.length > 0 {
s.length--
}
s.step = 1
return s.lastValue, false
}
func (s *ReverseCumulativeDeltaDecompressor) readVar() {
u64, n, err := s.buf.ReverseGetVarUint64(s.pos)
if err != nil {
panic(err)
}
s.pos -= n
s.lastValue = s.firstValue + float64(u64)/s.coef
var flag byte = 1 << s.idxOf8
if (s.s8 & flag) == flag {
s.length, n = s.buf.DecodeRunLength(s.pos)
s.pos -= n
}
if s.idxOf8 == 7 {
s.idxOf8 = 0
} else {
s.idxOf8++
}
}

345
chunkenc2/insdelta.go Normal file
View File

@@ -0,0 +1,345 @@
package chunkenc2
import (
"fmt"
"math"
"gordenko.dev/dima/qb/bin"
"gordenko.dev/dima/qb/conbuf"
)
// REVERSE
type ReverseInstantDeltaCompressor struct {
buf *conbuf.ContinuousBuffer
coef float64
pos int
firstValue float64
lastDelta int64
length uint16
numIdx int
}
func NewReverseInstantDeltaCompressor(buf *conbuf.ContinuousBuffer, size int, fracDigits byte) *ReverseInstantDeltaCompressor {
var coef float64 = 1
if fracDigits > 0 {
coef = math.Pow(10, float64(fracDigits))
}
s := &ReverseInstantDeltaCompressor{
buf: buf,
pos: size,
coef: coef,
}
if size > 0 {
s.restoreState()
}
return s
}
func (s *ReverseInstantDeltaCompressor) restoreState() {
i64, n, err := s.buf.GetVarInt64(0)
if err != nil {
panic(fmt.Sprintf("bug: get first value: %s", err))
}
s.firstValue = float64(i64) / s.coef
if s.pos > n {
pos := s.pos - 1
idxOf8 := uint(8 - s.buf.GetByte(pos))
pos--
s8 := s.buf.GetByte(pos)
pos--
var n int
s.lastDelta, n, err = s.buf.ReverseGetVarInt64(pos)
if err != nil {
panic(fmt.Sprintf("bug: get last delta: %s", err))
}
pos -= n
s.numIdx = pos + 1
var flag byte = 1 << idxOf8
if (s8 & flag) == flag {
s.length, _ = s.buf.DecodeRunLength(pos)
}
}
}
func (s *ReverseInstantDeltaCompressor) Size() int {
return s.pos
}
func (s *ReverseInstantDeltaCompressor) CalcRequiredSpace(value float64) int {
if s.pos == 0 {
n := bin.CalcVarInt64Length(int64(value * s.coef))
return n + 3
}
tmp := (value - s.firstValue) * s.coef
if tmp > 0 {
tmp += eps
} else {
tmp -= eps
}
delta := int64(tmp)
if delta == s.lastDelta {
if s.length == 0 {
return 1
} else {
newLength := s.length + 1
if newLength < 130 {
return 0
} else if newLength == 130 {
return 1
} else {
if newLength < 32769 {
return 0
} else {
n := bin.CalcVarInt64Length(delta)
n += 2
s8q := s.buf.GetByte(s.pos - 1)
if s8q == 8 {
n -= 1
} else {
n -= 2
}
return n
}
}
}
} else {
n := bin.CalcVarInt64Length(delta)
n += 2
s8q := s.buf.GetByte(s.pos - 1)
if s8q == 8 {
n -= 1
} else {
n -= 2
}
return n
}
}
// В начале буфера кодирую базовое значение.
func (s *ReverseInstantDeltaCompressor) Append(value float64) {
if s.pos == 0 {
n := s.buf.PutVarInt64(s.pos, int64(value*s.coef))
s.pos += n
s.firstValue = value
s.encodeNewDelta(0, 0, 1)
} else {
tmp := (value - s.firstValue) * s.coef
if tmp > 0 {
tmp += eps
} else {
tmp -= eps
}
delta := int64(tmp)
if delta == s.lastDelta {
if s.length == 0 {
s.length = 2
s.shiftOnePosToRight()
s.buf.SetByte(s.numIdx-1, 0)
s8q := s.buf.GetByte(s.pos - 1)
s.buf.SetFlag(s.pos-2, 1<<(8-s8q))
} else {
s.length++
if s.length < 130 {
s.buf.SetByte(s.numIdx-1, byte(s.length-2))
} else if s.length == 130 {
s.shiftOnePosToRight()
s.encode2bLength()
} else {
if s.length < 32769 {
s.encode2bLength()
} else {
s.appendNewDelta(delta)
}
}
}
} else {
s.appendNewDelta(delta)
}
}
}
func (s *ReverseInstantDeltaCompressor) appendNewDelta(delta int64) {
s.length = 0
s8 := s.buf.GetByte(s.pos - 2)
s8q := s.buf.GetByte(s.pos - 1)
if s8q == 8 {
s.pos -= 1
s8 = 0
s8q = 1
} else {
s.pos -= 2
s8q++
}
s.encodeNewDelta(delta, s8, s8q)
}
func (s *ReverseInstantDeltaCompressor) encodeNewDelta(delta int64, s8 byte, s8q byte) {
s.lastDelta = delta
s.numIdx = s.pos
n := s.buf.ReversePutVarInt64(s.pos, s.lastDelta)
s.pos += n
s.buf.SetByte(s.pos, s8)
s.pos++
s.buf.SetByte(s.pos, s8q)
s.pos++
}
func (s *ReverseInstantDeltaCompressor) shiftOnePosToRight() {
s.buf.ShiftOnePosToRight(s.numIdx, s.pos)
s.pos++
s.numIdx++
}
func (s *ReverseInstantDeltaCompressor) encode2bLength() {
num := s.length - 2
s.buf.SetByte(s.numIdx-1, byte(num&127)|128)
s.buf.SetByte(s.numIdx-2, byte(num>>7))
}
func (s *ReverseInstantDeltaCompressor) DeleteLast() {
var (
s8q = s.buf.GetByte(s.pos - 1)
s8 = s.buf.GetByte(s.pos - 2)
flag byte = 1 << uint(8-s8q)
)
if s.length > 0 {
if s.length == 2 {
s.length = 0
s.buf.UnsetFlag(s.pos-2, flag)
s.buf.ShiftOnePosToLeft(s.numIdx, s.pos)
s.numIdx--
s.pos--
} else if s.length < 130 {
s.length--
s.buf.SetByte(s.numIdx-1, byte(s.length)-2)
} else if s.length == 130 {
s.length--
s.buf.ShiftOnePosToLeft(s.numIdx, s.pos)
s.numIdx--
s.pos--
s.buf.SetByte(s.numIdx-1, byte(s.length)-2)
} else {
s.length--
s.encode2bLength()
}
} else {
if s8q > 1 {
s8q--
flag = 1 << uint(8-s8q)
s.pos = s.numIdx + 2
s.buf.SetByte(s.pos-2, s8)
s.buf.SetByte(s.pos-1, s8q)
} else {
s.pos = s.numIdx + 1
s.buf.SetByte(s.pos-1, 8)
s8 = s.buf.GetByte(s.pos - 2)
flag = 1
}
var (
pos = s.pos - 3
n int
err error
)
s.lastDelta, n, err = s.buf.ReverseGetVarInt64(pos)
if err != nil {
panic(err)
}
s.numIdx = pos - n
if (s8 & flag) == flag {
s.length, _ = s.buf.DecodeRunLength(s.numIdx - 1)
}
}
}
type ReverseInstantDeltaDecompressor struct {
step byte
buf *conbuf.ContinuousBuffer
pos int
bound int
firstValue float64
lastValue float64
length uint16
coef float64
idxOf8 uint
s8 byte
}
func NewReverseInstantDeltaDecompressor(buf *conbuf.ContinuousBuffer, size int, fracDigits byte) *ReverseInstantDeltaDecompressor {
var coef float64 = 1
if fracDigits > 0 {
coef = math.Pow(10, float64(fracDigits))
}
return &ReverseInstantDeltaDecompressor{
buf: buf,
coef: coef,
pos: size,
}
}
func (s *ReverseInstantDeltaDecompressor) NextValue() (value float64, done bool) {
if s.step > 0 {
if s.length > 0 {
s.length--
return s.lastValue, false
}
if s.pos < s.bound {
return 0, true
}
if s.idxOf8 == 0 {
s.s8 = s.buf.GetByte(s.pos)
s.pos--
}
s.readVar()
if s.length > 0 {
s.length--
}
return s.lastValue, false
}
i64, n, err := s.buf.GetVarInt64(0)
if err != nil {
panic(err)
}
s.firstValue = float64(i64) / s.coef
s.bound = n
s.pos--
s.idxOf8 = uint(8 - s.buf.GetByte(s.pos))
s.pos--
s.s8 = s.buf.GetByte(s.pos)
s.pos--
s.readVar()
if s.length > 0 {
s.length--
}
s.step = 1
return s.lastValue, false
}
func (s *ReverseInstantDeltaDecompressor) readVar() {
i64, n, err := s.buf.ReverseGetVarInt64(s.pos)
if err != nil {
panic(err)
}
s.pos -= n
s.lastValue = s.firstValue + float64(i64)/s.coef
var flag byte = 1 << s.idxOf8
if (s.s8 & flag) == flag {
s.length, n = s.buf.DecodeRunLength(s.pos)
s.pos -= n
}
if s.idxOf8 == 7 {
s.idxOf8 = 0
} else {
s.idxOf8++
}
}

View File

@@ -0,0 +1,374 @@
package chunkenc2
import (
"fmt"
"gordenko.dev/dima/qb/bin"
"gordenko.dev/dima/qb/conbuf"
)
// REVERSE
const (
lastUnixtimeIdx = 0
baseDeltaIdx = 4
)
type ReverseTimeDeltaOfDeltaCompressor struct {
buf *conbuf.ContinuousBuffer
pos int
baseDelta uint32
lastUnixtime uint32
lastDeltaOfDelta int64
length uint16
numIdx int
}
func NewReverseTimeDeltaOfDeltaCompressor(buf *conbuf.ContinuousBuffer, size int) *ReverseTimeDeltaOfDeltaCompressor {
s := &ReverseTimeDeltaOfDeltaCompressor{
buf: buf,
pos: size,
}
if size > 0 {
s.restoreState()
}
return s
}
func (s *ReverseTimeDeltaOfDeltaCompressor) restoreState() {
s.lastUnixtime = s.buf.GetUint32(lastUnixtimeIdx)
if s.pos > 4 {
u64, _, err := s.buf.GetVarUint64(baseDeltaIdx)
if err != nil {
panic(fmt.Sprintf("bug: get base delta: %s", err))
}
s.baseDelta = uint32(u64)
pos := s.pos - 1
idxOf8 := uint(8 - s.buf.GetByte(pos))
pos--
s8 := s.buf.GetByte(pos)
pos--
var n int
s.lastDeltaOfDelta, n, err = s.buf.ReverseGetVarInt64(pos)
if err != nil {
panic(err)
}
pos -= n
s.numIdx = pos + 1
var flag byte = 1 << idxOf8
if (s8 & flag) == flag {
s.length, _ = s.buf.DecodeRunLength(pos)
}
}
}
func (s *ReverseTimeDeltaOfDeltaCompressor) Size() int {
return s.pos
}
func (s *ReverseTimeDeltaOfDeltaCompressor) CalcRequiredSpace(unixtime uint32) int {
if s.pos == 0 {
return 4
}
if s.baseDelta == 0 {
baseDelta := unixtime - s.lastUnixtime
n := bin.CalcVarUint64Length(uint64(baseDelta))
return n + 3
}
deltaOfDelta := int64(unixtime-s.lastUnixtime) - int64(s.baseDelta)
if deltaOfDelta == s.lastDeltaOfDelta {
if s.length == 0 {
return 1
} else {
newLength := s.length + 1
if newLength < 130 {
return 0
} else if newLength == 130 {
return 1
} else {
if newLength < 32769 {
return 0
} else {
n := bin.CalcVarInt64Length(deltaOfDelta)
n += 2
s8q := s.buf.GetByte(s.pos - 1)
if s8q == 8 {
n -= 1
} else {
n -= 2
}
return n
}
}
}
} else {
n := bin.CalcVarInt64Length(deltaOfDelta)
n += 2
s8q := s.buf.GetByte(s.pos - 1)
if s8q == 8 {
n -= 1
} else {
n -= 2
}
return n
}
}
func (s *ReverseTimeDeltaOfDeltaCompressor) Append(unixtime uint32) {
if s.pos == 0 {
s.lastUnixtime = unixtime
s.buf.PutUint32(lastUnixtimeIdx, unixtime)
s.pos += 4
return
}
if s.baseDelta == 0 {
s.baseDelta = unixtime - s.lastUnixtime
s.lastDeltaOfDelta = 0
s.lastUnixtime = unixtime
s.buf.PutUint32(lastUnixtimeIdx, unixtime)
n := s.buf.PutVarUint64(s.pos, uint64(s.baseDelta))
s.pos += n
s.encodeNewDeltaOfDelta(0, 0, 1)
return
}
deltaOfDelta := int64(unixtime-s.lastUnixtime) - int64(s.baseDelta)
s.lastUnixtime = unixtime
s.buf.PutUint32(lastUnixtimeIdx, unixtime)
if deltaOfDelta == s.lastDeltaOfDelta {
if s.length == 0 {
s.length = 2
s.shiftOnePosToRight()
s.buf.SetByte(s.numIdx-1, 0)
s8q := s.buf.GetByte(s.pos - 1)
s.buf.SetFlag(s.pos-2, 1<<(8-s8q))
} else {
s.length++
if s.length < 130 {
s.buf.SetByte(s.numIdx-1, byte(s.length-2))
} else if s.length == 130 {
s.shiftOnePosToRight()
s.encode2bLength()
} else {
if s.length < 32769 {
s.encode2bLength()
} else {
s.appendNewDeltaOfDelta(deltaOfDelta)
}
}
}
} else {
s.appendNewDeltaOfDelta(deltaOfDelta)
}
}
func (s *ReverseTimeDeltaOfDeltaCompressor) appendNewDeltaOfDelta(deltaOfDelta int64) {
s.length = 0
s8 := s.buf.GetByte(s.pos - 2)
s8q := s.buf.GetByte(s.pos - 1)
if s8q == 8 {
s.pos -= 1
s8 = 0
s8q = 1
} else {
s.pos -= 2
s8q++
}
s.encodeNewDeltaOfDelta(deltaOfDelta, s8, s8q)
}
func (s *ReverseTimeDeltaOfDeltaCompressor) encodeNewDeltaOfDelta(deltaOfDelta int64, s8 byte, s8q byte) {
s.lastDeltaOfDelta = deltaOfDelta
s.numIdx = s.pos
n := s.buf.ReversePutVarInt64(s.pos, deltaOfDelta)
s.pos += n
s.buf.SetByte(s.pos, s8)
s.pos++
s.buf.SetByte(s.pos, s8q)
s.pos++
}
func (s *ReverseTimeDeltaOfDeltaCompressor) shiftOnePosToRight() {
s.buf.ShiftOnePosToRight(s.numIdx, s.pos)
s.pos++
s.numIdx++
}
func (s *ReverseTimeDeltaOfDeltaCompressor) encode2bLength() {
num := s.length - 2
s.buf.SetByte(s.numIdx-1, byte(num&127)|128)
s.buf.SetByte(s.numIdx-2, byte(num>>7))
}
func (s *ReverseTimeDeltaOfDeltaCompressor) DeleteLast() {
var (
s8q = s.buf.GetByte(s.pos - 1)
s8 = s.buf.GetByte(s.pos - 2)
flag byte = 1 << uint(8-s8q)
)
if s.length > 0 {
if s.length == 2 {
s.length = 0
s.buf.UnsetFlag(s.pos-2, flag)
s.buf.ShiftOnePosToLeft(s.numIdx, s.pos)
s.numIdx--
s.pos--
} else if s.length < 130 {
s.length--
s.buf.SetByte(s.numIdx-1, byte(s.length)-2)
} else if s.length == 130 {
s.length--
s.buf.ShiftOnePosToLeft(s.numIdx, s.pos)
s.numIdx--
s.pos--
s.buf.SetByte(s.numIdx-1, byte(s.length)-2)
} else {
s.length--
s.encode2bLength()
}
} else {
if s8q > 1 {
s8q--
flag = 1 << uint(8-s8q)
s.pos = s.numIdx + 2
s.buf.SetByte(s.pos-2, s8)
s.buf.SetByte(s.pos-1, s8q)
} else {
s.pos = s.numIdx + 1
s.buf.SetByte(s.pos-1, 8)
s8 = s.buf.GetByte(s.pos - 2)
flag = 1
}
var (
pos = s.pos - 3
n int
err error
)
s.lastDeltaOfDelta, n, err = s.buf.ReverseGetVarInt64(pos)
if err != nil {
panic(err)
}
s.numIdx = pos - n
if (s8 & flag) == flag {
s.length, _ = s.buf.DecodeRunLength(s.numIdx - 1)
}
}
delta := int64(s.baseDelta) + s.lastDeltaOfDelta
s.lastUnixtime = uint32(int64(s.lastUnixtime) - delta)
s.buf.PutUint32(lastUnixtimeIdx, s.lastUnixtime)
}
type ReverseTimeDeltaOfDeltaDecompressor struct {
step byte
buf *conbuf.ContinuousBuffer
pos int
bound int
lastUnixtime uint32
baseDelta uint32
lastDeltaOfDelta int64
length uint16
idxOf8 uint
s8 byte
}
func NewReverseTimeDeltaOfDeltaDecompressor(buf *conbuf.ContinuousBuffer, size int) *ReverseTimeDeltaOfDeltaDecompressor {
return &ReverseTimeDeltaOfDeltaDecompressor{
buf: buf,
pos: size,
}
}
func (s *ReverseTimeDeltaOfDeltaDecompressor) NextValue() (value uint32, done bool) {
if s.step == 0 {
if s.pos == 0 {
return 0, true
}
s.lastUnixtime = s.buf.GetUint32(lastUnixtimeIdx)
s.step = 1
return s.lastUnixtime, false
}
if s.step == 1 {
if s.pos == baseDeltaIdx {
return 0, true
}
u64, n, err := s.buf.GetVarUint64(baseDeltaIdx)
if err != nil {
panic("EOF")
}
s.bound = baseDeltaIdx + n
s.baseDelta = uint32(u64)
s.pos--
s.idxOf8 = uint(8 - s.buf.GetByte(s.pos))
s.pos--
s.s8 = s.buf.GetByte(s.pos)
s.pos--
s.readVar()
if s.length > 0 {
s.length--
}
s.step = 2
return s.lastUnixtime, false
}
if s.length > 0 {
s.length--
delta := int64(s.baseDelta) + s.lastDeltaOfDelta
s.lastUnixtime = uint32(int64(s.lastUnixtime) - delta)
return s.lastUnixtime, false
}
if s.pos < s.bound {
return 0, true
}
if s.idxOf8 == 0 {
s.s8 = s.buf.GetByte(s.pos)
s.pos--
}
s.readVar()
if s.length > 0 {
s.length--
}
return s.lastUnixtime, false
}
func (s *ReverseTimeDeltaOfDeltaDecompressor) readVar() {
var (
n int
err error
)
s.lastDeltaOfDelta, n, err = s.buf.ReverseGetVarInt64(s.pos)
if err != nil {
panic(err)
}
s.pos -= n
delta := int64(s.baseDelta) + s.lastDeltaOfDelta
s.lastUnixtime = uint32(int64(s.lastUnixtime) - delta)
var flag byte = 1 << s.idxOf8
if (s.s8 & flag) == flag {
s.length, n = s.buf.DecodeRunLength(s.pos)
s.pos -= n
}
if s.idxOf8 == 7 {
s.idxOf8 = 0
} else {
s.idxOf8++
}
}

View File

@@ -9,6 +9,7 @@ import (
) )
const chunkSize = 512 const chunkSize = 512
const ChunkSize = 512
var ( var (
ErrOutOfRange = errors.New("out of range") ErrOutOfRange = errors.New("out of range")

View File

@@ -215,7 +215,7 @@ func (s *Database) AddMetric(req proto.AddMetricReq) uint16 {
switch resultCode { switch resultCode {
case Succeed: case Succeed:
waitCh := s.txlog.WriteAddedMetric(txlog.AddedMetric{ waitCh := s.txlog.Append(txlog.AddedMetric{
MetricID: req.MetricID, MetricID: req.MetricID,
MetricType: req.MetricType, MetricType: req.MetricType,
FracDigits: req.FracDigits, FracDigits: req.FracDigits,
@@ -289,21 +289,18 @@ func (s *Database) DeleteMetric(req proto.DeleteMetricReq) uint16 {
switch result.ResultCode { switch result.ResultCode {
case Succeed: case Succeed:
var ( var (
freeDataPages []uint32 freePageNumbers []uint32
freeIndexPages []uint32
) )
if result.RootPageNo > 0 { if result.RootPageNo > 0 {
pageLists, err := s.atree.GetAllPages(result.RootPageNo) var err error
freePageNumbers, err = s.atree.GetAllPages(result.RootPageNo)
if err != nil { if err != nil {
diploma.Abort(diploma.FailedAtreeRequest, err) diploma.Abort(diploma.FailedAtreeRequest, err)
} }
freeDataPages = pageLists.DataPages
freeIndexPages = pageLists.IndexPages
} }
waitCh := s.txlog.WriteDeletedMetric(txlog.DeletedMetric{ waitCh := s.txlog.Append(txlog.DeletedMetric{
MetricID: req.MetricID, MetricID: req.MetricID,
FreeDataPages: freeDataPages, FreePageNumbers: freePageNumbers,
FreeIndexPages: freeIndexPages,
}) })
<-waitCh <-waitCh
@@ -348,7 +345,7 @@ func (s *Database) AppendMeasure(req proto.AppendMeasureReq) uint16 {
switch result.ResultCode { switch result.ResultCode {
case CanAppend: case CanAppend:
waitCh := s.txlog.WriteAppendMeasure(txlog.AppendedMeasure{ waitCh := s.txlog.Append(txlog.AppendedMeasure{
MetricID: req.MetricID, MetricID: req.MetricID,
Timestamp: req.Timestamp, Timestamp: req.Timestamp,
Value: req.Value, Value: req.Value,
@@ -357,35 +354,58 @@ func (s *Database) AppendMeasure(req proto.AppendMeasureReq) uint16 {
case NewPage: case NewPage:
filled := result.FilledPage filled := result.FilledPage
report, err := s.atree.AppendDataPage(atree.AppendDataPageReq{
var path atree.PathToDataPage
if filled.RootPageNo > 0 {
path, err := s.atree.FindPathToLastPage(filled.RootPageNo)
if err != nil {
// FIX
diploma.Abort(diploma.WriteToAtreeFailed, err)
}
// for _, leg := range path.Legs {
// pagesToRelease = append(pagesToRelease, leg.PageNo)
// }
if path.LastPageNo != filled.PrevPageNo {
diploma.Abort(
diploma.WrongPrevPageNo,
fmt.Errorf("bug: last pageNo %d in tree != prev pageNo %d in _metric",
path.LastPageNo, filled.PrevPageNo),
)
}
}
// report, err := s.atree.AppendDataPages(atree.AppendDataPageReq{
// MetricID: req.MetricID,
// Timestamp: req.Timestamp,
// Value: req.Value,
// Since: filled.Since,
// RootPageNo: filled.RootPageNo,
// PrevPageNo: filled.PrevPageNo,
// TimestampsChunks: filled.TimestampsChunks,
// TimestampsSize: filled.TimestampsSize,
// ValuesChunks: filled.ValuesChunks,
// ValuesSize: filled.ValuesSize,
// })
// if err != nil {
// diploma.Abort(diploma.WriteToAtreeFailed, err)
// }
// _ = report
//waitCh := s.txlog.WriteAppended(path, report)
waitCh := s.txlog.Append(txlog.AppendedPagesReq{
Legs: path.Legs,
MetricID: req.MetricID, MetricID: req.MetricID,
Timestamp: req.Timestamp, Timestamp: req.Timestamp,
Value: req.Value, Value: req.Value,
Since: filled.Since, LastPageNo: filled.PrevPageNo,
RootPageNo: filled.RootPageNo, //Pages: ,
PrevPageNo: filled.PrevPageNo,
TimestampsChunks: filled.TimestampsChunks, TimestampsChunks: filled.TimestampsChunks,
TimestampsSize: filled.TimestampsSize, TimestampsSize: filled.TimestampsSize,
ValuesChunks: filled.ValuesChunks, ValuesChunks: filled.ValuesChunks,
ValuesSize: filled.ValuesSize, ValuesSize: filled.ValuesSize,
})
if err != nil {
diploma.Abort(diploma.WriteToAtreeFailed, err)
}
_ = report
waitCh := s.txlog.WriteAppendedMeasureWithOverflow(
txlog.AppendedMeasureWithOverflow{
// FIX
// MetricID: req.MetricID,
// Timestamp: req.Timestamp,
// Value: req.Value,
// IsDataPageReused: report.IsDataPageReused,
// DataPageNo: report.DataPageNo,
// IsRootChanged: report.IsRootChanged,
// RootPageNo: report.NewRootPageNo,
// ReusedIndexPages: report.ReusedIndexPages,
}, },
false,
) )
<-waitCh <-waitCh
@@ -442,7 +462,9 @@ func (s *Database) AppendMeasures(req proto.AppendMeasuresReq) uint16 {
until = result.Until until = result.Until
untilValue = result.UntilValue untilValue = result.UntilValue
// //
toAppendMeasures []proto.Measure //toAppendMeasures []proto.Measure
dataPages []atree.NotLinkedDataPage
) )
for idx, measure := range req.Measures { for idx, measure := range req.Measures {
@@ -451,30 +473,32 @@ func (s *Database) AppendMeasures(req proto.AppendMeasuresReq) uint16 {
since = measure.Timestamp since = measure.Timestamp
} else { } else {
if measure.Timestamp <= until { if measure.Timestamp <= until {
if len(toAppendMeasures) > 0 { // пишу в БД що вже встиг закодувати FIX
waitCh := s.txlog.WriteAppendMeasures( // if len(toAppendMeasures) > 0 {
txlog.AppendedMeasures{ // waitCh := s.txlog.WriteAppendMeasures(
MetricID: req.MetricID, // txlog.AppendedMeasures{
Measures: toAppendMeasures, // MetricID: req.MetricID,
}, // Measures: toAppendMeasures,
false, // },
) // false,
<-waitCh // )
} // <-waitCh
// }
return proto.ErrExpiredMeasure return proto.ErrExpiredMeasure
} }
if result.MetricType == diploma.Cumulative && measure.Value < untilValue { if result.MetricType == diploma.Cumulative && measure.Value < untilValue {
if len(toAppendMeasures) > 0 { // пишу в БД що вже встиг закодувати FIX
waitCh := s.txlog.WriteAppendMeasures( // if len(toAppendMeasures) > 0 {
txlog.AppendedMeasures{ // waitCh := s.txlog.WriteAppendMeasures(
MetricID: req.MetricID, // txlog.AppendedMeasures{
Measures: toAppendMeasures, // MetricID: req.MetricID,
}, // Measures: toAppendMeasures,
false, // },
) // false,
<-waitCh // )
} // <-waitCh
// }
return proto.ErrNonMonotonicValue return proto.ErrNonMonotonicValue
} }
} }
@@ -485,38 +509,40 @@ func (s *Database) AppendMeasures(req proto.AppendMeasuresReq) uint16 {
totalSpace := timestamps.Size() + values.Size() + extraSpace totalSpace := timestamps.Size() + values.Size() + extraSpace
if totalSpace <= atree.DataPagePayloadSize { if totalSpace <= atree.DataPagePayloadSize {
// накопичую
timestamps.Append(measure.Timestamp) timestamps.Append(measure.Timestamp)
values.Append(measure.Value) values.Append(measure.Value)
toAppendMeasures = append(toAppendMeasures, measure) //toAppendMeasures = append(toAppendMeasures, measure)
} else { } else {
if len(toAppendMeasures) > 0 { // сторінка заповнена
waitCh := s.txlog.WriteAppendMeasures( // if len(toAppendMeasures) > 0 {
txlog.AppendedMeasures{ // waitCh := s.txlog.WriteAppendMeasures(
MetricID: req.MetricID, // txlog.AppendedMeasures{
Measures: toAppendMeasures, // MetricID: req.MetricID,
}, // Measures: toAppendMeasures,
true, // },
) // true,
<-waitCh // )
// <-waitCh
toAppendMeasures = nil // toAppendMeasures = nil
} // }
report, err := s.atree.AppendDataPage(atree.AppendDataPageReq{
MetricID: req.MetricID, pageData := make([]byte, atree.PageSize)
Timestamp: until,
Value: untilValue, atree.ChunksToNotLinkedDataPage(pageData, atree.ChunksToNotLinkedDataPageReq{
Since: since, //PrevPageNo: path.LastPageNo,
RootPageNo: rootPageNo,
PrevPageNo: prevPageNo,
TimestampsChunks: timestampsBuf.Chunks(), TimestampsChunks: timestampsBuf.Chunks(),
TimestampsSize: uint16(timestamps.Size()), TimestampsSize: uint16(timestamps.Size()),
ValuesChunks: valuesBuf.Chunks(), ValuesChunks: valuesBuf.Chunks(),
ValuesSize: uint16(values.Size()), ValuesSize: uint16(values.Size()),
}) })
if err != nil {
diploma.Abort(diploma.WriteToAtreeFailed, err) dataPages = append(dataPages, atree.NotLinkedDataPage{
} Since: since,
_ = report Data: pageData,
})
// FIX // FIX
// prevPageNo = report.DataPageNo // prevPageNo = report.DataPageNo
// if report.IsRootChanged { // if report.IsRootChanged {
@@ -540,7 +566,7 @@ func (s *Database) AppendMeasures(req proto.AppendMeasuresReq) uint16 {
timestampsBuf = conbuf.New(nil) timestampsBuf = conbuf.New(nil)
valuesBuf = conbuf.New(nil) valuesBuf = conbuf.New(nil)
timestamps = chunkenc.NewReverseTimeDeltaOfDeltaCompressor( timestamps = chunkenc.NewReverseTimeDeltaCompressor(
timestampsBuf, 0) timestampsBuf, 0)
if result.MetricType == diploma.Cumulative { if result.MetricType == diploma.Cumulative {
values = chunkenc.NewReverseCumulativeDeltaCompressor( values = chunkenc.NewReverseCumulativeDeltaCompressor(
@@ -560,16 +586,32 @@ func (s *Database) AppendMeasures(req proto.AppendMeasuresReq) uint16 {
untilValue = measure.Value untilValue = measure.Value
} }
if len(toAppendMeasures) > 0 { waitCh := s.txlog.Append(txlog.AppendedPagesReq{
waitCh := s.txlog.WriteAppendMeasures(
txlog.AppendedMeasures{
MetricID: req.MetricID, MetricID: req.MetricID,
Measures: toAppendMeasures, Timestamp: until,
Value: untilValue,
LastPageNo: prevPageNo,
//Measures: toAppendMeasures,
}, },
false,
) )
<-waitCh <-waitCh
}
// report, err := s.atree.AppendDataPage(atree.AppendDataPageReq{})
// if err != nil {
// diploma.Abort(diploma.WriteToAtreeFailed, err)
// }
// _ = report
// if len(toAppendMeasures) > 0 {
// waitCh := s.txlog.WriteAppendMeasures(
// txlog.AppendedMeasures{
// MetricID: req.MetricID,
// Measures: toAppendMeasures,
// },
// false,
// )
// <-waitCh
// }
case NoMetric: case NoMetric:
return proto.ErrNoMetric return proto.ErrNoMetric
@@ -602,22 +644,21 @@ func (s *Database) DeleteMeasures(req proto.DeleteMeasuresReq) uint16 {
case DeleteFromAtreeNotNeeded: case DeleteFromAtreeNotNeeded:
// регистрирую удаление в TransactionLog // регистрирую удаление в TransactionLog
waitCh := s.txlog.WriteDeletedMeasures(txlog.DeletedMeasures{ waitCh := s.txlog.Append(txlog.DeletedMeasures{
MetricID: req.MetricID, MetricID: req.MetricID,
}) })
<-waitCh <-waitCh
case DeleteFromAtreeRequired: case DeleteFromAtreeRequired:
// собираю номера всех data и index страниц метрики (типа запись REDO лога). // собираю номера всех data и index страниц метрики (типа запись REDO лога).
pageLists, err := s.atree.GetAllPages(req.MetricID) pageNumbers, err := s.atree.GetAllPages(req.MetricID)
if err != nil { if err != nil {
diploma.Abort(diploma.FailedAtreeRequest, err) diploma.Abort(diploma.FailedAtreeRequest, err)
} }
// регистрирую удаление в TransactionLog // регистрирую удаление в TransactionLog
waitCh := s.txlog.WriteDeletedMeasures(txlog.DeletedMeasures{ waitCh := s.txlog.Append(txlog.DeletedMeasures{
MetricID: req.MetricID, MetricID: req.MetricID,
FreeDataPages: pageLists.DataPages, FreePageNumbers: pageNumbers,
FreeIndexPages: pageLists.IndexPages,
}) })
<-waitCh <-waitCh

View File

@@ -109,7 +109,6 @@ func (s *Database) ListenAndServe() (err error) {
s.atree, err = atree.New(atree.Options{ s.atree, err = atree.New(atree.Options{
Dir: s.dir, Dir: s.dir,
DatabaseName: s.databaseName, DatabaseName: s.databaseName,
FreeList: s.freeList,
}) })
if err != nil { if err != nil {
return fmt.Errorf("atree.New: %s", err) return fmt.Errorf("atree.New: %s", err)
@@ -169,6 +168,8 @@ func (s *Database) recovery() {
Dir: s.dir, Dir: s.dir,
LogNumber: logNumber, LogNumber: logNumber,
AppendToWorkerQueue: s.appendJobToWorkerQueue, AppendToWorkerQueue: s.appendJobToWorkerQueue,
FreeList: s.freeList,
Atree: s.atree,
ExitCh: s.exitCh, ExitCh: s.exitCh,
WaitGroup: s.waitGroup, WaitGroup: s.waitGroup,
}) })
@@ -374,11 +375,8 @@ func (s *Database) replayChangesRecord(untyped any) error {
case txlog.DeletedMetric: case txlog.DeletedMetric:
delete(s.metrics, rec.MetricID) delete(s.metrics, rec.MetricID)
if len(rec.FreeDataPages) > 0 { if len(rec.FreePageNumbers) > 0 {
s.freeList.AddPages(rec.FreeDataPages) s.freeList.AddPages(rec.FreePageNumbers)
}
if len(rec.FreeIndexPages) > 0 {
s.freeList.AddPages(rec.FreeIndexPages)
} }
case txlog.AppendedMeasure: case txlog.AppendedMeasure:
@@ -413,34 +411,31 @@ func (s *Database) replayChangesRecord(untyped any) error {
} }
} }
case txlog.AppendedMeasureWithOverflow: // case txlog.AppendedMeasureWithOverflow:
metric, ok := s.metrics[rec.MetricID] // metric, ok := s.metrics[rec.MetricID]
if ok { // if ok {
metric.ReinitBy(rec.Timestamp, rec.Value) // metric.ReinitBy(rec.Timestamp, rec.Value)
if rec.IsRootChanged { // if rec.IsRootChanged {
metric.RootPageNo = rec.RootPageNo // metric.RootPageNo = rec.RootPageNo
} // }
metric.LastPageNo = rec.DataPageNo // metric.LastPageNo = rec.DataPageNo
// delete free pages // // delete free pages
if rec.IsDataPageReused { // if rec.IsDataPageReused {
s.freeList.DeleteReservedPages([]uint32{ // s.freeList.DeleteReservedPages([]uint32{
rec.DataPageNo, // rec.DataPageNo,
}) // })
} // }
if len(rec.ReusedIndexPages) > 0 { // if len(rec.ReusedIndexPages) > 0 {
s.freeList.DeleteReservedPages(rec.ReusedIndexPages) // s.freeList.DeleteReservedPages(rec.ReusedIndexPages)
} // }
} // }
case txlog.DeletedMeasures: case txlog.DeletedMeasures:
metric, ok := s.metrics[rec.MetricID] metric, ok := s.metrics[rec.MetricID]
if ok { if ok {
metric.DeleteMeasures() metric.DeleteMeasures()
if len(rec.FreeDataPages) > 0 { if len(rec.FreePageNumbers) > 0 {
s.freeList.AddPages(rec.FreeDataPages) s.freeList.AddPages(rec.FreePageNumbers)
}
if len(rec.FreeDataPages) > 0 {
s.freeList.AddPages(rec.FreeIndexPages)
} }
} }

View File

@@ -2,7 +2,6 @@ package database
import ( import (
octopus "gordenko.dev/dima/qb" octopus "gordenko.dev/dima/qb"
"gordenko.dev/dima/qb/chunkenc"
"gordenko.dev/dima/qb/conbuf" "gordenko.dev/dima/qb/conbuf"
) )
@@ -24,43 +23,44 @@ type _metric struct {
} }
func (s *_metric) ReinitBy(timestamp uint32, value float64) { func (s *_metric) ReinitBy(timestamp uint32, value float64) {
s.TimestampsBuf = conbuf.New(nil) // s.TimestampsBuf = conbuf.New(nil)
s.ValuesBuf = conbuf.New(nil) // s.ValuesBuf = conbuf.New(nil)
// // //
s.Timestamps = chunkenc.NewReverseTimeDeltaOfDeltaCompressor( // s.Timestamps = chunkenc.NewReverseTimeDeltaOfDeltaCompressor(
s.TimestampsBuf, 0) // s.TimestampsBuf, 0)
if s.MetricType == octopus.Cumulative { // if s.MetricType == octopus.Cumulative {
s.Values = chunkenc.NewReverseCumulativeDeltaCompressor( // s.Values = chunkenc.NewReverseCumulativeDeltaCompressor(
s.ValuesBuf, 0, s.FracDigits) // s.ValuesBuf, 0, s.FracDigits)
} else { // } else {
s.Values = chunkenc.NewReverseInstantDeltaCompressor( // s.Values = chunkenc.NewReverseInstantDeltaCompressor(
s.ValuesBuf, 0, s.FracDigits) // s.ValuesBuf, 0, s.FracDigits)
} // }
s.Timestamps.Append(timestamp) // s.Timestamps.Append(timestamp)
s.Values.Append(value) // s.Values.Append(value)
s.Since = timestamp // s.Since = timestamp
s.SinceValue = value // s.SinceValue = value
s.Until = timestamp // s.Until = timestamp
s.UntilValue = value // s.UntilValue = value
} }
func (s *_metric) DeleteMeasures() { func (s *_metric) DeleteMeasures() {
s.TimestampsBuf = conbuf.New(nil) s.TimestampsBuf = conbuf.New(nil)
s.ValuesBuf = conbuf.New(nil) s.ValuesBuf = conbuf.New(nil)
// // fix
s.Timestamps = chunkenc.NewReverseTimeDeltaOfDeltaCompressor( // s.Timestamps = chunkenc.NewReverseTimeDeltaOfDeltaCompressor(
s.TimestampsBuf, 0) // s.TimestampsBuf, 0)
if s.MetricType == octopus.Cumulative { // if s.MetricType == octopus.Cumulative {
s.Values = chunkenc.NewReverseCumulativeDeltaCompressor( // s.Values = chunkenc.NewReverseCumulativeDeltaCompressor(
s.ValuesBuf, 0, s.FracDigits) // s.ValuesBuf, 0, s.FracDigits)
} else { // } else {
s.Values = chunkenc.NewReverseInstantDeltaCompressor( // s.Values = chunkenc.NewReverseInstantDeltaCompressor(
s.ValuesBuf, 0, s.FracDigits) // s.ValuesBuf, 0, s.FracDigits)
} // }
// END OF FIX
s.RootPageNo = 0 s.RootPageNo = 0
s.LastPageNo = 0 s.LastPageNo = 0

View File

@@ -352,6 +352,11 @@ func (s *Database) tryAppendMeasure(req tryAppendMeasureReq) {
s.startAppendMeasure(metric, req, lockEntry) s.startAppendMeasure(metric, req, lockEntry)
} }
type ReadBound struct {
ValuesPos int
ValuesLastValue float64
}
func (s *Database) startAppendMeasure(metric *_metric, req tryAppendMeasureReq, lockEntry *metricLockEntry) { func (s *Database) startAppendMeasure(metric *_metric, req tryAppendMeasureReq, lockEntry *metricLockEntry) {
if req.Timestamp <= metric.Until { if req.Timestamp <= metric.Until {
req.ResultCh <- tryAppendMeasureResult{ req.ResultCh <- tryAppendMeasureResult{
@@ -466,8 +471,7 @@ func (s *Database) appendMeasure(rec txlog.AppendedMeasure) {
} }
} }
func (s *Database) appendMeasures(extended txlog.AppendedMeasuresExtended) { func (s *Database) appendMeasures(rec txlog.AppendedMeasures) {
rec := extended.Record
metric, ok := s.metrics[rec.MetricID] metric, ok := s.metrics[rec.MetricID]
if !ok { if !ok {
diploma.Abort(diploma.NoMetricBug, diploma.Abort(diploma.NoMetricBug,
@@ -500,11 +504,9 @@ func (s *Database) appendMeasures(extended txlog.AppendedMeasuresExtended) {
metric.UntilValue = measure.Value metric.UntilValue = measure.Value
} }
if !extended.HoldLock {
lockEntry.XLock = false lockEntry.XLock = false
s.doAfterReleaseXLock(rec.MetricID, metric, lockEntry) s.doAfterReleaseXLock(rec.MetricID, metric, lockEntry)
} }
}
func (s *Database) appendMeasureAfterOverflow(extended txlog.AppendedMeasureWithOverflowExtended) { func (s *Database) appendMeasureAfterOverflow(extended txlog.AppendedMeasureWithOverflowExtended) {
rec := extended.Record rec := extended.Record
@@ -544,11 +546,9 @@ func (s *Database) appendMeasureAfterOverflow(extended txlog.AppendedMeasureWith
s.freeList.DeleteReservedPages(rec.ReusedIndexPages) s.freeList.DeleteReservedPages(rec.ReusedIndexPages)
} }
if !extended.HoldLock {
lockEntry.XLock = false lockEntry.XLock = false
s.doAfterReleaseXLock(rec.MetricID, metric, lockEntry) s.doAfterReleaseXLock(rec.MetricID, metric, lockEntry)
} }
}
type tryAppendMeasuresReq struct { type tryAppendMeasuresReq struct {
MetricID uint32 MetricID uint32
@@ -880,7 +880,7 @@ func (s *Database) applyChanges(req txlog.Changes) {
case txlog.AppendedMeasure: case txlog.AppendedMeasure:
s.appendMeasure(rec) s.appendMeasure(rec)
case txlog.AppendedMeasuresExtended: case txlog.AppendedMeasures:
s.appendMeasures(rec) s.appendMeasures(rec)
case txlog.AppendedMeasureWithOverflowExtended: case txlog.AppendedMeasureWithOverflowExtended:
@@ -1019,11 +1019,8 @@ func (s *Database) deleteMetric(rec txlog.DeletedMetric) {
delete(s.metrics, rec.MetricID) delete(s.metrics, rec.MetricID)
delete(s.metricLockEntries, rec.MetricID) delete(s.metricLockEntries, rec.MetricID)
if len(rec.FreeDataPages) > 0 { if len(rec.FreePageNumbers) > 0 {
s.freeList.AddPages(rec.FreeDataPages) s.freeList.AddPages(rec.FreePageNumbers)
}
if len(rec.FreeIndexPages) > 0 {
s.freeList.AddPages(rec.FreeIndexPages)
} }
if len(addMetricReqs) > 0 { if len(addMetricReqs) > 0 {
@@ -1053,11 +1050,8 @@ func (s *Database) deleteMeasures(rec txlog.DeletedMeasures) {
} }
metric.DeleteMeasures() metric.DeleteMeasures()
lockEntry.XLock = false lockEntry.XLock = false
if len(rec.FreeDataPages) > 0 { if len(rec.FreePageNumbers) > 0 {
s.freeList.AddPages(rec.FreeDataPages) s.freeList.AddPages(rec.FreePageNumbers)
}
if len(rec.FreeDataPages) > 0 {
s.freeList.AddPages(rec.FreeIndexPages)
} }
s.doAfterReleaseXLock(rec.MetricID, metric, lockEntry) s.doAfterReleaseXLock(rec.MetricID, metric, lockEntry)
} }

Binary file not shown.

Binary file not shown.

8
go.mod
View File

@@ -3,11 +3,7 @@ module gordenko.dev/dima/qb
go 1.24.2 go 1.24.2
require ( require (
github.com/RoaringBitmap/roaring/v2 v2.14.4
gopkg.in/ini.v1 v1.67.1 gopkg.in/ini.v1 v1.67.1
) gordenko.dev/dima/bin v0.0.0-20251101161620-7fa2d0f2923a
gordenko.dev/dima/pretty v0.0.0-20221225212746-0c27d8c0ac69
require (
github.com/bits-and-blooms/bitset v1.24.2 // indirect
github.com/mschoch/smat v0.2.0 // indirect
) )

10
go.sum
View File

@@ -1,12 +1,6 @@
github.com/RoaringBitmap/roaring/v2 v2.14.4 h1:4aKySrrg9G/5oRtJ3TrZLObVqxgQ9f1znCRBwEwjuVw=
github.com/RoaringBitmap/roaring/v2 v2.14.4/go.mod h1:oMvV6omPWr+2ifRdeZvVJyaz+aoEUopyv5iH0u/+wbY=
github.com/bits-and-blooms/bitset v1.24.2 h1:M7/NzVbsytmtfHbumG+K2bremQPMJuqv1JD3vOaFxp0=
github.com/bits-and-blooms/bitset v1.24.2/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/mschoch/smat v0.2.0 h1:8imxQsjDm8yFEAVBe7azKmKSgzSkZXDuKkSq9374khM=
github.com/mschoch/smat v0.2.0/go.mod h1:kc9mz7DoBKqDyiRL7VZN8KvXQMWeTaVnttLRXOlotKw=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
@@ -24,3 +18,7 @@ gopkg.in/ini.v1 v1.67.1/go.mod h1:x/cyOwCgZqOkJoDIJ3c1KNHMo10+nLGAhh+kn3Zizss=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gordenko.dev/dima/bin v0.0.0-20251101161620-7fa2d0f2923a h1:IAJvA+4IxPAUVXqXAIGrlfljoAbbzek4jPgrLwWJm9E=
gordenko.dev/dima/bin v0.0.0-20251101161620-7fa2d0f2923a/go.mod h1:/I+9fvRUzXHgXSwGEwOK1mBBM4BJ3AtCmoCZ2upDiwk=
gordenko.dev/dima/pretty v0.0.0-20221225212746-0c27d8c0ac69 h1:nyJ3mzTQ46yUeMZCdLyYcs7B5JCS54c67v84miyhq2E=
gordenko.dev/dima/pretty v0.0.0-20221225212746-0c27d8c0ac69/go.mod h1:AxgKDktpqBVyIOhIcP+nlCpK+EsJyjN5kPdqyd8euVU=

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -9,9 +9,7 @@ import (
"io" "io"
"os" "os"
diploma "gordenko.dev/dima/qb"
"gordenko.dev/dima/qb/bin" "gordenko.dev/dima/qb/bin"
"gordenko.dev/dima/qb/proto"
) )
type Reader struct { type Reader struct {
@@ -102,49 +100,43 @@ func (s *Reader) parseRecords(body []byte) ([]any, error) {
switch recordType { switch recordType {
case CodeAddedMetric: case CodeAddedMetric:
var rec AddedMetric rec := new(AddedMetric)
rec, err = s.readAddedMetric(src) if err = rec.Parse(src); err != nil {
if err != nil {
return nil, err return nil, err
} }
records = append(records, rec) records = append(records, rec)
case CodeDeletedMetric: case CodeDeletedMetric:
var rec DeletedMetric rec := new(DeletedMetric)
rec, err = s.readDeletedMetric(src) if err = rec.Parse(src); err != nil {
if err != nil {
return nil, err return nil, err
} }
records = append(records, rec) records = append(records, rec)
case CodeAppendedMeasure: case CodeAppendedMeasure:
var rec AppendedMeasure rec := new(AppendedMeasure)
rec, err = s.readAppendedMeasure(src) if err = rec.Parse(src); err != nil {
if err != nil {
return nil, err return nil, err
} }
records = append(records, rec) records = append(records, rec)
case CodeAppendedMeasures: case CodeAppendedMeasures:
var rec AppendedMeasures rec := new(AppendedMeasures)
rec, err = s.readAppendedMeasures(src) if err = rec.Parse(src); err != nil {
if err != nil {
return nil, err return nil, err
} }
records = append(records, rec) records = append(records, rec)
case CodeAppendedMeasureWithOverflow: case CodeAppendedPages:
var rec AppendedMeasureWithOverflow rec := new(AppendedPages)
rec, err = s.readAppendedMeasureWithOverflow(src) if err = rec.Parse(src); err != nil {
if err != nil {
return nil, err return nil, err
} }
records = append(records, rec) records = append(records, rec)
case CodeDeletedMeasures: case CodeDeletedMeasures:
var rec DeletedMeasures rec := new(DeletedMeasures)
rec, err = s.readDeletedMeasures(src) if err = rec.Parse(src); err != nil {
if err != nil {
return nil, err return nil, err
} }
records = append(records, rec) records = append(records, rec)
@@ -155,183 +147,183 @@ func (s *Reader) parseRecords(body []byte) ([]any, error) {
} }
} }
func (s *Reader) readAddedMetric(src *bytes.Buffer) (_ AddedMetric, err error) { // func (s *Reader) readAddedMetric(src *bytes.Buffer) (_ AddedMetric, err error) {
arr, err := bin.ReadN(src, 6) // arr, err := bin.ReadN(src, 6)
if err != nil { // if err != nil {
return // return
} // }
return AddedMetric{ // return AddedMetric{
MetricID: bin.GetUint32(arr), // MetricID: bin.GetUint32(arr),
MetricType: diploma.MetricType(arr[4]), // MetricType: diploma.MetricType(arr[4]),
FracDigits: int(arr[5]), // FracDigits: int(arr[5]),
}, nil // }, nil
} // }
func (s *Reader) readDeletedMetric(src *bytes.Buffer) (_ DeletedMetric, err error) { // func (s *Reader) readDeletedMetric(src *bytes.Buffer) (_ DeletedMetric, err error) {
var rec DeletedMetric // var rec DeletedMetric
rec.MetricID, err = bin.ReadUint32(src) // rec.MetricID, err = bin.ReadUint32(src)
if err != nil { // if err != nil {
return // return
} // }
// free data pages // // free data pages
dataQty, _, err := bin.ReadVarUint64(src) // dataQty, _, err := bin.ReadVarUint64(src)
if err != nil { // if err != nil {
return // return
} // }
for range dataQty { // for range dataQty {
var pageNo uint32 // var pageNo uint32
pageNo, err = bin.ReadUint32(src) // pageNo, err = bin.ReadUint32(src)
if err != nil { // if err != nil {
return // return
} // }
rec.FreeDataPages = append(rec.FreeDataPages, pageNo) // rec.FreeDataPages = append(rec.FreeDataPages, pageNo)
} // }
// free index pages // // free index pages
indexQty, _, err := bin.ReadVarUint64(src) // indexQty, _, err := bin.ReadVarUint64(src)
if err != nil { // if err != nil {
return // return
} // }
for range indexQty { // for range indexQty {
var pageNo uint32 // var pageNo uint32
pageNo, err = bin.ReadUint32(src) // pageNo, err = bin.ReadUint32(src)
if err != nil { // if err != nil {
return // return
} // }
rec.FreeIndexPages = append(rec.FreeIndexPages, pageNo) // rec.FreeIndexPages = append(rec.FreeIndexPages, pageNo)
} // }
return rec, nil // return rec, nil
} // }
func (s *Reader) readAppendedMeasure(src *bytes.Buffer) (_ AppendedMeasure, err error) { // func (s *Reader) readAppendedMeasure(src *bytes.Buffer) (_ AppendedMeasure, err error) {
arr, err := bin.ReadN(src, 16) // arr, err := bin.ReadN(src, 16)
if err != nil { // if err != nil {
return // return
} // }
return AppendedMeasure{ // return AppendedMeasure{
MetricID: bin.GetUint32(arr[0:]), // MetricID: bin.GetUint32(arr[0:]),
Timestamp: bin.GetUint32(arr[4:]), // Timestamp: bin.GetUint32(arr[4:]),
Value: bin.GetFloat64(arr[8:]), // Value: bin.GetFloat64(arr[8:]),
}, nil // }, nil
} // }
func (s *Reader) readAppendedMeasures(src *bytes.Buffer) (_ AppendedMeasures, err error) { // func (s *Reader) readAppendedMeasures(src *bytes.Buffer) (_ AppendedMeasures, err error) {
var rec AppendedMeasures // var rec AppendedMeasures
rec.MetricID, err = bin.ReadUint32(src) // rec.MetricID, err = bin.ReadUint32(src)
if err != nil { // if err != nil {
return // return
} // }
qty, err := bin.ReadUint16(src) // qty, err := bin.ReadUint16(src)
if err != nil { // if err != nil {
return // return
} // }
for range qty { // for range qty {
var measure proto.Measure // var measure proto.Measure
measure.Timestamp, err = bin.ReadUint32(src) // measure.Timestamp, err = bin.ReadUint32(src)
if err != nil { // if err != nil {
return // return
} // }
measure.Value, err = bin.ReadFloat64(src) // measure.Value, err = bin.ReadFloat64(src)
if err != nil { // if err != nil {
return // return
} // }
rec.Measures = append(rec.Measures, measure) // rec.Measures = append(rec.Measures, measure)
} // }
return rec, nil // return rec, nil
} // }
func (s *Reader) readAppendedMeasureWithOverflow(src *bytes.Buffer) (_ AppendedMeasureWithOverflow, err error) { // func (s *Reader) readAppendedMeasureWithOverflow(src *bytes.Buffer) (_ AppendedMeasureWithOverflow, err error) {
var ( // var (
b byte // b byte
rec AppendedMeasureWithOverflow // rec AppendedMeasureWithOverflow
) // )
rec.MetricID, err = bin.ReadUint32(src) // rec.MetricID, err = bin.ReadUint32(src)
if err != nil { // if err != nil {
return // return
} // }
rec.Timestamp, err = bin.ReadUint32(src) // rec.Timestamp, err = bin.ReadUint32(src)
if err != nil { // if err != nil {
return // return
} // }
rec.Value, err = bin.ReadFloat64(src) // rec.Value, err = bin.ReadFloat64(src)
if err != nil { // if err != nil {
return // return
} // }
b, err = src.ReadByte() // b, err = src.ReadByte()
if err != nil { // if err != nil {
return // return
} // }
rec.IsDataPageReused = b == 1 // rec.IsDataPageReused = b == 1
rec.DataPageNo, err = bin.ReadUint32(src) // rec.DataPageNo, err = bin.ReadUint32(src)
if err != nil { // if err != nil {
return // return
} // }
b, err = src.ReadByte() // b, err = src.ReadByte()
if err != nil { // if err != nil {
return // return
} // }
if b == 1 { // if b == 1 {
rec.IsRootChanged = true // rec.IsRootChanged = true
rec.RootPageNo, err = bin.ReadUint32(src) // rec.RootPageNo, err = bin.ReadUint32(src)
if err != nil { // if err != nil {
return // return
} // }
} // }
// index pages // // index pages
indexQty, err := src.ReadByte() // indexQty, err := src.ReadByte()
if err != nil { // if err != nil {
return // return
} // }
for range indexQty { // for range indexQty {
var pageNo uint32 // var pageNo uint32
pageNo, err = bin.ReadUint32(src) // pageNo, err = bin.ReadUint32(src)
if err != nil { // if err != nil {
return // return
} // }
rec.ReusedIndexPages = append(rec.ReusedIndexPages, pageNo) // rec.ReusedIndexPages = append(rec.ReusedIndexPages, pageNo)
} // }
return rec, nil // return rec, nil
} // }
func (s *Reader) readDeletedMeasures(src *bytes.Buffer) (_ DeletedMeasures, err error) { // func (s *Reader) readDeletedMeasures(src *bytes.Buffer) (_ DeletedMeasures, err error) {
var ( // var (
rec DeletedMeasures // rec DeletedMeasures
) // )
rec.MetricID, err = bin.ReadUint32(src) // rec.MetricID, err = bin.ReadUint32(src)
if err != nil { // if err != nil {
return // return
} // }
// free data pages // // free data pages
rec.FreeDataPages, err = s.readFreePageNumbers(src) // rec.FreeDataPages, err = s.readFreePageNumbers(src)
if err != nil { // if err != nil {
return // return
} // }
// free index pages // // free index pages
rec.FreeIndexPages, err = s.readFreePageNumbers(src) // rec.FreeIndexPages, err = s.readFreePageNumbers(src)
if err != nil { // if err != nil {
return // return
} // }
return rec, nil // return rec, nil
} // }
// HELPERS // HELPERS
func (s *Reader) readFreePageNumbers(src *bytes.Buffer) ([]uint32, error) { // func (s *Reader) readFreePageNumbers(src *bytes.Buffer) ([]uint32, error) {
var freePages []uint32 // var freePages []uint32
qty, _, err := bin.ReadVarUint64(src) // qty, _, err := bin.ReadVarUint64(src)
if err != nil { // if err != nil {
return nil, err // return nil, err
} // }
for range qty { // for range qty {
var pageNo uint32 // var pageNo uint32
pageNo, err = bin.ReadUint32(src) // pageNo, err = bin.ReadUint32(src)
if err != nil { // if err != nil {
return nil, err // return nil, err
} // }
freePages = append(freePages, pageNo) // freePages = append(freePages, pageNo)
} // }
return freePages, nil // return freePages, nil
} // }
func (s *Reader) Seek(offset int64) error { func (s *Reader) Seek(offset int64) error {
ret, err := s.file.Seek(offset, 0) ret, err := s.file.Seek(offset, 0)

359
txlog/txlog.go Normal file
View File

@@ -0,0 +1,359 @@
package txlog
import (
"io"
bin "gordenko.dev/dima/bin/little"
"gordenko.dev/dima/qb"
"gordenko.dev/dima/qb/atree"
"gordenko.dev/dima/qb/conbuf"
"gordenko.dev/dima/qb/proto"
)
type AddedMetric struct {
MetricID uint32
MetricType qb.MetricType
FracDigits int
}
func (s AddedMetric) Pack(w io.Writer) {
arr := []byte{
CodeAddedMetric,
0, 0, 0, 0, //
byte(s.MetricType),
byte(s.FracDigits),
}
bin.PutUint32(arr[1:], s.MetricID)
w.Write(arr)
}
func (s *AddedMetric) Parse(src io.Reader) (err error) {
arr, err := bin.ReadN(src, 6)
if err != nil {
return
}
s.MetricID, _ = bin.GetUint32(arr)
s.MetricType = qb.MetricType(arr[4])
s.FracDigits = int(arr[5])
return nil
}
type DeletedMetric struct {
MetricID uint32
FreePageNumbers []uint32
}
func (s DeletedMetric) Pack(w io.Writer) {
arr := []byte{
CodeDeletedMetric,
0, 0, 0, 0, // metricID
}
bin.PutUint32(arr[1:], s.MetricID)
w.Write(arr)
bin.WriteVarSize(w, len(s.FreePageNumbers))
for _, pageNo := range s.FreePageNumbers {
bin.WriteUint32(w, pageNo)
}
}
func (s *DeletedMetric) Parse(src io.Reader) (err error) {
s.MetricID, err = bin.ReadUint32(src)
if err != nil {
return
}
// free pages
qty, err := bin.ReadVarSize(src)
if err != nil {
return
}
for range qty {
var pageNo uint32
pageNo, err = bin.ReadUint32(src)
if err != nil {
return
}
s.FreePageNumbers = append(s.FreePageNumbers, pageNo)
}
return nil
}
type AppendedMeasure struct {
MetricID uint32
Timestamp uint32
Value float64
}
func (s AppendedMeasure) Pack(w io.Writer) {
arr := []byte{
CodeAppendedMeasure,
0, 0, 0, 0, // metricID
0, 0, 0, 0, // timestamp
0, 0, 0, 0, 0, 0, 0, 0, // value
}
bin.PutUint32(arr[1:], s.MetricID)
bin.PutUint32(arr[5:], s.Timestamp)
bin.PutFloat64(arr[9:], s.Value)
w.Write(arr)
}
func (s *AppendedMeasure) Parse(src io.Reader) (err error) {
arr, err := bin.ReadN(src, 16)
if err != nil {
return
}
s.MetricID, _ = bin.GetUint32(arr[0:])
s.Timestamp, _ = bin.GetUint32(arr[4:])
s.Value, _ = bin.GetFloat64(arr[8:])
return nil
}
type AppendedMeasures struct {
MetricID uint32
Measures []proto.Measure
}
func (s AppendedMeasures) Pack(w io.Writer) {
arr := []byte{
CodeAppendedMeasures,
0, 0, 0, 0, // metricID
0, 0, // qty
}
bin.PutUint32(arr[1:], s.MetricID)
bin.PutUint16(arr[5:], uint16(len(s.Measures)))
w.Write(arr)
for _, measure := range s.Measures {
bin.WriteUint32(w, measure.Timestamp)
bin.WriteFloat64(w, measure.Value)
}
}
func (s *AppendedMeasures) Parse(src io.Reader) (err error) {
s.MetricID, err = bin.ReadUint32(src)
if err != nil {
return
}
qty, err := bin.ReadUint16(src)
if err != nil {
return
}
for range qty {
var measure proto.Measure
measure.Timestamp, err = bin.ReadUint32(src)
if err != nil {
return
}
measure.Value, err = bin.ReadFloat64(src)
if err != nil {
return
}
s.Measures = append(s.Measures, measure)
}
return nil
}
type AppendedPagesReq struct {
Legs []atree.PathLeg
MetricID uint32
Timestamp uint32 // last measure
Value float64 // last measure
//RootPageNo uint32
LastPageNo uint32
Pages []atree.NotLinkedDataPage
TimestampsChunks [][]byte
TimestampsSize uint16
ValuesChunks [][]byte
ValuesSize uint16
}
/*
4b metricID
4b timestamp
8b value
4b LastPageNo (0 if no new root page)
4b newRootPageNo (0 if no new root page)
Nb - varsize (pages qty)
[
4b pageNo
1b isReused
Xb page (page size)
]
2b - timestamps size (not filled page)
Nb - timestamps payload
2b - values size (not filled page)
Nb - values payload
*/
type AppendedPages struct {
MetricID uint32
Timestamp uint32 // last measure
Value float64 // last measure
NewRootPageNo uint32
LastPageNo uint32
Pages []atree.PageToWrite
TimestampsChunks [][]byte
TimestampsSize int
ValuesChunks [][]byte
ValuesSize int
}
func (s *AppendedPages) Pack(w io.Writer) {
w.Write([]byte{
CodeAppendedPages,
})
bin.WriteUint32(w, s.MetricID)
bin.WriteUint32(w, s.Timestamp)
bin.WriteFloat64(w, s.Value)
bin.WriteUint32(w, s.LastPageNo)
bin.WriteUint32(w, s.NewRootPageNo)
//
bin.WriteVarSize(w, len(s.Pages))
for _, p := range s.Pages {
bin.WriteUint32(w, p.PageNo)
if p.IsReused {
w.Write([]byte{1})
} else {
w.Write([]byte{0})
}
w.Write(p.Data)
}
// timestamps
writeChunkedPayloadTo(s.TimestampsChunks, int(s.TimestampsSize), w)
// values
writeChunkedPayloadTo(s.ValuesChunks, int(s.ValuesSize), w)
}
func (s *AppendedPages) Parse(src io.Reader) (err error) {
s.MetricID, err = bin.ReadUint32(src)
if err != nil {
return
}
s.Timestamp, err = bin.ReadUint32(src)
if err != nil {
return
}
s.Value, err = bin.ReadFloat64(src)
if err != nil {
return
}
s.LastPageNo, err = bin.ReadUint32(src)
if err != nil {
return
}
s.NewRootPageNo, err = bin.ReadUint32(src)
if err != nil {
return
}
pagesQty, err := bin.ReadVarSize(src)
if err != nil {
return
}
for range pagesQty {
var page atree.PageToWrite
page.PageNo, err = bin.ReadUint32(src)
if err != nil {
return
}
page.IsReused, err = bin.ReadBool(src)
if err != nil {
return
}
page.Data, err = bin.ReadN(src, atree.PageSize)
if err != nil {
return
}
s.Pages = append(s.Pages, page)
}
s.TimestampsChunks, s.TimestampsSize, err = readChunkedPayload(src)
if err != nil {
return
}
s.ValuesChunks, s.ValuesSize, err = readChunkedPayload(src)
if err != nil {
return
}
return nil
}
func writeChunkedPayloadTo(chunks [][]byte, size int, w io.Writer) {
bin.WriteVarSize(w, size)
for _, chunk := range chunks {
if size >= len(chunk) {
w.Write(chunk)
size -= len(chunk)
} else {
w.Write(chunk[:size])
return
}
}
}
func readChunkedPayload(src io.Reader) (_ [][]byte, _ int, err error) {
var (
chunks [][]byte
size int
remainingSize = size
)
size, err = bin.ReadVarSize(src)
if err != nil {
return
}
for remainingSize > 0 {
var chunk []byte
if remainingSize >= conbuf.ChunkSize {
chunk, err = bin.ReadN(src, conbuf.ChunkSize)
if err != nil {
return
}
} else {
chunk = make([]byte, conbuf.ChunkSize)
err = bin.ReadNInto(src, chunk[:remainingSize])
if err != nil {
return
}
}
chunks = append(chunks, chunk)
remainingSize -= conbuf.ChunkSize
}
return chunks, size, nil
}
// fix - add changed pages
type DeletedMeasures struct {
MetricID uint32
FreePageNumbers []uint32
}
func (s DeletedMeasures) Pack(w io.Writer) {
arr := []byte{
CodeDeletedMeasures,
0, 0, 0, 0,
}
bin.PutUint32(arr[1:], s.MetricID)
w.Write(arr)
bin.WriteVarSize(w, len(s.FreePageNumbers))
for _, pageNo := range s.FreePageNumbers {
bin.WriteUint32(w, pageNo)
}
}
func (s *DeletedMeasures) Parse(src io.Reader) (err error) {
s.MetricID, err = bin.ReadUint32(src)
if err != nil {
return
}
// free pages
qty, err := bin.ReadVarSize(src)
if err != nil {
return
}
for range qty {
var pageNo uint32
pageNo, err = bin.ReadUint32(src)
if err != nil {
return
}
s.FreePageNumbers = append(s.FreePageNumbers, pageNo)
}
return nil
}

View File

@@ -9,9 +9,9 @@ import (
"path/filepath" "path/filepath"
"sync" "sync"
bin "gordenko.dev/dima/bin/little"
octopus "gordenko.dev/dima/qb" octopus "gordenko.dev/dima/qb"
"gordenko.dev/dima/qb/bin" "gordenko.dev/dima/qb/atree"
"gordenko.dev/dima/qb/proto"
) )
const ( const (
@@ -32,10 +32,15 @@ const (
CodeDeletedMetric byte = 2 CodeDeletedMetric byte = 2
CodeAppendedMeasure byte = 4 CodeAppendedMeasure byte = 4
CodeAppendedMeasures byte = 5 CodeAppendedMeasures byte = 5
CodeAppendedMeasureWithOverflow byte = 6 CodeAppendedPages byte = 6
CodeDeletedMeasures byte = 7 CodeDeletedMeasures byte = 7
) )
type FreeList interface {
// використовується в allocPage
ReservePage() uint32
}
func JoinChangesFileName(dir string, logNumber int) string { func JoinChangesFileName(dir string, logNumber int) string {
return filepath.Join(dir, fmt.Sprintf("%d.changes", logNumber)) return filepath.Join(dir, fmt.Sprintf("%d.changes", logNumber))
} }
@@ -50,10 +55,15 @@ type Changes struct {
type Writer struct { type Writer struct {
mutex sync.Mutex mutex sync.Mutex
freelist FreeList
atree *atree.Atree
logNumber int logNumber int
dir string dir string
dataFile *os.File
file *os.File file *os.File
buf *bytes.Buffer input []any
//buf *bytes.Buffer
pagesToWrite []atree.PageToWrite
workerReqs []any workerReqs []any
waitCh chan struct{} waitCh chan struct{}
appendToWorkerQueue func(any) appendToWorkerQueue func(any)
@@ -69,6 +79,8 @@ type WriterOptions struct {
Dir string Dir string
LogNumber int // номер журнала LogNumber int // номер журнала
AppendToWorkerQueue func(any) AppendToWorkerQueue func(any)
FreeList FreeList
Atree *atree.Atree
ExitCh chan struct{} ExitCh chan struct{}
WaitGroup *sync.WaitGroup WaitGroup *sync.WaitGroup
} }
@@ -80,6 +92,12 @@ func NewWriter(opt WriterOptions) (*Writer, error) {
if opt.AppendToWorkerQueue == nil { if opt.AppendToWorkerQueue == nil {
return nil, errors.New("AppendToWorkerQueue option is required") return nil, errors.New("AppendToWorkerQueue option is required")
} }
if opt.FreeList == nil {
return nil, errors.New("FreeList option is required")
}
if opt.Atree == nil {
return nil, errors.New("Atree option is required")
}
if opt.ExitCh == nil { if opt.ExitCh == nil {
return nil, errors.New("ExitCh option is required") return nil, errors.New("ExitCh option is required")
} }
@@ -89,12 +107,14 @@ func NewWriter(opt WriterOptions) (*Writer, error) {
s := &Writer{ s := &Writer{
dir: opt.Dir, dir: opt.Dir,
buf: bytes.NewBuffer(nil),
appendToWorkerQueue: opt.AppendToWorkerQueue, appendToWorkerQueue: opt.AppendToWorkerQueue,
freelist: opt.FreeList,
atree: opt.Atree,
logNumber: opt.LogNumber, logNumber: opt.LogNumber,
exitCh: opt.ExitCh, exitCh: opt.ExitCh,
waitGroup: opt.WaitGroup, waitGroup: opt.WaitGroup,
signalCh: make(chan struct{}, 1), signalCh: make(chan struct{}, 1),
waitCh: make(chan struct{}),
} }
var err error var err error
@@ -119,8 +139,6 @@ func NewWriter(opt WriterOptions) (*Writer, error) {
return nil, err return nil, err
} }
} }
s.reset()
return s, nil return s, nil
} }
@@ -128,7 +146,7 @@ func (s *Writer) Run() {
for { for {
select { select {
case <-s.signalCh: case <-s.signalCh:
if err := s.flush(); err != nil { if err := s.packAndWrite(); err != nil {
octopus.Abort(octopus.FailedWriteToTxLog, err) octopus.Abort(octopus.FailedWriteToTxLog, err)
} }
@@ -139,21 +157,67 @@ func (s *Writer) Run() {
} }
} }
func (s *Writer) reset() { func (s *Writer) packAndWrite() error {
s.buf.Reset() s.mutex.Lock()
s.buf.Write([]byte{ input := s.input
0, 0, 0, 0, // packet length waitCh := s.waitCh
0, 0, 0, 0, // crc32 s.input = nil
0, 0, 0, 0, // lsn
})
s.workerReqs = nil
s.waitCh = make(chan struct{}) s.waitCh = make(chan struct{})
s.mutex.Unlock()
w := bytes.NewBuffer(nil)
for _, untyped := range input {
switch x := untyped.(type) {
case AppendedPagesReq:
s.packWriteAppended(w, x)
case AddedMetric:
case DeletedMetric:
if len(x.FreePageNumbers) > 0 {
//s.freeList.AddPages(x.FreePageNumbers)
}
case AppendedMeasure:
case AppendedMeasures:
case DeletedMeasures:
//case DeletedMeasuresSince:
}
}
// flush
close(waitCh)
return s.flush(w)
} }
func (s *Writer) flush() error { func (s *Writer) Append(req any) chan struct{} {
s.mutex.Lock()
s.input = append(s.input, req)
s.workerReqs = append(s.workerReqs, req)
waitCh := s.waitCh
s.mutex.Unlock()
select {
case s.signalCh <- struct{}{}:
default:
}
return waitCh
}
// func (s *Writer) reset() {
// s.buf.Reset()
// s.buf.Write([]byte{
// 0, 0, 0, 0, // packet length
// 0, 0, 0, 0, // crc32
// 0, 0, 0, 0, // lsn
// })
// s.pagesToWrite = nil
// s.workerReqs = nil
// s.waitCh = make(chan struct{})
// }
func (s *Writer) flush(w *bytes.Buffer) error {
s.mutex.Lock() s.mutex.Lock()
pagesToWrite := s.pagesToWrite
workerReqs := s.workerReqs workerReqs := s.workerReqs
waitCh := s.waitCh waitCh := s.waitCh
isExited := s.isExited isExited := s.isExited
@@ -163,12 +227,12 @@ func (s *Writer) flush() error {
exitWaitGroup = s.waitGroup exitWaitGroup = s.waitGroup
} }
if s.buf.Len() > packetPrefixSize { if w.Len() > packetPrefixSize {
s.lsn++ s.lsn++
lsn := s.lsn lsn := s.lsn
packet := make([]byte, s.buf.Len()) packet := make([]byte, w.Len())
copy(packet, s.buf.Bytes()) copy(packet, w.Bytes())
s.reset() //s.reset() fix
s.written += int64(len(packet)) + 12 s.written += int64(len(packet)) + 12
s.mutex.Unlock() s.mutex.Unlock()
@@ -181,14 +245,17 @@ func (s *Writer) flush() error {
if err != nil { if err != nil {
return fmt.Errorf("TxLog write: %s", err) return fmt.Errorf("TxLog write: %s", err)
} }
if n != len(packet) { if n != len(packet) {
return fmt.Errorf("TxLog written %d != packet size %d", n, len(packet)) return fmt.Errorf("TxLog written %d != packet size %d", n, len(packet))
} }
if err := s.file.Sync(); err != nil { if err := s.file.Sync(); err != nil {
return fmt.Errorf("TxLog sync: %s", err) return fmt.Errorf("TxLog sync: %s", err)
} }
err = s.writePagesToAtree(pagesToWrite)
if err != nil {
return fmt.Errorf("TxLog writePagesToAtree: %s", err)
}
} else { } else {
s.waitCh = make(chan struct{}) s.waitCh = make(chan struct{})
s.mutex.Unlock() s.mutex.Unlock()
@@ -232,263 +299,48 @@ func (s *Writer) flush() error {
return nil return nil
} }
func (s *Writer) writePagesToAtree(pages []atree.PageToWrite) error {
for _, p := range pages {
if len(p.Data) != atree.PageSize {
return fmt.Errorf("wrong page %d size: %d",
p.PageNo, len(p.Data))
}
off := (p.PageNo - 1) * atree.PageSize
n, err := s.dataFile.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))
}
}
return nil
}
func (s *Writer) exit() { func (s *Writer) exit() {
s.mutex.Lock() s.mutex.Lock()
s.isExited = true s.isExited = true
s.mutex.Unlock() s.mutex.Unlock()
if err := s.flush(); err != nil { if err := s.packAndWrite(); err != nil {
octopus.Abort(octopus.FailedWriteToTxLog, err) octopus.Abort(octopus.FailedWriteToTxLog, err)
} }
} }
// API // API
type AddedMetric struct { // FIX - add
MetricID uint32 // type DeletedMeasuresSince struct {
MetricType octopus.MetricType // MetricID uint32
FracDigits int // LastPageNo uint32
} // IsRootChanged bool
// RootPageNo uint32
func (s *Writer) WriteAddedMetric(req AddedMetric) chan struct{} { // FreeDataPages []uint32
arr := []byte{ // FreeIndexPages []uint32
CodeAddedMetric, // TimestampsBuf []byte
0, 0, 0, 0, // // ValuesBuf []byte
byte(req.MetricType), // }
byte(req.FracDigits),
}
bin.PutUint32(arr[1:], req.MetricID)
// пишу в буфер
s.mutex.Lock()
s.buf.Write(arr)
s.workerReqs = append(s.workerReqs, req)
s.mutex.Unlock()
s.sendSignal()
return s.waitCh
}
type DeletedMetric struct {
MetricID uint32
FreeDataPages []uint32
FreeIndexPages []uint32
}
func (s *Writer) WriteDeletedMetric(req DeletedMetric) chan struct{} {
arr := []byte{
CodeDeletedMetric,
0, 0, 0, 0, // metricID
}
bin.PutUint32(arr[1:], req.MetricID)
// пишу в буфер
s.mutex.Lock()
defer s.mutex.Unlock()
s.buf.Write(arr)
s.packFreeDataAndIndexPages(req.FreeDataPages, req.FreeIndexPages)
s.workerReqs = append(s.workerReqs, req)
s.sendSignal()
return s.waitCh
}
type AppendedMeasure struct {
MetricID uint32
Timestamp uint32
Value float64
}
func (s *Writer) WriteAppendMeasure(req AppendedMeasure) chan struct{} {
arr := []byte{
CodeAppendedMeasure,
0, 0, 0, 0, // metricID
0, 0, 0, 0, // timestamp
0, 0, 0, 0, 0, 0, 0, 0, // value
}
bin.PutUint32(arr[1:], req.MetricID)
bin.PutUint32(arr[5:], req.Timestamp)
bin.PutFloat64(arr[9:], req.Value)
//
s.mutex.Lock()
s.buf.Write(arr)
s.workerReqs = append(s.workerReqs, req)
s.mutex.Unlock()
s.sendSignal()
return s.waitCh
}
type AppendedMeasures struct {
MetricID uint32
Measures []proto.Measure
}
type AppendedMeasuresExtended struct {
Record AppendedMeasures
HoldLock bool
}
func (s *Writer) WriteAppendMeasures(req AppendedMeasures, holdLock bool) chan struct{} {
arr := []byte{
CodeAppendedMeasures,
0, 0, 0, 0, // metricID
0, 0, // qty
}
bin.PutUint32(arr[1:], req.MetricID)
bin.PutUint16(arr[5:], uint16(len(req.Measures)))
//
s.mutex.Lock()
s.buf.Write(arr)
for _, measure := range req.Measures {
bin.WriteUint32(s.buf, measure.Timestamp)
bin.WriteFloat64(s.buf, measure.Value)
}
s.workerReqs = append(s.workerReqs, AppendedMeasuresExtended{
Record: req,
HoldLock: holdLock,
})
s.mutex.Unlock()
s.sendSignal()
return s.waitCh
}
type AppendedMeasureWithOverflow struct {
MetricID uint32
Timestamp uint32
Value float64
IsDataPageReused bool
DataPageNo uint32
IsRootChanged bool
RootPageNo uint32
ReusedIndexPages []uint32
}
type AppendedMeasureWithOverflowExtended struct {
Record AppendedMeasureWithOverflow
HoldLock bool
}
/*
Формат:
1b code
4b metricID
4b timestamp
8b value
1b isReusedDataPage
4b dataPageNo
1b isRootChanged
[4b] newRootPageNo
1b reusedIndexPages length
[N * 4b] reusedIndexPages
------------------
4b metricID
4b timestamp
8b value
4b newRootPageNo (0 if no new root page)
Nb - varsize (N pages)
[
4b pageNo
1b isReused
Xb page (page size)
]
Nb - varsize (last data page size)
Nb - payload
*/
func (s *Writer) WriteAppendedMeasureWithOverflow(req AppendedMeasureWithOverflow, holdLock bool) chan struct{} {
size := 24 + len(req.ReusedIndexPages)*4
if req.IsRootChanged {
size += 4
}
tmp := make([]byte, size)
tmp[0] = CodeAppendedMeasureWithOverflow
bin.PutUint32(tmp[1:], req.MetricID)
bin.PutUint32(tmp[5:], req.Timestamp)
bin.PutFloat64(tmp[9:], req.Value)
if req.IsDataPageReused {
tmp[17] = 1
}
bin.PutUint32(tmp[18:], req.DataPageNo)
pos := 22
if req.IsRootChanged {
tmp[pos] = 1
bin.PutUint32(tmp[pos+1:], req.RootPageNo)
pos += 5
} else {
tmp[pos] = 0
pos += 1
}
tmp[pos] = byte(len(req.ReusedIndexPages))
pos += 1
for _, indexPageNo := range req.ReusedIndexPages {
bin.PutUint32(tmp[pos:], indexPageNo)
pos += 4
}
s.mutex.Lock()
s.buf.Write(tmp)
s.workerReqs = append(s.workerReqs, AppendedMeasureWithOverflowExtended{
Record: req,
HoldLock: holdLock,
})
s.mutex.Unlock()
s.sendSignal()
return s.waitCh
}
type DeletedMeasures struct {
MetricID uint32
FreeDataPages []uint32
FreeIndexPages []uint32
}
/*
Формат:
1b code
4b metricID
1b freeDataPages length
[N * 4b] freeDataPages
1b freeIndexPages length
[N * 4b] freeIndexPages
*/
func (s *Writer) WriteDeletedMeasures(op DeletedMeasures) chan struct{} {
tmp := []byte{
CodeDeletedMeasures,
0, 0, 0, 0,
}
bin.PutUint32(tmp[1:], op.MetricID)
// записываю часть фиксированного размера
s.mutex.Lock()
s.buf.Write(tmp)
s.packFreeDataAndIndexPages(op.FreeDataPages, op.FreeIndexPages)
s.workerReqs = append(s.workerReqs, op)
s.mutex.Unlock()
s.sendSignal()
return s.waitCh
}
type DeletedMeasuresSince struct {
MetricID uint32
LastPageNo uint32
IsRootChanged bool
RootPageNo uint32
FreeDataPages []uint32
FreeIndexPages []uint32
TimestampsBuf []byte
ValuesBuf []byte
}
func (s *Writer) sendSignal() { func (s *Writer) sendSignal() {
select { select {
@@ -497,17 +349,30 @@ func (s *Writer) sendSignal() {
} }
} }
// helper // Якщо (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,
})
func (s *Writer) packFreeDataAndIndexPages(freeDataPages, freeIndexPages []uint32) { m := AppendedPages{
// записываю data pages MetricID: req.MetricID,
bin.WriteVarUint64(s.buf, uint64(len(freeDataPages))) Timestamp: req.Timestamp,
for _, dataPageNo := range freeDataPages { Value: req.Value,
bin.WriteUint32(s.buf, dataPageNo) NewRootPageNo: report.NewRootPageNo,
} LastPageNo: report.LastPageNo,
// записываю index pages Pages: report.Pages,
bin.WriteVarUint64(s.buf, uint64(len(freeIndexPages))) TimestampsChunks: req.TimestampsChunks,
for _, indexPageNo := range freeIndexPages { TimestampsSize: int(req.TimestampsSize),
bin.WriteUint32(s.buf, indexPageNo) ValuesChunks: req.ValuesChunks,
ValuesSize: int(req.ValuesSize),
} }
m.Pack(w)
} }
// helpers