mz_clusterd/
usage_metrics.rs

1// Copyright Materialize, Inc. and contributors. All rights reserved.
2//
3// Use of this software is governed by the Business Source License
4// included in the LICENSE file.
5//
6// As of the Change Date specified in that file, in accordance with
7// the Business Source License, use of this software will be governed
8// by the Apache License, Version 2.0.
9
10//! Support for collecting system usage metrics.
11//!
12//! Currently only disk and swap usage is supported.
13//! We may want to add CPU and memory usage in the future.
14
15use std::path::PathBuf;
16
17use serde::Serialize;
18use tracing::error;
19
20/// A system usage metrics collector.
21pub(crate) struct Collector {
22    pub disk_root: Option<PathBuf>,
23}
24
25impl Collector {
26    /// Collect current system usage metrics.
27    pub fn collect(&self) -> Usage {
28        Usage {
29            disk_bytes: self.collect_disk_usage(),
30            swap_bytes: self.collect_swap_usage(),
31        }
32    }
33
34    fn collect_disk_usage(&self) -> Option<u64> {
35        let Some(root) = &self.disk_root else {
36            return None;
37        };
38
39        let stat = match nix::sys::statvfs::statvfs(root) {
40            Ok(stat) => stat,
41            Err(err) => {
42                error!("statvfs error: {err}");
43                return None;
44            }
45        };
46
47        // `fsblkcnt_t` is a `u32` on macOS but a `u64` on Linux.
48        #[allow(clippy::useless_conversion)]
49        let used_blocks = u64::from(stat.blocks() - stat.blocks_available());
50        let used_bytes = used_blocks * stat.fragment_size();
51
52        Some(used_bytes)
53    }
54
55    #[cfg(target_os = "linux")]
56    fn collect_swap_usage(&self) -> Option<u64> {
57        use mz_compute::memory_limiter::ProcStatus;
58        use mz_ore::cast::CastInto;
59
60        match ProcStatus::from_proc() {
61            Ok(status) => {
62                let bytes = status.vm_swap.cast_into();
63                Some(bytes)
64            }
65            Err(err) => {
66                error!("error reading /proc/self/status: {err}");
67                None
68            }
69        }
70    }
71
72    #[cfg(not(target_os = "linux"))]
73    fn collect_swap_usage(&self) -> Option<u64> {
74        None
75    }
76}
77
78/// A system usage measurement.
79#[derive(Serialize)]
80pub(crate) struct Usage {
81    disk_bytes: Option<u64>,
82    swap_bytes: Option<u64>,
83}