Files
qb/freelist/freelist.go

157 lines
3.5 KiB
Go
Raw Normal View History

2026-02-10 14:02:11 +00:00
package freelist
import (
2026-02-23 11:02:29 +00:00
"bytes"
2026-02-10 14:02:11 +00:00
"fmt"
2026-02-23 11:02:29 +00:00
"os"
"slices"
2026-02-10 14:02:11 +00:00
"sync"
2026-02-23 11:02:29 +00:00
"gordenko.dev/dima/qb/bin"
)
const (
pageSize = 1024
crcSize = 4
ptrSize = 4
pointersOnPage = (pageSize - crcSize) / ptrSize
flushTreshold = 1000 //= int(float(pageSize/ptrSize) * 1.3) // кількість вільних сторінок коли вже треба писати на диск
2026-02-10 14:02:11 +00:00
)
type FreeList struct {
2026-02-23 11:02:29 +00:00
mutex sync.Mutex
file *os.File
pages int
//free *roaring.Bitmap
//reserved *roaring.Bitmap
free []uint32
reserved []uint32
2026-02-10 14:02:11 +00:00
}
func New() *FreeList {
return &FreeList{
2026-02-23 11:02:29 +00:00
//free: roaring.New(),
//reserved: roaring.New(),
2026-02-10 14:02:11 +00:00
}
}
func (s *FreeList) Restore(serialized []byte) error {
2026-02-23 11:02:29 +00:00
if (len(serialized) % ptrSize) != 0 {
return fmt.Errorf("wrong size")
}
for i := 0; i < len(serialized); i += ptrSize {
s.free = append(s.free, bin.GetUint32(serialized[i:]))
2026-02-10 14:02:11 +00:00
}
return nil
}
func (s *FreeList) AddPages(pageNumbers []uint32) {
if len(pageNumbers) == 0 {
return
}
s.mutex.Lock()
2026-02-23 11:02:29 +00:00
s.free = append(s.free, pageNumbers...)
if len(s.free) > flushTreshold {
err := s.save()
if err != nil {
// ABORT
}
}
2026-02-10 14:02:11 +00:00
s.mutex.Unlock()
}
2026-02-23 11:02:29 +00:00
func (s *FreeList) save() error {
buf := make([]byte, pageSize)
//w := bytes.NewBuffer(nil)
//s.mutex.Lock()
i := crcSize
for _, pageNo := range s.free[:pointersOnPage] {
bin.PutUint32(buf[i:], pageNo)
i += ptrSize
}
// fix checksum
fileOffset := int64(s.pages-1) * pageSize
n, err := s.file.WriteAt(buf, fileOffset)
if err != nil {
return err
}
if n != pageSize {
return fmt.Errorf("written size %d bytes not equal page size %d", n, pageSize)
}
s.free = s.free[pointersOnPage:]
s.pages++
return nil
}
func (s *FreeList) loadLastPage() error {
var (
buf = make([]byte, pageSize)
offset = int64(s.pages-1) * pageSize
)
n, err := s.file.ReadAt(buf, offset)
if err != nil {
return fmt.Errorf("file.Seek: %s", err)
}
if n != s.pages {
return fmt.Errorf("read size %d bytes not equal page size %d", n, pageSize)
}
// check crc
//var pageNumbers []uint32
for i := crcSize; i < len(buf); i += ptrSize {
s.free = append(s.free, bin.GetUint32(buf[i:]))
}
s.pages--
return nil
}
2026-02-10 14:02:11 +00:00
// ReserveDataPage - аллокатор резервирует страницу, но не удаляет до визова
// DeleteFromFree, ибо транзакция может не завершится, а между віделением страници
// и падением транзакции - будет создан init файл.
func (s *FreeList) ReservePage() (pageNo uint32) {
s.mutex.Lock()
defer s.mutex.Unlock()
2026-02-23 11:02:29 +00:00
if len(s.free) == 0 {
if s.pages == 0 {
return
}
if err := s.loadLastPage(); err != nil {
// ABORT
return
}
2026-02-10 14:02:11 +00:00
}
2026-02-23 11:02:29 +00:00
lastIdx := len(s.free)
pageNo = s.free[lastIdx]
s.free = s.free[:lastIdx]
s.reserved = append(s.reserved, pageNo)
2026-02-10 14:02:11 +00:00
return
}
// Удаляет ранее зарезервированные страницы
2026-02-23 11:02:29 +00:00
func (s *FreeList) DeleteReservedPages(pageNumbers []uint32) {
var cleared []uint32
2026-02-10 14:02:11 +00:00
s.mutex.Lock()
2026-02-23 11:02:29 +00:00
for _, reservedPageNo := range s.reserved {
if slices.Contains(pageNumbers, reservedPageNo) {
// delete
} else {
cleared = append(cleared, reservedPageNo)
}
2026-02-10 14:02:11 +00:00
}
2026-02-23 11:02:29 +00:00
s.reserved = cleared
2026-02-10 14:02:11 +00:00
s.mutex.Unlock()
}
func (s *FreeList) Serialize() ([]byte, error) {
2026-02-23 11:02:29 +00:00
w := bytes.NewBuffer(nil)
2026-02-10 14:02:11 +00:00
s.mutex.Lock()
2026-02-23 11:02:29 +00:00
for _, pageNo := range s.free {
bin.WriteUint32(w, pageNo)
}
for _, pageNo := range s.reserved {
bin.WriteUint32(w, pageNo)
}
s.mutex.Unlock()
return w.Bytes(), nil
2026-02-10 14:02:11 +00:00
}