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 : pub(crate) 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 : // TODO: an actual 'heat' score that would let secondary locations prioritize downloading
57 : // the hottest layers, rather than trying to simply mirror whatever layers are on-disk on the primary.
58 : }
59 :
60 : impl HeatMapLayer {
61 108 : pub(crate) fn new(
62 108 : name: LayerName,
63 108 : metadata: LayerFileMetadata,
64 108 : access_time: SystemTime,
65 108 : ) -> Self {
66 108 : Self {
67 108 : name,
68 108 : metadata,
69 108 : access_time,
70 108 : }
71 108 : }
72 : }
73 :
74 : impl HeatMapTimeline {
75 32 : pub(crate) fn new(timeline_id: TimelineId, layers: Vec<HeatMapLayer>) -> Self {
76 32 : Self {
77 32 : timeline_id,
78 32 : layers,
79 32 : }
80 32 : }
81 : }
82 :
83 : pub(crate) struct HeatMapStats {
84 : pub(crate) bytes: u64,
85 : pub(crate) layers: usize,
86 : }
87 :
88 : impl HeatMapTenant {
89 0 : pub(crate) fn get_stats(&self) -> HeatMapStats {
90 0 : let mut stats = HeatMapStats {
91 0 : bytes: 0,
92 0 : layers: 0,
93 0 : };
94 0 : for timeline in &self.timelines {
95 0 : for layer in &timeline.layers {
96 0 : stats.layers += 1;
97 0 : stats.bytes += layer.metadata.file_size;
98 0 : }
99 : }
100 :
101 0 : stats
102 0 : }
103 :
104 0 : pub(crate) fn strip_atimes(self) -> Self {
105 0 : Self {
106 0 : timelines: self
107 0 : .timelines
108 0 : .into_iter()
109 0 : .map(|mut tl| {
110 0 : for layer in &mut tl.layers {
111 0 : layer.access_time = SystemTime::UNIX_EPOCH;
112 0 : }
113 0 : tl
114 0 : })
115 0 : .collect(),
116 0 : generation: self.generation,
117 0 : upload_period_ms: self.upload_period_ms,
118 0 : }
119 0 : }
120 : }
|