wp
This commit is contained in:
304
atree/io.go
304
atree/io.go
@@ -3,16 +3,15 @@ package atree
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"hash/crc32"
|
||||
"io/fs"
|
||||
"math"
|
||||
"os"
|
||||
|
||||
"gordenko.dev/dima/qb"
|
||||
"gordenko.dev/dima/qb/atree/redo"
|
||||
"gordenko.dev/dima/qb/bin"
|
||||
)
|
||||
|
||||
// fix - додати ID, щоб потім перемістити із тимчасового буфера в pages, або звільнити
|
||||
type AllocatedPage struct {
|
||||
PageNo uint32
|
||||
Data []byte
|
||||
@@ -26,114 +25,38 @@ type readResult struct {
|
||||
|
||||
// INDEX PAGES
|
||||
|
||||
func (s *Atree) DeleteIndexPages(pageNumbers []uint32) {
|
||||
func (s *Atree) DeletePages(pageNumbers []uint32) {
|
||||
s.mutex.Lock()
|
||||
for _, pageNo := range pageNumbers {
|
||||
delete(s.indexPages, pageNo)
|
||||
delete(s.pages, pageNo)
|
||||
}
|
||||
s.mutex.Unlock()
|
||||
}
|
||||
|
||||
func (s *Atree) fetchIndexPage(pageNo uint32) ([]byte, error) {
|
||||
s.mutex.Lock()
|
||||
p, ok := s.indexPages[pageNo]
|
||||
if ok {
|
||||
p.ReferenceCount++
|
||||
s.mutex.Unlock()
|
||||
return p.Buf, nil
|
||||
}
|
||||
|
||||
resultCh := make(chan readResult, 1)
|
||||
s.indexWaits[pageNo] = append(s.indexWaits[pageNo], resultCh)
|
||||
if len(s.indexWaits[pageNo]) == 1 {
|
||||
s.indexPagesToRead = append(s.indexPagesToRead, pageNo)
|
||||
s.mutex.Unlock()
|
||||
|
||||
select {
|
||||
case s.readSignalCh <- struct{}{}:
|
||||
default:
|
||||
}
|
||||
} else {
|
||||
s.mutex.Unlock()
|
||||
}
|
||||
|
||||
result := <-resultCh
|
||||
if result.Err == nil {
|
||||
result.Err = s.verifyCRC(result.Data, IndexPageSize)
|
||||
}
|
||||
return result.Data, result.Err
|
||||
}
|
||||
|
||||
func (s *Atree) releaseIndexPage(pageNo uint32) {
|
||||
s.mutex.Lock()
|
||||
defer s.mutex.Unlock()
|
||||
|
||||
p, ok := s.indexPages[pageNo]
|
||||
if ok {
|
||||
if p.ReferenceCount > 0 {
|
||||
p.ReferenceCount--
|
||||
return
|
||||
} else {
|
||||
qb.Abort(
|
||||
qb.ReferenceCountBug,
|
||||
fmt.Errorf("call releaseIndexPage on page %d with reference count = %d",
|
||||
pageNo, p.ReferenceCount),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Atree) allocIndexPage() AllocatedPage {
|
||||
var (
|
||||
allocated = AllocatedPage{
|
||||
Data: make([]byte, IndexPageSize),
|
||||
}
|
||||
)
|
||||
|
||||
allocated.PageNo = s.indexFreelist.ReservePage()
|
||||
s.mutex.Lock()
|
||||
if allocated.PageNo > 0 {
|
||||
allocated.IsReused = true
|
||||
} else {
|
||||
if s.allocatedIndexPagesQty == math.MaxUint32 {
|
||||
qb.Abort(qb.MaxAtreeSizeExceeded, errors.New("no space in Atree index"))
|
||||
}
|
||||
s.allocatedIndexPagesQty++
|
||||
allocated.PageNo = s.allocatedIndexPagesQty
|
||||
}
|
||||
|
||||
s.indexPages[allocated.PageNo] = &_page{
|
||||
PageNo: allocated.PageNo,
|
||||
Buf: allocated.Data,
|
||||
ReferenceCount: 1,
|
||||
}
|
||||
s.mutex.Unlock()
|
||||
return allocated
|
||||
}
|
||||
|
||||
// DATA PAGES
|
||||
|
||||
func (s *Atree) DeleteDataPages(pageNumbers []uint32) {
|
||||
s.mutex.Lock()
|
||||
for _, pageNo := range pageNumbers {
|
||||
delete(s.dataPages, pageNo)
|
||||
}
|
||||
s.mutex.Unlock()
|
||||
return s.fetchPage(pageNo, PageTypeIndex)
|
||||
}
|
||||
|
||||
func (s *Atree) fetchDataPage(pageNo uint32) ([]byte, error) {
|
||||
return s.fetchPage(pageNo, PageTypeData)
|
||||
}
|
||||
|
||||
func (s *Atree) fetchPage(pageNo uint32, pageTypeIdx byte) ([]byte, error) {
|
||||
s.mutex.Lock()
|
||||
p, ok := s.dataPages[pageNo]
|
||||
p, ok := s.pages[pageNo]
|
||||
if ok {
|
||||
if p.Buf[pageTypeIdx] != pageTypeIdx {
|
||||
return nil, fmt.Errorf("wrong pageType %d instead of %d", p.Buf[pageTypeIdx], pageTypeIdx)
|
||||
}
|
||||
p.ReferenceCount++
|
||||
s.mutex.Unlock()
|
||||
return p.Buf, nil
|
||||
}
|
||||
|
||||
resultCh := make(chan readResult, 1)
|
||||
s.dataWaits[pageNo] = append(s.dataWaits[pageNo], resultCh)
|
||||
if len(s.dataWaits[pageNo]) == 1 {
|
||||
s.dataPagesToRead = append(s.dataPagesToRead, pageNo)
|
||||
s.pageWaits[pageNo] = append(s.pageWaits[pageNo], resultCh)
|
||||
if len(s.pageWaits[pageNo]) == 1 {
|
||||
s.pagesToRead = append(s.pagesToRead, pageNo)
|
||||
s.mutex.Unlock()
|
||||
|
||||
select {
|
||||
@@ -143,18 +66,19 @@ func (s *Atree) fetchDataPage(pageNo uint32) ([]byte, error) {
|
||||
} else {
|
||||
s.mutex.Unlock()
|
||||
}
|
||||
|
||||
result := <-resultCh
|
||||
if result.Err == nil {
|
||||
result.Err = s.verifyCRC(result.Data, DataPageSize)
|
||||
result.Err = s.verifyCRC(result.Data, PageSize)
|
||||
}
|
||||
return result.Data, result.Err
|
||||
}
|
||||
|
||||
func (s *Atree) releaseDataPage(pageNo uint32) {
|
||||
func (s *Atree) releasePage(pageNo uint32) {
|
||||
s.mutex.Lock()
|
||||
defer s.mutex.Unlock()
|
||||
|
||||
p, ok := s.dataPages[pageNo]
|
||||
p, ok := s.pages[pageNo]
|
||||
if ok {
|
||||
if p.ReferenceCount > 0 {
|
||||
p.ReferenceCount--
|
||||
@@ -162,43 +86,102 @@ func (s *Atree) releaseDataPage(pageNo uint32) {
|
||||
} else {
|
||||
qb.Abort(
|
||||
qb.ReferenceCountBug,
|
||||
fmt.Errorf("call releaseDataPage on page %d with reference count = %d",
|
||||
fmt.Errorf("call releasePage on page %d with reference count = %d",
|
||||
pageNo, p.ReferenceCount),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Atree) allocDataPage() AllocatedPage {
|
||||
func (s *Atree) allocPage() AllocatedPage {
|
||||
var (
|
||||
allocated = AllocatedPage{
|
||||
Data: make([]byte, DataPageSize),
|
||||
Data: make([]byte, PageSize),
|
||||
}
|
||||
)
|
||||
|
||||
allocated.PageNo = s.dataFreelist.ReservePage()
|
||||
allocated.PageNo = s.freelist.ReservePage()
|
||||
s.mutex.Lock()
|
||||
if allocated.PageNo > 0 {
|
||||
allocated.IsReused = true
|
||||
s.mutex.Lock()
|
||||
} else {
|
||||
s.mutex.Lock()
|
||||
if s.allocatedDataPagesQty == math.MaxUint32 {
|
||||
qb.Abort(qb.MaxAtreeSizeExceeded,
|
||||
errors.New("no space in Atree index"))
|
||||
if s.allocatedPagesQty == math.MaxUint32 {
|
||||
qb.Abort(qb.MaxAtreeSizeExceeded, errors.New("no space in Atree index"))
|
||||
}
|
||||
s.allocatedDataPagesQty++
|
||||
allocated.PageNo = s.allocatedDataPagesQty
|
||||
s.allocatedPagesQty++
|
||||
allocated.PageNo = s.allocatedPagesQty
|
||||
}
|
||||
|
||||
s.dataPages[allocated.PageNo] = &_page{
|
||||
PageNo: allocated.PageNo,
|
||||
Buf: allocated.Data,
|
||||
ReferenceCount: 1,
|
||||
}
|
||||
// s.pages[allocated.PageNo] = &_page{
|
||||
// // fix pageType
|
||||
// PageNo: allocated.PageNo,
|
||||
// Buf: allocated.Data,
|
||||
// ReferenceCount: 1,
|
||||
// }
|
||||
s.mutex.Unlock()
|
||||
return allocated
|
||||
}
|
||||
|
||||
// // fix - без freelist
|
||||
// func (s *Atree) allocIndexPage() AllocatedPage {
|
||||
// var (
|
||||
// allocated = AllocatedPage{
|
||||
// Data: make([]byte, PageSize),
|
||||
// }
|
||||
// )
|
||||
|
||||
// allocated.PageNo = s.freelist.ReservePage()
|
||||
// s.mutex.Lock()
|
||||
// if allocated.PageNo > 0 {
|
||||
// allocated.IsReused = true
|
||||
// } else {
|
||||
// if s.allocatedPagesQty == math.MaxUint32 {
|
||||
// qb.Abort(qb.MaxAtreeSizeExceeded, errors.New("no space in Atree index"))
|
||||
// }
|
||||
// s.allocatedPagesQty++
|
||||
// allocated.PageNo = s.allocatedPagesQty
|
||||
// }
|
||||
|
||||
// s.pages[allocated.PageNo] = &_page{
|
||||
// // fix pageType
|
||||
// PageNo: allocated.PageNo,
|
||||
// Buf: allocated.Data,
|
||||
// ReferenceCount: 1,
|
||||
// }
|
||||
// s.mutex.Unlock()
|
||||
// return allocated
|
||||
// }
|
||||
|
||||
// func (s *Atree) allocDataPage() AllocatedPage {
|
||||
// var (
|
||||
// allocated = AllocatedPage{
|
||||
// Data: make([]byte, PageSize),
|
||||
// }
|
||||
// )
|
||||
|
||||
// allocated.PageNo = s.freelist.ReservePage()
|
||||
// s.mutex.Lock()
|
||||
// if allocated.PageNo > 0 {
|
||||
// allocated.IsReused = true
|
||||
// } else {
|
||||
// if s.allocatedPagesQty == math.MaxUint32 {
|
||||
// qb.Abort(qb.MaxAtreeSizeExceeded, errors.New("no space in Atree index"))
|
||||
// }
|
||||
// s.allocatedPagesQty++
|
||||
// allocated.PageNo = s.allocatedPagesQty
|
||||
// }
|
||||
|
||||
// s.pages[allocated.PageNo] = &_page{
|
||||
// // fix pageType
|
||||
// PageNo: allocated.PageNo,
|
||||
// Buf: allocated.Data,
|
||||
// ReferenceCount: 1,
|
||||
// }
|
||||
// s.mutex.Unlock()
|
||||
// return allocated
|
||||
// }
|
||||
|
||||
// DATA PAGES
|
||||
|
||||
// READ
|
||||
|
||||
func (s *Atree) pageReader() {
|
||||
@@ -212,27 +195,25 @@ func (s *Atree) pageReader() {
|
||||
|
||||
func (s *Atree) readPages() {
|
||||
s.mutex.Lock()
|
||||
if len(s.indexPagesToRead) == 0 && len(s.dataPagesToRead) == 0 {
|
||||
if len(s.pagesToRead) == 0 {
|
||||
s.mutex.Unlock()
|
||||
return
|
||||
}
|
||||
indexPagesToRead := s.indexPagesToRead
|
||||
s.indexPagesToRead = nil
|
||||
dataPagesToRead := s.dataPagesToRead
|
||||
s.dataPagesToRead = nil
|
||||
pagesToRead := s.pagesToRead
|
||||
s.pagesToRead = nil
|
||||
s.mutex.Unlock()
|
||||
|
||||
for _, pageNo := range dataPagesToRead {
|
||||
buf := make([]byte, DataPageSize)
|
||||
off := (pageNo - 1) * DataPageSize
|
||||
n, err := s.dataFile.ReadAt(buf, int64(off))
|
||||
if n != DataPageSize {
|
||||
err = fmt.Errorf("read %d instead of %d", n, DataPageSize)
|
||||
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)
|
||||
}
|
||||
|
||||
s.mutex.Lock()
|
||||
resultChannels := s.dataWaits[pageNo]
|
||||
delete(s.dataWaits, pageNo)
|
||||
resultChannels := s.pageWaits[pageNo]
|
||||
delete(s.pageWaits, pageNo)
|
||||
|
||||
if err != nil {
|
||||
s.mutex.Unlock()
|
||||
@@ -242,7 +223,8 @@ func (s *Atree) readPages() {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
s.dataPages[pageNo] = &_page{
|
||||
s.pages[pageNo] = &_page{
|
||||
// fix - page type
|
||||
PageNo: pageNo,
|
||||
Buf: buf,
|
||||
ReferenceCount: len(resultChannels),
|
||||
@@ -255,41 +237,6 @@ func (s *Atree) readPages() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, pageNo := range indexPagesToRead {
|
||||
buf := make([]byte, IndexPageSize)
|
||||
off := (pageNo - 1) * IndexPageSize
|
||||
n, err := s.indexFile.ReadAt(buf, int64(off))
|
||||
if n != IndexPageSize {
|
||||
err = fmt.Errorf("read %d instead of %d", n, IndexPageSize)
|
||||
}
|
||||
|
||||
s.mutex.Lock()
|
||||
resultChannels := s.indexWaits[pageNo]
|
||||
delete(s.indexWaits, pageNo)
|
||||
|
||||
if err != nil {
|
||||
s.mutex.Unlock()
|
||||
for _, resultCh := range resultChannels {
|
||||
resultCh <- readResult{
|
||||
Err: err,
|
||||
}
|
||||
}
|
||||
} else {
|
||||
s.indexPages[pageNo] = &_page{
|
||||
PageNo: pageNo,
|
||||
Buf: buf,
|
||||
ReferenceCount: len(resultChannels),
|
||||
}
|
||||
s.mutex.Unlock()
|
||||
for _, resultCh := range resultChannels {
|
||||
resultCh <- readResult{
|
||||
Data: buf,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// WRITE
|
||||
@@ -307,9 +254,8 @@ func (s *Atree) pageWriter() {
|
||||
}
|
||||
|
||||
type WriteTask struct {
|
||||
WaitCh chan struct{}
|
||||
DataPage redo.PageToWrite
|
||||
IndexPages []redo.PageToWrite
|
||||
WaitCh chan struct{}
|
||||
Pages []PageToWrite
|
||||
}
|
||||
|
||||
func (s *Atree) appendWriteTaskToQueue(task WriteTask) {
|
||||
@@ -330,31 +276,15 @@ func (s *Atree) writeTasks() error {
|
||||
s.mutex.Unlock()
|
||||
|
||||
for _, task := range tasks {
|
||||
// data page
|
||||
p := task.DataPage
|
||||
if len(p.Data) != DataPageSize {
|
||||
return fmt.Errorf("wrong data page %d size: %d",
|
||||
p.PageNo, len(p.Data))
|
||||
}
|
||||
off := (p.PageNo - 1) * DataPageSize
|
||||
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))
|
||||
}
|
||||
|
||||
// index pages
|
||||
for _, p := range task.IndexPages {
|
||||
if len(p.Data) != IndexPageSize {
|
||||
return fmt.Errorf("wrong index page %d size: %d",
|
||||
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[indexCRC32Idx:], crc32.ChecksumIEEE(p.Data[:indexCRC32Idx]))
|
||||
bin.PutUint32(p.Data[crc32Idx:], calcChecksum(p.Data[:crc32Idx]))
|
||||
|
||||
off := (p.PageNo - 1) * IndexPageSize
|
||||
n, err := s.indexFile.WriteAt(p.Data, int64(off))
|
||||
off := (p.PageNo - 1) * PageSize
|
||||
n, err := s.file.WriteAt(p.Data, int64(off))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -416,7 +346,7 @@ func (s *Atree) ApplyREDO(task WriteTask) {
|
||||
func (s *Atree) verifyCRC(data []byte, pageSize int) error {
|
||||
var (
|
||||
pos = pageSize - 4
|
||||
calculatedCRC = crc32.ChecksumIEEE(data[:pos])
|
||||
calculatedCRC = calcChecksum(data[:pos])
|
||||
storedCRC = bin.GetUint32(data[pos:])
|
||||
)
|
||||
if calculatedCRC != storedCRC {
|
||||
|
||||
Reference in New Issue
Block a user