Files
go-cart-actor/disk-storage.go
2024-11-08 23:00:28 +01:00

119 lines
2.2 KiB
Go

package main
import (
"encoding/gob"
"errors"
"fmt"
"log"
"os"
"time"
)
type DiskStorage struct {
stateFile string
lastSave int64
LastSaves map[CartId]int64
}
func NewDiskStorage(stateFile string) (*DiskStorage, error) {
ret := &DiskStorage{
stateFile: stateFile,
LastSaves: make(map[CartId]int64),
}
err := ret.loadState()
return ret, err
}
func saveMessages(messages []StorableMessage, id CartId) error {
log.Printf("%d messages to save for %s", len(messages), id)
if len(messages) == 0 {
return nil
}
var file *os.File
var err error
path := getCartPath(id.String())
file, err = os.OpenFile(path, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
return err
}
defer file.Close()
for _, m := range messages {
err := m.Write(file)
if err != nil {
return err
}
}
return err
}
func getCartPath(id string) string {
return fmt.Sprintf("data/%s.prot", id)
}
func loadMessages(grain Grain, id CartId) error {
var err error
path := getCartPath(id.String())
if _, err = os.Stat(path); errors.Is(err, os.ErrNotExist) {
return err
}
file, err := os.Open(path)
if err != nil {
return err
}
defer file.Close()
for err == nil {
var msg Message
err = MessageFromReader(file, &msg)
if err == nil {
grain.HandleMessage(&msg, true)
}
}
if err.Error() == "EOF" {
return nil
}
return err
}
func (s *DiskStorage) saveState() error {
tmpFile := s.stateFile + "_tmp"
file, err := os.Create(tmpFile)
if err != nil {
return err
}
defer file.Close()
err = gob.NewEncoder(file).Encode(s.LastSaves)
if err != nil {
return err
}
os.Remove(s.stateFile + ".bak")
os.Rename(s.stateFile, s.stateFile+".bak")
return os.Rename(tmpFile, s.stateFile)
}
func (s *DiskStorage) loadState() error {
file, err := os.Open(s.stateFile)
if err != nil {
return err
}
defer file.Close()
return gob.NewDecoder(file).Decode(&s.LastSaves)
}
func (s *DiskStorage) Store(id CartId, grain *CartGrain) error {
lastSavedMessage, ok := s.LastSaves[id]
if ok && lastSavedMessage > grain.GetLastChange() {
return nil
}
err := saveMessages(grain.GetStorageMessage(lastSavedMessage), id)
if err != nil {
return err
}
ts := time.Now().Unix()
s.LastSaves[id] = ts
s.lastSave = ts
return nil
}