Scriba.Telemetry (Scriba v0.1.0)

Copy Markdown View Source

Catalog of telemetry events emitted by Scriba.

Events are emitted directly from the modules where they fire — there is no central emitter. This module documents the surface so users attaching handlers (via :telemetry.attach_many/4) have a single place to read.

v0.1 events

EventEmitterMeasurementsMetadata
[:scriba, :projection, :event, :start]Scriba.Projection.Pipeline (via :telemetry.span/3)monotonic_time, system_timeprojection, event_type, stream_id, position, telemetry_span_context
[:scriba, :projection, :event, :stop]sameduration, monotonic_timesame as :start
[:scriba, :projection, :event, :exception]sameduration, monotonic_timestart metadata + kind, reason, stacktrace
[:scriba, :projection, :event, :skipped]the Pipeline (no handler ran)system_timeprojection, reason (:dedup or :handler), event_type, stream_id, position
[:scriba, :projection, :batch, :stop]Scriba.Projection.Pipeline.handle_batch/4 (manual emit, success branch only)duration, batch_sizeprojection
[:scriba, :projection, :dead_letter]Scriba.Projection.Pipeline.handle_batch/4 (one per dead-lettered event, after Multi commit)system_timeprojection, position, stream_id, event_type, error_kind
[:scriba, :projection, :started]Scriba.Projection.Coordinator (first :initializing → :running, once per process lifetime)system_timeprojection
[:scriba, :projection, :paused]Scriba.Projection.Coordinator (on :running → :paused transition, after source pause signal sent)system_timeprojection
[:scriba, :projection, :resumed]Scriba.Projection.Coordinator (on :paused → :running transition, after source resume signal sent)system_timeprojection
[:scriba, :projection, :cache_initialized]Scriba.Position.init_cache/3wiped_count, preloaded_countname, version, source
[:scriba, :source, :batch, :failed]the source's acknowledger (a batch failed to commit; nothing was acknowledged)countsubscription, reason
[:scriba, :projection, :halted]the Pipeline (structural commit failure; the projection has stopped making progress)system_timeprojection, reason, failure (SQLSTATE label)

projection is %{name: String.t(), version: pos_integer()}. event_type is event.type (source-adapter-supplied). duration is monotonic-time units — pass through System.convert_time_unit/3 to get ms/µs.

Batch :stop is emitted only when the target's apply_batch returns :ok — i.e. when the Multi committed.

Batch failure is observable through two events, and they mean different things:

  • [:scriba, :source, :batch, :failed] — a batch did not commit and nothing in it was acknowledged. Emitted by the source rather than the pipeline because that is where the consequence lands: the producer stops so the subscription rewinds and redelivers. Isolated occurrences are normal under transient database trouble. A sustained stream of them means no progress. See Scriba.BatchCommitError.

  • [:scriba, :projection, :halted] — a batch failed with a structural error (Scriba.Failure classifies SQLSTATE class 42 and anything unrecognised this way): the schema or permissions do not match the code. Neither replaying nor dead-lettering can resolve that, so the projection deliberately stops. This one is a page, not a warning. Nothing is lost — no event is acknowledged and no cursor moves — but nothing proceeds either until someone fixes the cause and restarts.

Deterministic per-event failures do not reach either event. A commit failure classified as :integrity is isolated by the pipeline's per-event fallback and recorded in scriba_dead_letters with error_kind "commit:<SQLSTATE>", and the projection carries on.

Counting skips, and why it matters

Skipped events emit no :event :start/:stop pair — no handler ran, so there is no latency to measure. They emit [:scriba, :projection, :event, :skipped] instead, with reason:

  • :dedup — the source redelivered an event at or below this stream's committed cursor. It was already applied.
  • :handler — the handler returned :skip. This projection does not care about this event.

Skip is the only outcome that leaves no other trace: no read-model row, no dead-letter row, no cursor anomaly. Without this event the conservation identity

events delivered == read-model rows + dead letters + skipped

cannot be closed by anyone, including an operator trying to answer "has my projection silently skipped anything?" A projection that has skipped a thousand events and one that is genuinely caught up look identical from every other signal Scriba emits.

A rising :dedup count is usually healthy (crash recovery replaying). A rising :handler count on event types you expected to handle is a missing handle/2 clause.

Out of scope for v0.1

Lag and throughput events ([:scriba, :projection, :lag], [:scriba, :projection, :throughput]) are v0.2 per SCRIBA_ARCHITECTURE.md §2. See §7.5 of the architecture doc for the implementation note on periodic timers that the v0.2 work will need.