Add daemon-attached goal recovery operator
Resume goals after transient API failures with persisted exponential backoff while respecting pauses and interruptions.
This commit is contained in:
parent
a4522e5ed6
commit
e4b84d9802
1 changed files with 1595 additions and 0 deletions
78
codex-keep/store.go
Normal file
78
codex-keep/store.go
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
type store struct {
|
||||
path string
|
||||
lock *os.File
|
||||
Watches map[string]*watch `json:"watches"`
|
||||
}
|
||||
|
||||
func openStore(path string) (*store, error) {
|
||||
if err := os.MkdirAll(filepath.Dir(path), 0700); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
lock, err := os.OpenFile(path+".lock", os.O_CREATE|os.O_RDWR, 0600)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := syscall.Flock(int(lock.Fd()), syscall.LOCK_EX|syscall.LOCK_NB); err != nil {
|
||||
return nil, fmt.Errorf("checkpoint is already in use: %w", errors.Join(err, lock.Close()))
|
||||
}
|
||||
s := &store{path: path, lock: lock, Watches: make(map[string]*watch)}
|
||||
data, err := os.ReadFile(path)
|
||||
if errors.Is(err, os.ErrNotExist) {
|
||||
return s, nil
|
||||
}
|
||||
if err == nil {
|
||||
err = json.Unmarshal(data, s)
|
||||
}
|
||||
if err == nil && s.Watches == nil {
|
||||
err = errors.New("checkpoint has no watches")
|
||||
}
|
||||
if err != nil {
|
||||
return nil, errors.Join(err, s.close())
|
||||
}
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func (s *store) close() error { return s.lock.Close() }
|
||||
|
||||
func (s *store) save() error {
|
||||
data, err := json.Marshal(s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
f, err := os.CreateTemp(filepath.Dir(s.path), ".state-*")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() { _ = os.Remove(f.Name()) }()
|
||||
if _, err = f.Write(data); err == nil {
|
||||
err = f.Sync()
|
||||
}
|
||||
closeErr := f.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if closeErr != nil {
|
||||
return closeErr
|
||||
}
|
||||
return os.Rename(f.Name(), s.path)
|
||||
}
|
||||
|
||||
func (s *store) watch(id string) *watch {
|
||||
w := s.Watches[id]
|
||||
if w == nil {
|
||||
w = &watch{}
|
||||
s.Watches[id] = w
|
||||
}
|
||||
return w
|
||||
}
|
||||
Loading…
Reference in a new issue