Read each membership's last value from mesh-grants' own state and say it on its subject without the assignments stream, so memberships keep flowing once nats_stream_remove takes the stream at the lease write (issue 774, research 072 row 33)
This commit is contained in:
@@ -4,7 +4,6 @@ import (
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"slices"
|
||||
"sort"
|
||||
@@ -25,12 +24,12 @@ var MembershipGaps = []Gap{
|
||||
{Input: "membership: root-free machines", Why: "asked of the controller's root-free verb at each pass and kept for that pass alone; a machine it does not call free (an error included) is composed as not free, and the pass says why"},
|
||||
}
|
||||
|
||||
// Publisher publishes a membership on the assignments stream (membership.Subject), last per subject, and reads the one
|
||||
// last published there. A refusal (the grant not yet on the bus) is an error the part says and tries again at the next
|
||||
// event.
|
||||
// Publisher says a membership the part wrote to its state memberships on its subject (membership.Subject), as a plain
|
||||
// message, for a runtime that follows the subject alone. The state is where a membership is published and read back
|
||||
// (ADR 0571 clause 7); the subject is a notice that depends on no stream, so the assignments stream's removal at the
|
||||
// lease write (research 072 row 33) stops nothing. A refusal is said and the membership stays written.
|
||||
type Publisher interface {
|
||||
LastMembership(node, module string) (body []byte, seq uint64, found bool, err error)
|
||||
PublishMembership(node, module string, body []byte, after uint64) error
|
||||
PublishMembership(node, module string, body []byte) error
|
||||
}
|
||||
|
||||
// Membership is one module's membership on one node, as composed now, and what became of its publishing.
|
||||
@@ -73,18 +72,20 @@ const (
|
||||
MembershipUnchanged = "unchanged"
|
||||
)
|
||||
|
||||
// publish publishes m when it differs from the membership last published for its assignment, carrying that one's private
|
||||
// network and received contributions; it answers m as published (or why not) and whether it was published now. held is
|
||||
// every holding the memberships composed now carry (Holdings.ByMemberships).
|
||||
func publish(p Publisher, m Membership, h Holdings, held map[string]bool) (Membership, bool) {
|
||||
// publish says whether m is to be published: whether it differs from was, the membership last published (the value of
|
||||
// its key in the part's own state memberships, found when it is there), carrying that one's private network and
|
||||
// received contributions. It answers m as composed and published, or why not. held is every holding the memberships
|
||||
// composed now carry (Holdings.ByMemberships). Pure: the caller writes and notifies.
|
||||
func publish(p Publisher, was []byte, found bool, m Membership, h Holdings, held map[string]bool) (Membership, bool) {
|
||||
if p == nil {
|
||||
m.Published = "not published: no publisher here (only the part's process publishes)"
|
||||
return m, false
|
||||
}
|
||||
was, seq, found, err := p.LastMembership(m.Node, m.Module)
|
||||
if err != nil {
|
||||
m.Published = "not published: the membership last published cannot be read: " + err.Error()
|
||||
return m, false
|
||||
if found {
|
||||
// A value of the state is the bare membership, or the wrapper written before 2026-10-30 (mesh-sdk's one codec);
|
||||
// one that is neither is no last membership, and m is a first one.
|
||||
bare, err := membership.BareGrantsMembership(was)
|
||||
was, found = bare, err == nil
|
||||
}
|
||||
if found {
|
||||
var last membership.Membership
|
||||
@@ -122,10 +123,6 @@ func publish(p Publisher, m Membership, h Holdings, held map[string]bool) (Membe
|
||||
}
|
||||
}
|
||||
}
|
||||
if err := p.PublishMembership(m.Node, m.Module, body, seq); err != nil {
|
||||
m.Published = fmt.Sprintf("not published (tried again at the next event): %v", err)
|
||||
return m, false
|
||||
}
|
||||
m.Published = MembershipPublished
|
||||
return m, true
|
||||
}
|
||||
|
||||
@@ -140,7 +140,7 @@ type Result struct {
|
||||
}
|
||||
|
||||
// NothingTakesEffect is what every composition says it did on the bus beyond its own state and the memberships.
|
||||
const NothingTakesEffect = "memberships (on the assignments stream, unless one would leave a seat a placed module claims with no holder, or drop a capability its holder still asks for, which stays a shadow difference), and each bucket, log or stream a manifest declares that the bus lacks, created once with the manifest's limits (the operator, 2026-10-26 08:54); the user list, the grants and any change to a stream the bus holds stay in shadow until take-over"
|
||||
const NothingTakesEffect = "memberships (in its own state memberships, each said on its subject, unless one would leave a seat a placed module claims with no holder, or drop a capability its holder still asks for, which stays a shadow difference), and each bucket, log or stream a manifest declares that the bus lacks, created once with the manifest's limits (the operator, 2026-10-26 08:54); the user list, the grants and any change to a stream the bus holds stay in shadow until take-over"
|
||||
|
||||
// Part is the grants part: it reads, calls and writes through these, and nothing else.
|
||||
type Part struct {
|
||||
@@ -328,7 +328,33 @@ func (p *Part) Compose() Result {
|
||||
}
|
||||
seated := holdings.ByMemberships(members...)
|
||||
for _, key := range sortedKeys(ms) {
|
||||
m, now := publish(p.Publish, ms[key], holdings, seated)
|
||||
// The last membership is the state's own (ADR 0571 clause 7), never the assignments stream's, which the bus
|
||||
// module's tools remove at the lease write (research 072 row 33): a last read there would freeze every membership
|
||||
// once it is gone.
|
||||
was, _, found, err := p.Store.Get(StateMemberships, key)
|
||||
if err != nil {
|
||||
res.NotPublished = append(res.NotPublished, key+": not published: the membership last published cannot be read from its own "+StateMemberships+": "+err.Error())
|
||||
continue
|
||||
}
|
||||
m, now := publish(p.Publish, was, found, ms[key], holdings, seated)
|
||||
// Published is written to the state, where every runtime reads it (process.FollowMembership); only one
|
||||
// published, so a membership held back never reaches a reader. The subject is a notice after it.
|
||||
if now {
|
||||
body := membership.EncodeGrantsMembership(m.Membership)
|
||||
if _, err := p.Store.Put(StateMemberships, key, body); err != nil {
|
||||
res.Errors = append(res.Errors, fmt.Sprintf("its own %s %s cannot be written: %v", StateMemberships, key, err))
|
||||
m.Published, now = "not published (tried again at the next event): its own "+StateMemberships+" cannot be written", false
|
||||
} else {
|
||||
res.MembershipsWritten++
|
||||
if err := p.Publish.PublishMembership(m.Node, m.Module, body); err != nil {
|
||||
res.Errors = append(res.Errors, fmt.Sprintf("the membership of %s is written, its notice on its subject not said: %v", key, err))
|
||||
}
|
||||
}
|
||||
}
|
||||
// One unchanged is written bare once where the state still holds the wrapper written before 2026-10-30.
|
||||
if m.Published == MembershipUnchanged && p.changed(&res, StateMemberships, key, membership.EncodeGrantsMembership(m.Membership)) {
|
||||
res.MembershipsWritten++
|
||||
}
|
||||
switch {
|
||||
case now:
|
||||
res.Published = append(res.Published, key)
|
||||
@@ -342,14 +368,6 @@ func (p *Part) Compose() Result {
|
||||
}
|
||||
res.Retired[key] = m.Retired
|
||||
}
|
||||
// The state holds the bare membership as the assignments stream carries it, through mesh-sdk's one codec (issue
|
||||
// 774): only one published now or unchanged, so a membership held back never reaches a reader by this way. What
|
||||
// became of each at this pass is the pass's own (Published, NotPublished, Retired), kept with it (Pass).
|
||||
if now || m.Published == MembershipUnchanged {
|
||||
if p.changed(&res, StateMemberships, key, membership.EncodeGrantsMembership(m.Membership)) {
|
||||
res.MembershipsWritten++
|
||||
}
|
||||
}
|
||||
}
|
||||
held, heldErr := HeldStreams(p.Call)
|
||||
var ask Ask
|
||||
|
||||
Reference in New Issue
Block a user