Files
mesh-sdk/go/motion/motion.go
T

108 lines
5.0 KiB
Go

// Package motion derives whether a module is being delivered on a node, from its want and its have (novox/hq ADR 0564 clause
// 13 and Limits 12, ADR 0540 §7; issue 1952 S7). The delivery view (Delivery) says it `moving`, `settled` or `stuck`, and
// the condition keeper (Health and repair) holds a module's own findings while it is delivered; two domains read the one
// derivation, so it is mesh-sdk's (ADR 0634 §1), over plain values: the caller fills Want and Have from the assigner's
// want and the node-engine's have as their contracts lay them out, so this package names no seat and imports no contract.
package motion
import "time"
// **Whether a module is being delivered on a node, read from its want and its have** (novox/hq ADR 0564 clause 13 and
// Limits 12, ADR 0540 §7; issue 1952 S7): one derivation, used by the delivery view, which says a module `moving`,
// `settled` or `stuck` (the messenger's hold hears those), and by the condition keeper, which holds a module's own
// findings while it is delivered there and raises what still stands when the delivery ends. Before, it lived in the view
// alone (mesh-delivery-view internal/view motionOf), and a second reader would have judged a delivery its own way.
// Want is what the derivation reads of a module's want on a node: its fingerprint and composition, and whether it is removed.
type Want struct {
FP string
Composed string
Removed bool
}
// Have is what it reads of the module's have on that node: the fingerprint and composition it runs, its phase and health
// (the node-engine's words) and the have's own detail.
type Have struct {
FP string
Composed string
Phase string
Health string
Detail string
}
// The phases and healths of a have the derivation tells apart, as the node-engine writes them.
const (
PhaseApplying = "applying"
PhaseRunning = "running"
PhaseFailed = "failed"
PhaseHeld = "held"
HealthOK = "ok"
HealthFailed = "failed"
)
// The motion of a module on a node, as the view says it.
const (
MotionMoving = "moving"
MotionSettled = "settled"
MotionStuck = "stuck"
)
// The outcomes of a motion that settled or stuck.
const (
// OutcomeRunning is a settled module: the have reached the want, running and healthy.
OutcomeRunning = "running"
// OutcomeFailed is a have at the wanted fingerprint whose apply or health check failed.
OutcomeFailed = "failed"
// OutcomeStopped is a module held, or a want the watchman says stopped moving past its bound.
OutcomeStopped = "stopped"
)
// DefaultDeliveryBound is how long a module's delivery holds its own findings when its manifest names no bound (ADR 0564
// clause 13 (d)): past it, what is held is raised, saying the delivery did not end.
const DefaultDeliveryBound = 15 * time.Minute
// Motion is where one module's want stands on one node.
type Motion struct {
// State is MotionMoving, MotionSettled or MotionStuck; empty when there is no want to move to (none, removed, or
// naming no fingerprint).
State string
// Outcome and Words are a settled or stuck motion's: running; failed with the have's words; stopped with the have's
// words (held) or the watchman's (stopped moving).
Outcome string
Words string
// Delivering says the module is being delivered there (clause 13 (a)): its want names a fingerprint other than its
// have's, or its have's phase is applying.
Delivering bool
// Failed says the delivery ended failed (Limits 12): the node refused or failed its apply (the have's phase failed,
// at whichever fingerprint it kept), or the have at the wanted fingerprint is unhealthy. What a hold kept is raised
// at once. Apart from State, which says failed only at the wanted fingerprint, as the view always has.
Failed bool
}
// Ended says the delivery ended, failed or settled healthy at the want: what a hold kept is judged now.
func (m Motion) Ended() bool { return m.Failed || (m.State == MotionSettled && !m.Delivering) }
// MotionOf is the motion of a module on a node from its want (wanted: one was read), its have (has: one was read) and
// the watchman's words when it says the want stopped moving (stuck: it says so and the want has not moved since).
func MotionOf(w Want, wanted bool, h Have, has bool, stuckWords string, stuck bool) Motion {
if !wanted || w.Removed || w.FP == "" {
return Motion{}
}
m := Motion{Delivering: !has || h.FP != w.FP || h.Phase == PhaseApplying,
Failed: has && (h.Phase == PhaseFailed || (h.FP == w.FP && h.Health == HealthFailed))}
applied := has && h.FP == w.FP && (h.Composed == "" || h.Composed == w.Composed)
switch {
case applied && h.Phase == PhaseRunning && h.Health == HealthOK:
m.State, m.Outcome = MotionSettled, OutcomeRunning
case has && h.Phase == PhaseHeld:
m.State, m.Outcome, m.Words = MotionStuck, OutcomeStopped, h.Detail
case has && h.FP == w.FP && (h.Health == HealthFailed || h.Phase == PhaseFailed):
m.State, m.Outcome, m.Words = MotionStuck, OutcomeFailed, h.Detail
case stuck:
m.State, m.Outcome, m.Words = MotionStuck, OutcomeStopped, stuckWords
default:
m.State = MotionMoving
}
return m
}