eventing

package
v0.1.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 28, 2026 License: Apache-2.0 Imports: 16 Imported by: 0

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, cron schedules, and bus-backed delivery are documented deferrals.

Index

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).

Types

type BlobEventData

type BlobEventData struct {
	Bucket  string    `json:"bucket"`
	Key     string    `json:"key"`
	Size    int64     `json:"size"`
	Version string    `json:"version"`
	Time    time.Time `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 (blob.Attributes exposes none in V1).

type BlobWatcher

type BlobWatcher struct {
	// contains filtered or unexported fields
}

BlobWatcher polls each registered blob EventSource, diffs each prefix against the persisted Watermark, and publishes one named CloudEvent per new object through the existing ADR-0108 Publisher (ADR-0119). Registration is driven by the KindEventSource reconciler (ADR-0015 one-per-gvk); Run is a side loop started by the pkg/funcd lifecycle, mirroring eventing.Source.Run for timers. Advance-on-emission gives at-least-once: a crash between publish and save re-emits next poll (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) Deregister

func (w *BlobWatcher) Deregister(ns v1.NamespaceName, source v1.ObjectName)

Deregister removes every named event of one EventSource (delete / kind-change).

func (*BlobWatcher) Register

func (w *BlobWatcher) Register(ns v1.NamespaceName, source v1.ObjectName, 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 (ADR-0119).

func (*BlobWatcher) Run

func (w *BlobWatcher) Run(ctx context.Context) error

Run 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.

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)
	Time            time.Time       `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 Cursor

type Cursor struct {
	MaxModTime time.Time `json:"maxModTime"`
	KeysAtMax  []string  `json:"keysAtMax"`
}

Cursor is the compact, bounded watermark for one named blob event (ADR-0119): the newest ModTime seen and the key set AT that timestamp (the tie-break set). A listed object is NEW iff its ModTime > MaxModTime, or (ModTime == MaxModTime and its key is not in KeysAtMax). KeysAtMax resets when MaxModTime advances, so it stays bounded to the objects sharing the newest timestamp — enough for Created-only dedup.

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()
}

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 NewFanout

func NewFanout() *Fanout

NewFanout builds an empty in-process fanout publisher.

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.

type KVWatermark

type KVWatermark struct {
	// contains filtered or unexported fields
}

KVWatermark is the V1 Watermark driver (ADR-0119): one small JSON Cursor per (ns, source, event) over the in-tree kvstore.KV substrate — durable get/put with its own backup path, so no second persistence mechanism. A missing record loads as the zero Cursor (nothing seen yet).

func NewKVWatermark

func NewKVWatermark(kv kvstore.KV) (*KVWatermark, error)

NewKVWatermark builds the KV-backed Watermark driver over kv (required).

func (*KVWatermark) Load

func (w *KVWatermark) Load(ctx context.Context, ns v1.NamespaceName, source, event v1.ObjectName) (Cursor, error)

Load returns the persisted Cursor for (ns, source, event); a missing record is the zero Cursor (no error).

func (*KVWatermark) Save

func (w *KVWatermark) Save(ctx context.Context, ns v1.NamespaceName, source, event v1.ObjectName, c Cursor) error

Save persists the advanced Cursor for (ns, source, event), overwriting the prior record.

type MemWatermark

type MemWatermark struct {
	// contains filtered or unexported fields
}

MemWatermark is an in-process Watermark driver (ADR-0119): a mutex-guarded map of cursors. It is the non-durable sibling of KVWatermark — used by tests and any deployment that does not need cross-restart dedup. Held to the same Watermark contract.

func NewMemWatermark

func NewMemWatermark() *MemWatermark

NewMemWatermark builds an empty in-memory Watermark driver.

func (*MemWatermark) Load

func (m *MemWatermark) Load(_ context.Context, ns v1.NamespaceName, source, event v1.ObjectName) (Cursor, error)

Load returns the stored Cursor for (ns, source, event); an absent key is the zero Cursor.

func (*MemWatermark) Save

func (m *MemWatermark) Save(_ context.Context, ns v1.NamespaceName, source, event v1.ObjectName, c Cursor) error

Save stores the Cursor for (ns, source, event) (a defensive copy of the slice).

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 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 NewSource

func NewSource(d Deps) (*Source, error)

NewSource builds the Source. Store and Publisher are required.

func (*Source) ActiveTimers

func (s *Source) ActiveTimers() int

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.

func (*Source) Run

func (s *Source) Run(ctx context.Context) error

Run ticks the registered named-event set until ctx is cancelled, publishing each event whose Interval has elapsed. Started by the pkg/funcd lifecycle (ADR-0033); not part of the reconciler.

type Watermark

type Watermark interface {
	Load(ctx context.Context, ns v1.NamespaceName, source, event v1.ObjectName) (Cursor, error)
	Save(ctx context.Context, ns v1.NamespaceName, source, event v1.ObjectName, c Cursor) error
}

Watermark persists per-event dedup state so a re-list and a restart never re-emit a seen object (ADR-0119). The V1 driver stores one JSON record per (ns, source, event) over internal/kvstore.KV.

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.
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.
badger
Package badger is the dead-letter-queue deadletter driver (ADR-0118), backed by Badger v4 in a DEDICATED instance (never the metastore).
Package badger is the dead-letter-queue deadletter driver (ADR-0118), backed by Badger v4 in a DEDICATED instance (never the metastore).
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.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL