99 lines
2.9 KiB
Go
99 lines
2.9 KiB
Go
package main
|
|
|
|
// **Each item's last good night, on the bus** (novox/hq ADR 0571 clause 3, ADR 0564 §6): beside good.json, the module's
|
|
// own state `good`, one key `<node>.<module>.<item>` for every item a module declares that a backup path covers, written
|
|
// when it changes, so the self-check's probe of a stale backup reads it there and no part reads the controller's records.
|
|
|
|
import (
|
|
"encoding/json"
|
|
"time"
|
|
|
|
stdio "git.novox.be/novox/mesh-sdk/go"
|
|
)
|
|
|
|
// GoodState is the state, GoodVersion the layout's version.
|
|
const (
|
|
GoodState = "good"
|
|
GoodVersion = 1
|
|
)
|
|
|
|
// GoodRow is one key: the item, the path whose snapshot keeps it, and the newest good night of its module (zero before
|
|
// the first). Moves to mesh-sdk's health package with the readers that share it.
|
|
type GoodRow struct {
|
|
V int `json:"v"`
|
|
Node string `json:"node"`
|
|
Module string `json:"module"`
|
|
Item string `json:"item"`
|
|
CoveredBy string `json:"covered_by"`
|
|
LastGood time.Time `json:"last_good,omitempty"`
|
|
}
|
|
|
|
// GoodKey is a row's key.
|
|
func GoodKey(node, module, item string) string { return node + "." + module + "." + item }
|
|
|
|
// GoodRows is the rows of this machine: every item a module declares that a path of the module's backup covers, with
|
|
// the module's newest good night.
|
|
func (b *Backups) GoodRows() (map[string]GoodRow, error) {
|
|
items, err := b.Items()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
declared, err := b.Declared()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
paths := map[string][]string{}
|
|
for _, d := range declared {
|
|
paths[d.Module] = d.Paths
|
|
}
|
|
good := b.Good()
|
|
out := map[string]GoodRow{}
|
|
for module, its := range items {
|
|
for _, it := range its {
|
|
if it.Class == "cache" || it.CoveredBy == "" {
|
|
continue
|
|
}
|
|
covered := false
|
|
for _, p := range paths[module] {
|
|
covered = covered || p == it.CoveredBy
|
|
}
|
|
if !covered {
|
|
continue
|
|
}
|
|
out[GoodKey(b.Node, module, it.ID)] = GoodRow{V: GoodVersion, Node: b.Node, Module: module, Item: it.ID,
|
|
CoveredBy: it.CoveredBy, LastGood: good[module].UTC()}
|
|
}
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
// syncGood writes the rows that differ from what it last wrote, once a minute while it runs (good.json is a file of
|
|
// the night's process, so a minute is how fast it can hear of a night); put is the state's write.
|
|
func syncGood(b *Backups, put func(key string, row GoodRow) error) {
|
|
time.Sleep(20 * time.Second) // the runtime serves first
|
|
wrote := map[string][]byte{}
|
|
for {
|
|
rows, err := b.GoodRows()
|
|
if err != nil {
|
|
b.Say("the last good nights could not be read: %v", err)
|
|
}
|
|
for k, r := range rows {
|
|
raw, _ := json.Marshal(r)
|
|
if string(wrote[k]) == string(raw) {
|
|
continue
|
|
}
|
|
if err := put(k, r); err != nil {
|
|
b.Say("the last good night of %s was not put: %v", k, err)
|
|
continue
|
|
}
|
|
wrote[k] = raw
|
|
}
|
|
time.Sleep(time.Minute)
|
|
}
|
|
}
|
|
|
|
func busGood(key string, row GoodRow) error {
|
|
_, err := stdio.State(GoodState).Put(key, row)
|
|
return err
|
|
}
|