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