Kepler.Emitter (Kepler v0.1.0)

Copy Markdown View Source

The bounded queue between a firing watch and the outside world.

Firing must never block evaluation, and a wedged endpoint must never become a memory leak. So the poller casts an event here and moves on; this process holds a fixed-size queue and runs a bounded number of deliveries at a time, under Kepler's own task supervisor.

When the queue is full, events are dropped and counted. That is the whole policy. There is no retry and no durable spool: a delivery lost to a crash is gone, and Kepler.status/0 will tell you how many. Anything else means either unbounded growth or retrying into a cluster that is already having a bad day.

Fan-out

An event routed to several sinks is delivered to all of them concurrently, and each delivery is isolated from the others. A sink that raises, exits, or wedges cannot stop its siblings receiving the same event — it is recorded as that sink's failure and the rest carry on.

Two bounds apply, and they compose:

  • :max_in_flight caps how many events are being delivered at once.
  • A wedged sink is killed after :delivery_timeout, so it cannot hold an in-flight slot forever behind an endpoint that accepted the connection and then stopped talking.

So the ceiling on concurrent outbound requests is max_in_flight times the number of sinks a watch routes to — a small, static number, since sinks are configured rather than discovered.

Telemetry

Kepler emits its own events, so you can watch the watcher:

  • [:kepler, :delivery, :stop]%{duration: native}, with :watch, :sink, :module, and :result in metadata.
  • [:kepler, :delivery, :exception] — a sink that raised, exited, or timed out. %{count: 1}, with :watch, :sink, and :reason.
  • [:kepler, :event, :dropped]%{count: 1}, with :watch, :severity, and :reason (:queue_full).

Summary

Functions

Returns a specification to start this module under a supervisor.

Queues event for delivery.

Functions

child_spec(init_arg)

Returns a specification to start this module under a supervisor.

See Supervisor.

emit(event, sinks \\ :all)

@spec emit(Kepler.Event.t(), :all | [atom()]) :: :ok

Queues event for delivery.

A cast, so it never blocks the caller — the poller is the only producer and it produces at most one event per watch per tick, which is why an unbounded mailbox is not a risk here. The bound that matters is the delivery queue, which this process enforces.