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