Files
qb/atree/io.go

375 lines
7.7 KiB
Go
Raw Normal View History

2026-02-10 14:02:11 +00:00
package atree
import (
"errors"
"fmt"
"io/fs"
"math"
"os"
2026-02-23 11:02:29 +00:00
"gordenko.dev/dima/qb"
2026-02-10 14:02:11 +00:00
"gordenko.dev/dima/qb/bin"
2026-05-21 23:38:52 +03:00
"gordenko.dev/dima/qb/util"
2026-02-10 14:02:11 +00:00
)
2026-02-23 19:37:05 +00:00
// fix - додати ID, щоб потім перемістити із тимчасового буфера в pages, або звільнити
2026-02-10 14:02:11 +00:00
type AllocatedPage struct {
PageNo uint32
Data []byte
IsReused bool
}
type readResult struct {
Data []byte
Err error
}
// INDEX PAGES
2026-02-23 19:37:05 +00:00
func (s *Atree) DeletePages(pageNumbers []uint32) {
2026-02-10 14:02:11 +00:00
s.mutex.Lock()
for _, pageNo := range pageNumbers {
2026-02-23 19:37:05 +00:00
delete(s.pages, pageNo)
2026-02-10 14:02:11 +00:00
}
s.mutex.Unlock()
}
func (s *Atree) fetchIndexPage(pageNo uint32) ([]byte, error) {
2026-05-10 00:59:47 +00:00
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
2026-02-23 19:37:05 +00:00
}
func (s *Atree) fetchDataPage(pageNo uint32) ([]byte, error) {
2026-05-10 00:59:47 +00:00
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
2026-02-23 19:37:05 +00:00
}
2026-05-10 00:59:47 +00:00
func (s *Atree) fetchPage(pageNo uint32) ([]byte, error) {
2026-02-10 14:02:11 +00:00
s.mutex.Lock()
2026-02-23 19:37:05 +00:00
p, ok := s.pages[pageNo]
2026-02-10 14:02:11 +00:00
if ok {
p.ReferenceCount++
s.mutex.Unlock()
return p.Buf, nil
}
resultCh := make(chan readResult, 1)
2026-02-23 19:37:05 +00:00
s.pageWaits[pageNo] = append(s.pageWaits[pageNo], resultCh)
if len(s.pageWaits[pageNo]) == 1 {
s.pagesToRead = append(s.pagesToRead, pageNo)
2026-02-10 14:02:11 +00:00
s.mutex.Unlock()
select {
case s.readSignalCh <- struct{}{}:
default:
}
} else {
s.mutex.Unlock()
}
result := <-resultCh
if result.Err == nil {
2026-02-23 19:37:05 +00:00
result.Err = s.verifyCRC(result.Data, PageSize)
2026-02-10 14:02:11 +00:00
}
return result.Data, result.Err
}
2026-02-23 19:37:05 +00:00
func (s *Atree) releasePage(pageNo uint32) {
2026-02-10 14:02:11 +00:00
s.mutex.Lock()
defer s.mutex.Unlock()
2026-02-23 19:37:05 +00:00
p, ok := s.pages[pageNo]
2026-02-10 14:02:11 +00:00
if ok {
if p.ReferenceCount > 0 {
p.ReferenceCount--
return
} else {
2026-02-23 11:02:29 +00:00
qb.Abort(
qb.ReferenceCountBug,
2026-02-23 19:37:05 +00:00
fmt.Errorf("call releasePage on page %d with reference count = %d",
2026-02-10 14:02:11 +00:00
pageNo, p.ReferenceCount),
)
}
}
}
2026-05-10 00:59:47 +00:00
func (s *Atree) allocPageNumber() (uint32, bool) {
// fix
return 0, true
}
2026-02-23 19:37:05 +00:00
func (s *Atree) allocPage() AllocatedPage {
2026-02-10 14:02:11 +00:00
var (
allocated = AllocatedPage{
2026-02-23 19:37:05 +00:00
Data: make([]byte, PageSize),
2026-02-10 14:02:11 +00:00
}
)
2026-05-10 00:59:47 +00:00
//allocated.PageNo = s.freelist.ReservePage()
2026-02-23 11:02:29 +00:00
s.mutex.Lock()
2026-05-10 00:59:47 +00:00
// if allocated.PageNo > 0 {
// allocated.IsReused = true
// } else {
// if s.allocatedPagesQty == math.MaxUint32 {
// qb.Abort(qb.MaxAtreeSizeExceeded, errors.New("no space in Atree index"))
// }
// s.allocatedPagesQty++
// allocated.PageNo = s.allocatedPagesQty
// }
2026-02-10 14:02:11 +00:00
2026-02-23 19:37:05 +00:00
// s.pages[allocated.PageNo] = &_page{
// // fix pageType
// PageNo: allocated.PageNo,
// Buf: allocated.Data,
// ReferenceCount: 1,
// }
2026-02-10 14:02:11 +00:00
s.mutex.Unlock()
return allocated
}
2026-02-23 19:37:05 +00:00
// // fix - без freelist
// func (s *Atree) allocIndexPage() AllocatedPage {
// var (
// allocated = AllocatedPage{
// Data: make([]byte, PageSize),
// }
// )
// allocated.PageNo = s.freelist.ReservePage()
// s.mutex.Lock()
// if allocated.PageNo > 0 {
// allocated.IsReused = true
// } else {
// if s.allocatedPagesQty == math.MaxUint32 {
// qb.Abort(qb.MaxAtreeSizeExceeded, errors.New("no space in Atree index"))
// }
// s.allocatedPagesQty++
// allocated.PageNo = s.allocatedPagesQty
// }
// s.pages[allocated.PageNo] = &_page{
// // fix pageType
// PageNo: allocated.PageNo,
// Buf: allocated.Data,
// ReferenceCount: 1,
// }
// s.mutex.Unlock()
// return allocated
// }
// func (s *Atree) allocDataPage() AllocatedPage {
// var (
// allocated = AllocatedPage{
// Data: make([]byte, PageSize),
// }
// )
// allocated.PageNo = s.freelist.ReservePage()
// s.mutex.Lock()
// if allocated.PageNo > 0 {
// allocated.IsReused = true
// } else {
// if s.allocatedPagesQty == math.MaxUint32 {
// qb.Abort(qb.MaxAtreeSizeExceeded, errors.New("no space in Atree index"))
// }
// s.allocatedPagesQty++
// allocated.PageNo = s.allocatedPagesQty
// }
// s.pages[allocated.PageNo] = &_page{
// // fix pageType
// PageNo: allocated.PageNo,
// Buf: allocated.Data,
// ReferenceCount: 1,
// }
// s.mutex.Unlock()
// return allocated
// }
2026-02-10 14:02:11 +00:00
2026-02-23 19:37:05 +00:00
// DATA PAGES
2026-02-10 14:02:11 +00:00
// READ
func (s *Atree) pageReader() {
for {
select {
case <-s.readSignalCh:
s.readPages()
}
}
}
func (s *Atree) readPages() {
s.mutex.Lock()
2026-02-23 19:37:05 +00:00
if len(s.pagesToRead) == 0 {
2026-02-10 14:02:11 +00:00
s.mutex.Unlock()
return
}
2026-02-23 19:37:05 +00:00
pagesToRead := s.pagesToRead
s.pagesToRead = nil
2026-02-10 14:02:11 +00:00
s.mutex.Unlock()
2026-02-23 19:37:05 +00:00
for _, pageNo := range pagesToRead {
buf := make([]byte, PageSize)
off := (pageNo - 1) * PageSize
n, err := s.file.ReadAt(buf, int64(off))
if n != PageSize {
err = fmt.Errorf("read %d instead of %d", n, PageSize)
2026-02-10 14:02:11 +00:00
}
s.mutex.Lock()
2026-02-23 19:37:05 +00:00
resultChannels := s.pageWaits[pageNo]
delete(s.pageWaits, pageNo)
2026-02-10 14:02:11 +00:00
if err != nil {
s.mutex.Unlock()
for _, resultCh := range resultChannels {
resultCh <- readResult{
Err: err,
}
}
} else {
2026-02-23 19:37:05 +00:00
s.pages[pageNo] = &_page{
// fix - page type
2026-02-10 14:02:11 +00:00
PageNo: pageNo,
Buf: buf,
ReferenceCount: len(resultChannels),
}
s.mutex.Unlock()
for _, resultCh := range resultChannels {
resultCh <- readResult{
Data: buf,
}
}
}
}
}
// WRITE
2026-05-10 00:59:47 +00:00
// func (s *Atree) pageWriter() {
// for {
// select {
// case <-s.writeSignalCh:
// err := s.writeTasks()
// if err != nil {
// qb.Abort(qb.WriteToAtreeFailed, err)
// }
// }
// }
// }
2026-02-10 14:02:11 +00:00
2026-05-10 00:59:47 +00:00
// type WriteTask struct {
// WaitCh chan struct{}
// Pages []PageToWrite
// }
2026-02-10 14:02:11 +00:00
2026-05-10 00:59:47 +00:00
// func (s *Atree) appendWriteTaskToQueue(task WriteTask) {
// s.mutex.Lock()
// s.writeTasksQueue = append(s.writeTasksQueue, task)
// s.mutex.Unlock()
2026-02-10 14:02:11 +00:00
2026-05-10 00:59:47 +00:00
// select {
// case s.writeSignalCh <- struct{}{}:
// default:
// }
// }
2026-02-10 14:02:11 +00:00
2026-05-10 00:59:47 +00:00
// func (s *Atree) writeTasks() error {
// s.mutex.Lock()
// tasks := s.writeTasksQueue
// s.writeTasksQueue = nil
// s.mutex.Unlock()
2026-02-10 14:02:11 +00:00
2026-05-10 00:59:47 +00:00
// for _, task := range tasks {
// for _, p := range task.Pages {
// if len(p.Data) != PageSize {
// return fmt.Errorf("wrong page %d size: %d",
// p.PageNo, len(p.Data))
// }
// bin.PutUint32(p.Data[crc32Idx:], calcChecksum(p.Data[:crc32Idx]))
// off := (p.PageNo - 1) * PageSize
// n, err := s.file.WriteAt(p.Data, int64(off))
// if err != nil {
// return err
// }
// if n != len(p.Data) {
// return fmt.Errorf("write %d instead of %d", n, len(p.Data))
// }
// }
// close(task.WaitCh)
// }
// return nil
// }
2026-02-10 14:02:11 +00:00
// IO
func isFileExist(fileName string) (bool, error) {
_, err := os.Stat(fileName)
if err != nil {
if errors.Is(err, fs.ErrNotExist) {
return false, nil
} else {
return false, err
}
} else {
return true, nil
}
}
func openFile(fileName string, pageSize int) (_ *os.File, _ uint32, err error) {
file, err := os.OpenFile(fileName, os.O_RDWR, filePerm)
if err != nil {
return
}
fi, err := file.Stat()
if err != nil {
return
}
fileSize := fi.Size()
if (fileSize % int64(pageSize)) > 0 {
err = fmt.Errorf("the file size %d is not a multiple of the page size %d",
fileSize, pageSize)
return
}
allocatedPagesQty := fileSize / int64(pageSize)
if allocatedPagesQty > math.MaxUint32 {
err = fmt.Errorf("allocated pages %d is > max pages %d",
allocatedPagesQty, math.MaxUint32)
return
}
return file, uint32(allocatedPagesQty), nil
}
2026-05-10 00:59:47 +00:00
// func (s *Atree) ApplyREDO(task WriteTask) {
// s.appendWriteTaskToQueue(task)
// }
2026-02-10 14:02:11 +00:00
func (s *Atree) verifyCRC(data []byte, pageSize int) error {
var (
pos = pageSize - 4
2026-05-21 23:38:52 +03:00
calculatedCRC = util.CalcChecksum(data[:pos])
2026-02-10 14:02:11 +00:00
storedCRC = bin.GetUint32(data[pos:])
)
if calculatedCRC != storedCRC {
return fmt.Errorf("calculatedCRC %d not equal storedCRC %d",
calculatedCRC, storedCRC)
}
return nil
}