Documentation
¶
Overview ¶
Package eventing implements funcd eventing (ADR-0023, reshaped by ADR-0108): a typed CloudEvents v1.0 envelope, an EventSource reconciler that registers a `timer:` source's NAMED events, and — on each firing — PUBLISHES a named CloudEvent onto a Publisher seam (the in-process Fanout) the F69 Sensor subscribes to. It owns only KindEventSource (one-reconciler-per-gvk, ADR-0015); ticking is a side Run loop, not the reconciler. The action side (invoke a function / start a workflow) is the Sensor (ADR-0109); the webhook source kind and bus-backed delivery are documented deferrals.
Index ¶
- func ParseSourceURI(uri string) (ns v1.NamespaceName, source v1.ObjectName, ok bool)
- func SourceURI(ns v1.NamespaceName, source v1.ObjectName) string
- func WatermarkContract(t *testing.T, newWatermark func(t *testing.T) Watermark)
- type BlobEventData
- type BlobWatcher
- func (w *BlobWatcher) ActiveWatches() int
- func (w *BlobWatcher) Advance(ctx context.Context, ns v1.NamespaceName, source v1.ObjectName) error
- func (w *BlobWatcher) Deregister(ns v1.NamespaceName, source v1.ObjectName)
- func (w *BlobWatcher) Pending(ctx context.Context, ns v1.NamespaceName, source v1.ObjectName) (map[string]int, error)
- func (w *BlobWatcher) Purge(ctx context.Context, ns v1.NamespaceName, source v1.ObjectName) error
- func (w *BlobWatcher) Register(ns v1.NamespaceName, source v1.ObjectName, uid v1.UID, bs *v1.BlobSource)
- func (w *BlobWatcher) Run(ctx context.Context) error
- func (w *BlobWatcher) SetHold(g hold.Gate)
- func (w *BlobWatcher) SetHooks(h WatchHooks)
- func (w *BlobWatcher) SetNamespaceHold(g hold.NamespaceGate)
- type BucketLister
- type CloudEvent
- type Deps
- type Fanout
- type MemWatermark
- func (m *MemWatermark) Delete(_ context.Context, ns v1.NamespaceName, source v1.ObjectName) error
- func (m *MemWatermark) ListSources(_ context.Context) ([]SourceRef, error)
- func (m *MemWatermark) Load(_ context.Context, ns v1.NamespaceName, source, event v1.ObjectName) (SeenList, error)
- func (m *MemWatermark) Range(fn func(ns v1.NamespaceName, source, event v1.ObjectName, s SeenList))
- func (m *MemWatermark) Save(_ context.Context, ns v1.NamespaceName, source, event v1.ObjectName, ...) error
- type Publisher
- type SeenList
- type Source
- type SourceRef
- type WatchHooks
- type Watermark
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func ParseSourceURI ¶
func ParseSourceURI(uri string) (ns v1.NamespaceName, source v1.ObjectName, ok bool)
ParseSourceURI is the inverse of SourceURI: it recovers (namespace, source) from a CloudEvent's `source` field so a subscriber can route by structured key (ADR-0108). ok=false for a foreign URI.
func SourceURI ¶
func SourceURI(ns v1.NamespaceName, source v1.ObjectName) string
SourceURI is the canonical CloudEvent `source` for an EventSource (ADR-0108).
func WatermarkContract ¶ added in v0.11.0
WatermarkContract exercises the Watermark port against a driver (ADR-0157, ADR-0201): every driver's test runs it, so the event store's seen-list view on disk and in memory and MemWatermark are held to one behavior.
Types ¶
type BlobEventData ¶
type BlobEventData struct {
Bucket string `json:"bucket"`
Key string `json:"key"`
Size int64 `json:"size"`
Version string `json:"version"`
Time v1.Timestamp `json:"time"`
}
BlobEventData is the `data` payload of a blob CloudEvent (ADR-0119, F83): what landed under a watched Bucket prefix, for a Sensor to project (e.g. `${{ event.data.key }}`). Version is a (ModTime,Size) fingerprint of the observed object version — NOT a content ETag (a `data.etag` from blob.Attributes.MD5, ADR-0159, is ADR-0119's follow-up).
type BlobWatcher ¶
type BlobWatcher struct {
// contains filtered or unexported fields
}
BlobWatcher polls each registered blob EventSource, diffs each prefix against the persisted SeenList, and publishes one named CloudEvent per new or rewritten object through the existing ADR-0108 Publisher (ADR-0119, ADR-0157). Registration is driven by the KindEventSource reconciler (ADR-0015 one-per-gvk); Run is a side loop started by the pkg/funcd lifecycle. A key is recorded after its publish, so a crash between publish and save re-emits next poll (at-least-once; the Sensor/ADR-0118 absorb it).
func NewBlobWatcher ¶
func NewBlobWatcher(lister BucketLister, pub Publisher, marks Watermark, interval time.Duration, log *slog.Logger) (*BlobWatcher, error)
NewBlobWatcher builds the watcher. lister/publisher/marks are required; interval ≤ 0 ⇒ the 15s default.
func (*BlobWatcher) ActiveWatches ¶
func (w *BlobWatcher) ActiveWatches() int
ActiveWatches reports the number of registered named blob events (observability + the reconcile scenario).
func (*BlobWatcher) Advance ¶ added in v0.11.0
func (w *BlobWatcher) Advance(ctx context.Context, ns v1.NamespaceName, source v1.ObjectName) error
Advance marks every key the source's events list as seen and publishes nothing (ADR-0206 Decision 8), so the polls after a release replay none of them. A source with no registered event is fault.Unavailable: it is not Ready. Rerun-safe: each event's record is saved on its own.
func (*BlobWatcher) Deregister ¶
func (w *BlobWatcher) Deregister(ns v1.NamespaceName, source v1.ObjectName)
Deregister removes every named event of one EventSource and keeps its records (BucketNotFound).
func (*BlobWatcher) Pending ¶ added in v0.11.0
func (w *BlobWatcher) Pending(ctx context.Context, ns v1.NamespaceName, source v1.ObjectName) (map[string]int, error)
Pending counts, per event of the source, the listed keys whose version its seen list lacks: what a release replays (ADR-0206 Decision 1). A source with no registered event has none.
func (*BlobWatcher) Purge ¶ added in v0.4.0
func (w *BlobWatcher) Purge(ctx context.Context, ns v1.NamespaceName, source v1.ObjectName) error
Purge removes every named event of one EventSource and deletes its records (delete, kind change). The incremented purge epoch makes a poll in flight stop without saving, also across a same-UID Register.
func (*BlobWatcher) Register ¶
func (w *BlobWatcher) Register(ns v1.NamespaceName, source v1.ObjectName, uid v1.UID, bs *v1.BlobSource)
Register (re)registers every named event of a blob source and prunes events dropped from the spec — the reconciler calls it on a Ready blob EventSource. It stamps the source's current purge epoch into each entry and never increments it. A pruned event keeps its record until the source is purged (ADR-0157 Decision 5).
func (*BlobWatcher) Run ¶
func (w *BlobWatcher) Run(ctx context.Context) error
Run sweeps orphan records once, then polls the registered set every interval until ctx is cancelled. Started by the pkg/funcd lifecycle beside the timer Source.Run; it is not part of the reconciler. While held it neither sweeps nor polls; the first tick after the release sweeps, then polls replay what the seen lists lack (ADR-0206 Decision 6).
func (*BlobWatcher) SetHold ¶ added in v0.11.0
func (w *BlobWatcher) SetHold(g hold.Gate)
SetHold installs the platform hold before Run (ADR-0206): while held Run neither sweeps nor polls.
func (*BlobWatcher) SetHooks ¶ added in v0.4.0
func (w *BlobWatcher) SetHooks(h WatchHooks)
SetHooks installs the store hooks the start sweep and the save-failure condition use.
func (*BlobWatcher) SetNamespaceHold ¶ added in v0.11.0
func (w *BlobWatcher) SetNamespaceHold(g hold.NamespaceGate)
SetNamespaceHold installs the per-namespace hold before Run (ADR-0222 Decision 8): a held namespace's sources are not polled.
type BucketLister ¶
type BucketLister interface {
List(ctx context.Context, ns v1.NamespaceName, bucket v1.ObjectName, prefix string) ([]blob.Attributes, error)
}
BucketLister resolves a Bucket resource to its scoped blob listing surface (ADR-0007/0080) and lists a prefix over it. The V1 driver reuses pkg/funcd's s3BucketFor resolver — the SAME blob.Bucket view external S3-frontend writes land in — so detection is writer-agnostic (it depends only on List). A missing Bucket resolves as fault.NotFound.
type CloudEvent ¶
type CloudEvent struct {
SpecVersion string `json:"specversion"`
ID string `json:"id"`
Source string `json:"source"` // funcd://<ns>/eventsource/<name>
Type string `json:"type"` // the event name (ADR-0108)
Subject string `json:"subject,omitempty"` // a SQL change's table (ADR-0235)
Time v1.Timestamp `json:"time"`
DataContentType string `json:"datacontenttype,omitempty"`
Data json.RawMessage `json:"data,omitempty"`
}
CloudEvent is a CloudEvents v1.0 envelope serialized in the JSON event format. Hand-defined (no cloudevents/sdk-go); a later SDK swap stays behind this type.
func NewBlobEvent ¶
func NewBlobEvent(ns v1.NamespaceName, source, event v1.ObjectName, d BlobEventData) (CloudEvent, error)
NewBlobEvent builds a named CloudEvent for a landed object (ADR-0119): the same envelope as NewNamedEvent (source URI carries ns+source, type carries the event name), but `data` is the object descriptor instead of the timer's empty `{}`. It flows the unchanged Fanout → Sensor path.
func NewNamedEvent ¶
func NewNamedEvent(ns v1.NamespaceName, source, event v1.ObjectName) (CloudEvent, error)
NewNamedEvent builds a well-formed named CloudEvent for one event of an EventSource (ADR-0108): the `source` URI carries the namespace + source name, `type` carries the event name. A timer event has an empty `{}` payload. The id is a fresh random hex string (unique per call).
type Deps ¶
type Deps struct {
Store store.Store // required
Publisher Publisher // where a firing emits its named CloudEvent (required)
Blob *BlobWatcher // ADR-0119: the poll watcher a `blob:` source registers on; nil ⇒ no blob support
Logger *slog.Logger // default slog.Default()
Clock clock.Clock // ADR-0182: the timer seed and the Run loop read it; default clock.System()
// BucketRecheckInterval is eventing.bucketRecheckInterval (ADR-0163); 0 ⇒ bucketRecheckInterval.
BucketRecheckInterval time.Duration
// Hold is the platform hold (ADR-0206): while held the Run tick fires no timer; nil ⇒ never held.
Hold hold.Gate
// NamespaceHold is the per-namespace hold (ADR-0222 Decision 8): a held namespace's timers neither fire nor
// advance; nil ⇒ none held.
NamespaceHold hold.NamespaceGate
}
Deps configures the eventing Source (internal component, ADR-0002 §1; reshaped by ADR-0108, extended by ADR-0119).
type Fanout ¶
type Fanout struct {
// contains filtered or unexported fields
}
Fanout is the in-process Publisher driver (ADR-0108): a fired CloudEvent is delivered to every live subscriber whose (namespace, source, event) key matches it. Routing derives that key from the envelope — ParseSourceURI(ev.Source) + ev.Type — the inverse of NewNamedEvent, so the URI is the single canonical name↔key mapping. The F69 Sensor subscribes; a firing to no subscriber is a no-op. Delivery is non-durable (a fire while a subscriber is down is lost); a bus-backed Publisher is a V2 swap.
func (*Fanout) Publish ¶
func (f *Fanout) Publish(ctx context.Context, ev CloudEvent) error
Publish delivers ev to every subscriber of its derived (ns, source, event) key. A source URI that is not a funcd EventSource URI is dropped (never an error) — the seam only carries funcd named events.
func (*Fanout) Subscribe ¶
func (f *Fanout) Subscribe(ns v1.NamespaceName, source, event v1.ObjectName, fn func(context.Context, CloudEvent)) (cancel func())
Subscribe registers fn for the named event (ns, source, event); the returned cancel deregisters it.
func (*Fanout) Subscribed ¶ added in v0.13.0
func (f *Fanout) Subscribed(ns v1.NamespaceName, source, event v1.ObjectName) bool
Subscribed reports whether the named event (ns, source, event) has a subscriber, as the SQL change feed's boot gate asks (ADR-0235 Decision 4).
type MemWatermark ¶
type MemWatermark struct {
// contains filtered or unexported fields
}
MemWatermark is the in-process, non-durable Watermark driver: the event store's seen lists in memory (ADR-0201).
func NewMemWatermark ¶
func NewMemWatermark() *MemWatermark
NewMemWatermark builds an empty in-memory Watermark driver.
func (*MemWatermark) Delete ¶ added in v0.4.0
func (m *MemWatermark) Delete(_ context.Context, ns v1.NamespaceName, source v1.ObjectName) error
Delete removes every event record of one source.
func (*MemWatermark) ListSources ¶ added in v0.4.0
func (m *MemWatermark) ListSources(_ context.Context) ([]SourceRef, error)
ListSources returns each source with at least one record, once.
func (*MemWatermark) Load ¶
func (m *MemWatermark) Load(_ context.Context, ns v1.NamespaceName, source, event v1.ObjectName) (SeenList, error)
Load returns a copy of the stored SeenList for (ns, source, event).
func (*MemWatermark) Range ¶ added in v0.11.0
func (m *MemWatermark) Range(fn func(ns v1.NamespaceName, source, event v1.ObjectName, s SeenList))
Range calls fn with a copy of every record, in no order, under the lock, so the records are one point in time (the event store's snapshot, ADR-0201 Decision 9); fn must not call m.
func (*MemWatermark) Save ¶
func (m *MemWatermark) Save(_ context.Context, ns v1.NamespaceName, source, event v1.ObjectName, s SeenList) error
Save stores a copy of s for (ns, source, event).
type Publisher ¶
type Publisher interface {
Publish(ctx context.Context, ev CloudEvent) error
}
Publisher is the delivery seam a Source emits named CloudEvents to (ADR-0108). The V1 driver is the in-process Fanout (fanout.go) the F69 Sensor subscribes to; a bus-backed driver is a V2 swap. Emitting to nobody is a no-op by design (an EventSource with no Sensor yet is valid).
type SeenList ¶ added in v0.4.0
type SeenList struct {
Bucket v1.ObjectName `json:"bucket"`
Prefix string `json:"prefix"`
UID v1.UID `json:"uid"`
Seen map[string]string `json:"seen"` // object key → the data.version it fired with (versionOf)
}
SeenList is the dedup record of one named blob event (ADR-0157), JSON in the event store (ADR-0201).
func LargeSeenList ¶ added in v0.11.0
LargeSeenList returns a SeenList whose JSON is at least size bytes.
type Source ¶
type Source struct {
// contains filtered or unexported fields
}
Source is the EventSource reconciler + the registered named-event timer set. A firing PUBLISHES a named CloudEvent (ADR-0108); the action side (invoke a function / start a workflow, and the Invocation record) is the F69 Sensor (ADR-0109). The Source no longer invokes.
func (*Source) ActiveTimers ¶
ActiveTimers reports the number of registered named-event timers (observability; a status/metrics surface and the reconcile scenario both read it).
func (*Source) Fire ¶
func (s *Source) Fire(ctx context.Context, ns v1.NamespaceName, source, event v1.ObjectName) error
Fire performs one deterministic tick of a named event: build its CloudEvent and publish it. A published-to-nobody event is a no-op (no Sensor subscribed yet) — never an error.
func (*Source) Reconcile ¶
func (s *Source) Reconcile(ctx context.Context, req controller.Request) (controller.Result, error)
Reconcile owns KindEventSource (ADR-0108): it registers every named event of a `timer:` source (→ Ready), deregisters on delete, and registers nothing for a source with no timer kind. A deleted or non-blob source has its blob records purged (ADR-0157).
func (*Source) Run ¶
Run ticks the registered named-event set until ctx is cancelled, publishing each event that is due. Started by the pkg/funcd lifecycle (ADR-0033); not part of the reconciler. A tick while held skips dueTimers, so after the release each timer fires at most once for the periods it missed (ADR-0206 Decision 6).
type SourceRef ¶ added in v0.4.0
type SourceRef struct {
Namespace v1.NamespaceName
Name v1.ObjectName
}
SourceRef names one EventSource that has blob event records.
type WatchHooks ¶ added in v0.4.0
type WatchHooks struct {
Exists func(ctx context.Context, src SourceRef) (bool, error)
SaveFailing func(ctx context.Context, src SourceRef, failing map[v1.ObjectName]error) error // empty map clears
}
WatchHooks connect the BlobWatcher to the store; NewSource sets them before Run (ADR-0157 Decisions 8, 9).
type Watermark ¶
type Watermark interface {
// Load never returns a nil Seen: a missing record, or one saved with "seen":null, loads with an empty map.
Load(ctx context.Context, ns v1.NamespaceName, source, event v1.ObjectName) (SeenList, error)
Save(ctx context.Context, ns v1.NamespaceName, source, event v1.ObjectName, s SeenList) error
// Delete removes every event record of one source; a source with no record is not an error.
Delete(ctx context.Context, ns v1.NamespaceName, source v1.ObjectName) error
// ListSources returns each source that has at least one record, once (the start sweep, ADR-0157 Decision 8).
ListSources(ctx context.Context) ([]SourceRef, error)
}
Watermark persists per-event dedup state so a re-list and a restart never re-emit a seen object (ADR-0119, ADR-0157). The durable driver is the event store's seen-list view (ADR-0201, internal/eventing/eventstore).
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package deadletter is the eventing dead-letter queue PORT (ADR-0118, F85): the durable park for a Sensor action-delivery that failed past the bounded retry cap, or that was parked before its attempts ran out (ADR-0156: the Sensor's queue was full, the Sensor changed, or the daemon shut down).
|
Package deadletter is the eventing dead-letter queue PORT (ADR-0118, F85): the durable park for a Sensor action-delivery that failed past the bounded retry cap, or that was parked before its attempts ran out (ADR-0156: the Sensor's queue was full, the Sensor changed, or the daemon shut down). |
|
badger
Package badger is the dead-letter-queue deadletter driver (ADR-0118), backed by Badger v4 in its own instance (never the metastore), which the event store shares with the blob seen lists (ADR-0201, NewOnDB).
|
Package badger is the dead-letter-queue deadletter driver (ADR-0118), backed by Badger v4 in its own instance (never the metastore), which the event store shares with the blob seen lists (ADR-0201, NewOnDB). |
|
memory
Package memory is the in-memory deadletter.Store driver (ADR-0118): a small pure-Go map with no files, for cross-platform tests and the InMemory preset.
|
Package memory is the in-memory deadletter.Store driver (ADR-0118): a small pure-Go map with no files, for cross-platform tests and the InMemory preset. |
|
Package eventstore is eventing's one durable store (ADR-0201): one Badger instance holding the Sensor's dead letters under dl/<ns>/<ulid> (ADR-0118), the BlobWatcher's seen lists under seen/ (ADR-0157) and the SQL change feed's watermarks under cdc/ (ADR-0235), each tenant iterating only its own prefix.
|
Package eventstore is eventing's one durable store (ADR-0201): one Badger instance holding the Sensor's dead letters under dl/<ns>/<ulid> (ADR-0118), the BlobWatcher's seen lists under seen/ (ADR-0157) and the SQL change feed's watermarks under cdc/ (ADR-0235), each tenant iterating only its own prefix. |