Line data Source code
1 : use utils::serde_system_time::SystemTime;
2 :
3 : /// Pageserver current utilization and scoring for how good candidate the pageserver would be for
4 : /// the next tenant.
5 : ///
6 : /// See and maintain pageserver openapi spec for `/v1/utilization_score` as the truth.
7 : ///
8 : /// `format: int64` fields must use `ser_saturating_u63` because openapi generated clients might
9 : /// not handle full u64 values properly.
10 6 : #[derive(serde::Serialize, serde::Deserialize, Debug, Clone)]
11 : pub struct PageserverUtilization {
12 : /// Used disk space
13 : #[serde(serialize_with = "ser_saturating_u63")]
14 : pub disk_usage_bytes: u64,
15 : /// Free disk space
16 : #[serde(serialize_with = "ser_saturating_u63")]
17 : pub free_space_bytes: u64,
18 : /// Lower is better score for how good candidate for a next tenant would this pageserver be.
19 : #[serde(serialize_with = "ser_saturating_u63")]
20 : pub utilization_score: u64,
21 : /// When was this snapshot captured, pageserver local time.
22 : ///
23 : /// Use millis to give confidence that the value is regenerated often enough.
24 : pub captured_at: SystemTime,
25 : }
26 :
27 : /// openapi knows only `format: int64`, so avoid outputting a non-parseable value by generated clients.
28 : ///
29 : /// Instead of newtype, use this because a newtype would get require handling deserializing values
30 : /// with the highest bit set which is properly parsed by serde formats, but would create a
31 : /// conundrum on how to handle and again serialize such values at type level. It will be a few
32 : /// years until we can use more than `i64::MAX` bytes on a disk.
33 6 : fn ser_saturating_u63<S: serde::Serializer>(value: &u64, serializer: S) -> Result<S::Ok, S::Error> {
34 6 : const MAX_FORMAT_INT64: u64 = i64::MAX as u64;
35 6 :
36 6 : let value = (*value).min(MAX_FORMAT_INT64);
37 6 :
38 6 : serializer.serialize_u64(value)
39 6 : }
40 :
41 : #[cfg(test)]
42 : mod tests {
43 : use std::time::Duration;
44 :
45 : use super::*;
46 :
47 : #[test]
48 2 : fn u64_max_is_serialized_as_u63_max() {
49 2 : let doc = PageserverUtilization {
50 2 : disk_usage_bytes: u64::MAX,
51 2 : free_space_bytes: 0,
52 2 : utilization_score: u64::MAX,
53 2 : captured_at: SystemTime(
54 2 : std::time::SystemTime::UNIX_EPOCH + Duration::from_secs(1708509779),
55 2 : ),
56 2 : };
57 2 :
58 2 : let s = serde_json::to_string(&doc).unwrap();
59 2 :
60 2 : let expected = r#"{"disk_usage_bytes":9223372036854775807,"free_space_bytes":0,"utilization_score":9223372036854775807,"captured_at":"2024-02-21T10:02:59.000Z"}"#;
61 2 :
62 2 : assert_eq!(s, expected);
63 2 : }
64 : }
|