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