package delivery import ( "encoding/json" "fmt" "time" ) // WatchmanModule is the watchman's module: its buckets are its own state, `_`, and the controller's // broker grants it alone the bus's connect and disconnect events (novox/hq ADR 0431). const WatchmanModule = "mesh-delivery-watchman" // StuckBucket holds what stopped moving (novox/hq ADR 0433 ยง2): one key per key whose pulse expired, written by the // watchman, read by the controller's self-check, which raises each as a condition until it says moved. No key of it // is removed: a key that moves again is written as moved. const StuckBucket = WatchmanModule + "_stuck" // StuckKey is the key, in StuckBucket, of the key that stopped moving: `.`, formed as PulseKey forms a // pulse's, so a node or part gone offline is `mesh-delivery-watchman_presence.`. func StuckKey(bucket, key string) (string, error) { stuck, err := PulseKey(bucket, key) if err != nil { return "", fmt.Errorf("delivery: no stuck key: %w", err) } return stuck, nil } // ParseStuckKey is the inverse of StuckKey, splitting at the first dot. func ParseStuckKey(stuckKey string) (bucket, key string, err error) { bucket, key, err = ParsePulseKey(stuckKey) if err != nil { return "", "", fmt.Errorf("delivery: %q is not a stuck key: %w", stuckKey, err) } return bucket, key, nil } // Stuck is the value of a stuck key: the key that stopped moving, what it waited for in words, since when, and // whether it has moved again since. The watchman writes it; the controller reads it. type Stuck struct { V int `json:"v"` Bucket string `json:"bucket"` Key string `json:"key"` Waited string `json:"waited,omitempty"` Since time.Time `json:"since"` Moved bool `json:"moved,omitempty"` } // EncodeStuck is s as JSON at Version. func EncodeStuck(s Stuck) ([]byte, error) { s.V = Version return json.Marshal(s) } // DecodeStuck reads a stuck value; it refuses an unknown version (ErrUnknownVersion). func DecodeStuck(data []byte) (Stuck, error) { var s Stuck err := decode(data, &s) return s, err } // SameStuck says whether two stuck values are the same, so a writer writes only when they differ. func SameStuck(a, b Stuck) bool { return a.V == b.V && a.Bucket == b.Bucket && a.Key == b.Key && a.Waited == b.Waited && a.Since.Equal(b.Since) && a.Moved == b.Moved }