ForgeOpsTracker.MetricBuffer (forge_ops_tracker v0.19.0)

Copy Markdown View Source

Collects individual ForgeOpsTracker.capture_metric/2 / capture_infrastructure_metric/3 calls and periodically flushes them as one batch, rather than one network call per capture. Unlike ForgeOpsTracker.PerformanceFlusher this keeps a list of individually meaningful entries instead of summing them into buckets: a customer's own signup or payment is exactly the kind of thing they will want a genuinely accurate count/sum of later, so the server stores one row per entry as-is. Ported from gems/forge_ops_tracker's metric_buffer.rb and infrastructure_metric_buffer.rb, which are the same class twice; here it is one GenServer started twice under ForgeOpsTracker.Application (:custom and :infrastructure), each registered under its own name.

Same shape as PerformanceFlusher: a supervised GenServer, a periodic Process.send_after flush, and a flush in terminate/2 on a normal supervised shutdown (the BEAM equivalent of the Ruby gem's at_exit, which is what lets a short-lived cron script that captures a few readings and ends still deliver them, given Process.flag(:trap_exit, true)). Delivery runs synchronously inside the server, so a record cast that arrives mid-delivery just waits in the mailbox and is handled right after: nothing recorded during a delivery can be lost, and a delivery that failed for now leaves the entries in place for the next flush. A batch the server rejected, or one that failed ForgeOpsTracker.DeliveryStatus.max_failed_flushes/0 flushes in a row, is dropped.

Differences from the Ruby buffers: the buffer is capped at max_entries/0 and drops further entries once full (an unreachable server fails every flush, and an uncapped buffer would keep growing until the batch is given up on), and a non-numeric value is dropped at record time (the BEAM has no NaN or infinity to worry about, but a string would still be an invalid metric).

Summary

Functions

Delivers whatever is buffered right now, without waiting for the next interval.

The most entries one buffer holds; once full, further entries are dropped until a flush lets go of them.

The registered name of the buffer for kind.

Adds one entry (everything but recorded_at, which is stamped here). Returns :ok immediately; a non-numeric value is dropped, and so is anything past max_entries/0.

Types

kind()

@type kind() :: :custom | :infrastructure

Functions

flush(kind)

@spec flush(kind()) :: :ok

Delivers whatever is buffered right now, without waiting for the next interval.

max_entries()

@spec max_entries() :: pos_integer()

The most entries one buffer holds; once full, further entries are dropped until a flush lets go of them.

name(atom)

@spec name(kind()) :: atom()

The registered name of the buffer for kind.

record(kind, entry)

@spec record(kind(), map()) :: :ok

Adds one entry (everything but recorded_at, which is stamped here). Returns :ok immediately; a non-numeric value is dropped, and so is anything past max_entries/0.