This commit is contained in:
2026-06-10 06:18:45 +03:00
parent 4089ca40c9
commit 9ec86282ad
47 changed files with 3493 additions and 4502 deletions

View File

@@ -1,49 +1,12 @@
package atree
import (
"errors"
"fmt"
"os"
"path/filepath"
"sync"
"gordenko.dev/dima/qb/bin"
)
const (
PageTypeData = 1
PageTypeIndex = 2
filePerm = 0770
// common
crc32Idx = PageSize - 4
pageTypeIdx = PageSize - 5
// index page
indexCRC32Idx = IndexPageSize - 4
isLastLevelIdx = IndexPageSize - 5
indexRecordsQtyIdx = IndexPageSize - 7
indexRecordSize = 8
// data page
dataCRC32Idx = DataPageSize - 4
prevPageIdx = DataPageSize - 8
valuesSizeIdx = DataPageSize - 10
timestampsSizeIdx = DataPageSize - 12
timestampSize = 4
pairSize = timestampSize + PageNoSize
indexFooterIdx = indexRecordsQtyIdx
dataFooterIdx = timestampsSizeIdx
PageSize = 8192
DataPageSize = 8192
IndexPageSize = 1024
PageNoSize = 4
//
DataPagePayloadSize int = dataFooterIdx
)
type _page struct {
@@ -70,48 +33,49 @@ type Options struct {
}
func New(opt Options) (*Atree, error) {
if opt.Dir == "" {
return nil, errors.New("Dir option is required")
}
if opt.DatabaseName == "" {
return nil, errors.New("DatabaseName option is required")
}
// открываю или создаю dbName.data и dbName.index файлы
var (
fileName = filepath.Join(opt.Dir, opt.DatabaseName+".db")
file *os.File
allocatedPagesQty uint32
)
// if opt.Dir == "" {
// return nil, errors.New("Dir option is required")
// }
// if opt.DatabaseName == "" {
// return nil, errors.New("DatabaseName option is required")
// }
// // открываю или создаю dbName.data и dbName.index файлы
// var (
// fileName = filepath.Join(opt.Dir, opt.DatabaseName+".db")
// file *os.File
// allocatedPagesQty uint32
// )
// При создании data файла сразу создается индекс, поэтому корректное
// состояние БД: либо оба файла есть, либо ни одного файла нет.
isDataExist, err := isFileExist(fileName)
if err != nil {
return nil, fmt.Errorf("check data file is exist: %s", err)
}
if isDataExist {
file, allocatedPagesQty, err = openFile(fileName, PageSize)
if err != nil {
return nil, fmt.Errorf("open data file: %s", err)
}
} else {
// нет файла
file, err = os.OpenFile(fileName, os.O_CREATE|os.O_RDWR, filePerm)
if err != nil {
return nil, err
}
}
// // При создании data файла сразу создается индекс, поэтому корректное
// // состояние БД: либо оба файла есть, либо ни одного файла нет.
// isDataExist, err := isFileExist(fileName)
// if err != nil {
// return nil, fmt.Errorf("check data file is exist: %s", err)
// }
// if isDataExist {
// file, allocatedPagesQty, err = openFile(fileName, PageSize)
// if err != nil {
// return nil, fmt.Errorf("open data file: %s", err)
// }
// } else {
// // нет файла
// file, err = os.OpenFile(fileName, os.O_CREATE|os.O_RDWR, filePerm)
// if err != nil {
// return nil, err
// }
// }
tree := &Atree{
file: file,
allocatedPagesQty: allocatedPagesQty,
pages: make(map[uint32]*_page),
pageWaits: make(map[uint32][]chan readResult),
readSignalCh: make(chan struct{}, 1),
writeSignalCh: make(chan struct{}, 1),
}
// tree := &Atree{
// file: file,
// allocatedPagesQty: allocatedPagesQty,
// pages: make(map[uint32]*_page),
// pageWaits: make(map[uint32][]chan readResult),
// readSignalCh: make(chan struct{}, 1),
// writeSignalCh: make(chan struct{}, 1),
// }
return tree, nil
// return tree, nil
return nil, nil
}
func (s *Atree) Run() {
@@ -122,27 +86,28 @@ func (s *Atree) Run() {
// FIND
func (s *Atree) findDataPage(rootPageNo uint32, timestamp uint32) (uint32, []byte, error) {
indexPageNo := rootPageNo
for {
buf, err := s.fetchIndexPage(indexPageNo)
if err != nil {
return 0, nil, fmt.Errorf("fetchIndexPage(%d): %s", indexPageNo, err)
}
// indexPageNo := rootPageNo
// for {
// buf, err := s.fetchIndexPage(indexPageNo)
// if err != nil {
// return 0, nil, fmt.Errorf("fetchIndexPage(%d): %s", indexPageNo, err)
// }
foundPageNo := findPageNo(buf, timestamp)
s.releasePage(indexPageNo)
// foundPageNo := findPageNo(buf, timestamp)
// s.releasePage(indexPageNo)
// fix
if buf[isLastLevelIdx] == 1 {
buf, err := s.fetchDataPage(foundPageNo)
if err != nil {
return 0, nil, fmt.Errorf("fetchDataPage(%d): %s", foundPageNo, err)
}
return foundPageNo, buf, nil
}
// вглубь
indexPageNo = foundPageNo
}
// // fix
// if buf[isLastLevelIdx] == 1 {
// buf, err := s.fetchDataPage(foundPageNo)
// if err != nil {
// return 0, nil, fmt.Errorf("fetchDataPage(%d): %s", foundPageNo, err)
// }
// return foundPageNo, buf, nil
// }
// // вглубь
// indexPageNo = foundPageNo
// }
return 0, nil, nil
}
type PathLeg struct {
@@ -156,37 +121,38 @@ type PathToDataPage struct {
}
func (s *Atree) FindPathToLastPage(rootPageNo uint32) (_ PathToDataPage, err error) {
var (
pageNo = rootPageNo
legs []PathLeg
)
// var (
// pageNo = rootPageNo
// legs []PathLeg
// )
for {
var buf []byte
buf, err = s.fetchIndexPage(pageNo)
if err != nil {
err = fmt.Errorf("FetchIndexPage(%d): %s", pageNo, err)
return
}
// for {
// var buf []byte
// buf, err = s.fetchIndexPage(pageNo)
// if err != nil {
// err = fmt.Errorf("FetchIndexPage(%d): %s", pageNo, err)
// return
// }
legs = append(legs, PathLeg{
PageNo: pageNo,
Data: buf,
// childIdx не нужен
})
// legs = append(legs, PathLeg{
// PageNo: pageNo,
// Data: buf,
// // childIdx не нужен
// })
foundPageNo := getLastPageNo(buf)
// foundPageNo := getLastPageNo(buf)
// fix
if buf[isLastLevelIdx] == 1 {
return PathToDataPage{
Legs: legs,
LastPageNo: foundPageNo,
}, nil
}
// вглубь
pageNo = foundPageNo
}
// // fix
// if buf[isLastLevelIdx] == 1 {
// return PathToDataPage{
// Legs: legs,
// LastPageNo: foundPageNo,
// }, nil
// }
// // вглубь
// pageNo = foundPageNo
// }
return
}
// DELETE
@@ -199,72 +165,76 @@ type Level struct {
}
func (s *Atree) GetAllPages(rootPageNo uint32) (_ []uint32, err error) {
var (
pageNumbers []uint32
levels []*Level
)
// var (
// pageNumbers []uint32
// levels []*Level
// )
buf, err := s.fetchIndexPage(rootPageNo)
if err != nil {
return nil, fmt.Errorf("fetchIndexPage(%d): %s", rootPageNo, err)
}
pageNumbers = append(pageNumbers, rootPageNo)
// if buf[isDataPageNumbersIdx] == 1 {
// pageNumbers := listPageNumbers(buf)
// dataPages = append(dataPages, pageNumbers...)
// s.releasePage(rootPageNo)
// return PageLists{
// DataPages: dataPages,
// IndexPages: indexPages,
// }, nil
// buf, err := s.fetchIndexPage(rootPageNo)
// if err != nil {
// return nil, fmt.Errorf("fetchIndexPage(%d): %s", rootPageNo, err)
// }
// pageNumbers = append(pageNumbers, rootPageNo)
levels = append(levels, &Level{
PageNo: rootPageNo,
PageData: buf,
Idx: 0,
ChildQty: bin.GetUint16AsInt(buf[indexRecordsQtyIdx:]),
})
// // if buf[isDataPageNumbersIdx] == 1 {
// // pageNumbers := listPageNumbers(buf)
// // dataPages = append(dataPages, pageNumbers...)
for {
if len(levels) == 0 {
return pageNumbers, nil
}
// // s.releasePage(rootPageNo)
lastIdx := len(levels) - 1
level := levels[lastIdx]
// // return PageLists{
// // DataPages: dataPages,
// // IndexPages: indexPages,
// // }, nil
// // }
if level.Idx < level.ChildQty {
pageNo := getPageNo(level.PageData, level.Idx)
level.Idx++
// childQty, _ := bin.GetUint16(buf[indexRecordsQtyIdx:])
var buf []byte
buf, err = s.fetchPage(pageNo)
if err != nil {
return nil, fmt.Errorf("fetchPage(%d): %s", pageNo, err)
}
pageNumbers = append(pageNumbers, pageNo)
// levels = append(levels, &Level{
// PageNo: rootPageNo,
// PageData: buf,
// Idx: 0,
// ChildQty: int(childQty),
// })
if buf[pageTypeIdx] == PageTypeData {
//pageNumbers := listPageNumbers(buf)
//dataPages = append(dataPages, pageNumbers...)
s.releasePage(pageNo)
} else {
levels = append(levels, &Level{
PageNo: pageNo,
PageData: buf,
Idx: 0,
ChildQty: bin.GetUint16AsInt(buf[indexRecordsQtyIdx:]),
})
}
} else {
s.releasePage(level.PageNo)
levels = levels[:lastIdx]
}
}
// for {
// if len(levels) == 0 {
// return pageNumbers, nil
// }
// lastIdx := len(levels) - 1
// level := levels[lastIdx]
// if level.Idx < level.ChildQty {
// pageNo := getPageNo(level.PageData, level.Idx)
// level.Idx++
// var buf []byte
// buf, err = s.fetchPage(pageNo)
// if err != nil {
// return nil, fmt.Errorf("fetchPage(%d): %s", pageNo, err)
// }
// pageNumbers = append(pageNumbers, pageNo)
// if buf[pageTypeIdx] == PageTypeData {
// //pageNumbers := listPageNumbers(buf)
// //dataPages = append(dataPages, pageNumbers...)
// s.releasePage(pageNo)
// } else {
// childQty, _ = bin.GetUint16(buf[indexRecordsQtyIdx:])
// levels = append(levels, &Level{
// PageNo: pageNo,
// PageData: buf,
// Idx: 0,
// ChildQty: int(childQty),
// })
// }
// } else {
// s.releasePage(level.PageNo)
// levels = levels[:lastIdx]
// }
// }
return
}
// const (