Berserk Docs
Aggregate FunctionsMetrics

otel_rate

Computes the per-second rate from an OpenTelemetry type=sum metric.

The binder desugars otel_rate($raw) into 4 column inputs (value, start_time, timestamp, aggregation_temporality) so column pruning knows what the scan must provide. The accumulator reads its fields by name either way, addressing each through the field's own KIX rather than materializing the row. For cumulative sums, uses start_time for deterministic counter-reset detection. For delta sums, sums the deltas over the time span. Returns null if rows have mixed temporalities. (bzrk extension)

The argument is a dynamic of metric shape — the field set produced by the OTel-to-bzrk mapping (timestamp, value, start_time, aggregation_temporality, metric_hash, …) — of which $raw over a metric table is the usual source. Every field the aggregate needs, the series key included, is read from that one argument.

Series are detected via metric_hash (#4072), so a group holding many series — the usual multi-pod dashboard bin — is handled correctly: each series is reduced independently over its own observed span, a restarted epoch (start_time change) within a series counts in full, and the per-series rates are summed. by metric_hash is therefore no longer required for correctness; add it only when you want one output row per series. A series with a single observation in the group defines no rate and contributes nothing; when no series has two observations the result is null.

Rows without a metric_hash column share one pseudo-series, which is what a datatable literal or an explicit project that drops the column gets.

The optional second parameter is the rate denominator — per-second versus per-minute — and not a window. otel_rate($raw, 30d) is "the per-series rate, expressed per 30 days", so a series observed for a few minutes still claims the full 30 days and the group total runs high by the restart count. For a total over the window use otel_increase, which sums each series' contribution without dividing by a span.

In make-series, a bin that no observation reached is null, not zero: a rate of zero says the counter was seen at both ends of the bin and did not move, which is a different claim from having no sample there (#4993). A counter observed to be flat therefore still reports 0, and a group this aggregate cannot answer for — mixed temporalities — is null across every bin rather than a flat zero line.

otel_rate is itself an aggregate: use it directly as a summarize/make-series output — never nested inside another aggregate such as avg(...) or sum(...) (that is a WRONG FUNCTION CONTEXT error).

Syntax

otel_rate($raw)

Parameters

Prop

Type

Returns: real

Syntax

otel_rate($raw, per_duration)

Parameters

Prop

Type

Returns: real

Examples

Example 1

// total rate across every series, per bin — series are split internally
OtelMetrics
| where metric_type == "sum"
| summarize rate = otel_rate($raw) by bin(timestamp, 1m)
timestamp (datetime)rate (real)
2024-01-01T00:00:00Znull
2024-01-01T00:01:00Znull
2024-01-01T00:02:00Znull

Example 2

OtelMetrics
| where metric_type == "sum"
| summarize otel_rate($raw, 1m) by metric_name
metric_name (dynamic)otel_rate_$raw (real)
"http.requests.total"6000.0

Example 3

// one row per series: group by metric_hash when you want them broken out
OtelMetrics
| where metric_name == "http.server.request.count"
| summarize rate = otel_rate($raw) by metric_hash, bin(timestamp, 5m)
metric_hash (dynamic)timestamp (datetime)rate (real)

On this page