Documentation
¶
Overview ¶
Package usage emits one append-only event per completed request — the raw material a future control plane aggregates into per-tenant counts, quotas, and billing. This slice is log-only: no counting, no enforcement, no entitlement state. A request finishes, we record what we already know at the convergence point (rid, tenant, sizes, timing, status), and something downstream derives everything else.
The package is interface-first so the transport is swappable without touching call sites. The default ZapSink writes a structured "usage" line through the chassis's existing zap logger; a later file / Kafka / NATS / OTEL sink drops in behind the same Sink.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Register ¶ added in v0.2.4
func Register(name string, c Constructor)
Register adds a sink constructor. Called from a backend package's init(); the chassis activates a backend with a blank import.
Types ¶
type Constructor ¶ added in v0.2.4
type Constructor func(SinkConfig) (Sink, error)
Constructor builds a Sink from resolved config. Called by Open.
type Sink ¶
type Sink interface {
WriteEvent(ev UsageEvent)
Name() string
Close(ctx context.Context) error
}
Sink consumes usage events. WriteEvent must be safe for concurrent calls (the bus loop emits from per-request goroutines). Close is called once on chassis shutdown; synchronous sinks return nil immediately. Name reports the backend's registered name for the startup load log.
type SinkConfig ¶ added in v0.2.4
type SinkConfig struct {
// Epoch is a per-process token that changes every boot — the
// counter-reset boundary for a sink that keeps cumulative state.
Epoch string
// NodeID is a stable identity for this chassis (FQDN-ish), for
// attribution when many nodes write to one store.
NodeID string
// DataDir is where a sink may put a node-local database.
DataDir string
// Logger is the chassis logger; a sink may emit observability lines.
Logger *zap.Logger
}
SinkConfig carries the node/runtime context a sink may need, resolved from chassis config. It is deliberately generic — no billing, quota, or transport vocabulary — so the seam stays unopinionated: a backend reads any backend-specific settings (a DSN, periods) from its own env in its constructor, the same discipline as continuation/trace StoreConfig. The bundled "zap" sink uses only Logger; the other fields are there for an out-of-tree sink that aggregates per node.
type UsageEvent ¶
type UsageEvent struct {
RID string
Tenant string
Src string // "http" | "tcp" | "cron"
Stack string // entry stage the request dispatched to
DurationMS int64
Status string // "ok" | "error"
BytesIn int
BytesOut int
// MemBytes is peak guest memory for a compute invocation (src="compute").
// Zero for request-level usage (http/tcp/cron have no wasm memory) and
// omitted from the log line in that case.
MemBytes int
// Fuel is the total per-request fuel consumed across accounted actions
// (scope-enter, repeat-transition, EXEC, secret-materialize in v1;
// finer-grained costs follow in v2). Zero on requests that did no
// accounted work (empty / unrouted), in which case it is omitted from
// the log line — mirrors the MemBytes conditional pattern. This is the
// metering primitive; per-request enforcement happens upstream via the
// chassis budget guards and is invisible to the sink.
Fuel int64
// AdmissionDenied marks a request the admission gate rejected (suspend,
// rate limit, concurrency, or drain) before the customer stack ran. For
// such requests Billable is false and Fuel is zeroed, so a downstream
// aggregator never counts rejected traffic as usage. AdmissionReason is
// the machine token ("rate_limited" | "at_capacity" | "suspended" |
// "payment_required" | "draining").
AdmissionDenied bool
AdmissionReason string
Billable bool
}
UsageEvent is the per-request record. Deliberately flat and derived-metric-free: ops counts, monthly rollups, and rate-limit inputs are a downstream consumer's job, not the runtime's.
Tenant is the resolved tenant slug when routing succeeded; it is "_sys" (or empty) for unrouted/404 traffic, which is logged as-is so rejected requests are still measured.
type ZapSink ¶
type ZapSink struct {
// contains filtered or unexported fields
}
ZapSink is the bundled default sink: it folds the event into a single structured log line. The stable "usage" message is the downstream filter key; the timestamp is zap's own. Close is a no-op — the logger's flush lifecycle is owned by the chassis, not here.
func NewZapSink ¶
NewZapSink wraps an existing chassis logger. The logger is expected to be non-nil; callers only construct a ZapSink when usage is enabled.
func (*ZapSink) WriteEvent ¶
func (s *ZapSink) WriteEvent(ev UsageEvent)