Line data Source code
1 : //! An utilization metric which is used to decide on which pageserver to put next tenant.
2 : //!
3 : //! The metric is exposed via `GET /v1/utilization`. Refer and maintain it's openapi spec as the
4 : //! truth.
5 :
6 : use anyhow::Context;
7 : use std::path::Path;
8 : use utils::serde_percent::Percent;
9 :
10 : use pageserver_api::models::PageserverUtilization;
11 :
12 : use crate::{config::PageServerConf, metrics::NODE_UTILIZATION_SCORE, tenant::mgr::TenantManager};
13 :
14 0 : pub(crate) fn regenerate(
15 0 : conf: &PageServerConf,
16 0 : tenants_path: &Path,
17 0 : tenant_manager: &TenantManager,
18 0 : ) -> anyhow::Result<PageserverUtilization> {
19 0 : let statvfs = nix::sys::statvfs::statvfs(tenants_path)
20 0 : .map_err(std::io::Error::from)
21 0 : .context("statvfs tenants directory")?;
22 :
23 : // https://unix.stackexchange.com/a/703650
24 0 : let blocksz = if statvfs.fragment_size() > 0 {
25 0 : statvfs.fragment_size()
26 : } else {
27 0 : statvfs.block_size()
28 : };
29 :
30 : #[cfg_attr(not(target_os = "macos"), allow(clippy::unnecessary_cast))]
31 0 : let free = statvfs.blocks_available() as u64 * blocksz;
32 0 :
33 0 : #[cfg_attr(not(target_os = "macos"), allow(clippy::unnecessary_cast))]
34 0 : let used = statvfs
35 0 : .blocks()
36 0 : // use blocks_free instead of available here to match df in case someone compares
37 0 : .saturating_sub(statvfs.blocks_free()) as u64
38 0 : * blocksz;
39 0 :
40 0 : let captured_at = std::time::SystemTime::now();
41 :
42 : // Calculate aggregate utilization from tenants on this pageserver
43 0 : let (disk_wanted_bytes, shard_count) = tenant_manager.calculate_utilization()?;
44 :
45 : // Fetch the fraction of disk space which may be used
46 0 : let disk_usable_pct = match conf.disk_usage_based_eviction.clone() {
47 0 : Some(e) => e.max_usage_pct,
48 0 : None => Percent::new(100).unwrap(),
49 : };
50 :
51 : // Express a static value for how many shards we may schedule on one node
52 : const MAX_SHARDS: u32 = 20000;
53 :
54 0 : let mut doc = PageserverUtilization {
55 0 : disk_usage_bytes: used,
56 0 : free_space_bytes: free,
57 0 : disk_wanted_bytes,
58 0 : disk_usable_pct,
59 0 : shard_count,
60 0 : max_shard_count: MAX_SHARDS,
61 0 : utilization_score: None,
62 0 : captured_at: utils::serde_system_time::SystemTime(captured_at),
63 0 : };
64 0 :
65 0 : // Initialize `PageserverUtilization::utilization_score`
66 0 : let score = doc.cached_score();
67 0 : NODE_UTILIZATION_SCORE.set(score);
68 0 :
69 0 : Ok(doc)
70 0 : }
|