Skip to main content

Sections: discovery the product defines

A product often needs to know which services offer something of its own: the modules of an application, the providers of a search, the plugins of a pipeline. A section is that, without a registry of the product's: a service offers a proto message of the product's under a name, and any service follows the name across the installation.

// a module service
sections.Offer(root, "komeet.module", &modulev1.Manifest{Name: "media", Routes: ...})

// the platform service
modules := sections.Follow[*modulev1.Manifest](root, "komeet.module")

for service, m := range modules.Get() {
// service is the offering service's name, m its section
}

Names are lower case, segments joined by ., _ or -; the product chooses them, so prefix them with the product's name. A service offers a name once.

Offer and Follow are declarations: call them while the service is declared (in a constructor), not in a component's goroutine. The followed names are read when the service starts, so Follow after Run panics instead of silently following nothing.

What a follower sees​

The section goes into the offering service's manifest. Backplane publishes, by name, the sections of every serving service — the highest version with a registered, healthy, serving instance — to Consul KV backplane/sections/<name>. Follow watches that key while the service runs:

  • Get() is the current set by service; empty until it was read, or when no serving service offers the name.
  • Known() tells an empty set from one not read yet; without Consul nothing is ever known.
  • Changes(ctx) signals each change: a service starting, stopping, or shipping a new version with another section.

A service that offers the name with a message other than T is left out of the follower's set.

Live sections​

A section the manifest holds is fixed for a version. When the value follows something that changes while the service runs — its live configuration, say — offer it live and set it whenever that changes:

login := sections.OfferLive[*kratosv1.LoginSection](root, "kratos.login")

// on start and on every settings change
if err := login.Set(section(settings)); err != nil { ... }

The manifest declares the name with T's empty message. Each instance publishes the value it set with its instance state, at once; Backplane takes the value of a serving instance of the version it publishes and, when it changes, rewrites the name's key, so followers see Changes. Until an instance sets a value, followers see the empty message. Setting an equal value changes nothing.

Declare Offer and Follow before Run, like routes and hooks. A section that does not marshal is a declaration error that Run fails with and Service.Validate reports.

In tests, backplanetest.Offered(h, name) shows what a service offers (its live value when one is set, else the manifest's), and backplanetest.SetSections(h, name, services) publishes a set to the harness's followers as Backplane would (see Testing).