Line data Source code
1 : use std::sync::Arc;
2 :
3 : use crate::tenant::{OffloadedTimeline, Tenant, TimelineOrOffloaded};
4 :
5 : use super::{
6 : delete::{delete_local_timeline_directory, DeleteTimelineFlow, DeletionGuard},
7 : Timeline,
8 : };
9 :
10 0 : pub(crate) async fn offload_timeline(
11 0 : tenant: &Tenant,
12 0 : timeline: &Arc<Timeline>,
13 0 : ) -> anyhow::Result<()> {
14 0 : tracing::info!("offloading archived timeline");
15 0 : let (timeline, guard) = DeleteTimelineFlow::prepare(tenant, timeline.timeline_id)?;
16 :
17 0 : let TimelineOrOffloaded::Timeline(timeline) = timeline else {
18 0 : tracing::error!("timeline already offloaded, but given timeline object");
19 0 : return Ok(());
20 : };
21 :
22 : // Now that the Timeline is in Stopping state, request all the related tasks to shut down.
23 0 : timeline.shutdown(super::ShutdownMode::Hard).await;
24 :
25 : // TODO extend guard mechanism above with method
26 : // to make deletions possible while offloading is in progress
27 :
28 : // TODO mark timeline as offloaded in S3
29 :
30 0 : let conf = &tenant.conf;
31 0 : delete_local_timeline_directory(conf, tenant.tenant_shard_id, &timeline).await?;
32 :
33 0 : remove_timeline_from_tenant(tenant, &timeline, &guard).await?;
34 :
35 0 : {
36 0 : let mut offloaded_timelines = tenant.timelines_offloaded.lock().unwrap();
37 0 : offloaded_timelines.insert(
38 0 : timeline.timeline_id,
39 0 : Arc::new(OffloadedTimeline::from_timeline(&timeline)),
40 0 : );
41 0 : }
42 0 :
43 0 : Ok(())
44 0 : }
45 :
46 : /// It is important that this gets called when DeletionGuard is being held.
47 : /// For more context see comments in [`DeleteTimelineFlow::prepare`]
48 0 : async fn remove_timeline_from_tenant(
49 0 : tenant: &Tenant,
50 0 : timeline: &Timeline,
51 0 : _: &DeletionGuard, // using it as a witness
52 0 : ) -> anyhow::Result<()> {
53 0 : // Remove the timeline from the map.
54 0 : let mut timelines = tenant.timelines.lock().unwrap();
55 0 : let children_exist = timelines
56 0 : .iter()
57 0 : .any(|(_, entry)| entry.get_ancestor_timeline_id() == Some(timeline.timeline_id));
58 0 : // XXX this can happen because `branch_timeline` doesn't check `TimelineState::Stopping`.
59 0 : // We already deleted the layer files, so it's probably best to panic.
60 0 : // (Ideally, above remove_dir_all is atomic so we don't see this timeline after a restart)
61 0 : if children_exist {
62 0 : panic!("Timeline grew children while we removed layer files");
63 0 : }
64 0 :
65 0 : timelines
66 0 : .remove(&timeline.timeline_id)
67 0 : .expect("timeline that we were deleting was concurrently removed from 'timelines' map");
68 0 :
69 0 : drop(timelines);
70 0 :
71 0 : Ok(())
72 0 : }
|