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 }