LCOV - code coverage report
Current view: top level - pageserver/src/tenant/secondary - heatmap.rs (source / functions) Coverage Total Hit
Test: 1b0a6a0c05cee5a7de360813c8034804e105ce1c.info Lines: 33.8 % 65 22
Test Date: 2025-03-12 00:01:28 Functions: 8.3 % 36 3

            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              : }
        

Generated by: LCOV version 2.1-beta