LCOV - differential code coverage report
Current view: top level - libs/tenant_size_model/src - calculation.rs (source / functions) Coverage Total Hit UBC CBC
Current: f6946e90941b557c917ac98cd5a7e9506d180f3e.info Lines: 98.4 % 122 120 2 120
Current Date: 2023-10-19 02:04:12 Functions: 80.0 % 5 4 1 4
Baseline: c8637f37369098875162f194f92736355783b050.info
Baseline Date: 2023-10-18 20:25:20

           TLA  Line data    Source code
       1                 : use crate::{SegmentMethod, SegmentSizeResult, SizeResult, StorageModel};
       2                 : 
       3                 : //
       4                 : //                 *-g--*---D--->
       5                 : //                /
       6                 : //               /
       7                 : //              /                 *---b----*-B--->
       8                 : //             /                 /
       9                 : //            /                 /
      10                 : //      -----*--e---*-----f----* C
      11                 : //           E                  \
      12                 : //                               \
      13                 : //                                *--a---*---A-->
      14                 : //
      15                 : // If A and B need to be retained, is it cheaper to store
      16                 : // snapshot at C+a+b, or snapshots at A and B ?
      17                 : //
      18                 : // If D also needs to be retained, which is cheaper:
      19                 : //
      20                 : // 1. E+g+e+f+a+b
      21                 : // 2. D+C+a+b
      22                 : // 3. D+A+B
      23                 : 
      24                 : /// `Segment` which has had its size calculated.
      25 CBC        1108 : #[derive(Clone, Debug)]
      26                 : struct SegmentSize {
      27                 :     method: SegmentMethod,
      28                 : 
      29                 :     // calculated size of this subtree, using this method
      30                 :     accum_size: u64,
      31                 : 
      32                 :     seg_id: usize,
      33                 :     children: Vec<SegmentSize>,
      34                 : }
      35                 : 
      36                 : struct SizeAlternatives {
      37                 :     // cheapest alternative if parent is available.
      38                 :     incremental: SegmentSize,
      39                 : 
      40                 :     // cheapest alternative if parent node is not available
      41                 :     non_incremental: Option<SegmentSize>,
      42                 : }
      43                 : 
      44                 : impl StorageModel {
      45              80 :     pub fn calculate(&self) -> SizeResult {
      46              80 :         // Build adjacency list. 'child_list' is indexed by segment id. Each entry
      47              80 :         // contains a list of all child segments of the segment.
      48              80 :         let mut roots: Vec<usize> = Vec::new();
      49              80 :         let mut child_list: Vec<Vec<usize>> = Vec::new();
      50              80 :         child_list.resize(self.segments.len(), Vec::new());
      51                 : 
      52             366 :         for (seg_id, seg) in self.segments.iter().enumerate() {
      53             366 :             if let Some(parent_id) = seg.parent {
      54             286 :                 child_list[parent_id].push(seg_id);
      55             286 :             } else {
      56              80 :                 roots.push(seg_id);
      57              80 :             }
      58                 :         }
      59                 : 
      60              80 :         let mut segment_results = Vec::new();
      61              80 :         segment_results.resize(
      62              80 :             self.segments.len(),
      63              80 :             SegmentSizeResult {
      64              80 :                 method: SegmentMethod::Skipped,
      65              80 :                 accum_size: 0,
      66              80 :             },
      67              80 :         );
      68              80 : 
      69              80 :         let mut total_size = 0;
      70             160 :         for root in roots {
      71              80 :             if let Some(selected) = self.size_here(root, &child_list).non_incremental {
      72              80 :                 StorageModel::fill_selected_sizes(&selected, &mut segment_results);
      73              80 :                 total_size += selected.accum_size;
      74              80 :             } else {
      75 UBC           0 :                 // Couldn't find any way to get this root. Error?
      76               0 :             }
      77                 :         }
      78                 : 
      79 CBC          80 :         SizeResult {
      80              80 :             total_size,
      81              80 :             segments: segment_results,
      82              80 :         }
      83              80 :     }
      84                 : 
      85             366 :     fn fill_selected_sizes(selected: &SegmentSize, result: &mut Vec<SegmentSizeResult>) {
      86             366 :         result[selected.seg_id] = SegmentSizeResult {
      87             366 :             method: selected.method,
      88             366 :             accum_size: selected.accum_size,
      89             366 :         };
      90                 :         // recurse to children
      91             366 :         for child in selected.children.iter() {
      92             286 :             StorageModel::fill_selected_sizes(child, result);
      93             286 :         }
      94             366 :     }
      95                 : 
      96                 :     //
      97                 :     // This is the core of the sizing calculation.
      98                 :     //
      99                 :     // This is a recursive function, that for each Segment calculates the best way
     100                 :     // to reach all the Segments that are marked as needed in this subtree, under two
     101                 :     // different conditions:
     102                 :     // a) when the parent of this segment is available (as a snaphot or through WAL), and
     103                 :     // b) when the parent of this segment is not available.
     104                 :     //
     105             366 :     fn size_here(&self, seg_id: usize, child_list: &Vec<Vec<usize>>) -> SizeAlternatives {
     106             366 :         let seg = &self.segments[seg_id];
     107             366 :         // First figure out the best way to get each child
     108             366 :         let mut children = Vec::new();
     109             366 :         for child_id in &child_list[seg_id] {
     110             286 :             children.push(self.size_here(*child_id, child_list))
     111                 :         }
     112                 : 
     113                 :         // Method 1. If this node is not needed, we can skip it as long as we
     114                 :         // take snapshots later in each sub-tree
     115             366 :         let snapshot_later = if !seg.needed {
     116             115 :             let mut snapshot_later = SegmentSize {
     117             115 :                 seg_id,
     118             115 :                 method: SegmentMethod::Skipped,
     119             115 :                 accum_size: 0,
     120             115 :                 children: Vec::new(),
     121             115 :             };
     122             115 : 
     123             115 :             let mut possible = true;
     124             122 :             for child in children.iter() {
     125             122 :                 if let Some(non_incremental) = &child.non_incremental {
     126              80 :                     snapshot_later.accum_size += non_incremental.accum_size;
     127              80 :                     snapshot_later.children.push(non_incremental.clone())
     128                 :                 } else {
     129              42 :                     possible = false;
     130              42 :                     break;
     131                 :                 }
     132                 :             }
     133             115 :             if possible {
     134              73 :                 Some(snapshot_later)
     135                 :             } else {
     136              42 :                 None
     137                 :             }
     138                 :         } else {
     139             251 :             None
     140                 :         };
     141                 : 
     142                 :         // Method 2. Get a snapshot here. This assumed to be possible, if the 'size' of
     143                 :         // this Segment was given.
     144             366 :         let snapshot_here = if !seg.needed || seg.parent.is_none() {
     145             171 :             if let Some(snapshot_size) = seg.size {
     146             139 :                 let mut snapshot_here = SegmentSize {
     147             139 :                     seg_id,
     148             139 :                     method: SegmentMethod::SnapshotHere,
     149             139 :                     accum_size: snapshot_size,
     150             139 :                     children: Vec::new(),
     151             139 :                 };
     152             153 :                 for child in children.iter() {
     153             153 :                     snapshot_here.accum_size += child.incremental.accum_size;
     154             153 :                     snapshot_here.children.push(child.incremental.clone())
     155                 :                 }
     156             139 :                 Some(snapshot_here)
     157                 :             } else {
     158              32 :                 None
     159                 :             }
     160                 :         } else {
     161             195 :             None
     162                 :         };
     163                 : 
     164                 :         // Method 3. Use WAL to get here from parent
     165             366 :         let wal_here = {
     166             366 :             let mut wal_here = SegmentSize {
     167             366 :                 seg_id,
     168             366 :                 method: SegmentMethod::Wal,
     169             366 :                 accum_size: if let Some(parent_id) = seg.parent {
     170             286 :                     seg.lsn - self.segments[parent_id].lsn
     171                 :                 } else {
     172              80 :                     0
     173                 :                 },
     174             366 :                 children: Vec::new(),
     175                 :             };
     176             652 :             for child in children {
     177             286 :                 wal_here.accum_size += child.incremental.accum_size;
     178             286 :                 wal_here.children.push(child.incremental)
     179                 :             }
     180             366 :             wal_here
     181             366 :         };
     182             366 : 
     183             366 :         // If the parent is not available, what's the cheapest method involving
     184             366 :         // a snapshot here or later?
     185             366 :         let mut cheapest_non_incremental: Option<SegmentSize> = None;
     186             366 :         if let Some(snapshot_here) = snapshot_here {
     187             139 :             cheapest_non_incremental = Some(snapshot_here);
     188             227 :         }
     189             366 :         if let Some(snapshot_later) = snapshot_later {
     190                 :             // Use <=, to prefer skipping if the size is equal
     191              73 :             if let Some(parent) = &cheapest_non_incremental {
     192              41 :                 if snapshot_later.accum_size <= parent.accum_size {
     193              34 :                     cheapest_non_incremental = Some(snapshot_later);
     194              34 :                 }
     195              32 :             } else {
     196              32 :                 cheapest_non_incremental = Some(snapshot_later);
     197              32 :             }
     198             293 :         }
     199                 : 
     200                 :         // And what's the cheapest method, if the parent is available?
     201             366 :         let cheapest_incremental = if let Some(cheapest_non_incremental) = &cheapest_non_incremental
     202                 :         {
     203                 :             // Is it cheaper to use a snapshot here or later, anyway?
     204                 :             // Use <, to prefer Wal over snapshot if the cost is the same
     205             171 :             if wal_here.accum_size < cheapest_non_incremental.accum_size {
     206             150 :                 wal_here
     207                 :             } else {
     208              21 :                 cheapest_non_incremental.clone()
     209                 :             }
     210                 :         } else {
     211             195 :             wal_here
     212                 :         };
     213                 : 
     214             366 :         SizeAlternatives {
     215             366 :             incremental: cheapest_incremental,
     216             366 :             non_incremental: cheapest_non_incremental,
     217             366 :         }
     218             366 :     }
     219                 : }
        

Generated by: LCOV version 2.1-beta