embedworker

package
v0.24.0 Latest Latest
Warning

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

Go to latest
Published: Jul 14, 2026 License: AGPL-3.0 Imports: 8 Imported by: 0

Documentation

Overview

Package embedworker is the production span-embedding loop: it runs the bounded span-embedding pass (see pkg/spanembed) on its own interval, in its own process (`tapes serve embed-worker`) — split out of the derive worker so that embedding NEVER blocks derivation.

Why a separate process: embedding calls an external provider (OpenAI, Ollama) whose latency and availability are outside Tapes' control. As an inline step of the derive loop it could not fail or hang without stalling the projection of raw turns into the read model. Here it has its own memory budget, its own failure domain, and its own replica count; derivation is untouched whatever the embedding backend does.

The pass is idempotent and keyed by span identity, so running it on a timer — and running multiple replicas — only costs redundant reads. A per-span embed failure (the provider rejected the input or was unreachable for that span) is counted and logged but never aborts the pass; the span stays un-embedded and the next run retries it.

Index

Constants

View Source
const (
	DefaultInterval     = time.Minute
	DefaultMaxBackoff   = 5 * time.Minute
	DefaultDrainTimeout = 30 * time.Second
)

Defaults for Config fields left zero.

Variables

This section is empty.

Functions

This section is empty.

Types

type Config

type Config struct {
	// Interval is how often a full embed pass runs. A pass also runs
	// once immediately at startup to clear the standing backlog.
	Interval time.Duration

	// MaxBackoff caps the jittered exponential backoff applied between
	// passes while the pass keeps failing on infrastructure (database or
	// embedding backend unreachable), so an outage costs one line per
	// retry at a widening cadence instead of hammering a dead backend
	// every Interval.
	MaxBackoff time.Duration

	// DrainTimeout bounds graceful shutdown: after the run context is
	// canceled, an in-flight pass may keep running this long before its
	// store/backend calls are aborted.
	DrainTimeout time.Duration

	// Ready optionally backs the readiness probe. Nil reports ready as
	// soon as the worker exists.
	Ready ReadyFunc

	// Metrics optionally injects a pre-built metrics surface so the
	// hosting command can mount /metrics before the store is reachable.
	// NewWorker builds a fresh one when nil.
	Metrics *Metrics
}

Config tunes the worker loop. Zero fields take the package defaults.

type Metrics

type Metrics struct {

	// Passes counts embed passes by outcome: ok / error (an
	// infrastructure failure — candidate listing or prune — that aborted
	// the pass; per-span failures are not pass failures).
	Passes *prometheus.CounterVec

	// PassDuration is the wall time of one full pass.
	PassDuration prometheus.Histogram

	// Per-span outcomes accumulated across passes. Together they close the
	// identity scanned = embedded + upToDate + empty + poisoned + failed,
	// so a dashboard can both account for every candidate and SEE the
	// re-stream cost: the embed pass lists and renders every main llm
	// span each pass, so a high upToDate (or scanned) with embedded≈0 is
	// the wasteful-rescan signature, invisible if only embedded/failed
	// were exposed. Failed is a per-span embed/write error retried next
	// pass; UpToDate skipped because content+model already match; Empty
	// skipped because the delta rendered to no embeddable text; Poisoned
	// skipped because the span already failed deterministically under this
	// content and model.
	SpansScanned  prometheus.Counter
	SpansEmbedded prometheus.Counter
	SpansUpToDate prometheus.Counter
	SpansEmpty    prometheus.Counter
	SpansPoisoned prometheus.Counter
	SpansFailed   prometheus.Counter

	// SpansChunked counts spans whose text exceeded the model context
	// window and was embedded as several pieces; ChunkRows counts the
	// pieces written (so chunked spans contribute more than one row).
	SpansChunked prometheus.Counter
	ChunkRows    prometheus.Counter

	// Oversize counts spans the model rejected as too large; OversizeTokens
	// is the distribution of their reported/estimated token sizes — the
	// answer to "how big are the inputs we're chunking?".
	Oversize       prometheus.Counter
	OversizeTokens prometheus.Histogram

	// SpanFailures counts deterministic per-span failures by reason (e.g.
	// oversize, api_400) — the recorded, non-retried failures, a subset of
	// SpansFailed broken out for alerting on a specific cause.
	SpanFailures *prometheus.CounterVec

	// OrphansPruned counts embedding rows removed because their span no
	// longer exists as a main llm span.
	OrphansPruned prometheus.Counter

	// ConsecutiveFailures mirrors the worker's in-memory outage counter:
	// non-zero means passes are currently failing on infrastructure and
	// backing off. Alert on sustained non-zero.
	ConsecutiveFailures prometheus.Gauge

	// LastSuccessTimestamp is the Unix time of the last successful pass.
	// Alert on staleness (time() - metric > interval): it catches a
	// wedged-but-not-erroring worker, which ConsecutiveFailures cannot.
	LastSuccessTimestamp prometheus.Gauge
	// contains filtered or unexported fields
}

Metrics is the Prometheus surface for the embed worker. Each worker owns its own registry so tests scrape in isolation and the hosting command mounts Handler on its listener.

func NewMetrics

func NewMetrics() *Metrics

NewMetrics constructs the embed worker's counters on a fresh registry.

func (*Metrics) Handler

func (m *Metrics) Handler() http.Handler

Handler returns the /metrics scrape handler for this worker's registry.

func (*Metrics) Registry

func (m *Metrics) Registry() *prometheus.Registry

Registry exposes the registry so tests can scrape it.

type Pass

type Pass interface {
	Run(ctx context.Context) (*spanembed.Report, error)
}

Pass runs one bounded span-embedding pass and reports what it did. *spanembed.Pass satisfies it; tests substitute a fake.

type ReadyFunc

type ReadyFunc func(ctx context.Context) error

ReadyFunc reports whether the worker's dependencies are reachable — the /readyz signal. Typically a database ping. Nil means always ready (the process is up).

type Worker

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

Worker owns the startup-pass / interval / drain loop.

func NewWorker

func NewWorker(cfg Config, pass Pass, logger *slog.Logger) *Worker

NewWorker creates an embed worker. logger must be non-nil.

func (*Worker) Metrics

func (w *Worker) Metrics() *Metrics

Metrics exposes the worker's Prometheus surface so the hosting command can mount a scrape endpoint.

func (*Worker) Ready

func (w *Worker) Ready(ctx context.Context) error

Ready reports whether the worker can do useful work right now. With no ReadyFunc configured it reports ready once the worker exists.

func (*Worker) Run

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

Run blocks until ctx is canceled. One pass runs immediately at startup (clearing the standing backlog), then the interval timer takes over.

Shutdown is graceful: canceling ctx stops scheduling new passes, but an in-flight pass keeps running on a detached context for up to DrainTimeout before its calls are aborted. Run then returns nil.

Jump to

Keyboard shortcuts

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