LCOV - differential code coverage report
Current view: top level - pageserver/src/tenant/timeline - init.rs (source / functions) Coverage Total Hit UBC CBC
Current: f6946e90941b557c917ac98cd5a7e9506d180f3e.info Lines: 91.9 % 124 114 10 114
Current Date: 2023-10-19 02:04:12 Functions: 73.7 % 19 14 5 14
Baseline: c8637f37369098875162f194f92736355783b050.info
Baseline Date: 2023-10-18 20:25:20

           TLA  Line data    Source code
       1                 : use crate::{
       2                 :     is_temporary,
       3                 :     tenant::{
       4                 :         ephemeral_file::is_ephemeral_file,
       5                 :         remote_timeline_client::{
       6                 :             self,
       7                 :             index::{IndexPart, LayerFileMetadata},
       8                 :         },
       9                 :         storage_layer::LayerFileName,
      10                 :         Generation,
      11                 :     },
      12                 :     METADATA_FILE_NAME,
      13                 : };
      14                 : use anyhow::Context;
      15                 : use camino::Utf8Path;
      16                 : use std::{collections::HashMap, str::FromStr};
      17                 : use utils::lsn::Lsn;
      18                 : 
      19                 : /// Identified files in the timeline directory.
      20                 : pub(super) enum Discovered {
      21                 :     /// The only one we care about
      22                 :     Layer(LayerFileName, u64),
      23                 :     /// Old ephmeral files from previous launches, should be removed
      24                 :     Ephemeral(String),
      25                 :     /// Old temporary timeline files, unsure what these really are, should be removed
      26                 :     Temporary(String),
      27                 :     /// Temporary on-demand download files, should be removed
      28                 :     TemporaryDownload(String),
      29                 :     /// "metadata" file we persist locally and include in `index_part.json`
      30                 :     Metadata,
      31                 :     /// Backup file from previously future layers
      32                 :     IgnoredBackup,
      33                 :     /// Unrecognized, warn about these
      34                 :     Unknown(String),
      35                 : }
      36                 : 
      37                 : /// Scans the timeline directory for interesting files.
      38 CBC         314 : pub(super) fn scan_timeline_dir(path: &Utf8Path) -> anyhow::Result<Vec<Discovered>> {
      39             314 :     let mut ret = Vec::new();
      40                 : 
      41            5014 :     for direntry in path.read_dir_utf8()? {
      42            5014 :         let direntry = direntry?;
      43            5014 :         let file_name = direntry.file_name().to_string();
      44                 : 
      45            5014 :         let discovered = match LayerFileName::from_str(&file_name) {
      46            4615 :             Ok(file_name) => {
      47            4615 :                 let file_size = direntry.metadata()?.len();
      48            4615 :                 Discovered::Layer(file_name, file_size)
      49                 :             }
      50                 :             Err(_) => {
      51             399 :                 if file_name == METADATA_FILE_NAME {
      52             314 :                     Discovered::Metadata
      53              85 :                 } else if file_name.ends_with(".old") {
      54                 :                     // ignore these
      55 UBC           0 :                     Discovered::IgnoredBackup
      56 CBC          85 :                 } else if remote_timeline_client::is_temp_download_file(direntry.path()) {
      57 UBC           0 :                     Discovered::TemporaryDownload(file_name)
      58 CBC          85 :                 } else if is_ephemeral_file(&file_name) {
      59              77 :                     Discovered::Ephemeral(file_name)
      60               8 :                 } else if is_temporary(direntry.path()) {
      61               8 :                     Discovered::Temporary(file_name)
      62                 :                 } else {
      63 UBC           0 :                     Discovered::Unknown(file_name)
      64                 :                 }
      65                 :             }
      66                 :         };
      67                 : 
      68 CBC        5014 :         ret.push(discovered);
      69                 :     }
      70                 : 
      71             314 :     Ok(ret)
      72             314 : }
      73                 : 
      74                 : /// Decision on what to do with a layer file after considering its local and remote metadata.
      75 UBC           0 : #[derive(Clone, Debug)]
      76                 : pub(super) enum Decision {
      77                 :     /// The layer is not present locally.
      78                 :     Evicted(LayerFileMetadata),
      79                 :     /// The layer is present locally, but local metadata does not match remote; we must
      80                 :     /// delete it and treat it as evicted.
      81                 :     UseRemote {
      82                 :         local: LayerFileMetadata,
      83                 :         remote: LayerFileMetadata,
      84                 :     },
      85                 :     /// The layer is present locally, and metadata matches.
      86                 :     UseLocal(LayerFileMetadata),
      87                 : }
      88                 : 
      89                 : /// A layer needs to be left out of the layer map.
      90               0 : #[derive(Debug)]
      91                 : pub(super) enum DismissedLayer {
      92                 :     /// The related layer is is in future compared to disk_consistent_lsn, it must not be loaded.
      93                 :     Future {
      94                 :         /// The local metadata. `None` if the layer is only known through [`IndexPart`].
      95                 :         local: Option<LayerFileMetadata>,
      96                 :     },
      97                 :     /// The layer only exists locally.
      98                 :     ///
      99                 :     /// In order to make crash safe updates to layer map, we must dismiss layers which are only
     100                 :     /// found locally or not yet included in the remote `index_part.json`.
     101                 :     LocalOnly(LayerFileMetadata),
     102                 : }
     103                 : 
     104                 : /// Merges local discoveries and remote [`IndexPart`] to a collection of decisions.
     105 CBC         314 : pub(super) fn reconcile(
     106             314 :     discovered: Vec<(LayerFileName, u64)>,
     107             314 :     index_part: Option<&IndexPart>,
     108             314 :     disk_consistent_lsn: Lsn,
     109             314 :     generation: Generation,
     110             314 : ) -> Vec<(LayerFileName, Result<Decision, DismissedLayer>)> {
     111             314 :     use Decision::*;
     112             314 : 
     113             314 :     // name => (local, remote)
     114             314 :     type Collected = HashMap<LayerFileName, (Option<LayerFileMetadata>, Option<LayerFileMetadata>)>;
     115             314 : 
     116             314 :     let mut discovered = discovered
     117             314 :         .into_iter()
     118            4615 :         .map(|(name, file_size)| {
     119            4615 :             (
     120            4615 :                 name,
     121            4615 :                 // The generation here will be corrected to match IndexPart in the merge below, unless
     122            4615 :                 // it is not in IndexPart, in which case using our current generation makes sense
     123            4615 :                 // because it will be uploaded in this generation.
     124            4615 :                 (Some(LayerFileMetadata::new(file_size, generation)), None),
     125            4615 :             )
     126            4615 :         })
     127             314 :         .collect::<Collected>();
     128             314 : 
     129             314 :     // merge any index_part information, when available
     130             314 :     index_part
     131             314 :         .as_ref()
     132             314 :         .map(|ip| ip.layer_metadata.iter())
     133             314 :         .into_iter()
     134             314 :         .flatten()
     135            8078 :         .map(|(name, metadata)| (name, LayerFileMetadata::from(metadata)))
     136             314 :         .for_each(|(name, metadata)| {
     137            8078 :             if let Some(existing) = discovered.get_mut(name) {
     138            4347 :                 existing.1 = Some(metadata);
     139            4347 :             } else {
     140            3731 :                 discovered.insert(name.to_owned(), (None, Some(metadata)));
     141            3731 :             }
     142            8078 :         });
     143             314 : 
     144             314 :     discovered
     145             314 :         .into_iter()
     146            8346 :         .map(|(name, (local, remote))| {
     147            8346 :             let decision = if name.is_in_future(disk_consistent_lsn) {
     148               7 :                 Err(DismissedLayer::Future { local })
     149                 :             } else {
     150            8339 :                 match (local, remote) {
     151            4347 :                     (Some(local), Some(remote)) if local != remote => {
     152            1768 :                         Ok(UseRemote { local, remote })
     153                 :                     }
     154            2579 :                     (Some(x), Some(_)) => Ok(UseLocal(x)),
     155            3731 :                     (None, Some(x)) => Ok(Evicted(x)),
     156             261 :                     (Some(x), None) => Err(DismissedLayer::LocalOnly(x)),
     157                 :                     (None, None) => {
     158 UBC           0 :                         unreachable!("there must not be any non-local non-remote files")
     159                 :                     }
     160                 :                 }
     161                 :             };
     162                 : 
     163 CBC        8346 :             (name, decision)
     164            8346 :         })
     165             314 :         .collect::<Vec<_>>()
     166             314 : }
     167                 : 
     168              85 : pub(super) fn cleanup(path: &Utf8Path, kind: &str) -> anyhow::Result<()> {
     169              85 :     let file_name = path.file_name().expect("must be file path");
     170              85 :     tracing::debug!(kind, ?file_name, "cleaning up");
     171              85 :     std::fs::remove_file(path).with_context(|| format!("failed to remove {kind} at {path}"))
     172              85 : }
     173                 : 
     174               1 : pub(super) fn cleanup_local_file_for_remote(
     175               1 :     path: &Utf8Path,
     176               1 :     local: &LayerFileMetadata,
     177               1 :     remote: &LayerFileMetadata,
     178               1 : ) -> anyhow::Result<()> {
     179               1 :     let local_size = local.file_size();
     180               1 :     let remote_size = remote.file_size();
     181               1 : 
     182               1 :     let file_name = path.file_name().expect("must be file path");
     183               1 :     tracing::warn!("removing local file {file_name:?} because it has unexpected length {local_size}; length in remote index is {remote_size}");
     184               1 :     if let Err(err) = crate::tenant::timeline::rename_to_backup(path) {
     185 UBC           0 :         assert!(
     186               0 :             path.exists(),
     187               0 :             "we would leave the local_layer without a file if this does not hold: {path}",
     188                 :         );
     189               0 :         Err(err)
     190                 :     } else {
     191 CBC           1 :         Ok(())
     192                 :     }
     193               1 : }
     194                 : 
     195               7 : pub(super) fn cleanup_future_layer(
     196               7 :     path: &Utf8Path,
     197               7 :     name: &LayerFileName,
     198               7 :     disk_consistent_lsn: Lsn,
     199               7 : ) -> anyhow::Result<()> {
     200               7 :     // future image layers are allowed to be produced always for not yet flushed to disk
     201               7 :     // lsns stored in InMemoryLayer.
     202               7 :     let kind = name.kind();
     203               7 :     tracing::info!("found future {kind} layer {name} disk_consistent_lsn is {disk_consistent_lsn}");
     204               7 :     std::fs::remove_file(path)?;
     205               7 :     Ok(())
     206               7 : }
     207                 : 
     208             261 : pub(super) fn cleanup_local_only_file(
     209             261 :     path: &Utf8Path,
     210             261 :     name: &LayerFileName,
     211             261 :     local: &LayerFileMetadata,
     212             261 : ) -> anyhow::Result<()> {
     213             261 :     let kind = name.kind();
     214             261 :     tracing::info!("found local-only {kind} layer {name}, metadata {local:?}");
     215             261 :     std::fs::remove_file(path)?;
     216             261 :     Ok(())
     217             261 : }
        

Generated by: LCOV version 2.1-beta