mesh/repo-check head c61212eba77c merged into trunk c6e669f71e8e — pass: its merge-check.sh passed, each part in its toolchain: merge-check.sh (typescript)…
mesh/merge-gate head c61212eba77c merged into trunk c6e669f71e8e — pass: group feat/r4d composed: every machine composes with the change as it did without …
mesh/delivery delivered
mesh/delivery-group group feat/r4d delivered: every member is delivered
mesh/check failed: unit: ..test: node:internal/modules/esm/resolve:241 | c61212eba77c on 5652312f09d2 | unit 1629 run, 0 kept, 3 skipped | integration…
54 lines
2.0 KiB
Go
54 lines
2.0 KiB
Go
package process
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/nats-io/nats.go"
|
|
|
|
"git.novox.be/novox/mesh-sdk/go/membership"
|
|
)
|
|
|
|
// FollowMembership reads this account's own membership and follows it (novox/hq ADR 0160): apply is called
|
|
// with every membership the mesh issues this account's node and module, the one already issued first and
|
|
// each later one as it is published, so a change reaches a running process without a restart. A body that is
|
|
// not a membership is handed to onBad (nil to ignore it) and skipped.
|
|
//
|
|
// The subject is followed before it is read, so an issue landing between the two is applied, not missed. The
|
|
// read is the subject-addressed direct get, the one request this account may make of the stream; no
|
|
// membership issued yet, or the read failing, is not an error: the process follows the subject and is told
|
|
// when one arrives. The returned error is a refusal to follow at all.
|
|
func (c *Conn) FollowMembership(apply func(membership.Membership), onBad func(error)) error {
|
|
if c.account.Node == "" || c.account.Module == "" {
|
|
return fmt.Errorf("the account names no node or module, so it has no membership")
|
|
}
|
|
bad := func(err error) {
|
|
if onBad != nil {
|
|
onBad(err)
|
|
}
|
|
}
|
|
take := func(body []byte) {
|
|
var issued membership.Membership
|
|
if err := json.Unmarshal(body, &issued); err != nil {
|
|
bad(fmt.Errorf("a membership arrived that is not one: %w", err))
|
|
return
|
|
}
|
|
apply(issued)
|
|
}
|
|
subject := membership.Subject(c.account.Node, c.account.Module)
|
|
if _, err := c.nc.Subscribe(subject, func(m *nats.Msg) { take(m.Data) }); err != nil {
|
|
return fmt.Errorf("cannot follow %s: %w", subject, err)
|
|
}
|
|
got, err := c.nc.Request(membership.DirectGetSubject(c.account.Node, c.account.Module), nil, 5*time.Second)
|
|
switch {
|
|
case err != nil:
|
|
bad(fmt.Errorf("cannot read the membership issued on %s yet (%v); following it", subject, err))
|
|
case got.Header.Get("Status") != "" || len(got.Data) == 0:
|
|
// None issued yet: the follow above delivers it.
|
|
default:
|
|
take(got.Data)
|
|
}
|
|
return nil
|
|
}
|