1966 subtask 2: the grants part composes a binding from an answer of mesh-broker's list without binding
An answer no entry of which carries binding gets, for a key-value or object-store entry whose settings hold the bus object its bus-name names, the binding composed from the answer's own fields; a stream, a ledger and an entry not held stay unbound and are logged once per answer; grants_first_views shows composed and unbound (1966/AC203-3..n). An answer where any entry carries binding is applied by §4 alone. Builds: design 25 bindings-first-view §9, §4, §6, §7, §8 Claude-Session: https://claude.ai/code/session_01JG73KEq4zoDfM9WtYpcQgZ
This commit is contained in:
@@ -224,6 +224,8 @@ func TestTheGrantsPartWhole(t *testing.T) {
|
|||||||
type listGate struct{ asked, release chan struct{} }
|
type listGate struct{ asked, release chan struct{} }
|
||||||
var gate atomic.Pointer[listGate]
|
var gate atomic.Pointer[listGate]
|
||||||
var answeredGated, listAsked, listAnswered atomic.Int32
|
var answeredGated, listAsked, listAnswered atomic.Int32
|
||||||
|
// While set, the bus module answers as a build from before the field `binding` (design 25 bindings-first-view §9).
|
||||||
|
var oldBuild atomic.Bool
|
||||||
for _, subject := range []string{"mesh.seat.mesh-broker.call.list.>", "mesh.seat.mesh-broker.tool.list"} {
|
for _, subject := range []string{"mesh.seat.mesh-broker.call.list.>", "mesh.seat.mesh-broker.tool.list"} {
|
||||||
if _, err := nc.Subscribe(subject, func(m *nats.Msg) {
|
if _, err := nc.Subscribe(subject, func(m *nats.Msg) {
|
||||||
if !listing.Load() {
|
if !listing.Load() {
|
||||||
@@ -233,6 +235,15 @@ func TestTheGrantsPartWhole(t *testing.T) {
|
|||||||
listAsked.Add(1)
|
listAsked.Add(1)
|
||||||
defer listAnswered.Add(1)
|
defer listAnswered.Add(1)
|
||||||
var more []any
|
var more []any
|
||||||
|
if oldBuild.Load() {
|
||||||
|
answer(m, map[string]any{"entries": []any{
|
||||||
|
map[string]any{"module": "mod-c", "local-name": "late", "storage-type": "key-value", "template": "state", "bus-name": "mod-c_late",
|
||||||
|
"settings": map[string]any{"KV_mod-c_late": map[string]any{}}},
|
||||||
|
map[string]any{"module": "mod-c", "local-name": "feed", "storage-type": "stream", "bus-name": "mod-c_feed",
|
||||||
|
"settings": map[string]any{"mod-c_feed": map[string]any{}}},
|
||||||
|
}})
|
||||||
|
return
|
||||||
|
}
|
||||||
if g := gate.Load(); g != nil {
|
if g := gate.Load(); g != nil {
|
||||||
g.asked <- struct{}{}
|
g.asked <- struct{}{}
|
||||||
<-g.release
|
<-g.release
|
||||||
@@ -440,6 +451,23 @@ func TestTheGrantsPartWhole(t *testing.T) {
|
|||||||
if b := bindingOf(); len(b) != 0 {
|
if b := bindingOf(); len(b) != 0 {
|
||||||
t.Fatalf("after an answer that lists a binding dropped since its ask: %+v", b)
|
t.Fatalf("after an answer that lists a binding dropped since its ask: %+v", b)
|
||||||
}
|
}
|
||||||
|
// 1966/AC203-3..: a bus module's build from before the field answers no binding on any entry (design 25 bindings-first-view
|
||||||
|
// §9): the key-value entry the bus holds gets its binding composed from the answer's fields, the stream is left unbound.
|
||||||
|
oldBuild.Store(true)
|
||||||
|
askedAgain, _ = json.Marshal(grants.Ask{By: "someone@desk", At: time.Now().Add(2 * time.Second), FirstView: grants.FirstViewBindings})
|
||||||
|
if _, err := kvs[grants.Module+"."+grants.StateUserList].Put(ctx, grants.KeyAsked, askedAgain); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
until("the binding of the entry composed from an answer with no binding, the stream left unbound", func() bool {
|
||||||
|
e, err := kvs[grants.Module+"."+grants.StateUserList].Get(ctx, grants.KeyFirstViews)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
var st grants.FirstViewsState
|
||||||
|
b := bindingOf()
|
||||||
|
return json.Unmarshal(e.Value(), &st) == nil && st.Bindings != nil && st.Bindings.Composed == 1 &&
|
||||||
|
slices.Equal(st.Bindings.Unbound, []string{"mod-c/feed (stream)"}) && len(b) == 1 && b[0].Bucket == "mod-c_late"
|
||||||
|
})
|
||||||
for _, bucket := range []string{"mesh-controller_lease", grants.Module + "_lease", grants.Module + "_takeover"} {
|
for _, bucket := range []string{"mesh-controller_lease", grants.Module + "_lease", grants.Module + "_takeover"} {
|
||||||
if _, err := js.KeyValue(ctx, bucket); !errors.Is(err, jetstream.ErrBucketNotFound) {
|
if _, err := js.KeyValue(ctx, bucket); !errors.Is(err, jetstream.ErrBucketNotFound) {
|
||||||
t.Errorf("the bucket %s: %v, want none", bucket, err)
|
t.Errorf("the bucket %s: %v, want none", bucket, err)
|
||||||
|
|||||||
@@ -3064,8 +3064,16 @@ func TestGrants(t *testing.T) {
|
|||||||
"binding": map[string]any{"local": local, "storage": "key-value", "template": "state", "bucket": bucket}}
|
"binding": map[string]any{"local": local, "storage": "key-value", "template": "state", "bucket": bucket}}
|
||||||
}
|
}
|
||||||
withoutBinding := map[string]any{"module": "mod-x", "local-name": "waiting", "storage-type": "key-value", "bus-name": "mod-x_waiting", "state": "waiting"}
|
withoutBinding := map[string]any{"module": "mod-x", "local-name": "waiting", "storage-type": "key-value", "bus-name": "mod-x_waiting", "state": "waiting"}
|
||||||
oldBusModule := map[string]any{"module": "mod-x", "local-name": "late", "storage-type": "key-value", "bus-name": "mod-x_late",
|
// Design 25 bindings-first-view §9: the entries of a bus module's answer from before the field, with no binding. An
|
||||||
"settings": map[string]any{"mod-x_late": map[string]any{}}}
|
// entry carries what the bus holds in settings, keyed by the bus object's name (KV_ and OBJ_ before the bus name).
|
||||||
|
noBinding := func(local, storage, object string, held bool) map[string]any {
|
||||||
|
e := map[string]any{"module": "mod-x", "local-name": local, "storage-type": storage, "template": "state", "bus-name": "mod-x_" + local}
|
||||||
|
if held {
|
||||||
|
e["settings"] = map[string]any{object + "mod-x_" + local: map[string]any{}}
|
||||||
|
}
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
oldBusModule := noBinding("late", "key-value", "KV_", true)
|
||||||
// A bus module's setup: the part with the first view, the conditions it said, the bindings it keeps for mod-x.
|
// A bus module's setup: the part with the first view, the conditions it said, the bindings it keeps for mod-x.
|
||||||
type setup struct {
|
type setup struct {
|
||||||
b *fakeBus
|
b *fakeBus
|
||||||
@@ -3109,7 +3117,7 @@ func TestGrants(t *testing.T) {
|
|||||||
var bound grants.Bound
|
var bound grants.Bound
|
||||||
_ = json.Unmarshal(raw, &bound)
|
_ = json.Unmarshal(raw, &bound)
|
||||||
for local, b := range bound.Bindings {
|
for local, b := range bound.Bindings {
|
||||||
out[local] = b.Bucket
|
out[local] = b.Bucket + b.ObjectStore
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return out
|
return out
|
||||||
@@ -3122,17 +3130,21 @@ func TestGrants(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
for _, row := range []struct {
|
for _, row := range []struct {
|
||||||
name string
|
name string
|
||||||
held map[string]string // kept before, by local name
|
held map[string]string // kept before, by local name
|
||||||
answer []any
|
answer []any
|
||||||
during func(*setup) // heard while the ask runs
|
during func(*setup) // heard while the ask runs
|
||||||
want map[string]string
|
want map[string]string
|
||||||
status [3]int // written, rewritten, listed
|
status [3]int // written, rewritten, listed
|
||||||
said map[string]string
|
said map[string]string
|
||||||
legacy bool
|
// 1966/AC203-3..: §9's composed count and unbound entries after the last answer, and how many answers said
|
||||||
again []any // a second answer
|
// that they carry no binding on any entry.
|
||||||
wantAgn map[string]string
|
composed int
|
||||||
saidAgn map[string]string
|
unbound []string
|
||||||
|
noted int
|
||||||
|
again []any // a second answer
|
||||||
|
wantAgn map[string]string
|
||||||
|
saidAgn map[string]string
|
||||||
}{
|
}{
|
||||||
{name: "a binding the events never brought is written", answer: []any{entry("late", "mod-x_late")}, want: map[string]string{"late": "mod-x_late"}, status: [3]int{1, 0, 1}},
|
{name: "a binding the events never brought is written", answer: []any{entry("late", "mod-x_late")}, want: map[string]string{"late": "mod-x_late"}, status: [3]int{1, 0, 1}},
|
||||||
{name: "the same binding writes nothing", held: map[string]string{"late": "mod-x_late"}, answer: []any{entry("late", "mod-x_late")},
|
{name: "the same binding writes nothing", held: map[string]string{"late": "mod-x_late"}, answer: []any{entry("late", "mod-x_late")},
|
||||||
@@ -3140,7 +3152,7 @@ func TestGrants(t *testing.T) {
|
|||||||
{name: "a different binding is rewritten", held: map[string]string{"late": "mod-x_old"}, answer: []any{entry("late", "mod-x_late")},
|
{name: "a different binding is rewritten", held: map[string]string{"late": "mod-x_old"}, answer: []any{entry("late", "mod-x_late")},
|
||||||
want: map[string]string{"late": "mod-x_late"}, status: [3]int{0, 1, 1}},
|
want: map[string]string{"late": "mod-x_late"}, status: [3]int{0, 1, 1}},
|
||||||
{name: "an entry listed without a binding changes nothing", held: map[string]string{"waiting": "mod-x_waiting"}, answer: []any{withoutBinding},
|
{name: "an entry listed without a binding changes nothing", held: map[string]string{"waiting": "mod-x_waiting"}, answer: []any{withoutBinding},
|
||||||
want: map[string]string{"waiting": "mod-x_waiting"}, status: [3]int{0, 0, 1}},
|
want: map[string]string{"waiting": "mod-x_waiting"}, status: [3]int{0, 0, 1}, unbound: []string{"mod-x/waiting (key-value)"}, noted: 1},
|
||||||
{name: "a binding the answer omits is kept, raises its condition, and a later answer that lists it clears it",
|
{name: "a binding the answer omits is kept, raises its condition, and a later answer that lists it clears it",
|
||||||
held: map[string]string{"gone": "mod-x_gone"}, answer: []any{}, want: map[string]string{"gone": "mod-x_gone"},
|
held: map[string]string{"gone": "mod-x_gone"}, answer: []any{}, want: map[string]string{"gone": "mod-x_gone"},
|
||||||
said: map[string]string{grants.BindingUnlisted + ".mod-x.gone": "raised"},
|
said: map[string]string{grants.BindingUnlisted + ".mod-x.gone": "raised"},
|
||||||
@@ -3151,8 +3163,26 @@ func TestGrants(t *testing.T) {
|
|||||||
{name: "a dropped heard since the ask is not written back, and raises nothing", held: map[string]string{"late": "mod-x_late"},
|
{name: "a dropped heard since the ask is not written back, and raises nothing", held: map[string]string{"late": "mod-x_late"},
|
||||||
answer: []any{entry("late", "mod-x_late")}, during: func(st *setup) { hear(st, "dropped", "late", "") },
|
answer: []any{entry("late", "mod-x_late")}, during: func(st *setup) { hear(st, "dropped", "late", "") },
|
||||||
want: map[string]string{}, status: [3]int{0, 0, 1}, said: map[string]string{}},
|
want: map[string]string{}, status: [3]int{0, 0, 1}, said: map[string]string{}},
|
||||||
{name: "an answer of a bus module from before the field writes nothing and says so once", answer: []any{oldBusModule},
|
// §9 (1966/AC203-3..): an answer no entry of which carries a binding.
|
||||||
want: map[string]string{}, status: [3]int{0, 0, 1}, legacy: true, again: []any{oldBusModule}, wantAgn: map[string]string{}},
|
{name: "§9 a key-value entry the bus holds is composed from the answer's fields, and said once per answer", answer: []any{oldBusModule},
|
||||||
|
want: map[string]string{"late": "mod-x_late"}, status: [3]int{1, 0, 1}, composed: 1, noted: 2, again: []any{oldBusModule},
|
||||||
|
wantAgn: map[string]string{"late": "mod-x_late"}},
|
||||||
|
{name: "§9 an object-store entry is composed with its object store", answer: []any{noBinding("media", "object-store", "OBJ_", true)},
|
||||||
|
want: map[string]string{"media": "mod-x_media"}, status: [3]int{1, 0, 1}, composed: 1, noted: 1},
|
||||||
|
{name: "§9 a stream and a ledger are left unbound and logged", answer: []any{noBinding("s", "stream", "", true), noBinding("l", "ledger", "", true)},
|
||||||
|
want: map[string]string{}, status: [3]int{0, 0, 2}, unbound: []string{"mod-x/l (ledger)", "mod-x/s (stream)"}, noted: 1},
|
||||||
|
{name: "§9 an entry without settings is left unbound and logged", answer: []any{noBinding("waiting", "key-value", "KV_", false)},
|
||||||
|
want: map[string]string{}, status: [3]int{0, 0, 1}, unbound: []string{"mod-x/waiting (key-value)"}, noted: 1},
|
||||||
|
{name: "§9 an entry whose settings lack its bus object is left unbound and logged",
|
||||||
|
answer: []any{func() map[string]any {
|
||||||
|
e := noBinding("late", "key-value", "KV_", true)
|
||||||
|
e["settings"] = map[string]any{"mod-x_late": map[string]any{}}
|
||||||
|
return e
|
||||||
|
}()},
|
||||||
|
want: map[string]string{}, status: [3]int{0, 0, 1}, unbound: []string{"mod-x/late (key-value)"}, noted: 1},
|
||||||
|
{name: "§9 an answer where any entry carries a binding is applied by §4 alone, nothing composed",
|
||||||
|
answer: []any{entry("other", "mod-x_other"), oldBusModule},
|
||||||
|
want: map[string]string{"other": "mod-x_other"}, status: [3]int{1, 0, 2}},
|
||||||
} {
|
} {
|
||||||
t.Run(row.name, func(t *testing.T) {
|
t.Run(row.name, func(t *testing.T) {
|
||||||
st := newSetup()
|
st := newSetup()
|
||||||
@@ -3181,7 +3211,7 @@ func TestGrants(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
check(row.want, row.said)
|
check(row.want, row.said)
|
||||||
if s := st.v.Status(); [3]int{s.Written, s.Rewritten, s.Listed} != row.status && !row.legacy && row.again == nil {
|
if s := st.v.Status(); [3]int{s.Written, s.Rewritten, s.Listed} != row.status && row.again == nil {
|
||||||
t.Errorf("status wrote %d, rewrote %d, listed %d; want %v", s.Written, s.Rewritten, s.Listed, row.status)
|
t.Errorf("status wrote %d, rewrote %d, listed %d; want %v", s.Written, s.Rewritten, s.Listed, row.status)
|
||||||
}
|
}
|
||||||
if row.again != nil {
|
if row.again != nil {
|
||||||
@@ -3194,8 +3224,16 @@ func TestGrants(t *testing.T) {
|
|||||||
t.Errorf("by hand %d, want 1", st.v.Status().ByHand)
|
t.Errorf("by hand %d, want 1", st.v.Status().ByHand)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if told := strings.Count(strings.Join(st.logs, "\n"), "no binding (a bus module from before the field)"); row.legacy && told != 1 || !row.legacy && told != 0 {
|
if told := strings.Count(strings.Join(st.logs, "\n"), "answered with no binding on any entry"); told != row.noted {
|
||||||
t.Errorf("a bus module from before the field was told %d times", told)
|
t.Errorf("an answer with no binding was told %d times, want %d", told, row.noted)
|
||||||
|
}
|
||||||
|
if s := st.v.Status(); s.Composed != row.composed || !slices.Equal(s.Unbound, row.unbound) {
|
||||||
|
t.Errorf("status composed %d, unbound %v; want %d, %v", s.Composed, s.Unbound, row.composed, row.unbound)
|
||||||
|
}
|
||||||
|
for _, u := range row.unbound {
|
||||||
|
if !strings.Contains(strings.Join(st.logs, "\n"), u) {
|
||||||
|
t.Errorf("the log does not name the unbound entry %s: %v", u, st.logs)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,6 +43,11 @@ type BindingsStatus struct {
|
|||||||
Listed int `json:"listed"`
|
Listed int `json:"listed"`
|
||||||
Written int `json:"written"`
|
Written int `json:"written"`
|
||||||
Rewritten int `json:"rewritten"`
|
Rewritten int `json:"rewritten"`
|
||||||
|
// Composed is how many entries of the last answer had their binding composed from the answer's own fields, and Unbound
|
||||||
|
// the entries it left unbound, `<module>/<local name> (<storage type>)`: both only for an answer no entry of which
|
||||||
|
// carries `binding` (§9).
|
||||||
|
Composed int `json:"composed"`
|
||||||
|
Unbound []string `json:"unbound,omitempty"`
|
||||||
// Unlisted are the bindings the part holds that the last answer did not list, `<module>/<local name>`.
|
// Unlisted are the bindings the part holds that the last answer did not list, `<module>/<local name>`.
|
||||||
Unlisted []string `json:"unlisted,omitempty"`
|
Unlisted []string `json:"unlisted,omitempty"`
|
||||||
Asks int `json:"asks"`
|
Asks int `json:"asks"`
|
||||||
@@ -77,7 +82,6 @@ type BindingsView struct {
|
|||||||
held []heldEvent
|
held []heldEvent
|
||||||
touched map[string]bool
|
touched map[string]bool
|
||||||
raised map[string]bool // the unlisted conditions raised, by `<module>/<local name>`
|
raised map[string]bool // the unlisted conditions raised, by `<module>/<local name>`
|
||||||
legacy bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type heldEvent struct {
|
type heldEvent struct {
|
||||||
@@ -323,27 +327,20 @@ func (v *BindingsView) apply(p *Part, raw json.RawMessage) error {
|
|||||||
if err := json.Unmarshal(raw, &answer); err != nil {
|
if err := json.Unmarshal(raw, &answer); err != nil {
|
||||||
return fmt.Errorf("%s answered what is not a list: %w", CallBrokerList, err)
|
return fmt.Errorf("%s answered what is not a list: %w", CallBrokerList, err)
|
||||||
}
|
}
|
||||||
|
entries, composed, unbound := v.compose(answer.Entries)
|
||||||
p.bindMu.Lock()
|
p.bindMu.Lock()
|
||||||
written, rewritten, holdsNoBinding, err := v.take(p, answer.Entries)
|
written, rewritten, err := v.take(p, entries, composed)
|
||||||
p.bindMu.Unlock()
|
p.bindMu.Unlock()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if holdsNoBinding > 0 {
|
|
||||||
v.mu.Lock()
|
|
||||||
said := v.legacy
|
|
||||||
v.legacy = true
|
|
||||||
v.mu.Unlock()
|
|
||||||
if !said {
|
|
||||||
v.logf("the seat %s's verb %s lists %d entries whose objects the bus holds with no binding (a bus module from before the field): nothing is written from the answer, and the events are heard as before", BrokerSeat, BrokerListVerb, holdsNoBinding)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
v.mu.Lock()
|
v.mu.Lock()
|
||||||
wasRaised := v.st.Raised
|
wasRaised := v.st.Raised
|
||||||
v.st.Raised, v.st.Error = false, ""
|
v.st.Raised, v.st.Error = false, ""
|
||||||
v.st.Answered = v.now()
|
v.st.Answered = v.now()
|
||||||
v.st.Listed = len(answer.Entries)
|
v.st.Listed = len(answer.Entries)
|
||||||
v.st.Written, v.st.Rewritten = written, rewritten
|
v.st.Written, v.st.Rewritten = written, rewritten
|
||||||
|
v.st.Composed, v.st.Unbound = len(composed), unbound
|
||||||
status := v.statusLocked()
|
status := v.statusLocked()
|
||||||
v.mu.Unlock()
|
v.mu.Unlock()
|
||||||
if wasRaised {
|
if wasRaised {
|
||||||
@@ -354,10 +351,57 @@ func (v *BindingsView) apply(p *Part, raw json.RawMessage) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// compose gives the entries of an answer that carries no `binding` on any entry (a bus module's build from before the
|
||||||
|
// field) the binding its own fields name whole (design 25 bindings-first-view §9): for a key-value entry or an object-store
|
||||||
|
// entry whose settings hold the bus object its bus name names. It answers the entries with those bindings, the entries
|
||||||
|
// composed (`<module>/<local name>`) and the ones left unbound, and says once what it did. An answer where any entry carries
|
||||||
|
// `binding` is applied by §4 alone: it answers the entries as they are.
|
||||||
|
func (v *BindingsView) compose(entries []meshbroker.Held) ([]meshbroker.Held, map[string]bool, []string) {
|
||||||
|
if len(entries) == 0 || slices.ContainsFunc(entries, func(e meshbroker.Held) bool { return e.Binding != nil }) {
|
||||||
|
return entries, nil, nil
|
||||||
|
}
|
||||||
|
out := slices.Clone(entries)
|
||||||
|
composed := map[string]bool{}
|
||||||
|
var unbound []string
|
||||||
|
for i, e := range out {
|
||||||
|
if b, ok := composeBinding(e); ok {
|
||||||
|
out[i].Binding = b
|
||||||
|
composed[localKey(e.Module, e.LocalName)] = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
unbound = append(unbound, fmt.Sprintf("%s (%s)", localKey(e.Module, e.LocalName), e.StorageType))
|
||||||
|
}
|
||||||
|
sort.Strings(unbound)
|
||||||
|
v.logf("the seat %s's verb %s answered with no binding on any entry (a bus module from before the field): %d bindings composed from its fields, left unbound: %s",
|
||||||
|
BrokerSeat, BrokerListVerb, len(composed), strings.Join(unbound, ", "))
|
||||||
|
return out, composed, unbound
|
||||||
|
}
|
||||||
|
|
||||||
|
// composeBinding is §9's binding of one entry: only for the storage types whose binding the answer names whole, and only
|
||||||
|
// when the entry's settings hold the bus object its bus name names (KV_ and the bus name, OBJ_ and the bus name).
|
||||||
|
func composeBinding(e meshbroker.Held) (*meshbroker.Binding, bool) {
|
||||||
|
b := &meshbroker.Binding{Local: e.LocalName, Storage: e.StorageType, Template: e.Template}
|
||||||
|
switch e.StorageType {
|
||||||
|
case "key-value":
|
||||||
|
if _, held := e.Settings["KV_"+e.BusName]; !held {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
b.Bucket = e.BusName
|
||||||
|
case "object-store":
|
||||||
|
if _, held := e.Settings["OBJ_"+e.BusName]; !held {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
b.ObjectStore = e.BusName
|
||||||
|
default:
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
return b, e.LocalName != "" && e.BusName != ""
|
||||||
|
}
|
||||||
|
|
||||||
// take writes the bindings of the entries an event did not name since the ask was sent, keeps every binding the answer does
|
// take writes the bindings of the entries an event did not name since the ask was sent, keeps every binding the answer does
|
||||||
// not list and raises its unlisted condition, and answers how many bindings it wrote and rewrote, and how many entries it
|
// not list and raises its unlisted condition, and answers how many bindings it wrote and rewrote; composed names the
|
||||||
// lists with their objects held and no binding. The caller holds the part's bindMu.
|
// entries whose binding compose gave them. The caller holds the part's bindMu.
|
||||||
func (v *BindingsView) take(p *Part, entries []meshbroker.Held) (written, rewritten, noBinding int, err error) {
|
func (v *BindingsView) take(p *Part, entries []meshbroker.Held, composed map[string]bool) (written, rewritten int, err error) {
|
||||||
v.mu.Lock()
|
v.mu.Lock()
|
||||||
touched := copyOf(v.touched)
|
touched := copyOf(v.touched)
|
||||||
v.mu.Unlock()
|
v.mu.Unlock()
|
||||||
@@ -366,9 +410,6 @@ func (v *BindingsView) take(p *Part, entries []meshbroker.Held) (written, rewrit
|
|||||||
for _, e := range entries {
|
for _, e := range entries {
|
||||||
listed[localKey(e.Module, e.LocalName)] = true
|
listed[localKey(e.Module, e.LocalName)] = true
|
||||||
if e.Binding == nil {
|
if e.Binding == nil {
|
||||||
if len(e.Settings) > 0 {
|
|
||||||
noBinding++
|
|
||||||
}
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
byModule[e.Module] = append(byModule[e.Module], e)
|
byModule[e.Module] = append(byModule[e.Module], e)
|
||||||
@@ -384,7 +425,7 @@ func (v *BindingsView) take(p *Part, entries []meshbroker.Held) (written, rewrit
|
|||||||
}
|
}
|
||||||
b, berr := p.bound(module)
|
b, berr := p.bound(module)
|
||||||
if berr != nil {
|
if berr != nil {
|
||||||
return written, rewritten, noBinding, berr
|
return written, rewritten, berr
|
||||||
}
|
}
|
||||||
changed := false
|
changed := false
|
||||||
for _, e := range byModule[module] {
|
for _, e := range byModule[module] {
|
||||||
@@ -396,6 +437,10 @@ func (v *BindingsView) take(p *Part, entries []meshbroker.Held) (written, rewrit
|
|||||||
v.logf("the binding of %s %s in the answer of %s does not read: not written", module, e.LocalName, CallBrokerList)
|
v.logf("the binding of %s %s in the answer of %s does not read: not written", module, e.LocalName, CallBrokerList)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
cause := "the first view of the bindings (" + CallBrokerList + ")"
|
||||||
|
if composed[localKey(module, e.LocalName)] {
|
||||||
|
cause = "the first view of the bindings, composed from " + CallBrokerList + "'s fields"
|
||||||
|
}
|
||||||
was, had := b.Bindings[e.LocalName]
|
was, had := b.Bindings[e.LocalName]
|
||||||
if had && sameBinding(was, bind) {
|
if had && sameBinding(was, bind) {
|
||||||
continue
|
continue
|
||||||
@@ -404,20 +449,20 @@ func (v *BindingsView) take(p *Part, entries []meshbroker.Held) (written, rewrit
|
|||||||
changed = true
|
changed = true
|
||||||
if had {
|
if had {
|
||||||
rewritten++
|
rewritten++
|
||||||
v.logf("the binding of %s %s was rewritten, the cause the first view of the bindings (%s)", module, e.LocalName, CallBrokerList)
|
v.logf("the binding of %s %s was rewritten, the cause %s", module, e.LocalName, cause)
|
||||||
} else {
|
} else {
|
||||||
written++
|
written++
|
||||||
v.logf("the binding of %s %s was written, the cause the first view of the bindings (%s)", module, e.LocalName, CallBrokerList)
|
v.logf("the binding of %s %s was written, the cause %s", module, e.LocalName, cause)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if changed {
|
if changed {
|
||||||
raw, _ := json.Marshal(b)
|
raw, _ := json.Marshal(b)
|
||||||
if _, perr := p.Store.Put(StateMemberships, BoundKey(module), raw); perr != nil {
|
if _, perr := p.Store.Put(StateMemberships, BoundKey(module), raw); perr != nil {
|
||||||
return written, rewritten, noBinding, fmt.Errorf("its own %s %s cannot be written: %w", StateMemberships, BoundKey(module), perr)
|
return written, rewritten, fmt.Errorf("its own %s %s cannot be written: %w", StateMemberships, BoundKey(module), perr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return written, rewritten, noBinding, v.unlisted(p, listed, touched)
|
return written, rewritten, v.unlisted(p, listed, touched)
|
||||||
}
|
}
|
||||||
|
|
||||||
// unlisted keeps every binding the answer does not list and raises its condition, one per binding; a binding it lists again
|
// unlisted keeps every binding the answer does not list and raises its condition, one per binding; a binding it lists again
|
||||||
|
|||||||
Reference in New Issue
Block a user