спеціалізована СУБД для зберігання та обробки показань датчиків та лічильників
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
diploma/atree/redo/writer.go

207 lines
4.4 KiB

package redo
import (
"errors"
"fmt"
"hash"
"hash/crc32"
"os"
"path/filepath"
"gordenko.dev/dima/diploma/bin"
)
const (
FlagReused byte = 1 // сторінка із FreeList
FlagNewRoot byte = 2 // новая страница
)
type PageToWrite struct {
PageNo uint32
Data []byte
}
type Writer struct {
metricID uint32
timestamp uint32
value float64
tmp []byte
fileName string
file *os.File
hasher hash.Hash32
isDataPageReused bool
dataPageNo uint32
isRootChanged bool
newRootPageNo uint32
indexPages []uint32
reusedIndexPages []uint32
indexPagesToWrite []PageToWrite
}
type WriterOptions struct {
Dir string
MetricID uint32
Value float64
Timestamp uint32
IsDataPageReused bool
DataPageNo uint32
Page []byte
}
// dataPage можно записати 1 раз. Щоб не заплутувати інтерфейс - передаю data сторінку
// через Options. Index сторінок може бути від 1 до N, тому виділяю окремий метод
func NewWriter(opt WriterOptions) (*Writer, error) {
if opt.Dir == "" {
return nil, errors.New("Dir option is required")
}
if opt.MetricID == 0 {
return nil, errors.New("MetricID option is required")
}
if opt.DataPageNo == 0 {
return nil, errors.New("DataPageNo option is required")
}
// if len(opt.Page) != octopus.DataPageSize {
// return nil, fmt.Errorf("bug: wrong data page size %d", len(opt.Page))
// }
s := &Writer{
fileName: JoinREDOFileName(opt.Dir, opt.MetricID),
metricID: opt.MetricID,
timestamp: opt.Timestamp,
value: opt.Value,
tmp: make([]byte, 21),
isDataPageReused: opt.IsDataPageReused,
dataPageNo: opt.DataPageNo,
hasher: crc32.NewIEEE(),
}
var err error
s.file, err = os.OpenFile(s.fileName, os.O_CREATE|os.O_WRONLY, 0770)
if err != nil {
return nil, err
}
err = s.init(opt.Page)
if err != nil {
return nil, err
}
return s, nil
}
/*
Формат:
4b metricID
8b value
4b timestamp
1b flags (reused)
4b dataPageNo
8KB dataPage
*/
func (s *Writer) init(dataPage []byte) error {
bin.PutUint32(s.tmp[0:], s.metricID)
bin.PutUint32(s.tmp[4:], s.timestamp)
bin.PutFloat64(s.tmp[8:], s.value)
if s.isDataPageReused {
s.tmp[16] = 1
}
bin.PutUint32(s.tmp[17:], s.dataPageNo)
_, err := s.file.Write(s.tmp)
if err != nil {
return err
}
_, err = s.file.Write(dataPage)
if err != nil {
return err
}
s.hasher.Write(s.tmp)
s.hasher.Write(dataPage)
return nil
}
/*
Формат
1b index page flags
4b indexPageNo
Nb indexPage
*/
func (s *Writer) AppendIndexPage(indexPageNo uint32, indexPage []byte, flags byte) error {
s.tmp[0] = flags
bin.PutUint32(s.tmp[1:], indexPageNo)
_, err := s.file.Write(s.tmp[:5])
if err != nil {
return err
}
_, err = s.file.Write(indexPage)
if err != nil {
return err
}
s.hasher.Write(s.tmp[:5])
s.hasher.Write(indexPage)
s.indexPages = append(s.indexPages, indexPageNo)
if (flags & FlagReused) == FlagReused {
s.reusedIndexPages = append(s.reusedIndexPages, indexPageNo)
}
if (flags & FlagNewRoot) == FlagNewRoot {
s.newRootPageNo = indexPageNo
s.isRootChanged = true
}
s.indexPagesToWrite = append(s.indexPagesToWrite,
PageToWrite{
PageNo: indexPageNo,
Data: indexPage,
})
return nil
}
func (s *Writer) IndexPagesToWrite() []PageToWrite {
return s.indexPagesToWrite
}
func (s *Writer) Close() (err error) {
// финализирую запись
bin.PutUint32(s.tmp, s.hasher.Sum32())
_, err = s.file.Write(s.tmp[:4])
if err != nil {
return err
}
err = s.file.Sync()
if err != nil {
return
}
return s.file.Close()
}
type Report struct {
FileName string
IsDataPageReused bool
DataPageNo uint32
IsRootChanged bool
NewRootPageNo uint32
ReusedIndexPages []uint32
}
func (s *Writer) GetReport() Report {
return Report{
FileName: s.fileName,
IsDataPageReused: s.isDataPageReused,
DataPageNo: s.dataPageNo,
//IndexPages: s.indexPages,
IsRootChanged: s.isRootChanged,
NewRootPageNo: s.newRootPageNo,
ReusedIndexPages: s.reusedIndexPages,
}
}
// HELPERS
func JoinREDOFileName(dir string, metricID uint32) string {
return filepath.Join(dir, fmt.Sprintf("m%d.redo", metricID))
}