The floor asks the seat's versions, bridge_tags and indexer_requirements; a silent source is named in the floor finding and woken by an event of that source (the holder's published, the forge bridge's next message, the indexer's next manifest-accepted) or its module's condition clearing, or by hand (builder_floor_again), never by a timer (design 51 *The floor*, ADR 0617 §1). The old form's window (ADR 0624 §1-§3, ADR 0618 §2): each counted engine gets the newest form it reads among those written, the new form only once every present engine states it, the oldest kept in the single reference; a node not serving manifest-reads counts as the baseline; a node dropped by hand counts no more; old-form-closed is said once, and a node holding it seven days is a finding. Claude-Session: https://claude.ai/code/session_01JG73KJNrH5EgFXMKKYSrig
180 lines
6.1 KiB
Go
180 lines
6.1 KiB
Go
package versions
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"slices"
|
|
"sort"
|
|
"strings"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
// FloorSources are the three sources of a name's floor (design 51 *The floor*): each a tool call that answers the
|
|
// versions it knows of a name, or an error when it does not answer.
|
|
type FloorSources struct {
|
|
// Seat is `seat:<registry>.versions` of the registry seat the name is published to.
|
|
Seat func(ctx context.Context, seat, name string) ([]string, error)
|
|
// Tags is the forge bridge's bridge_tags: the package's git tags.
|
|
Tags func(ctx context.Context, name string) ([]string, error)
|
|
// Requirements is the indexer's indexer_requirements: the highest pseudo-versions consumers require.
|
|
Requirements func(ctx context.Context, name string) ([]string, error)
|
|
}
|
|
|
|
// Ask asks the three sources of name; a source that errs, or is not wired, is named in Silent with its error in why.
|
|
func (s FloorSources) Ask(ctx context.Context, seat, name string) (Floor, map[string]string) {
|
|
var f Floor
|
|
why := map[string]string{}
|
|
ask := func(source string, call func() ([]string, error), into *[]string) {
|
|
if call == nil {
|
|
f.Silent, why[source] = append(f.Silent, source), "not asked: the builder has no path to it"
|
|
return
|
|
}
|
|
got, err := call()
|
|
if err != nil {
|
|
f.Silent, why[source] = append(f.Silent, source), err.Error()
|
|
return
|
|
}
|
|
*into = got
|
|
}
|
|
var seatCall, tagsCall, reqCall func() ([]string, error)
|
|
if s.Seat != nil {
|
|
seatCall = func() ([]string, error) { return s.Seat(ctx, seat, name) }
|
|
}
|
|
if s.Tags != nil {
|
|
tagsCall = func() ([]string, error) { return s.Tags(ctx, name) }
|
|
}
|
|
if s.Requirements != nil {
|
|
reqCall = func() ([]string, error) { return s.Requirements(ctx, name) }
|
|
}
|
|
ask(SourceSeat, seatCall, &f.Seat)
|
|
ask(SourceTags, tagsCall, &f.Tags)
|
|
ask(SourceRequirements, reqCall, &f.Requirements)
|
|
return f, why
|
|
}
|
|
|
|
// What wakes a name waiting on its floor, never a timer (design 51, DLV-B3b): the source that was silent saying
|
|
// something, or its module's health condition clearing.
|
|
const (
|
|
// WakePublished is the holder's `published` on the builder's durable consumer of `<seat>.events`.
|
|
WakePublished = "published"
|
|
// WakeForge is the forge bridge's next message on its stream `forge`.
|
|
WakeForge = "forge"
|
|
// WakeAccepted is the indexer's next `manifest-accepted`.
|
|
WakeAccepted = "manifest-accepted"
|
|
// WakeCleared is the condition keeper's clear of a silent module's health condition on `mesh-conditions`.
|
|
WakeCleared = "condition-cleared"
|
|
// WakeByHand is builder_floor_again, a hand act.
|
|
WakeByHand = "by hand"
|
|
)
|
|
|
|
// sourceOfWake is the floor source each event says something of; a clear names the source by its module.
|
|
var sourceOfWake = map[string]string{WakePublished: SourceSeat, WakeForge: SourceTags, WakeAccepted: SourceRequirements}
|
|
|
|
// ModuleOfSource is the module whose health condition's clear wakes a silent source.
|
|
var ModuleOfSource = map[string]string{
|
|
SourceTags: "mesh-delivery-forge-bridge",
|
|
SourceRequirements: "mesh-delivery-indexer",
|
|
}
|
|
|
|
// FloorWait is one name waiting on its floor: the sources still silent and why, since when; the finding the builder
|
|
// writes names them, cleared once all three answer.
|
|
type FloorWait struct {
|
|
Name string `json:"name"`
|
|
Seat string `json:"seat"`
|
|
Silent []string `json:"silent"`
|
|
Why map[string]string `json:"why,omitempty"`
|
|
Since time.Time `json:"since"`
|
|
// Holder is the module holding the name's seat: its condition's clear wakes the seat source.
|
|
Holder string `json:"holder,omitempty"`
|
|
}
|
|
|
|
// Finding is the floor finding's words.
|
|
func (w FloorWait) Finding() string {
|
|
var parts []string
|
|
for _, s := range w.Silent {
|
|
p := s
|
|
if why := w.Why[s]; why != "" {
|
|
p += " (" + why + ")"
|
|
}
|
|
parts = append(parts, p)
|
|
}
|
|
return fmt.Sprintf("the floor of %s is not known since %s: %s did not answer; nothing of it is numbered until all three answer",
|
|
w.Name, w.Since.UTC().Format(time.RFC3339), strings.Join(parts, ", "))
|
|
}
|
|
|
|
// FloorWaits are the names waiting on their floor. Wake answers which names to ask again for an event, Answered drops
|
|
// a name once all three answered; the builder's finding for each is the wait's Finding.
|
|
type FloorWaits struct {
|
|
mu sync.Mutex
|
|
waits map[string]FloorWait
|
|
}
|
|
|
|
// Wait records that name's floor is not known.
|
|
func (fw *FloorWaits) Wait(name, seat, holder string, f Floor, why map[string]string, now time.Time) FloorWait {
|
|
fw.mu.Lock()
|
|
defer fw.mu.Unlock()
|
|
if fw.waits == nil {
|
|
fw.waits = map[string]FloorWait{}
|
|
}
|
|
w, had := fw.waits[name]
|
|
if !had {
|
|
w.Since = now
|
|
}
|
|
w.Name, w.Seat, w.Holder, w.Silent, w.Why = name, seat, holder, slices.Clone(f.Silent), why
|
|
fw.waits[name] = w
|
|
return w
|
|
}
|
|
|
|
// Answered drops name: its floor is known. It says whether the name waited, so its finding is cleared.
|
|
func (fw *FloorWaits) Answered(name string) bool {
|
|
fw.mu.Lock()
|
|
defer fw.mu.Unlock()
|
|
_, had := fw.waits[name]
|
|
delete(fw.waits, name)
|
|
return had
|
|
}
|
|
|
|
// Wake answers the names to ask their floor again on an event: kind one of the Wake constants; seat the seat a
|
|
// `published` came on; module the module whose condition cleared. A name is woken only when the event says something
|
|
// of a source it waits on; WakeByHand with a name wakes that name alone.
|
|
func (fw *FloorWaits) Wake(kind, seat, module, name string) []string {
|
|
fw.mu.Lock()
|
|
defer fw.mu.Unlock()
|
|
var out []string
|
|
for n, w := range fw.waits {
|
|
switch kind {
|
|
case WakeByHand:
|
|
if n == name {
|
|
out = append(out, n)
|
|
}
|
|
case WakeCleared:
|
|
for _, s := range w.Silent {
|
|
if ModuleOfSource[s] == module || (s == SourceSeat && w.Holder != "" && w.Holder == module) {
|
|
out = append(out, n)
|
|
break
|
|
}
|
|
}
|
|
default:
|
|
src := sourceOfWake[kind]
|
|
if slices.Contains(w.Silent, src) && (kind != WakePublished || seat == "" || seat == w.Seat) {
|
|
out = append(out, n)
|
|
}
|
|
}
|
|
}
|
|
sort.Strings(out)
|
|
return out
|
|
}
|
|
|
|
// Waiting is every wait, by name.
|
|
func (fw *FloorWaits) Waiting() []FloorWait {
|
|
fw.mu.Lock()
|
|
defer fw.mu.Unlock()
|
|
out := make([]FloorWait, 0, len(fw.waits))
|
|
for _, w := range fw.waits {
|
|
out = append(out, w)
|
|
}
|
|
sort.Slice(out, func(i, j int) bool { return out[i].Name < out[j].Name })
|
|
return out
|
|
}
|