jonnifyechomq

Telemetry & the read plane · dive 02

Zero cost when absent

Make the telemetry dependency optional, and pay nothing when the host has not opted in.

A library that meters itself has a choice: depend on the telemetry library and force it on every host, or make the dependency optional and pay nothing when the host has not opted in. EchoMQ.Meter takes the second path, on a single guard. The property is worth stating plainly: the bus carries no :telemetry dependency edge. A host that wants metering adds :telemetry itself; a host that does not is never charged for the emitter calls in the lifecycle. Flip the dependency on the right to see what an emit does in each world.

The guard · one boolean decides

every emission asks: function_exported(:telemetry, :execute, 3)? true false :telemetry.execute(…) the rooted event fires attached handlers run metered: numbers flow host added :telemetry do nothing → :ok a no-op no event constructed free: a branch and a return no dependency edge
Flip the dependency below to see which branch an emit takes.

The guard — function_exported(:telemetry, :execute, 3)

Before any emission, the meter asks one question: is :telemetry actually loaded? It answers with a runtime check — :erlang.function_exported(:telemetry, :execute, 3) — which is true only if the module is present and that function is exported. If the dependency was never added, the check is false, and:

emit/3 does nothing and answers :ok — a no-op. attach/4 does nothing and answers :ok — the handler is never registered, and that is harmless, because the event it would have caught never fires either. So an emit in a hot lifecycle path is, when telemetry is absent, a branch on a boolean and a return: no event constructed, no handler list walked, no dependency loaded. The cost is a function-export lookup, not a metering pipeline.

# echo_mq — EchoMQ.Meter
# The single guard: is :telemetry actually loaded? Answered at runtime by the
# function-export check — true only if the module is present and execute/3 is
# exported. If the dependency was never added, this is false, so every emission
# is a no-op. The bus carries NO :telemetry dependency edge — a host opts in by
# adding :telemetry itself.
defp loaded?, do: :erlang.function_exported(:telemetry, :execute, 3)

# emit/3: with :telemetry loaded, fire the rooted event; without it, do nothing
# and answer :ok. An emit in a hot path is a branch on a boolean, never a
# metering pipeline.
def emit(event_suffix, measurements, metadata) when is_list(event_suffix) do
  if loaded?() do
    apply(:telemetry, :execute, [[@root | event_suffix], measurements, metadata])
  end

  :ok
end

The same guard wraps attach/4, attach_many/4, and span/3. The one careful asymmetry is in span/3: the guard protects only the events, never the work. With telemetry absent, span/3 runs the wrapped function directly and returns its result — the metering vanishes, the computation does not. A block you wrapped to measure is never skipped because nobody is listening.

The cost, both worlds

The claim — zero cost when absent — is meant literally, so it is worth pricing both worlds side by side. The same line of lifecycle code, Meter.job_completed(…), runs in both; what differs is only whether the host added the dependency. Pick a world to see what that one call does.

One emit call · what it costs

the call does · the dependency edge ·

A queue is infrastructure — it sits beneath an application with its own opinions about observability. The opt-in guard makes metering a decision the host makes, not a tax the library levies: the cost of the feature, to a host that does not use it, is zero — and the guard makes that claim literally true.

The name is collision-avoidance, not decoration

There is a second reason the module is called EchoMQ.Meter, and it is load-bearing. A frozen first-generation reference already defines a telemetry module under the obvious name, and both load on one code path. A same-named module would shadow the bus's telemetry surface non-deterministically — whichever loaded last would win, and the answer would depend on load order. The capability here is that same metering surface re-derived; the name is chosen to be collision-free so the bus's meter and the frozen reference never contend for one module name. Teaching the surface, the name to reach for is EchoMQ.Meter — that is the module the bus exposes, and the one this whole module is about.

This is the same instinct the BCS law expresses at the system boundary: a surface should not collide with, or depend on, a neighbour to hold itself up. The meter avoids both — no telemetry dependency edge (the guard), and no module-name collision (the name) — so it slots into any host cleanly, metered or not.

Pattern & implementation

the pattern (Redis Patterns Applied)

Instrument the system, but do not make instrumentation mandatory — a library's observability should be the host's choice. Production Operations teaches running the tier.

the implementation (echo_mq)

Every emission guards function_exported(:telemetry, :execute, 3), so with no :telemetry loaded an emit is a no-op and an attach answers :ok — no dependency edge — and the module name is chosen collision-free so it never shadows the frozen reference on the shared code path.

The pattern says: instrument, but do not impose. The implementation imposes nothing — a guard makes the dependency optional, and a chosen name keeps it from colliding.

Recap — optional dependency, chosen name

One runtime check — :erlang.function_exported(:telemetry, :execute, 3) — makes the telemetry dependency optional: with it absent, every emit is a no-op and every attach answers :ok, so the bus carries no :telemetry dependency edge and a host opts in by adding the dep. The guard protects only the events, never the wrapped work. And the module is named EchoMQ.Meter to stay collision-free against the frozen reference on the shared code path. The next dive turns from push to pull: the read plane, which has no dependency to guard at all.

Next in the module: The read plane — EchoMQ.Metrics, the pull side that answers a question by reading the structures, never by changing them.

References

Where the zero-cost guard and the optional-dependency property are grounded.

Sources

Related in this course