Replica resource usage
View as MarkdownEvery process of a cluster replica reports its own resource usage through
mz_introspection.mz_cluster_replica_resource_usage.
Each row is one measurement, taken from one source, reported as that source
gave it. Sources measure overlapping but distinct quantities, and the
differences between them are informative, so no row is a combination of two
others. Deciding which number is “the” memory usage of a replica, or how close
it is to its limit, is left to queries over the relation.
A metric whose name ends in peak is a high-water mark since the process
started, and the rest are instantaneous. Peaks the operating system maintains
itself are exact, and are unaffected by how often the replica reads them. Peaks
folded from samples are marked as such below and can miss a spike shorter than
the sampling interval, which makes them lower bounds. An observation the
replica could not read is absent rather than zero, so which metrics appear
depends on the platform and the kernel version.
Sources
| Source | Reads | Measures |
|---|---|---|
cgroup |
the process’s cgroup v2 interface files | the whole container, and the accounting that limit enforcement and the OOM killer act on |
proc_status |
/proc/self/status |
this process only, with resident memory broken down by backing |
rusage |
getrusage(RUSAGE_SELF) |
this process only |
statvfs |
the replica’s scratch filesystem | the filesystem as a whole, absent where disk is provided as swap |
Metrics
| Source | Metric | Meaning |
|---|---|---|
cgroup |
memory_current |
Memory charged to the cgroup: anonymous, page cache, kernel and socket memory. |
cgroup |
memory_peak |
High-water mark of memory_current, maintained by the kernel. |
cgroup |
memory_max |
The cgroup’s memory limit. |
cgroup |
swap_current |
Swap charged to the cgroup, including pages already read back whose swap slot is still allocated. |
cgroup |
swap_peak |
High-water mark of swap_current, maintained by the kernel. |
cgroup |
swap_max |
The cgroup’s swap limit. |
cgroup |
anon |
The part of memory_current backed by no file. |
cgroup |
file |
Page cache charged to the cgroup. |
cgroup |
shmem |
Shared memory and tmpfs pages. |
cgroup |
swapcached |
Pages resident in memory whose swap slot is still allocated. Counted in both anon and swap_current. |
cgroup |
kernel |
Kernel memory charged to the cgroup. |
cgroup |
slab |
Kernel slab allocations, part of kernel. |
cgroup |
sock |
Socket buffer memory. |
cgroup |
events_max |
Times an allocation hit memory_max. See the caveat below before using this as a limit-hit signal. |
cgroup |
events_oom_kill |
Processes in the cgroup killed by the OOM killer. |
proc_status |
vm_rss |
Resident set size of this process, the sum of rss_anon, rss_file and rss_shmem. |
proc_status |
rss_anon |
Resident memory backed by no file. The replica’s own memory. |
proc_status |
rss_file |
Resident file-backed memory, largely this binary’s text. Shared between replicas and charged to whichever cgroup first faulted it in. |
proc_status |
rss_shmem |
Resident shared memory. |
proc_status |
vm_swap |
This process’s pages currently in swap. Excludes swap-cached pages, so it reads below cgroup swap_current. |
proc_status |
vm_swap_peak |
Maximum vm_swap over samples, so a lower bound on the true peak. |
proc_status |
heap |
vm_rss plus vm_swap, the quantity a replica is limited on. |
proc_status |
heap_peak |
Maximum heap over samples, so a lower bound on the true peak. |
rusage |
max_rss |
Peak resident set size. Maintained by the kernel, but refreshed only at internal checkpoints, so it can read below a concurrent vm_rss. |
statvfs |
fs_used |
Used bytes of the filesystem, which on a shared filesystem counts writes this replica never made. |
statvfs |
fs_used_peak |
Maximum fs_used over samples, so a lower bound on the true peak. |
Interpreting
Values from different sources are not interchangeable, and adding them together generally produces a number that means nothing. In particular:
- How close is this replica to its memory limit? Compare
cgroupmemory_currentagainstmemory_max. Did it ever reach it? Comparememory_peakagainstmemory_max. Amemory_peakat the limit means the replica ran out of RAM and spilled to swap, even if the current reading is comfortable. - How close is it to its heap limit? Compare
proc_statusheapagainstmz_internal.mz_cluster_replica_metrics’sheap_limit.heap_peakbounds the high-water mark from below, and no source bounds it from above: thecgrouppeaks describe a smaller quantity, andmax_rsslags. - Do not use
events_maxas a limit-hit signal. Where swap is configured it stays at zero even for a replica pinned at its ceiling, because reclaim succeeds by swapping instead of failing.events_oom_killdoes report kills. - How much memory does this replica itself account for? Use
proc_statusrss_anon. Do not usevm_rss: it includesrss_file, which is charged to another cgroup and so runs a roughly constant amount above the replica’s own charge. - Do not add
memory_currentandswap_current. A page read back from swap is counted in both, andswapcachedreports how much is in that state. - Where disk is provided as swap, disk usage appears as
swap_currentand there are nostatvfsrows at all. - These readings live and die with the replica process. A restart resets
every peak. For history that survives restarts, see
mz_internal.mz_cluster_replica_metrics_history.
Example
Introspection relations are replica-local: a query reads the replica that serves it, so pin both the cluster and the replica. This reports how close each process came to a memory-limiter kill, comparing the quantity the limiter enforces against the limit it enforces:
SET cluster = <cluster_name>;
SET cluster_replica = <replica_name>;
SELECT
u.process_id,
round((max(u.value) FILTER (WHERE u.metric = 'heap'))::numeric / 1073741824, 2) AS heap_gib,
round((max(u.value) FILTER (WHERE u.metric = 'heap_peak'))::numeric / 1073741824, 2) AS heap_peak_gib,
round(m.heap_limit::numeric / 1073741824, 2) AS limit_gib,
round(100 * (max(u.value) FILTER (WHERE u.metric = 'heap_peak'))::numeric / m.heap_limit, 1) AS peak_pct
FROM mz_introspection.mz_cluster_replica_resource_usage u
JOIN mz_cluster_replicas r
ON r.name = current_setting('cluster_replica')
AND r.cluster_id = (SELECT id FROM mz_clusters WHERE name = current_setting('cluster'))
JOIN mz_internal.mz_cluster_replica_metrics m
ON m.replica_id = r.id AND m.process_id = u.process_id
WHERE u.source = 'proc_status'
GROUP BY u.process_id, m.heap_limit
ORDER BY u.process_id;
peak_pct is a lower bound, because heap_peak is: a spike shorter than the
sampling interval can slip through it. No source provides a matching upper
bound. The query stays within proc_status deliberately. The cgroup metrics
measure a different quantity, and memory_current plus swap_current
double-counts every swap-cached page.