LCOV - code coverage report
Current view: top level - pageserver/src/tenant/timeline - uninit.rs (source / functions) Coverage Total Hit
Test: 8ac049b474321fdc72ddcb56d7165153a1a900e8.info Lines: 82.6 % 132 109
Test Date: 2023-09-06 10:18:01 Functions: 57.1 % 28 16

            Line data    Source code
       1              : use std::{collections::hash_map::Entry, fs, path::PathBuf, sync::Arc};
       2              : 
       3              : use anyhow::Context;
       4              : use tracing::{error, info, info_span, warn};
       5              : use utils::{crashsafe, fs_ext, id::TimelineId, lsn::Lsn};
       6              : 
       7              : use crate::{context::RequestContext, import_datadir, tenant::Tenant};
       8              : 
       9              : use super::Timeline;
      10              : 
      11              : /// A timeline with some of its files on disk, being initialized.
      12              : /// This struct ensures the atomicity of the timeline init: it's either properly created and inserted into pageserver's memory, or
      13              : /// its local files are removed. In the worst case of a crash, an uninit mark file is left behind, which causes the directory
      14              : /// to be removed on next restart.
      15              : ///
      16              : /// The caller is responsible for proper timeline data filling before the final init.
      17              : #[must_use]
      18              : pub struct UninitializedTimeline<'t> {
      19              :     pub(crate) owning_tenant: &'t Tenant,
      20              :     timeline_id: TimelineId,
      21              :     raw_timeline: Option<(Arc<Timeline>, TimelineUninitMark)>,
      22              : }
      23              : 
      24              : impl<'t> UninitializedTimeline<'t> {
      25         1041 :     pub(crate) fn new(
      26         1041 :         owning_tenant: &'t Tenant,
      27         1041 :         timeline_id: TimelineId,
      28         1041 :         raw_timeline: Option<(Arc<Timeline>, TimelineUninitMark)>,
      29         1041 :     ) -> Self {
      30         1041 :         Self {
      31         1041 :             owning_tenant,
      32         1041 :             timeline_id,
      33         1041 :             raw_timeline,
      34         1041 :         }
      35         1041 :     }
      36              : 
      37              :     /// Finish timeline creation: insert it into the Tenant's timelines map and remove the
      38              :     /// uninit mark file.
      39              :     ///
      40              :     /// This function launches the flush loop if not already done.
      41              :     ///
      42              :     /// The caller is responsible for activating the timeline (function `.activate()`).
      43         1036 :     pub(crate) fn finish_creation(mut self) -> anyhow::Result<Arc<Timeline>> {
      44         1036 :         let timeline_id = self.timeline_id;
      45         1036 :         let tenant_id = self.owning_tenant.tenant_id;
      46              : 
      47         1036 :         let (new_timeline, uninit_mark) = self.raw_timeline.take().with_context(|| {
      48            0 :             format!("No timeline for initalization found for {tenant_id}/{timeline_id}")
      49         1036 :         })?;
      50              : 
      51              :         // Check that the caller initialized disk_consistent_lsn
      52         1036 :         let new_disk_consistent_lsn = new_timeline.get_disk_consistent_lsn();
      53         1036 :         anyhow::ensure!(
      54         1036 :             new_disk_consistent_lsn.is_valid(),
      55            0 :             "new timeline {tenant_id}/{timeline_id} has invalid disk_consistent_lsn"
      56              :         );
      57              : 
      58         1036 :         let mut timelines = self.owning_tenant.timelines.lock().unwrap();
      59         1036 :         match timelines.entry(timeline_id) {
      60            0 :             Entry::Occupied(_) => anyhow::bail!(
      61            0 :                 "Found freshly initialized timeline {tenant_id}/{timeline_id} in the tenant map"
      62            0 :             ),
      63         1036 :             Entry::Vacant(v) => {
      64         1036 :                 uninit_mark.remove_uninit_mark().with_context(|| {
      65            0 :                     format!(
      66            0 :                         "Failed to remove uninit mark file for timeline {tenant_id}/{timeline_id}"
      67            0 :                     )
      68         1036 :                 })?;
      69         1036 :                 v.insert(Arc::clone(&new_timeline));
      70         1036 : 
      71         1036 :                 new_timeline.maybe_spawn_flush_loop();
      72         1036 :             }
      73         1036 :         }
      74         1036 : 
      75         1036 :         Ok(new_timeline)
      76         1036 :     }
      77              : 
      78              :     /// Prepares timeline data by loading it from the basebackup archive.
      79            5 :     pub(crate) async fn import_basebackup_from_tar(
      80            5 :         self,
      81            5 :         copyin_read: &mut (impl tokio::io::AsyncRead + Send + Sync + Unpin),
      82            5 :         base_lsn: Lsn,
      83            5 :         broker_client: storage_broker::BrokerClientChannel,
      84            5 :         ctx: &RequestContext,
      85            5 :     ) -> anyhow::Result<Arc<Timeline>> {
      86            5 :         let raw_timeline = self.raw_timeline()?;
      87              : 
      88            5 :         import_datadir::import_basebackup_from_tar(raw_timeline, copyin_read, base_lsn, ctx)
      89          543 :             .await
      90            5 :             .context("Failed to import basebackup")?;
      91              : 
      92              :         // Flush the new layer files to disk, before we make the timeline as available to
      93              :         // the outside world.
      94              :         //
      95              :         // Flush loop needs to be spawned in order to be able to flush.
      96            3 :         raw_timeline.maybe_spawn_flush_loop();
      97            3 : 
      98            3 :         fail::fail_point!("before-checkpoint-new-timeline", |_| {
      99            0 :             anyhow::bail!("failpoint before-checkpoint-new-timeline");
     100            3 :         });
     101              : 
     102            3 :         raw_timeline
     103            3 :             .freeze_and_flush()
     104            3 :             .await
     105            3 :             .context("Failed to flush after basebackup import")?;
     106              : 
     107              :         // All the data has been imported. Insert the Timeline into the tenant's timelines
     108              :         // map and remove the uninit mark file.
     109            3 :         let tl = self.finish_creation()?;
     110            3 :         tl.activate(broker_client, None, ctx);
     111            3 :         Ok(tl)
     112            5 :     }
     113              : 
     114          683 :     pub(crate) fn raw_timeline(&self) -> anyhow::Result<&Arc<Timeline>> {
     115          683 :         Ok(&self
     116          683 :             .raw_timeline
     117          683 :             .as_ref()
     118          683 :             .with_context(|| {
     119            0 :                 format!(
     120            0 :                     "No raw timeline {}/{} found",
     121            0 :                     self.owning_tenant.tenant_id, self.timeline_id
     122            0 :                 )
     123          683 :             })?
     124              :             .0)
     125          683 :     }
     126              : }
     127              : 
     128              : impl Drop for UninitializedTimeline<'_> {
     129              :     fn drop(&mut self) {
     130         1040 :         if let Some((_, uninit_mark)) = self.raw_timeline.take() {
     131            4 :             let _entered = info_span!("drop_uninitialized_timeline", tenant_id = %self.owning_tenant.tenant_id, timeline_id = %self.timeline_id).entered();
     132            4 :             error!("Timeline got dropped without initializing, cleaning its files");
     133            4 :             cleanup_timeline_directory(uninit_mark);
     134         1036 :         }
     135         1040 :     }
     136              : }
     137              : 
     138            5 : pub(crate) fn cleanup_timeline_directory(uninit_mark: TimelineUninitMark) {
     139            5 :     let timeline_path = &uninit_mark.timeline_path;
     140            5 :     match fs_ext::ignore_absent_files(|| fs::remove_dir_all(timeline_path)) {
     141              :         Ok(()) => {
     142            5 :             info!("Timeline dir {timeline_path:?} removed successfully, removing the uninit mark")
     143              :         }
     144            0 :         Err(e) => {
     145            0 :             error!("Failed to clean up uninitialized timeline directory {timeline_path:?}: {e:?}")
     146              :         }
     147              :     }
     148            5 :     drop(uninit_mark); // mark handles its deletion on drop, gets retained if timeline dir exists
     149            5 : }
     150              : 
     151              : /// An uninit mark file, created along the timeline dir to ensure the timeline either gets fully initialized and loaded into pageserver's memory,
     152              : /// or gets removed eventually.
     153              : ///
     154              : /// XXX: it's important to create it near the timeline dir, not inside it to ensure timeline dir gets removed first.
     155              : #[must_use]
     156              : pub(crate) struct TimelineUninitMark {
     157              :     uninit_mark_deleted: bool,
     158              :     uninit_mark_path: PathBuf,
     159              :     pub(crate) timeline_path: PathBuf,
     160              : }
     161              : 
     162              : impl TimelineUninitMark {
     163         1050 :     pub(crate) fn new(uninit_mark_path: PathBuf, timeline_path: PathBuf) -> Self {
     164         1050 :         Self {
     165         1050 :             uninit_mark_deleted: false,
     166         1050 :             uninit_mark_path,
     167         1050 :             timeline_path,
     168         1050 :         }
     169         1050 :     }
     170              : 
     171         1036 :     fn remove_uninit_mark(mut self) -> anyhow::Result<()> {
     172         1036 :         if !self.uninit_mark_deleted {
     173         1036 :             self.delete_mark_file_if_present()?;
     174            0 :         }
     175              : 
     176         1036 :         Ok(())
     177         1036 :     }
     178              : 
     179         1049 :     fn delete_mark_file_if_present(&mut self) -> anyhow::Result<()> {
     180         1049 :         let uninit_mark_file = &self.uninit_mark_path;
     181         1049 :         let uninit_mark_parent = uninit_mark_file
     182         1049 :             .parent()
     183         1049 :             .with_context(|| format!("Uninit mark file {uninit_mark_file:?} has no parent"))?;
     184         1049 :         fs_ext::ignore_absent_files(|| fs::remove_file(uninit_mark_file)).with_context(|| {
     185            0 :             format!("Failed to remove uninit mark file at path {uninit_mark_file:?}")
     186         1049 :         })?;
     187         1049 :         crashsafe::fsync(uninit_mark_parent).context("Failed to fsync uninit mark parent")?;
     188         1049 :         self.uninit_mark_deleted = true;
     189         1049 : 
     190         1049 :         Ok(())
     191         1049 :     }
     192              : }
     193              : 
     194              : impl Drop for TimelineUninitMark {
     195         1049 :     fn drop(&mut self) {
     196         1049 :         if !self.uninit_mark_deleted {
     197           13 :             if self.timeline_path.exists() {
     198            0 :                 error!(
     199            0 :                     "Uninit mark {} is not removed, timeline {} stays uninitialized",
     200            0 :                     self.uninit_mark_path.display(),
     201            0 :                     self.timeline_path.display()
     202            0 :                 )
     203              :             } else {
     204              :                 // unblock later timeline creation attempts
     205           13 :                 warn!(
     206           13 :                     "Removing intermediate uninit mark file {}",
     207           13 :                     self.uninit_mark_path.display()
     208           13 :                 );
     209           13 :                 if let Err(e) = self.delete_mark_file_if_present() {
     210            0 :                     error!("Failed to remove the uninit mark file: {e}")
     211           13 :                 }
     212              :             }
     213         1036 :         }
     214         1049 :     }
     215              : }
        

Generated by: LCOV version 2.1-beta