Line data Source code
1 : use std::collections::HashMap;
2 : use std::time::SystemTime;
3 :
4 : use serde::{Deserialize, Serialize};
5 : use serde_with::{DisplayFromStr, TimestampSeconds, serde_as};
6 : use utils::generation::Generation;
7 : use utils::id::TimelineId;
8 :
9 : use crate::tenant::remote_timeline_client::index::LayerFileMetadata;
10 : use crate::tenant::storage_layer::LayerName;
11 :
12 0 : #[derive(Serialize, Deserialize)]
13 : pub(crate) struct HeatMapTenant {
14 : /// Generation of the attached location that uploaded the heatmap: this is not required
15 : /// for correctness, but acts as a hint to secondary locations in order to detect thrashing
16 : /// in the unlikely event that two attached locations are both uploading conflicting heatmaps.
17 : pub(super) generation: Generation,
18 :
19 : pub(super) timelines: Vec<HeatMapTimeline>,
20 :
21 : /// Uploaders provide their own upload period in the heatmap, as a hint to downloaders
22 : /// of how frequently it is worthwhile to check for updates.
23 : ///
24 : /// This is optional for backward compat, and because we sometimes might upload
25 : /// a heatmap explicitly via API for a tenant that has no periodic upload configured.
26 : #[serde(default)]
27 : pub(super) upload_period_ms: Option<u128>,
28 : }
29 :
30 : impl HeatMapTenant {
31 0 : pub(crate) fn into_timelines_index(self) -> HashMap<TimelineId, HeatMapTimeline> {
32 0 : self.timelines
33 0 : .into_iter()
34 0 : .map(|htl| (htl.timeline_id, htl))
35 0 : .collect()
36 0 : }
37 : }
38 :
39 : #[serde_as]
40 0 : #[derive(Serialize, Deserialize, Clone)]
41 : pub(crate) struct HeatMapTimeline {
42 : #[serde_as(as = "DisplayFromStr")]
43 : pub(crate) timeline_id: TimelineId,
44 :
45 : layers: Vec<HeatMapLayer>,
46 : }
47 :
48 : #[serde_as]
49 0 : #[derive(Serialize, Deserialize, Clone)]
50 : pub(crate) struct HeatMapLayer {
51 : pub(crate) name: LayerName,
52 : pub(crate) metadata: LayerFileMetadata,
53 :
54 : #[serde_as(as = "TimestampSeconds<i64>")]
55 : pub(crate) access_time: SystemTime,
56 :
57 : #[serde(default)]
58 : pub(crate) cold: bool, // TODO: an actual 'heat' score that would let secondary locations prioritize downloading
59 : // the hottest layers, rather than trying to simply mirror whatever layers are on-disk on the primary.
60 : }
61 :
62 : impl HeatMapLayer {
63 108 : pub(crate) fn new(
64 108 : name: LayerName,
65 108 : metadata: LayerFileMetadata,
66 108 : access_time: SystemTime,
67 108 : cold: bool,
68 108 : ) -> Self {
69 108 : Self {
70 108 : name,
71 108 : metadata,
72 108 : access_time,
73 108 : cold,
74 108 : }
75 108 : }
76 : }
77 :
78 : impl HeatMapTimeline {
79 32 : pub(crate) fn new(timeline_id: TimelineId, layers: Vec<HeatMapLayer>) -> Self {
80 32 : Self {
81 32 : timeline_id,
82 32 : layers,
83 32 : }
84 32 : }
85 :
86 0 : pub(crate) fn into_hot_layers(self) -> impl Iterator<Item = HeatMapLayer> {
87 0 : self.layers.into_iter().filter(|l| !l.cold)
88 0 : }
89 :
90 0 : pub(crate) fn hot_layers(&self) -> impl Iterator<Item = &HeatMapLayer> {
91 0 : self.layers.iter().filter(|l| !l.cold)
92 0 : }
93 :
94 116 : pub(crate) fn all_layers(&self) -> impl Iterator<Item = &HeatMapLayer> {
95 116 : self.layers.iter()
96 116 : }
97 : }
98 :
99 : pub(crate) struct HeatMapStats {
100 : pub(crate) bytes: u64,
101 : pub(crate) layers: usize,
102 : }
103 :
104 : impl HeatMapTenant {
105 0 : pub(crate) fn get_stats(&self) -> HeatMapStats {
106 0 : let mut stats = HeatMapStats {
107 0 : bytes: 0,
108 0 : layers: 0,
109 0 : };
110 0 : for timeline in &self.timelines {
111 0 : for layer in timeline.hot_layers() {
112 0 : stats.layers += 1;
113 0 : stats.bytes += layer.metadata.file_size;
114 0 : }
115 : }
116 :
117 0 : stats
118 0 : }
119 :
120 0 : pub(crate) fn strip_atimes(self) -> Self {
121 0 : Self {
122 0 : timelines: self
123 0 : .timelines
124 0 : .into_iter()
125 0 : .map(|mut tl| {
126 0 : for layer in &mut tl.layers {
127 0 : layer.access_time = SystemTime::UNIX_EPOCH;
128 0 : }
129 0 : tl
130 0 : })
131 0 : .collect(),
132 0 : generation: self.generation,
133 0 : upload_period_ms: self.upload_period_ms,
134 0 : }
135 0 : }
136 : }
|