Documentation
¶
Overview ¶
Package beehive is an embedded, Kubernetes-inspired control plane backed by a durable store: users declare desired Spec and controllers reconcile actual state toward it, level-triggered, coordinating only through the shared store.
Index ¶
- Constants
- Variables
- func EventDetail[T any](e Event) (T, error)
- func GenerateName(prefix string) string
- func Register[Spec, Status any](bh *Beehive, gk GroupKind, c Controller[Spec, Status], opts ...Option) error
- type AdminClient
- func (a *AdminClient[Status]) AddDependency(ctx context.Context, fromID, toID ObjectID) error
- func (a *AdminClient[Status]) AddEvent(ctx context.Context, id ObjectID, event EventSpec) error
- func (a *AdminClient[Status]) DeleteCondition(ctx context.Context, id ObjectID, conditionType string) error
- func (a *AdminClient[Status]) DeleteDependency(ctx context.Context, fromID, toID ObjectID) error
- func (a *AdminClient[Status]) DeleteFinalizer(ctx context.Context, id ObjectID, finalizer string) error
- func (a *AdminClient[Status]) SetCondition(ctx context.Context, id ObjectID, condition Condition) error
- func (a *AdminClient[Status]) SetConditions(ctx context.Context, id ObjectID, conditions []Condition) error
- func (a *AdminClient[Status]) UpdateStatus(ctx context.Context, id ObjectID, status Status) error
- type Beehive
- type ChangeType
- type Client
- type Condition
- type ConditionStatus
- type Controller
- type ControllerClient
- type DeletionCascadeChild
- type Event
- type EventID
- type EventOption
- type EventRetention
- type EventSpec
- type EventStream
- type EventType
- type EventsAddInput
- type GroupKind
- type LoadOption
- type LoadSet
- type Migrator
- type Object
- func (o *Object[Spec, Status]) Dependencies() ([]ObjectRef, error)
- func (o *Object[Spec, Status]) Dependents() ([]ObjectRef, error)
- func (o *Object[Spec, Status]) Events() ([]Event, error)
- func (o *Object[Spec, Status]) Owned() ([]ObjectRef, error)
- func (o *Object[Spec, Status]) Owner() (ObjectRef, bool, error)
- type ObjectChange
- type ObjectID
- type ObjectListStream
- type ObjectRef
- type ObjectStream
- type ObjectWrite
- type ObjectsCreateInput
- type Option
- func WithConcurrency(n int) Option
- func WithEventRetention(perTimeline int, maxAge time.Duration) Option
- func WithFinalizers(f ...string) Option
- func WithFullPassInterval(d time.Duration) Option
- func WithGCInterval(d time.Duration) Option
- func WithIndividualPassInterval(d time.Duration) Option
- func WithLogLevel(level slog.Level) Option
- func WithLogger(l *slog.Logger) Option
- func WithMaxRetryInterval(d time.Duration) Option
- func WithMigrator(m Migrator) Option
- func WithOnCreate(fn func(ctx context.Context)) Option
- func WithOwedPassInterval(d time.Duration) Option
- func WithOwner(id ObjectID) Option
- func WithStaleDependentsInterval(d time.Duration) Option
- func WithStartupFullPass(enabled bool) Option
- func WithTriggerByID(ch <-chan ObjectID) Option
- func WithTriggerByName(ch <-chan string) Option
- func WithWatchFloorInterval(d time.Duration) Option
- func WithWriteLogRetention(perKind int, maxAge time.Duration) Option
- type RawEvent
- type RawObject
- type ReconcileResult
- type Relation
- type RequeueOption
- type Schedule
- type StalePos
- type Store
- type WatchOption
- type WriteOp
Constants ¶
const ( RelationOwnedBy = storeapi.RelationOwnedBy RelationDependsOn = storeapi.RelationDependsOn )
const ( WriteCreate = storeapi.WriteCreate WriteUpdate = storeapi.WriteUpdate WriteDelete = storeapi.WriteDelete )
The soft delete is a WriteUpdate: the row is still live and readable, so only collection is WriteDelete.
Variables ¶
var ErrConcurrentNestedTx = storeapi.ErrConcurrentNestedTx
ErrConcurrentNestedTx is returned by the outermost Within when a nested frame is still open at commit, which can only mean another goroutine holds one.
var ErrDeletionPending = storeapi.ErrDeletionPending
ErrDeletionPending is returned by Update and UpdateByName when the object is being torn down. The write is refused rather than applied: a pass on a deleting row runs collection, not reconcile, so the spec would be discarded.
Distinct from ErrNotFound, because the answers differ — ErrNotFound means create it, ErrDeletionPending means you cannot, since the name stays held until GC releases it. A caller whose object is owned can treat it as "not yet" and do nothing: a physical delete pushes its owner, so the owner's next pass creates the replacement.
var ErrDuplicateConditionType = storeapi.ErrDuplicateConditionType
ErrDuplicateConditionType is returned by SetConditions when one call names a condition type twice, whose outcome would otherwise depend on apply order.
var ErrInvalidCondition = storeapi.ErrInvalidCondition
ErrInvalidCondition is returned by SetConditions when a condition's type, status, reason or message is not valid UTF-8.
var ErrInvalidName = storeapi.ErrInvalidName
ErrInvalidName is returned by name-keyed calls when the name is empty.
var ErrInvalidOption = errors.New("beehive: option value is invalid")
ErrInvalidOption reports an option value that has no meaning (e.g. a non-positive GC interval). It is about the argument alone, so it is returned regardless of the target — distinct from an option being inapplicable, which is ignored by design.
var ErrInvalidResult = errors.New("beehive: unusable ReconcileResult")
ErrInvalidResult is what an unusable ReconcileResult — the zero value, or Fail(nil) — fails the pass with. It settles nothing and takes the backoff ladder.
var ErrInvalidStoreIdentity = errors.New("beehive: store reports no identity")
ErrInvalidStoreIdentity is returned by Start when the store's Identity is empty. "" names no database, so two unrelated stores would collide on it.
var ErrNameTaken = storeapi.ErrNameTaken
ErrNameTaken is returned by Create when the name is already held, by a live row or a deletion-pending one. GetOrCreate returns the existing row instead.
var ErrNoController = errors.New("beehive: no controller registered for kind")
ErrNoController is returned by Requeue when the client's kind has no registered controller: there is no reconcile loop to schedule against.
var ErrNotFound = storeapi.ErrNotFound
ErrNotFound is returned by Store reads when no object matches.
var ErrNotLoaded = errors.New("beehive: secondary lookup not loaded")
ErrNotLoaded is returned by the secondary-lookup accessors when the requested relation was not fetched on the read that produced the object.
var ErrReconcileReturned = errors.New("beehive: the ControllerClient passed to Reconcile is no longer usable")
ErrReconcileReturned is returned by every method of the ControllerClient Reconcile was passed, once it has returned.
var ErrSchemaVersionDowngrade = storeapi.ErrSchemaVersionDowngrade
ErrSchemaVersionDowngrade is returned by Objects().UpdateSpec/UpdateStatus when the caller's schema version is lower than the one stamped on the row.
var ErrStaleTxContext = storeapi.ErrStaleTxContext
ErrStaleTxContext is returned by a nested Within whose ctx is not the transaction's live innermost frame. Deep nesting on one goroutine is fine; using a ctx from outside the frame you are in is not.
var ErrStopped = errors.New("beehive: the beehive has stopped")
ErrStopped ends a watch whose Beehive has stopped. It is what separates shutdown from the caller cancelling: resubscribing answers ErrWatchTooOld and cannot answer this one, since a stopped Beehive does not start again.
var ErrStoreInUse = errors.New("beehive: store already has a running Beehive")
ErrStoreInUse is returned by Start when another Beehive is already running over the store. Distinct from the "already started" error beside it: that one reports misuse of this Beehive, which its caller can already see.
var ErrWatchTooNew = errors.New("beehive: watch resumes above the log's head")
ErrWatchTooNew ends a resume whose position is above everything the log has held, which means the position did not come from this store — a restored backup or a swapped file restarts the sequence. Answered the same way as ErrWatchTooOld: subscribe again without the resume option. Unreported, such a stream says "caught up" and then drops everything until the sequence climbs past the position.
var ErrWatchTooOld = errors.New("beehive: watch is below the log's retention horizon")
ErrWatchTooOld ends a watch whose unread entries retention has already removed — the object write log for Watch/WatchList, the event log for WatchEvents. The stream cannot continue truthfully, so it reports this and closes; the caller answers by subscribing again for a fresh snapshot.
var ErrWrongKind = storeapi.ErrWrongKind
ErrWrongKind is returned by an id-keyed write whose target belongs to another kind. The store folds the caller's kind into every write, so a wrong id fails loudly instead of corrupting another kind's row.
Functions ¶
func EventDetail ¶ added in v0.11.0
EventDetail unmarshals an event's Detail payload into T. An empty Detail yields the zero value with a nil error.
func GenerateName ¶ added in v0.19.0
func Register ¶
func Register[Spec, Status any](bh *Beehive, gk GroupKind, c Controller[Spec, Status], opts ...Option) error
Register installs controller c for the resource kind gk. It must be called before Start, and only once per kind. The kind's ControllerClient reaches the controller as a parameter of Reconcile.
Types ¶
type AdminClient ¶ added in v0.26.0
type AdminClient[Status any] struct { // contains filtered or unexported fields }
AdminClient writes what only a reconcile pass can otherwise write — status, conditions, finalizers, events and dependency edges — for one kind, from outside a pass. Two uses: a test fixture, and maintenance on a stopped beehive, where a data migration, a backfill or a wedged object has to be written by hand. Not for reconcile logic, which holds a ControllerClient bound to the object it was handed.
Every verb takes an id and is scoped to gk: another kind's id is ErrWrongKind. It writes through a ControllerClient nothing ever ends, so each verb means exactly what it means during a pass.
Run it with beehive stopped. A write while beehive runs races that object's own pass and the later write wins; a write from a second process is never supported at all. See docs/adr/2026-08-18-an-admin-client-writes-outside-a-pass.md.
func NewAdminClient ¶ added in v0.26.0
func NewAdminClient[Status any](bh *Beehive, gk GroupKind) *AdminClient[Status]
NewAdminClient returns an AdminClient for gk. Needs no registered controller and no running beehive.
func (*AdminClient[Status]) AddDependency ¶ added in v0.26.0
func (a *AdminClient[Status]) AddDependency(ctx context.Context, fromID, toID ObjectID) error
AddDependency records that fromID depends on toID. See ControllerClient.AddDependency.
func (*AdminClient[Status]) AddEvent ¶ added in v0.26.0
AddEvent adds an observation to id's event log. See ControllerClient.AddEvent.
func (*AdminClient[Status]) DeleteCondition ¶ added in v0.26.0
func (a *AdminClient[Status]) DeleteCondition(ctx context.Context, id ObjectID, conditionType string) error
DeleteCondition removes id's condition of that type. See ControllerClient.DeleteCondition.
func (*AdminClient[Status]) DeleteDependency ¶ added in v0.26.0
func (a *AdminClient[Status]) DeleteDependency(ctx context.Context, fromID, toID ObjectID) error
DeleteDependency drops the fromID→toID edge. See ControllerClient.DeleteDependency. This is the only way to drop an edge whose source has no controller.
func (*AdminClient[Status]) DeleteFinalizer ¶ added in v0.26.0
func (a *AdminClient[Status]) DeleteFinalizer(ctx context.Context, id ObjectID, finalizer string) error
DeleteFinalizer removes id's finalizer, which is how a wedged object is unstuck. See ControllerClient.DeleteFinalizer.
func (*AdminClient[Status]) SetCondition ¶ added in v0.26.0
func (a *AdminClient[Status]) SetCondition(ctx context.Context, id ObjectID, condition Condition) error
SetCondition writes id's condition of that type. See ControllerClient.SetCondition.
func (*AdminClient[Status]) SetConditions ¶ added in v0.26.0
func (a *AdminClient[Status]) SetConditions(ctx context.Context, id ObjectID, conditions []Condition) error
SetConditions writes every named condition together. See ControllerClient.SetConditions.
func (*AdminClient[Status]) UpdateStatus ¶ added in v0.26.0
func (a *AdminClient[Status]) UpdateStatus(ctx context.Context, id ObjectID, status Status) error
UpdateStatus records status for id. See ControllerClient.UpdateStatus. Never stamps observed_generation: the handshake stays beehive's, so an object given a status here is still unsettled and the owed pass reconciles it. Holding no pass, it holds no loaded status either, so every call reaches the store and the no-op is the store's own — ErrNotFound included.
type Beehive ¶
type Beehive struct {
// contains filtered or unexported fields
}
Beehive is the control plane: it owns the durable store and the set of registered controllers, and drives their reconcile loops between Start and Stop.
func New ¶
New creates a control plane backed by store s. Register controllers on the returned Beehive before calling Start.
func (*Beehive) Start ¶
Start brings the control plane up: the dependency waker, the reconcile loops, the stale-dependents pass, and the GC sweeper — all periodic. It returns a stop function that tears everything down. A Beehive is one-shot: starting twice, or after stop, is an error.
startCtx covers startup only; the long-lived loops end when the returned stop is called. Startup reads the store to seed the dependency waker, so a startCtx that expires while the store is busy fails the start — a store *error* there does not, since the waker is an optimisation.
A store another Beehive is already running is ErrStoreInUse, and one whose Identity is empty is ErrInvalidStoreIdentity. Both cover this process only; keeping a second process off the database is the embedder's.
The store's claim is released once the loops have stopped, which a nil from the stop call that owns the teardown is proof of. Nothing else is: stop returns its own ctx's error if the loops outlast it, and a second concurrent call returns nil at once rather than waiting for the first. So start another Beehive over the same store only after a nil from the owning call.
type ChangeType ¶ added in v0.12.0
type ChangeType = storeapi.ChangeType
ChangeType classifies a Change.
type Client ¶
type Client[Spec, Status any] interface { // Create inserts a new object under name, which is required and immutable. // A name already held by a live or deletion-pending row fails with // ErrNameTaken — Create never writes to a row it found; use GetOrCreate // when "already there" is acceptable. The new object is unsettled and owed // its first reconcile. Create(ctx context.Context, name string, spec Spec, opts ...Option) (*Object[Spec, Status], error) // CreateOrUpdate makes whatever holds name hold spec, creating it if // absent; created says which. The resolve and the write are one // transaction. // // opts are honoured on create and IGNORED on update, as in GetOrCreate: a // created=false result says nothing about whether the row matches opts. A // deletion-pending row is refused with ErrDeletionPending rather than // rewritten, and its name stays held until GC releases it. // // Like GetOrCreate's, created is synchronous inside a caller's Within — // route create-conditional side effects through WithOnCreate. CreateOrUpdate(ctx context.Context, name string, spec Spec, opts ...Option) (*Object[Spec, Status], bool, error) // Delete soft-deletes id by setting DeletionRequestedAt. A registered kind // reaches its controller at commit; a client-only kind waits for the GC // sweeper, which takes every deletion from there either way. Returns // ErrNotFound if id holds no object — it was collected out from under the // caller, which is worth hearing about. Idempotent on an already-pending // row. Kind-scoped. Delete(ctx context.Context, id ObjectID) error // DeleteByName is Delete keyed by name: it acts on whatever holds name now. // Idempotent, and absence folds to nil — a name nothing holds is the // desired state — which is the one place it departs from Delete. DeleteByName(ctx context.Context, name string) error // Get loads id, or returns ErrNotFound. Kind-scoped: another kind's row is // not found. The read half of a read-modify-write, whose write half is // Update. Get(ctx context.Context, id ObjectID, loads ...LoadOption) (*Object[Spec, Status], error) // GetByName is Get keyed by name: it loads whatever holds name now, or // returns ErrNotFound. Kind-scoped — another kind's row holding the same // name is not found. GetByName(ctx context.Context, name string, loads ...LoadOption) (*Object[Spec, Status], error) // GetLatestEvent returns the current run in id's category timeline. ok is // false (with a nil error) when the timeline is empty. // // This, ListEvents and WatchEvents all read by id and are not kind-scoped: an // event carries no kind of its own, so any id's log reads through any client. // The write is the asymmetry — ControllerClient.AddEvent appends to the // object its pass was handed. GetLatestEvent(ctx context.Context, id ObjectID, category string) (Event, bool, error) // GetOrCreate returns the object with the given name, creating it from spec // if absent. It NEVER mutates an existing row: a name held by a live or // deletion-pending row is returned as-is with created=false, options // ignored — do not read created=false as "exists and matches opts". The // read-or-create is atomic, so concurrent creates can't both win. // // There is no name-keyed upsert: to change an existing row, follow with // Update. spec and opts are validated up front even when the row exists, so // a caller bug fails regardless of store state. // // The created bool is synchronous: inside a caller's Within it is set // before the transaction commits, so route create-conditional side effects // through WithOnCreate instead. A non-nil err means nothing was created — // a new row that fails to decode rolls back rather than committing bytes // the process can't read. GetOrCreate(ctx context.Context, name string, spec Spec, opts ...Option) (*Object[Spec, Status], bool, error) // GetOwner returns id's owner, if it has one; ok is false with a nil error // when it has none. The lazy counterpart to LoadOwner(). // // This and ListDependencies/ListDependents/ListOwned run their edge query // directly with no kind check: a foreign id reads that kind's edges and a // missing id reads empty — neither returns ErrNotFound. Use them for ids // this client owns. GetOwner(ctx context.Context, id ObjectID) (ObjectRef, bool, error) // GetSchedule reports when the reconcile loop has scheduled id to be // requeued (a pending backoff or RequeueAfter delay; for a queued id, the // moment it became due), or the zero Schedule when nothing is scheduled. A // non-blocking read of in-memory state: a missing or foreign id, and a // client-only kind, all read as the zero Schedule; the error is never // returned today. // // This is the next *scheduled* requeue, not a prediction: the periodic // passes and dependency wakes never appear here, so the real next reconcile // may be sooner. Treat it as observability; use WatchSchedule to follow it // live. GetSchedule(ctx context.Context, id ObjectID) (Schedule, error) List(ctx context.Context, loads ...LoadOption) ([]*Object[Spec, Status], error) // ListDependencies returns the objects id depends on (outgoing depends_on). // The lazy counterpart to LoadDependencies(). ListDependencies(ctx context.Context, id ObjectID) ([]ObjectRef, error) // ListDependents returns the objects that depend on id (incoming // depends_on). The lazy counterpart to LoadDependents(). ListDependents(ctx context.Context, id ObjectID) ([]ObjectRef, error) // ListEvents returns id's event-log runs, newest-first, filtered by opts. // Reads by id, not kind-scoped. An empty log is an empty slice. ListEvents(ctx context.Context, id ObjectID, opts ...EventOption) ([]Event, error) // ListOwned returns the objects id owns (its incoming owned_by edges). The // lazy counterpart to LoadOwned(). ListOwned(ctx context.Context, id ObjectID) ([]ObjectRef, error) // ListOwnedObjects returns the objects owned by ownerID that belong to THIS // client's kind, fully decoded — the typed, kind-scoped form of ListOwned, // resolved in one query instead of a Get per child. ownerID is typically // another kind and is not existence-checked: no children, or no such owner, // both read empty. Deletion-pending children are included; undecodable rows // are quarantined and logged, as in List. Takes the same LoadOptions as // List. ListOwnedObjects(ctx context.Context, ownerID ObjectID, loads ...LoadOption) ([]*Object[Spec, Status], error) // Requeue queues id for reconcile now. A latency hint, not a synchronous // run: correctness rests on the periodic drivers. By default it keeps id's // retry backoff — a requeue almost never proves a failure is over — pass // WithResetBackoff() when it is. Returns ErrNotFound if id does not exist, // ErrNoController if the kind has no controller. Requeue(ctx context.Context, id ObjectID, opts ...RequeueOption) error // Update replaces id's spec, or returns ErrNotFound (a missing row is not // "already in the desired state"). A spec whose bytes match what is stored // writes nothing at all, so a controller re-applying its own spec does not // wake itself forever. The write half of a read-modify-write. // // A row whose deletion has been requested is refused with // ErrDeletionPending, not written: a pass on a deleting row runs collection, // so the spec would be discarded. That is distinct from ErrNotFound, because // the answers differ — absent means create it, pending means you cannot, // since the name stays held until GC releases it. A caller whose object is // owned can do nothing and wait: a physical delete pushes its owner, so the // owner's next pass creates the replacement. Left unhandled inside a // Reconcile the sentinel surfaces as a failure and enters the retry ladder, // against a condition that clears on GC's schedule and not on retrying. Update(ctx context.Context, id ObjectID, spec Spec) (*Object[Spec, Status], error) // UpdateByName is Update keyed by name: it writes whatever holds name now, // resolving and writing in one transaction. Not for a read-modify-write — // a collect-and-recreate between the read and the write would land it on a // different incarnation; use Update. Same ErrDeletionPending refusal. UpdateByName(ctx context.Context, name string, spec Spec) (*Object[Spec, Status], error) // Watch returns one object's current state plus a stream of the changes // above it: Added/Modified/Deleted until ctx is cancelled. Kind-scoped, and // needs no registered controller — the tail reads the write log, not a // reconciler. It follows one incarnation: an id holding nothing is a nil // ObjectStream.Object rather than ErrNotFound, and a recreate under the // same name is a different id, so the stream ends at Deleted. // // The snapshot is read before Watch returns, on the caller's goroutine, so a // caller may subscribe and then act: a change it makes afterwards — including // a delete — is always in the stream. The snapshot's ResourceVersion is the // log position it is complete as of, and the stream carries changes strictly // above it: no overlap, no gap. A failed snapshot read is returned rather // than handed back as a stream whose guarantee is void. // // Everything after comes from the kind's shared tailer: a commit wakes it, // and a floor tick covers what a wake cannot. Delivery is latest-per-object, // so changes to one object collapse. A watch cannot be opened inside a // transaction: its snapshot takes the writer, which the transaction holds. // // The wider rule is worth stating on its own. Pass the ctx you were given to // every store call inside a transaction. An ordinary read on another ctx // runs on the read pool, so it does not block — it returns committed state, // silently missing the transaction's own writes. Watch(ctx context.Context, id ObjectID, opts ...WatchOption) (*ObjectStream[Spec, Status], error) // WatchEvents streams id's event log: a snapshot of the runs matching opts, // the position it was read at, and the runs the log grows by above it. An // extend re-samples ResourceVersion, so a run that grew is delivered again // carrying its latest state. WithEventsResumeFrom starts above a position // instead of snapshotting; one retention has passed is ErrWatchTooOld and // one above the log's head is ErrWatchTooNew. The stream reports the // configured EventRetention, so a caller holding runs in memory can bound // its own list. Reads by id, not kind-scoped. Requires a registered // controller for this client's kind, which is a property of the caller and // not of the target. WatchEvents(ctx context.Context, id ObjectID, opts ...EventOption) (*EventStream, error) // WatchList is Watch over every object of this client's kind: the same // snapshot-and-stream contract, tailer and errors. See Watch. WatchList(ctx context.Context, opts ...WatchOption) (*ObjectListStream[Spec, Status], error) // WatchOwnedObjects is ListOwnedObjects as a watch: a snapshot of // ownerID's children of this kind, then every change to one of them. A child // created under ownerID later arrives as Added and its collection as Deleted. // Same options, errors and shared tailer as WatchList. // // Assumes the one owner WithOwner can express, as GetOwner and Owner() do: a // child carrying several owned_by edges — reachable only through a direct // Store call — streams to one of them. See docs/TODO.md. WatchOwnedObjects(ctx context.Context, ownerID ObjectID, opts ...WatchOption) (*ObjectListStream[Spec, Status], error) // WatchSchedule streams id's schedule as a gauge: the current value, then // a new Schedule whenever it changes. Unlike the other watches it reports // in-memory state as the work queue moves it — no polling, emits only on // change. The channel closes when ctx is cancelled OR when the Beehive // stops (after delivering the final schedule); a reader cannot tell the two // apart. A client-only kind returns ErrNoController; id need not exist. WatchSchedule(ctx context.Context, id ObjectID) (<-chan Schedule, error) }
Client is the user-facing API for a single resource kind: creating, reading, updating, deleting, and watching objects.
type Condition ¶
type Condition struct {
Type string
Status ConditionStatus
Reason string
Message string
// Liveness marks a condition valid only within the writing process. The
// store downgrades a liveness condition written by a prior process to
// Unknown until a controller re-confirms it.
Liveness bool
// Unconfirmed reports that Status is a downgrade this process derived, not a
// status anyone wrote: a liveness condition an earlier process left behind,
// read as Unknown until a controller re-confirms it. Reason, Message and the
// stamps below are the pre-downgrade write's, so they describe the last known
// status rather than this Unknown.
Unconfirmed bool
// Set by the store on read, ignored on write. A downgraded liveness
// condition keeps the stored write's stamps.
TransitionedAt time.Time // when Status last changed
UpdatedAt time.Time // when the condition was last written
}
Condition is a standard observation about an object's state, reported by its controller (e.g. type "Ready", status True).
type ConditionStatus ¶
type ConditionStatus string
ConditionStatus is the state of a Condition: True, False, or Unknown.
const ( ConditionTrue ConditionStatus = "True" ConditionFalse ConditionStatus = "False" ConditionUnknown ConditionStatus = "Unknown" )
type Controller ¶
type Controller[Spec, Status any] interface { Reconcile(ctx context.Context, client ControllerClient[Status], obj *Object[Spec, Status]) ReconcileResult }
Controller is the user-supplied reconcile logic for a resource kind. Reconcile drives an object toward its desired state; the client is the status-write surface for this controller's kind. Build the return with Settled, Unsettled or Fail.
type ControllerClient ¶
type ControllerClient[Status any] interface { // AddDependency records that this pass's object depends on toID, so beehive // reconciles it again when toID changes. Every call that creates the edge // records one owed reconcile, durably and atomically with the edge, so a // declared dependency is a guarantee rather than a subscription. // Re-asserting existing edges records nothing. AddDependency(ctx context.Context, toID ObjectID) error // AddEvent adds an observation to the object's event log. Repeating the // latest run's (Category, Type, Reason) extends that run rather than // appending, so a controller can report every poll without growing the log // per poll. AddEvent(ctx context.Context, event EventSpec) error DeleteCondition(ctx context.Context, conditionType string) error DeleteDependency(ctx context.Context, toID ObjectID) error DeleteFinalizer(ctx context.Context, finalizer string) error // GetOwner returns the object's owner, if any. ok is false with a nil error // when it has none. GetOwner(ctx context.Context) (ObjectRef, bool, error) // HasIncomingEdges reports whether any object with a live claim still points // at this pass's object: an owned child, or a dependent that is not itself // being deleted. A finalizer can gate teardown on it. HasIncomingEdges(ctx context.Context) (bool, error) // ListDependencies returns the objects this one depends on (outgoing // depends_on). ListDependencies(ctx context.Context) ([]ObjectRef, error) // ListDependents returns the objects that depend on this one (incoming // depends_on). ListDependents(ctx context.Context) ([]ObjectRef, error) // ListOwned returns the objects this one owns (its incoming owned_by edges). ListOwned(ctx context.Context) ([]ObjectRef, error) // SetCondition writes the condition of that type. The store stamps // TransitionedAt and UpdatedAt; the passed values are ignored. Refuses text // that is not valid UTF-8 with ErrInvalidCondition, as SetConditions does. SetCondition(ctx context.Context, condition Condition) error // SetConditions writes every named condition together, under a single version // bump, so a watcher never sees half a pass. A type named twice is refused // with ErrDuplicateConditionType; text that is not valid UTF-8 — in Type, // Status, Reason or Message — with ErrInvalidCondition, which a Message // carrying a raw err.Error() can reach. An empty slice writes nothing. Same // stamping as SetCondition. SetConditions(ctx context.Context, conditions []Condition) error // UpdateStatus records status and nothing else — the handshake is beehive's, // recorded by returning Settled. Status that marshals to the bytes this pass // was loaded with writes nothing and reaches no store, so a controller can // report on every poll. That skip reads nothing, so it cannot report an // object collected mid-pass: it returns nil where a write returns ErrNotFound. UpdateStatus(ctx context.Context, status Status) error // Within runs fn inside a single transaction: writes made with fn's ctx all // commit together or roll back on error. Pass fn's ctx to every store call // it makes. A read on any other ctx does not join the transaction, and reads // run on their own connection, so it quietly returns state from before the // transaction rather than failing. Watches cannot be opened inside it. // // fn's ctx belongs to one goroutine. Issuing store calls on it from two at // once interleaves them on one prepared statement's cursor: each sees part of // the other's rows, and neither reports an error. Within(ctx context.Context, fn func(ctx context.Context) error) error }
ControllerClient is the write surface a controller uses to report observed state. It writes only Status and metadata — never Spec, which the user owns.
It is bound to the object its Reconcile was handed and acts on that object alone; the ids below are the other end of an edge. It lives for the one Reconcile it is passed to: afterwards every method returns ErrReconcileReturned, and there is no other way to hold one.
type DeletionCascadeChild ¶ added in v0.19.0
type DeletionCascadeChild = storeapi.DeletionCascadeChild
DeletionCascadeChild is one owned child of a deletion cascade, as DeletionRequests().CreateFromOwner reports it.
type Event ¶ added in v0.11.0
type Event struct {
ID EventID
ObjectID ObjectID
Category string
Type EventType
Reason string
Message string // latest occurrence's message
Detail json.RawMessage // latest occurrence's payload; nil = none
Count int // occurrences in this run (>= 1)
FirstAt time.Time // run start
LastAt time.Time // run end (latest occurrence)
// ResourceVersion orders the log and is what a watch resumes above. An
// extend re-samples it, so a run that grew carries a fresh one.
ResourceVersion int64
}
Event is one contiguous run of observations about an object, aggregated by (Category, Type, Reason).
type EventOption ¶ added in v0.11.0
type EventOption func(*eventConfig)
EventOption configures a Client.ListEvents / WatchEvents read.
func WithEventCategory ¶ added in v0.11.0
func WithEventCategory(category string) EventOption
WithEventCategory restricts a read to a single timeline. The category "" is the default timeline (distinct from "no filter", the absence of this option).
func WithEventLimit ¶ added in v0.11.0
func WithEventLimit(n int) EventOption
WithEventLimit caps a read to the newest n runs. On WatchEvents it bounds the snapshot only: a tail has no end to count back from.
func WithEventReason ¶ added in v0.11.0
func WithEventReason(reason string) EventOption
WithEventReason restricts a read to runs with the given reason.
func WithEventType ¶ added in v0.11.0
func WithEventType(t EventType) EventOption
WithEventType restricts a read to one severity (Normal or Warning).
func WithEventsResumeFrom ¶ added in v0.19.0
func WithEventsResumeFrom(rv int64) EventOption
WithEventsResumeFrom streams the runs above rv instead of taking a snapshot. WatchEvents only — the other reads ignore it, the way an Option ignores a target it does not recognise. A position retention has passed ends the stream with ErrWatchTooOld, answered by subscribing again without this option.
func WithEventsSince ¶ added in v0.11.0
func WithEventsSince(t time.Time) EventOption
WithEventsSince restricts a read to runs still active at or after t (LastAt >= t).
type EventRetention ¶ added in v0.23.0
type EventRetention struct {
// PerTimeline caps each (object, category) timeline to its newest N runs,
// so it bounds one stream only when the watch is scoped to one category.
PerTimeline int
// MaxAge drops runs whose window ended more than MaxAge ago, across every
// timeline.
MaxAge time.Duration
}
EventRetention is the event-log bound the GC sweeper enforces, as configured by WithEventRetention. A zero field is that bound unset.
type EventSpec ¶ added in v0.11.0
type EventSpec struct {
Category string // independent timeline; "" = default
Type EventType
Reason string // machine-readable token, e.g. "ProbeFailed"
Message string // human-readable; sampled, not keyed
Detail any // optional payload; marshaled on write; nil = none
}
EventSpec is the caller-supplied portion of an event, passed to ControllerClient.Events().Add. Consecutive emissions sharing (Category, Type, Reason) coalesce into one run; Message and Detail are sampled (latest wins).
type EventStream ¶ added in v0.19.0
type EventStream struct {
// Runs is the snapshot, newest-first like Events().List. Empty on a resume.
Runs []Event
// ResourceVersion is the position Runs was read at, and the value to hand
// back to WithEventsResumeFrom.
ResourceVersion int64
// Events delivers the runs above ResourceVersion, oldest-first, until ctx
// ends or the stream fails. Closed exactly once.
Events <-chan Event
// Retention is the bound the sweeper enforces on this log, so a consumer
// holding runs in memory can size its own list from it. Zero fields mean
// unbounded; it is process configuration, fixed for the stream's life.
Retention EventRetention
// contains filtered or unexported fields
}
EventStream is a live view of one object's event log: the runs matching the query as of the subscribe, the position they were read at, and what the log grows by after it.
func (*EventStream) Err ¶ added in v0.19.0
func (s *EventStream) Err() error
Err reports why the stream ended, after Events is closed: ErrWatchTooOld, ErrNotFound for a collected object, ErrStopped, or nil when the caller's own context ended.
type EventsAddInput ¶ added in v0.19.0
type EventsAddInput = storeapi.EventsAddInput
EventsAddInput is the write shape Events().Add accepts — only the fields a recorded observation carries.
type LoadOption ¶ added in v0.4.0
type LoadOption func(*LoadSet)
LoadOption selects a secondary lookup to fetch alongside an object on a read (Get/GetByName/List). The lazy alternative: omit it and call Client.GetOwner/ListDependencies when the data is needed.
func LoadDependencies ¶ added in v0.4.0
func LoadDependencies() LoadOption
LoadDependencies selects the objects this one depends on (outgoing depends_on).
func LoadDependents ¶ added in v0.4.0
func LoadDependents() LoadOption
LoadDependents selects the objects that depend on this one (incoming depends_on).
func LoadEvents ¶ added in v0.11.0
func LoadEvents() LoadOption
LoadEvents selects the object's event-log runs, read via Object.Events(). For filtered or bounded reads use the lazy Client.ListEvents instead.
func LoadOwned ¶ added in v0.5.0
func LoadOwned() LoadOption
LoadOwned selects the objects this one owns (its incoming owned_by edges).
func LoadOwner ¶ added in v0.4.0
func LoadOwner() LoadOption
LoadOwner selects the object's owner (its outgoing owned_by edge).
type LoadSet ¶ added in v0.4.0
type LoadSet uint8
LoadSet is a bitset of secondary lookups (owner, dependencies, dependents, owned, events) to fetch alongside an object. The zero value loads nothing.
const ( // LoadOwnerBit selects the object's owner (its outgoing owned_by edge). LoadOwnerBit LoadSet = 1 << iota // LoadDependenciesBit selects the object's dependencies (outgoing depends_on). LoadDependenciesBit // LoadDependentsBit selects the objects that depend on it (incoming depends_on). LoadDependentsBit // LoadOwnedBit selects the objects this one owns (incoming owned_by edges). LoadOwnedBit // LoadEventsBit selects the object's event-log runs. LoadEventsBit )
type Migrator ¶ added in v0.3.0
type Migrator interface {
// SchemaVersionSpec is the spec schema version this build writes.
// 0 means spec is not versioned for this kind.
SchemaVersionSpec() int
// SchemaVersionStatus is the status schema version this build writes.
// 0 means status is not versioned for this kind.
SchemaVersionStatus() int
// ConvertSpec upgrades spec bytes written at version from to the current
// version. Called only when 0 <= from < SchemaVersionSpec(); from == 0 is
// the unversioned baseline and must be handled.
ConvertSpec(from int, raw json.RawMessage) (json.RawMessage, error)
// ConvertStatus upgrades status bytes written at version from to the
// current version; same contract as ConvertSpec.
ConvertStatus(from int, raw json.RawMessage) (json.RawMessage, error)
}
Migrator upgrades a kind's stored Spec/Status JSON to the shape this build expects, at the decode boundary. Spec and Status carry independent versions and convert independently; a current version of 0 means "not versioned". Conversion is lazy: bytes are upgraded on read and re-stamped only when the blob is next written. Register one per kind via WithMigrator.
type Object ¶
type Object[Spec, Status any] struct { ID ObjectID Group string Kind string Name string Spec Spec Status *Status Generation int64 // bumped on every Spec write that isn't a no-op ObservedGeneration *int64 // Generation the controller last reconciled; nil until first reconcile ObservedAt *time.Time // when ObservedGeneration was recorded; not a reconcile heartbeat ResourceVersion int64 // bumped on every write DeletionRequestedAt *time.Time // set when deletion is requested; object lingers until finalizers clear Finalizers []string Conditions []Condition // per-type observations reported by controllers CreatedAt time.Time UpdatedAt time.Time // contains filtered or unexported fields }
Object is a single resource: user-owned desired state (Spec) plus controller-owned observed state (Status), along with the metadata Beehive uses to track convergence and deletion.
func (*Object[Spec, Status]) Dependencies ¶ added in v0.4.0
Dependencies returns the objects this one depends on, or ErrNotLoaded if LoadDependencies() was not passed to the read.
func (*Object[Spec, Status]) Dependents ¶ added in v0.4.0
Dependents returns the objects that depend on this one, or ErrNotLoaded if LoadDependents() was not passed to the read.
func (*Object[Spec, Status]) Events ¶ added in v0.19.0
Events returns the object's event-log runs, newest-first, or ErrNotLoaded if LoadEvents() was not passed to the read.
type ObjectChange ¶ added in v0.19.0
type ObjectChange[Spec, Status any] struct { Type ChangeType // ID is the object this change is about, set whether or not Object is. ID ObjectID // ResourceVersion is the log position this change was reported at, and what // WithResumeFrom takes to continue from here. ResourceVersion int64 Object *Object[Spec, Status] }
ObjectChange reports a change to a watched object. On a Deleted change, Object carries the row's final state, or is nil when that state could not be decoded — the removal is reported either way, because nothing later in the log mentions a deleted id. Why a stream ended is reported beside it, by the stream's own Err.
type ObjectListStream ¶ added in v0.23.0
type ObjectListStream[Spec, Status any] struct { // Objects is the snapshot. Empty on a resume. Objects []*Object[Spec, Status] // ResourceVersion is the position Objects is complete as of, and the value // to hand back to WithResumeFrom. ResourceVersion int64 // Changes delivers the changes above ResourceVersion, ascending by resource // version, until ctx ends or the stream fails. Closed exactly once. Changes <-chan ObjectChange[Spec, Status] // contains filtered or unexported fields }
ObjectListStream is ObjectStream over many objects: a kind, or one owner's children of a kind.
func (ObjectListStream) Err ¶ added in v0.23.0
func (f ObjectListStream) Err() error
Err reports why the stream ended, after its change channel is closed: ErrWatchTooOld for a stream that fell below retention, ErrWatchTooNew for a resume position this store never issued, ErrStopped for a Beehive that stopped, or nil when the caller's own context ended. Before the close it reports nil, which says nothing.
type ObjectRef ¶ added in v0.19.0
ObjectRef identifies a related object — an owner, a dependency, or a dependent — carrying the GroupKind needed to address it.
type ObjectStream ¶ added in v0.23.0
type ObjectStream[Spec, Status any] struct { // Object is the current state, or nil when the id holds nothing yet. Nil on // a resume. Object *Object[Spec, Status] // ResourceVersion is the position Object was read at, and the value to hand // back to WithResumeFrom. ResourceVersion int64 // Changes delivers the changes above ResourceVersion, ascending by resource // version, until ctx ends or the stream fails. Closed exactly once. Changes <-chan ObjectChange[Spec, Status] // contains filtered or unexported fields }
ObjectStream is a live view of one object: its state as of the subscribe, the position that state was read at, and the changes above it.
func (ObjectStream) Err ¶ added in v0.23.0
func (f ObjectStream) Err() error
Err reports why the stream ended, after its change channel is closed: ErrWatchTooOld for a stream that fell below retention, ErrWatchTooNew for a resume position this store never issued, ErrStopped for a Beehive that stopped, or nil when the caller's own context ended. Before the close it reports nil, which says nothing.
type ObjectWrite ¶ added in v0.19.0
type ObjectWrite = storeapi.ObjectWrite
ObjectWrite is one entry of the object write log.
type ObjectsCreateInput ¶ added in v0.19.0
type ObjectsCreateInput = storeapi.ObjectsCreateInput
ObjectsCreateInput is the write shape Objects().Create accepts — only the fields a create honours.
type Option ¶
Option configures a target — a Beehive, a reconciler, or a per-object operation — depending on where it is passed. Each option type-switches on the targets it understands and ignores the rest.
func WithConcurrency ¶
WithConcurrency sets the number of concurrent worker goroutines for a controller; <= 1 means single-threaded (the default). Passed to New it is the default for all controllers; passed to Register it overrides one.
func WithEventRetention ¶ added in v0.11.0
WithEventRetention bounds the event log, enforced globally by the GC sweeper. perTimeline > 0 caps each (object, category) timeline to its newest perTimeline runs — runs, not occurrences, since an extend grows a run in place; maxAge > 0 drops runs whose window ended more than maxAge ago, across every timeline. A zero bound is skipped; both zero (the default) leaves the log unbounded. The sweeper enforces the cap on its own interval, so a burst can sit above it until the next sweep. Meaningful only at New.
func WithFinalizers ¶
WithFinalizers attaches finalizers that must be cleared before an object is physically deleted.
It requires a controller registered for the kind in this process, and the call is rejected with ErrInvalidOption otherwise: only ControllerClient.DeleteFinalizer can clear a finalizer, so one no controller here can remove would leave the row deletion-pending forever, RESTRICT- blocking its owner's delete. The check is process-local and evaluated at call time — the store tracks no registrations — so register the kind first.
func WithFullPassInterval ¶ added in v0.19.0
WithFullPassInterval sets how often a controller re-dispatches *every* object it owns, converged or not. Default 0 (disabled).
This is the expensive pass, and the only one that reaches an object nothing has recorded as owing work — process-scoped state a restart invalidated, or a wake lost for a reason nothing observed. It is opt-in because its cost scales with the object count, and convergence is already covered by the owed pass and the startup pass. It does not pace the owed-work tick.
func WithGCInterval ¶ added in v0.19.0
WithGCInterval sets how often the global GC sweeper runs: collecting deletion-pending objects of every kind, applying event-log retention, and releasing freed space. Meaningful only at New.
Unlike WithFullPassInterval it cannot be disabled: d <= 0 is rejected with ErrInvalidOption. Nothing on the public surface triggers collect, so a sweeper-less Beehive would strand deletion-pending rows with no recourse. A long interval expresses "collect rarely"; there is no "never".
A deletion cascade over *registered* kinds advances a level per commit, so this is not its latency. A client-only level has no push at all and costs one interval, so a subtree of client-only kinds takes one interval per level. The sweeper's per-sweep work budgets scale with d, so a longer interval trims and reclaims proportionally more rather than at a lower rate. See docs/adr/2026-08-06-driver-cadences-are-configurable.md.
func WithIndividualPassInterval ¶ added in v0.27.0
WithIndividualPassInterval gives every object of the kind a pass roughly every d, measured from the end of each object's own last pass. Default 0 (disabled).
It schedules a pass that returned settled without asking to be requeued; every other result keeps its own schedule, so d is a default cadence rather than a ceiling. Armings are jittered upward, and a scan at startup spreads the first pass of each object across d — pair it with WithStartupFullPass for a kind that needs that first pass promptly. See docs/adr/2026-08-19-an-individual-pass-interval.md.
Passed to New it sets the default for all controllers; passed to Register it overrides that default for one.
func WithLogLevel ¶
WithLogLevel sets the minimum level beehive emits, on top of whatever the logger's own handler filters. No effect without WithLogger. Passed to New it applies to the control plane and is the default for all controllers; passed to Register it overrides one.
func WithLogger ¶
WithLogger routes beehive's internal logging through l (zap, zerolog, logrus and logr all ship slog bridges). A nil logger disables logging entirely, which is the default. Passed to New it sets the control plane's logger and the default for all controllers; passed to Register it overrides one.
func WithMaxRetryInterval ¶
WithMaxRetryInterval caps the exponential backoff between failed reconciles for a controller. A value <= 0 is ignored, keeping the default — a non-positive cap would busy-loop the reconciler on a persistent error.
The cap bounds the retry rate outright: a wake arriving while the object sits on its backoff alarm is absorbed by that alarm rather than dispatched.
func WithMigrator ¶ added in v0.3.0
WithMigrator registers a Migrator for the controller's kind, applied to stored Spec/Status JSON on read. Meaningful only at Register, which installs it into the registry both the client and the reconciler decode through.
func WithOnCreate ¶ added in v0.17.0
WithOnCreate registers fn to run once, on the caller's ctx, only if the call actually inserts a new row — and only after the outermost transaction commits (Store.AfterCommit), so a rollback never runs it. Use it for create-conditional side effects instead of GetOrCreate's returned bool, which inside a caller's Within reports true before the transaction commits.
func WithOwedPassInterval ¶ added in v0.19.0
WithOwedPassInterval sets how often a controller drains work the store records as owed: unconverged specs and owed dependency wakes. Default 30s. Dispatches at New and at Register, so one kind can differ from the rest.
Every trigger for a registered kind pushes at commit, so this is not the latency of a local write. What lengthening it costs is how long a *lost* push waits — a crash between the commit and the dispatch, a stamp made while no reconciler was registered for the source's kind — plus the first drain after a restart, which runs at startup regardless. See docs/adr/2026-08-06-driver-cadences-are-configurable.md.
Cannot be disabled: d <= 0 is rejected with ErrInvalidOption. Unlike WithFullPassInterval, this pass is what makes convergence a guarantee rather than an optimisation, and its cost is bounded by what is outstanding rather than by the object count — so "rarely" is expressible and "never" is not.
func WithStaleDependentsInterval ¶ added in v0.19.0
WithStaleDependentsInterval sets how often the stale-dependents pass re-derives which dependents a dependency has moved under. Default 60s. Global and meaningful only at New.
The dependency waker propagates a target change per commit, so this is not dependency latency. What lengthening it costs is how long a dependent stays stale when that wake was *lost* — a crash before the scan, a failed seed, a write no waker cursor covers — and it is the only thing that re-derives, so a long value is a long window. It also paces the waker's abandon jump, which hands a range to this pass on the argument that it has already swept it.
Cannot be disabled: d <= 0 is rejected with ErrInvalidOption.
func WithStartupFullPass ¶ added in v0.19.0
WithStartupFullPass sets whether a controller re-dispatches *every* object once at startup, converged ones included. Default false.
Enable it for a kind whose reconcile establishes in-process state — a live connection, a running worker, a liveness condition — which a restart invalidated and no owed-work listing can see: the store reads settled, because observed_generation was written by a process that is gone.
Unlike the periodic full pass, a kind may depend on this one. It costs O(objects) once per process, and for a controller whose reconcile is what opens the connection or starts the worker it is the only thing that reconverges the object after a restart. What it guarantees: every object of a kind that enables it is reconciled at least once per process.
Passed to New it sets the default for all controllers; passed to Register it overrides that default for one — prefer Register, so the declaration names the kinds that actually own in-process state.
func WithTriggerByID ¶ added in v0.28.0
WithTriggerByID requeues each id received on ch, as Client.Requeue would — retry backoff and all. An id naming nothing, or an object of another kind, is a no-op, and closing ch ends that feed once what it already sent has drained. Repeated options accumulate; meaningful only at Register, and a channel serves one kind.
A poke is a latency hint that nothing records, so correctness rests on the kind's own cadence. See docs/adr/2026-08-19-a-trigger-channel-requeues-by-id-or-name.md.
func WithTriggerByName ¶ added in v0.28.0
WithTriggerByName is WithTriggerByID keyed by name, carrying the same contract. A name matching nothing — "" included — is a no-op, since whether a record exists for an address is the app's business and changes under it.
func WithWatchFloorInterval ¶ added in v0.19.0
WithWatchFloorInterval sets how often a watch reads without a wake — a kind's tailer, and an object's event reader. Default 30s. Global and meaningful only at New.
Both watch families read on a commit wake, so this is not delivery latency for anything this Beehive writes. What lengthening it costs is staleness for what a wake cannot cover: a retention trim, a step that failed after its retry ladder gave up, and a write by a second writer over the same store — which is an unsupported deployment, not a slow one. A failed read still retries on its own ladder, capped in seconds, whatever this is set to.
Cannot be disabled: d <= 0 is rejected with ErrInvalidOption.
func WithWriteLogRetention ¶ added in v0.19.0
WithWriteLogRetention bounds the object write log, enforced globally by the GC sweeper. perKind > 0 caps each (group, kind) log to its newest perKind entries — per kind, so a hot kind cannot evict a quiet one; maxAge > 0 drops entries written more than maxAge ago. A zero bound is skipped.
The default is defaultWriteLogMaxAge and no count bound; both zero leaves the log unbounded, which also leaves every resume window unbounded. Retention is what defines that window: a stream cannot resume below what has been trimmed. Meaningful only at New.
type RawObject ¶
RawObject is the untyped row below the generic boundary: opaque Spec/Status JSON plus Beehive-owned metadata.
type ReconcileResult ¶ added in v0.24.0
type ReconcileResult struct {
// contains filtered or unexported fields
}
ReconcileResult is what Reconcile returns. Build it with Settled, Unsettled or Fail; the zero value fails the pass with ErrInvalidResult.
func Fail ¶ added in v0.24.0
func Fail(err error) ReconcileResult
Fail reports a failed pass: settles nothing, takes the backoff ladder. A nil err is itself a failure, reported as ErrInvalidResult.
func Settled ¶ added in v0.24.0
func Settled() ReconcileResult
Settled reports that the pass observed the object's current generation, which beehive records. It claims nothing about health. Schedules nothing of its own.
func Unsettled ¶ added in v0.24.0
func Unsettled() ReconcileResult
Unsettled reports a successful pass over an object not caught up to its spec, so no generation is recorded. Requeues at the work queue's per-object floor unless RequeueAfter says otherwise.
func (ReconcileResult) Err ¶ added in v0.25.0
func (r ReconcileResult) Err() error
Err returns the error a failed pass carries, or nil for a successful one. The zero value and Fail(nil) report ErrInvalidResult, the failure beehive records for them.
func (ReconcileResult) RequeueAfter ¶ added in v0.25.0
func (r ReconcileResult) RequeueAfter(d time.Duration) ReconcileResult
RequeueAfter schedules the object's next pass, overriding what the result kind schedules on its own. Zero or less requeues as soon as the work queue's per-object floor allows. Ignored on a failed result, which takes the backoff ladder.
type RequeueOption ¶ added in v0.8.0
type RequeueOption func(*requeueOptions)
RequeueOption configures a Client.Requeue call.
func WithResetBackoff ¶ added in v0.8.0
func WithResetBackoff() RequeueOption
WithResetBackoff makes a Requeue clear the object's retry backoff ladder. Pass it only when the failure condition is known to be resolved; a plain Requeue preserves the ladder.
type Schedule ¶ added in v0.13.0
type Schedule struct {
// NextRequeueAt is when the reconcile loop has scheduled the object to be
// requeued, or the zero time when nothing is scheduled. It reflects only
// per-id timers (backoff, a result's requeue delay, an immediate enqueue),
// not the periodic drivers.
NextRequeueAt time.Time
}
Schedule reports when an object is next due to reconcile. A struct so fields can be added without a breaking change.
type StalePos ¶ added in v0.19.0
StalePos is a position in the stale-dependents scan. See storeapi.StalePos.
type Store ¶
Store is the durable-store contract Beehive depends on. It is non-generic and deals only in raw rows; the generic boundary lives in the typedController adapter. See storeapi.Store for the full contract.
type WatchOption ¶ added in v0.19.0
type WatchOption func(*watchConfig)
WatchOption configures one watch call. A distinct type from Option: these are meaningful only here, and dispatching them on a Beehive or a controller would silently accept nonsense.
func WithLoads ¶ added in v0.19.0
func WithLoads(loads ...LoadOption) WatchOption
WithLoads eager-loads the same secondary lookups List takes, on the snapshot and on every delivered batch. Batched per batch, not per object, so a watch does not become an N+1.
func WithResumeFrom ¶ added in v0.19.0
func WithResumeFrom(rv int64) WatchOption
WithResumeFrom streams the changes above rv instead of taking a snapshot. The returned stream holds no objects and carries rv back. A position retention has already passed ends the stream with Err reporting ErrWatchTooOld — the same way a live stream reports it — which the caller answers by subscribing again without this option. A position above the log's head arrives the same way with ErrWatchTooNew: it did not come from this store, so no retention window would have kept it.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
internal
|
|
|
driver
Package driver holds the two periodic-scan loop shapes, plus the timer and backoff primitives a wake-driven loop builds its own shape from.
|
Package driver holds the two periodic-scan loop shapes, plus the timer and backoff primitives a wake-driven loop builds its own shape from. |
|
logging
Package logging resolves the user-supplied logger into the never-nil, optionally level-gated *slog.Logger the rest of beehive logs through.
|
Package logging resolves the user-supplied logger into the never-nil, optionally level-gated *slog.Logger the rest of beehive logs through. |
|
rategate
Package rategate holds a key for a fixed interval after it acts.
|
Package rategate holds a key for a fixed interval after it acts. |
|
sqlitemigrate
Package sqlitemigrate is a tiny, forward-only SQL migration runner for SQLite.
|
Package sqlitemigrate is a tiny, forward-only SQL migration runner for SQLite. |
|
storeapi
Package storeapi defines the storage contract between the beehive control plane and its store implementations (e.g.
|
Package storeapi defines the storage contract between the beehive control plane and its store implementations (e.g. |
|
Package sqlite provides a durable, SQLite-backed implementation of the beehive Store.
|
Package sqlite provides a durable, SQLite-backed implementation of the beehive Store. |