package main import ( "encoding/json" "errors" "fmt" "log" "os" "time" ) type DiskStorage struct { stateFile string lastSave int64 LastSaves map[string]int64 } func NewDiskStorage(stateFile string) (*DiskStorage, error) { ret := &DiskStorage{ stateFile: stateFile, LastSaves: make(map[string]int64), } err := ret.loadState() return ret, err } func saveMessages(messages []StorableMessage, id string) 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) 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 { b, err := m.GetBytes() if err != nil { return err } file.Write(b) } return err } func getCartPath(id string) string { return fmt.Sprintf("data/%s.prot", id) } func loadMessages(grain Grain, id string) error { var err error path := getCartPath(id) 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() var reply CartGrain for err == nil { msg := &Message{} err = msg.FromReader(file, msg) if err == nil { grain.HandleMessage(msg, true, &reply) } } 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 = json.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("data/state.json") if err != nil { return err } defer file.Close() return json.NewDecoder(file).Decode(&s.LastSaves) } func (s *DiskStorage) Store(id string, grain Grain) 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 }