traffic.overview, which
reads two pre-aggregated ClickHouse rollups, unions them, and folds the
result into a per-minute series plus per-modality totals.
The console exposes five window presets — 15 min, 30 min, 1 hour,
6 hours, 24 hours — and mirrors the selection into the
w query
parameter so a window is shareable by URL. The query refetches every
30 seconds, and the refresh button forces a refetch.
Where the numbers come from
Two independent collectors feed this screen. They are unioned, not merged: a row from either side becomes a(modality, bucket) row in the
result.
The engine meter is the instrumentation that TeleQuick modules call
when they move bytes. It already knows the modality, direction, and
packet counts, so its rows pass through nearly untouched.
The QUIC sampler is different in kind. It walks live QUIC connections
and samples transport counters per
(subject_kind, subject_id). Before
it lands in this view it is grouped down to subject_kind per bucket,
with subject_id collapsed into a distinct-connection count. That
grouping is where packet counts are lost and where “sessions” gets its
meaning.
After the union, rows are re-aggregated per (modality, bucket):
bytes_in, bytes_out, packets_in, and packets_out are summed;
sessions takes the maximum, not the sum.
A row whose modality is empty is bucketed under unknown.
Modality keys
The modality key is the raw string from the collector. The console maps known keys to a display label and a stable colour; an unrecognised key still renders, labelled with the raw key and coloured from a fallback palette. A newly wired transport therefore appears on the chart before anyone teaches the UI about it. Keys sourced from the engine traffic meter:
Keys sourced from the QUIC sampler’s
subject_kind:
voice and voice_rtp are distinct keys from distinct collectors and
are not deduplicated. A call whose media rides QUIC to the relay and
RTP to a carrier contributes to both rows. Read the breakdown table as
“bytes per transport”, not “bytes per call”.
Two historical gaps are worth knowing when you look at older windows.
The QUIC sampler rows were dark until 2026-09-10, because the sampler
only walked the native quiche listener while production served MoQT over
lsquic — none of the subject_kind modalities appeared before that
date. The inference, turn, mqtt, wt_media, whatsapp, and
compute keys were wired on the same date; those modules moved bytes
before then and reported none.
Why some modalities report no packets
The union writes literal zeros forpackets_in and packets_out on
every QUIC-sampler row. The sampler’s grouping step discards per-subject
packet counters, so there is nothing to carry forward. Byte counts on
those rows are real; packet counts are simply unknown and are
represented as zero rather than as null.
Consequently packetsIn / packetsOut in the byModality payload are
only meaningful for engine-meter modalities. The console’s breakdown
table does not render them at all, which avoids showing a QUIC modality
that moved gigabytes next to a packet count of 0. If you consume the
procedure directly, apply the same caution: do not derive average packet
size or loss ratios for a QUIC-sampled modality.
Bucketing, lag, and the query window
Both rollups are pre-bucketed to one minute. The procedure selects rows wherebucket > now() - INTERVAL <windowMinutes> MINUTE, so the window
is a rolling wallclock range, and the resolution of the series is always
one minute regardless of which preset you pick — a 24-hour window
returns 1-minute buckets, not coarser ones.
Because bucketing is on wallclock minute boundaries, a session that
spans a boundary is split across buckets. There is no attribution of a
whole session to its start minute.
New traffic appears once the minute bucket it belongs to has been rolled
up and becomes visible to the query. The console’s empty state describes
this as usually within about a minute; the screen’s own 30-second
refetch means you may need one or two polls after a session starts
before its first bucket is on the chart.
The procedure’s input schema caps windowMinutes at 1440. For anything
longer-range than that, use the per-call records described in
Telemetry rather than this view.
If ClickHouse is unreachable, or the rollup tables have not been
migrated on this deployment, the procedure catches the error and returns
the zero-valued empty payload. The screen shows the quiet empty state
rather than an error banner, matching the other observability
procedures. The source field is always real; there is no synthetic
or sampled mode behind this screen.
How averages and peaks are computed
Everything is derived from the bucket series, in the procedure, not in ClickHouse. Window length. The denominator is the number of buckets actually present in the result, times 60 seconds, with a floor of 60 seconds:windowMinutes is deliberately not used. If you ask for 24
hours but the org only carried traffic for 3 minutes, the average is
computed over those 3 minutes. This keeps “Avg b/w” honest for bursty
tenants, but it also means the average is not comparable to a
bytes_total / windowMinutes figure you compute yourself, and widening
the window does not necessarily lower the average.
Average bandwidth, both the KPI and the per-modality column:
KB / MB / GB / TB
labels. Bandwidth is decimal megabits (* 8 / 1e6). The throughput
chart plots bytes / 60 / 1024 and labels the axis KB/s, so that chart
is kibibytes per second while the KPI beside it is decimal megabits per
second.
Sessions are peak concurrency, not volume
Thesessions column is the most commonly misread number on the screen.
Within a modality, sessions is the maximum across the buckets in
the window, not the sum. On the QUIC side each bucket’s value is a
count of distinct connection subjects active in that minute. So a
modality’s session figure answers “what was the highest number of
concurrent connections seen in any single minute of this window”, not
“how many sessions occurred”.
The KPI card then sums those per-modality maxima to produce the
sessions figure shown under the Modalities card. That total is a sum
of peaks taken independently per modality, so the peaks need not have
occurred in the same minute.
Practical consequences:
- Widening the window can only raise a modality’s session count, never lower it, because more buckets can only raise a maximum.
- Session counts do not add up over time and must not be used as a call volume, stream count, or connection-attempt count.
- For “how many calls happened”, use Call Detail Records.
Observed, not enforced
This subsystem measures. It does not gate. The procedure reads two rollup tables and returns arithmetic over them; nothing in the path rejects, shapes, throttles, or bills traffic, and no threshold value exists in it to compare against. The screen states this directly in its empty state: bandwidth is observed, not enforced. Read a number here as “this much moved”, never as “this much is allowed to move”. Any limit that applies to your account comes from somewhere else and is not reflected in this payload.Reconciling traffic with call detail records
Both this screen and CDRs count bytes, and they will not agree. They are not measuring the same thing.
Specific reasons a sum over this screen exceeds a sum over CDRs for the
same period:
- Non-call modalities are included.
sip,http,ws,kafka_egress,tunnel,vpn,inference, and the rest have no CDR at all. - The same call is counted per transport.
voice_rtp,voice(QUIC),webrtc, andturnare separate rows for what a CDR treats as one call. - Boundaries differ. A call in flight at the edge of the window contributes only its overlapping buckets here, while its CDR lands whole, later, when the call ends.
call_sid. Neither is an invoice.
Reading the payload directly
If you consumetraffic.overview outside the console:
series[].byModality holds combined ingress + egress per modality, so it
cannot be split by direction per modality per bucket. Direction is
available window-wide (kpi), per modality (byModality), and per
bucket in aggregate (series[].bytesIn / bytesOut) — but not at the
intersection of modality and bucket.
bucket arrives as a ClickHouse DateTime string. Depending on the
server’s date_time_output_format, it is either space-separated with no
zone (2026-07-15 12:34:56) or already ISO with a trailing Z. Normalise
the space to T and only append Z when no zone marker is present —
appending unconditionally produces an invalid date on the ISO form.
Related
- Telemetry — metrics, traces, CDRs, and SIP capture
- Telephony Metrics — definitions for the call-quality numbers