Documentation
¶
Overview ¶
Package graphkit is the runtime under a project's generated composition graph (internal/app/graph_gen.go): memoized construction of each node, the root's Build — force the closure, then seal — and the lifecycle nodes of what was built, in the order it was built, for lifecyclekit.Supervise.
The graph itself is generated Go. Every edge is a value expression in the project's internal/app/compose.go, and every accessor is a typed method on the project's *app.Graph that calls Get. Nothing here resolves a dependency, looks a value up by name, or inspects a type: which node a Deps field gets was decided at generate time, and which lifecycle methods a node has was read off its type by the generator and handed to Get as a Node literal.
func (g *Graph) Billing() *billing.Service {
return graphkit.Get(&g.b, &g.m.Billing, "billing", g.newBilling, nil)
}
Construction is eager within a root and fails loud at boot: Build forces the root's whole closure before anything runs, a construction error unwinds with the path that reached it ("build server: api_listener → user_routes → billing: …"), and once the graph is sealed an accessor for a node the root never reached panics instead of constructing it on a first request.
While it builds, a Builder reports each node's construction to a Progress (ReportTo) — in a generated graph, the boot probes, so /readyz names the node whose constructor is blocking boot.
A Builder is used from one goroutine while it builds. After Build, reading an already-built node is safe from any goroutine.
Index ¶
- Variables
- func Catch(f func()) (err error)
- func Get[T any](b *Builder, m *Memo[T], name string, ctor func() (T, error), ...) T
- type Builder
- func (b *Builder) Build(root string, force func()) error
- func (b *Builder) BuildWithRollback(ctx context.Context, root string, timeout time.Duration, force func()) error
- func (b *Builder) Built() []string
- func (b *Builder) Nodes() []lifecyclekit.Node
- func (b *Builder) ReportTo(p Progress)
- func (b *Builder) Rollback(timeout time.Duration) error
- func (b *Builder) Sealed() bool
- func (b *Builder) Supervise(ctx context.Context, policy lifecyclekit.Policy, probes *serverkit.Probes, ...) error
- type Memo
- type Plan
- type PlanNode
- type Progress
Constants ¶
This section is empty.
Variables ¶
var ErrSealed = errors.New("graphkit: the graph is sealed")
ErrSealed is the cause of the panic an accessor raises for a node the root's Build did not construct.
Functions ¶
func Catch ¶
func Catch(f func()) (err error)
Catch runs f and returns the construction error a node's accessor panicked with, for a caller that uses the graph's accessors outside Build. Any other panic is re-raised.
func Get ¶
func Get[T any](b *Builder, m *Memo[T], name string, ctor func() (T, error), node func(T) lifecyclekit.Node) T
Get returns the node m memoizes, constructing it with ctor the first time. name labels it in errors and in the supervisor's logs. node, when non-nil, is the node's lifecycle — its Run, Drain, Close and Ready methods, read off its type at generate time; a Node with none of them is not supervised.
A construction error does not return: it panics, carrying the path of nodes that reached it, and Build recovers it. Calling an accessor outside Build (a hand-written main using the graph unsealed) gets the same panic; Catch turns it into an error.
Types ¶
type Builder ¶
type Builder struct {
// contains filtered or unexported fields
}
Builder is one graph's construction state. The zero value is ready to use.
func (*Builder) Build ¶
Build constructs a root: force calls the root's field accessors, so every node the root reaches is constructed once, dependencies first. Build then seals the graph. A construction error comes back as "build <root>: a → b → c: <cause>". Building a second root on a sealed graph is an error: one process runs one root.
func (*Builder) BuildWithRollback ¶ added in v1.4.0
func (b *Builder) BuildWithRollback(ctx context.Context, root string, timeout time.Duration, force func()) error
BuildWithRollback is Build with construction-failure resource ownership. It is opt-in; Build retains its existing behavior. ctx is checked before and after force (constructors must honor it themselves). timeout must be positive. On failure, every successfully constructed node's Close runs in reverse completion order, using one fresh background context bounded by timeout. Close must honor that context, just as it must during normal supervision. Run and Drain are not called: these resources have not started serving.
The original build/cancellation error and all cleanup errors remain reachable through errors.Is/As. A failed graph is terminal: its memoized values may now be closed, so neither access, rebuilding nor supervision is permitted. On success ownership transfers to Supervise; construction rollback never closes a successfully built graph. A constructor returning an error still owns its own partial value; only successful Get acquisitions are registered. Like Build, this method and construction use one goroutine.
func (*Builder) Built ¶
Built is the name of every constructed node, in construction-completion order: each after everything it depends on.
func (*Builder) Nodes ¶
func (b *Builder) Nodes() []lifecyclekit.Node
Nodes are the lifecycle nodes of every constructed node that has a Run, Drain, Close or Ready, in construction order — the order Supervise starts them in, so a node starts after everything it depends on and stops before.
func (*Builder) ReportTo ¶ added in v1.0.0
ReportTo sets where construction progress goes from now on; nil reports nowhere. Call it before Build.
func (*Builder) Rollback ¶ added in v1.4.0
Rollback releases acquisitions before supervision, including when a resource outside the graph (for example a probe listener) fails after Build succeeded. It is idempotent and terminal. After Supervise takes ownership it is a no-op, so a deferred rollback cannot double-close resources on normal shutdown. The same cooperative timeout contract as BuildWithRollback applies.
func (*Builder) Supervise ¶
func (b *Builder) Supervise(ctx context.Context, policy lifecyclekit.Policy, probes *serverkit.Probes, first ...lifecyclekit.Node) error
Supervise runs the built nodes under lifecyclekit.Supervise until ctx ends. Every node that reports Ready joins probes' /readyz, and probes' Drainer is the policy's readiness gate and shutdown budget when the policy names none. probes may be nil — a root with no probe route. Call it after Build.
first are nodes the graph did not build — a root's probe listener — that start before every built node and stop after all of them, so they answer for the whole of boot and drain.
type Memo ¶
type Memo[T any] struct { // contains filtered or unexported fields }
Memo holds one node's value once it is constructed. The zero value is an unbuilt node.
type Plan ¶ added in v1.0.0
type Plan []PlanNode
Plan is a root's nodes in the order its Build constructs them — each after everything it depends on, the root's last field first — preceded, for a root none of whose nodes serves the probes, by the probe listener Run starts before every node and stops after them.
It is the plan, not a record: a node whose constructor returns a nil value at run time is not supervised, and Built() names only what a real Build constructed (never the probe listener).
type PlanNode ¶ added in v1.0.0
PlanNode is one node of a root's plan: its name — the name Built and the supervisor's "drain:" lines use — and which lifecycle methods its type has, read off the type when graph_gen.go was generated.
func (PlanNode) Supervised ¶ added in v1.0.0
Supervised reports whether Supervise runs the node: it has a Run, Drain, Close or Ready. A node without one is constructed and never started or stopped.
type Progress ¶ added in v1.0.0
Progress is told about each node's construction as it happens: Constructing as a node's constructor starts, and the done it returns as that constructor ends — with its error, nil once the node is built. Nodes nest: a node's dependencies start and end inside it, so the latest started and not yet done is the constructor actually running. A dependency's failure ends every node that reached it, each with that error. Construction is sequential: one goroutine, one node's constructor at a time.
serverkit's boot probes satisfy it, so a root's /readyz names the node boot is waiting on.