Files
mesh-delivery/modules/mesh-delivery-builder/internal/versions/floor.go
T
jschoubben 2274155766 DLV-B3, DLV-B3b, DLV-B9: the floor's three sources and what wakes a silent one; the old reference form's window
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
2026-11-04 11:41:05 +01:00

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
}