Line data Source code
1 : //! This module contains global `(tenant_id, timeline_id)` -> `Arc<Timeline>` mapping.
2 : //! All timelines should always be present in this map, this is done by loading them
3 : //! all from the disk on startup and keeping them in memory.
4 :
5 : use crate::safekeeper::ServerInfo;
6 : use crate::timeline::{get_tenant_dir, get_timeline_dir, Timeline, TimelineError};
7 : use crate::timelines_set::TimelinesSet;
8 : use crate::wal_backup_partial::RateLimiter;
9 : use crate::SafeKeeperConf;
10 : use anyhow::{bail, Context, Result};
11 : use camino::Utf8PathBuf;
12 : use once_cell::sync::Lazy;
13 : use serde::Serialize;
14 : use std::collections::HashMap;
15 : use std::str::FromStr;
16 : use std::sync::atomic::Ordering;
17 : use std::sync::{Arc, Mutex};
18 : use std::time::{Duration, Instant};
19 : use tracing::*;
20 : use utils::id::{TenantId, TenantTimelineId, TimelineId};
21 : use utils::lsn::Lsn;
22 :
23 : struct GlobalTimelinesState {
24 : timelines: HashMap<TenantTimelineId, Arc<Timeline>>,
25 :
26 : // A tombstone indicates this timeline used to exist has been deleted. These are used to prevent
27 : // on-demand timeline creation from recreating deleted timelines. This is only soft-enforced, as
28 : // this map is dropped on restart.
29 : tombstones: HashMap<TenantTimelineId, Instant>,
30 :
31 : conf: Option<SafeKeeperConf>,
32 : broker_active_set: Arc<TimelinesSet>,
33 : load_lock: Arc<tokio::sync::Mutex<TimelineLoadLock>>,
34 : partial_backup_rate_limiter: RateLimiter,
35 : }
36 :
37 : // Used to prevent concurrent timeline loading.
38 : pub struct TimelineLoadLock;
39 :
40 : impl GlobalTimelinesState {
41 : /// Get configuration, which must be set once during init.
42 0 : fn get_conf(&self) -> &SafeKeeperConf {
43 0 : self.conf
44 0 : .as_ref()
45 0 : .expect("GlobalTimelinesState conf is not initialized")
46 0 : }
47 :
48 : /// Get dependencies for a timeline constructor.
49 0 : fn get_dependencies(&self) -> (SafeKeeperConf, Arc<TimelinesSet>, RateLimiter) {
50 0 : (
51 0 : self.get_conf().clone(),
52 0 : self.broker_active_set.clone(),
53 0 : self.partial_backup_rate_limiter.clone(),
54 0 : )
55 0 : }
56 :
57 : /// Insert timeline into the map. Returns error if timeline with the same id already exists.
58 0 : fn try_insert(&mut self, timeline: Arc<Timeline>) -> Result<()> {
59 0 : let ttid = timeline.ttid;
60 0 : if self.timelines.contains_key(&ttid) {
61 0 : bail!(TimelineError::AlreadyExists(ttid));
62 0 : }
63 0 : self.timelines.insert(ttid, timeline);
64 0 : Ok(())
65 0 : }
66 :
67 : /// Get timeline from the map. Returns error if timeline doesn't exist.
68 0 : fn get(&self, ttid: &TenantTimelineId) -> Result<Arc<Timeline>, TimelineError> {
69 0 : self.timelines
70 0 : .get(ttid)
71 0 : .cloned()
72 0 : .ok_or(TimelineError::NotFound(*ttid))
73 0 : }
74 :
75 0 : fn delete(&mut self, ttid: TenantTimelineId) {
76 0 : self.timelines.remove(&ttid);
77 0 : self.tombstones.insert(ttid, Instant::now());
78 0 : }
79 : }
80 :
81 0 : static TIMELINES_STATE: Lazy<Mutex<GlobalTimelinesState>> = Lazy::new(|| {
82 0 : Mutex::new(GlobalTimelinesState {
83 0 : timelines: HashMap::new(),
84 0 : tombstones: HashMap::new(),
85 0 : conf: None,
86 0 : broker_active_set: Arc::new(TimelinesSet::default()),
87 0 : load_lock: Arc::new(tokio::sync::Mutex::new(TimelineLoadLock)),
88 0 : partial_backup_rate_limiter: RateLimiter::new(1),
89 0 : })
90 0 : });
91 :
92 : /// A zero-sized struct used to manage access to the global timelines map.
93 : pub struct GlobalTimelines;
94 :
95 : impl GlobalTimelines {
96 : /// Inject dependencies needed for the timeline constructors and load all timelines to memory.
97 0 : pub async fn init(conf: SafeKeeperConf) -> Result<()> {
98 0 : // clippy isn't smart enough to understand that drop(state) releases the
99 0 : // lock, so use explicit block
100 0 : let tenants_dir = {
101 0 : let mut state = TIMELINES_STATE.lock().unwrap();
102 0 : state.partial_backup_rate_limiter = RateLimiter::new(conf.partial_backup_concurrency);
103 0 : state.conf = Some(conf);
104 0 :
105 0 : // Iterate through all directories and load tenants for all directories
106 0 : // named as a valid tenant_id.
107 0 : state.get_conf().workdir.clone()
108 0 : };
109 0 : let mut tenant_count = 0;
110 0 : for tenants_dir_entry in std::fs::read_dir(&tenants_dir)
111 0 : .with_context(|| format!("failed to list tenants dir {}", tenants_dir))?
112 : {
113 0 : match &tenants_dir_entry {
114 0 : Ok(tenants_dir_entry) => {
115 0 : if let Ok(tenant_id) =
116 0 : TenantId::from_str(tenants_dir_entry.file_name().to_str().unwrap_or(""))
117 : {
118 0 : tenant_count += 1;
119 0 : GlobalTimelines::load_tenant_timelines(tenant_id).await?;
120 0 : }
121 : }
122 0 : Err(e) => error!(
123 0 : "failed to list tenants dir entry {:?} in directory {}, reason: {:?}",
124 : tenants_dir_entry, tenants_dir, e
125 : ),
126 : }
127 : }
128 :
129 0 : info!(
130 0 : "found {} tenants directories, successfully loaded {} timelines",
131 0 : tenant_count,
132 0 : TIMELINES_STATE.lock().unwrap().timelines.len()
133 : );
134 0 : Ok(())
135 0 : }
136 :
137 : /// Loads all timelines for the given tenant to memory. Returns fs::read_dir
138 : /// errors if any.
139 : ///
140 : /// It is async for update_status_notify sake. Since TIMELINES_STATE lock is
141 : /// sync and there is no important reason to make it async (it is always
142 : /// held for a short while) we just lock and unlock it for each timeline --
143 : /// this function is called during init when nothing else is running, so
144 : /// this is fine.
145 0 : async fn load_tenant_timelines(tenant_id: TenantId) -> Result<()> {
146 0 : let (conf, broker_active_set, partial_backup_rate_limiter) = {
147 0 : let state = TIMELINES_STATE.lock().unwrap();
148 0 : state.get_dependencies()
149 0 : };
150 0 :
151 0 : let timelines_dir = get_tenant_dir(&conf, &tenant_id);
152 0 : for timelines_dir_entry in std::fs::read_dir(&timelines_dir)
153 0 : .with_context(|| format!("failed to list timelines dir {}", timelines_dir))?
154 : {
155 0 : match &timelines_dir_entry {
156 0 : Ok(timeline_dir_entry) => {
157 0 : if let Ok(timeline_id) =
158 0 : TimelineId::from_str(timeline_dir_entry.file_name().to_str().unwrap_or(""))
159 : {
160 0 : let ttid = TenantTimelineId::new(tenant_id, timeline_id);
161 0 : match Timeline::load_timeline(&conf, ttid) {
162 0 : Ok(timeline) => {
163 0 : let tli = Arc::new(timeline);
164 0 : TIMELINES_STATE
165 0 : .lock()
166 0 : .unwrap()
167 0 : .timelines
168 0 : .insert(ttid, tli.clone());
169 0 : tli.bootstrap(
170 0 : &conf,
171 0 : broker_active_set.clone(),
172 0 : partial_backup_rate_limiter.clone(),
173 0 : );
174 0 : }
175 : // If we can't load a timeline, it's most likely because of a corrupted
176 : // directory. We will log an error and won't allow to delete/recreate
177 : // this timeline. The only way to fix this timeline is to repair manually
178 : // and restart the safekeeper.
179 0 : Err(e) => error!(
180 0 : "failed to load timeline {} for tenant {}, reason: {:?}",
181 : timeline_id, tenant_id, e
182 : ),
183 : }
184 0 : }
185 : }
186 0 : Err(e) => error!(
187 0 : "failed to list timelines dir entry {:?} in directory {}, reason: {:?}",
188 : timelines_dir_entry, timelines_dir, e
189 : ),
190 : }
191 : }
192 :
193 0 : Ok(())
194 0 : }
195 :
196 : /// Take a lock for timeline loading.
197 0 : pub async fn loading_lock() -> Arc<tokio::sync::Mutex<TimelineLoadLock>> {
198 0 : TIMELINES_STATE.lock().unwrap().load_lock.clone()
199 0 : }
200 :
201 : /// Load timeline from disk to the memory.
202 0 : pub async fn load_timeline<'a>(
203 0 : _guard: &tokio::sync::MutexGuard<'a, TimelineLoadLock>,
204 0 : ttid: TenantTimelineId,
205 0 : ) -> Result<Arc<Timeline>> {
206 0 : let (conf, broker_active_set, partial_backup_rate_limiter) =
207 0 : TIMELINES_STATE.lock().unwrap().get_dependencies();
208 0 :
209 0 : match Timeline::load_timeline(&conf, ttid) {
210 0 : Ok(timeline) => {
211 0 : let tli = Arc::new(timeline);
212 0 :
213 0 : // TODO: prevent concurrent timeline creation/loading
214 0 : {
215 0 : let mut state = TIMELINES_STATE.lock().unwrap();
216 0 :
217 0 : // We may be have been asked to load a timeline that was previously deleted (e.g. from `pull_timeline.rs`). We trust
218 0 : // that the human doing this manual intervention knows what they are doing, and remove its tombstone.
219 0 : if state.tombstones.remove(&ttid).is_some() {
220 0 : warn!("Un-deleted timeline {ttid}");
221 0 : }
222 :
223 0 : state.timelines.insert(ttid, tli.clone());
224 0 : }
225 0 :
226 0 : tli.bootstrap(&conf, broker_active_set, partial_backup_rate_limiter);
227 0 :
228 0 : Ok(tli)
229 : }
230 : // If we can't load a timeline, it's bad. Caller will figure it out.
231 0 : Err(e) => bail!("failed to load timeline {}, reason: {:?}", ttid, e),
232 : }
233 0 : }
234 :
235 : /// Get the number of timelines in the map.
236 0 : pub fn timelines_count() -> usize {
237 0 : TIMELINES_STATE.lock().unwrap().timelines.len()
238 0 : }
239 :
240 : /// Get the global safekeeper config.
241 0 : pub fn get_global_config() -> SafeKeeperConf {
242 0 : TIMELINES_STATE.lock().unwrap().get_conf().clone()
243 0 : }
244 :
245 0 : pub fn get_global_broker_active_set() -> Arc<TimelinesSet> {
246 0 : TIMELINES_STATE.lock().unwrap().broker_active_set.clone()
247 0 : }
248 :
249 : /// Create a new timeline with the given id. If the timeline already exists, returns
250 : /// an existing timeline.
251 0 : pub(crate) async fn create(
252 0 : ttid: TenantTimelineId,
253 0 : server_info: ServerInfo,
254 0 : commit_lsn: Lsn,
255 0 : local_start_lsn: Lsn,
256 0 : ) -> Result<Arc<Timeline>> {
257 0 : let (conf, broker_active_set, partial_backup_rate_limiter) = {
258 0 : let state = TIMELINES_STATE.lock().unwrap();
259 0 : if let Ok(timeline) = state.get(&ttid) {
260 : // Timeline already exists, return it.
261 0 : return Ok(timeline);
262 0 : }
263 0 :
264 0 : if state.tombstones.contains_key(&ttid) {
265 0 : anyhow::bail!("Timeline {ttid} is deleted, refusing to recreate");
266 0 : }
267 0 :
268 0 : state.get_dependencies()
269 0 : };
270 0 :
271 0 : info!("creating new timeline {}", ttid);
272 :
273 0 : let timeline = Arc::new(Timeline::create_empty(
274 0 : &conf,
275 0 : ttid,
276 0 : server_info,
277 0 : commit_lsn,
278 0 : local_start_lsn,
279 0 : )?);
280 :
281 : // Take a lock and finish the initialization holding this mutex. No other threads
282 : // can interfere with creation after we will insert timeline into the map.
283 : {
284 0 : let mut shared_state = timeline.write_shared_state().await;
285 :
286 : // We can get a race condition here in case of concurrent create calls, but only
287 : // in theory. create() will return valid timeline on the next try.
288 0 : TIMELINES_STATE
289 0 : .lock()
290 0 : .unwrap()
291 0 : .try_insert(timeline.clone())?;
292 :
293 : // Write the new timeline to the disk and start background workers.
294 : // Bootstrap is transactional, so if it fails, the timeline will be deleted,
295 : // and the state on disk should remain unchanged.
296 0 : if let Err(e) = timeline
297 0 : .init_new(
298 0 : &mut shared_state,
299 0 : &conf,
300 0 : broker_active_set,
301 0 : partial_backup_rate_limiter,
302 0 : )
303 0 : .await
304 : {
305 : // Note: the most likely reason for init failure is that the timeline
306 : // directory already exists on disk. This happens when timeline is corrupted
307 : // and wasn't loaded from disk on startup because of that. We want to preserve
308 : // the timeline directory in this case, for further inspection.
309 :
310 : // TODO: this is an unusual error, perhaps we should send it to sentry
311 : // TODO: compute will try to create timeline every second, we should add backoff
312 0 : error!("failed to init new timeline {}: {}", ttid, e);
313 :
314 : // Timeline failed to init, it cannot be used. Remove it from the map.
315 0 : TIMELINES_STATE.lock().unwrap().timelines.remove(&ttid);
316 0 : return Err(e);
317 0 : }
318 0 : // We are done with bootstrap, release the lock, return the timeline.
319 0 : // {} block forces release before .await
320 0 : }
321 0 : Ok(timeline)
322 0 : }
323 :
324 : /// Get a timeline from the global map. If it's not present, it doesn't exist on disk,
325 : /// or was corrupted and couldn't be loaded on startup. Returned timeline is always valid,
326 : /// i.e. loaded in memory and not cancelled.
327 0 : pub(crate) fn get(ttid: TenantTimelineId) -> Result<Arc<Timeline>, TimelineError> {
328 0 : let tli_res = {
329 0 : let state = TIMELINES_STATE.lock().unwrap();
330 0 : state.get(&ttid)
331 0 : };
332 0 : match tli_res {
333 0 : Ok(tli) => {
334 0 : if tli.is_cancelled() {
335 0 : return Err(TimelineError::Cancelled(ttid));
336 0 : }
337 0 : Ok(tli)
338 : }
339 0 : _ => tli_res,
340 : }
341 0 : }
342 :
343 : /// Returns all timelines. This is used for background timeline processes.
344 0 : pub fn get_all() -> Vec<Arc<Timeline>> {
345 0 : let global_lock = TIMELINES_STATE.lock().unwrap();
346 0 : global_lock
347 0 : .timelines
348 0 : .values()
349 0 : .filter(|t| !t.is_cancelled())
350 0 : .cloned()
351 0 : .collect()
352 0 : }
353 :
354 : /// Returns all timelines belonging to a given tenant. Used for deleting all timelines of a tenant,
355 : /// and that's why it can return cancelled timelines, to retry deleting them.
356 0 : fn get_all_for_tenant(tenant_id: TenantId) -> Vec<Arc<Timeline>> {
357 0 : let global_lock = TIMELINES_STATE.lock().unwrap();
358 0 : global_lock
359 0 : .timelines
360 0 : .values()
361 0 : .filter(|t| t.ttid.tenant_id == tenant_id)
362 0 : .cloned()
363 0 : .collect()
364 0 : }
365 :
366 : /// Cancels timeline, then deletes the corresponding data directory.
367 : /// If only_local, doesn't remove WAL segments in remote storage.
368 0 : pub(crate) async fn delete(
369 0 : ttid: &TenantTimelineId,
370 0 : only_local: bool,
371 0 : ) -> Result<TimelineDeleteForceResult> {
372 0 : let tli_res = {
373 0 : let state = TIMELINES_STATE.lock().unwrap();
374 0 :
375 0 : if state.tombstones.contains_key(ttid) {
376 : // Presence of a tombstone guarantees that a previous deletion has completed and there is no work to do.
377 0 : info!("Timeline {ttid} was already deleted");
378 0 : return Ok(TimelineDeleteForceResult {
379 0 : dir_existed: false,
380 0 : was_active: false,
381 0 : });
382 0 : }
383 0 :
384 0 : state.get(ttid)
385 : };
386 :
387 0 : let result = match tli_res {
388 0 : Ok(timeline) => {
389 0 : let was_active = timeline.broker_active.load(Ordering::Relaxed);
390 :
391 : // Take a lock and finish the deletion holding this mutex.
392 0 : let mut shared_state = timeline.write_shared_state().await;
393 :
394 0 : info!("deleting timeline {}, only_local={}", ttid, only_local);
395 0 : let dir_existed = timeline.delete(&mut shared_state, only_local).await?;
396 :
397 0 : Ok(TimelineDeleteForceResult {
398 0 : dir_existed,
399 0 : was_active, // TODO: we probably should remove this field
400 0 : })
401 : }
402 : Err(_) => {
403 : // Timeline is not memory, but it may still exist on disk in broken state.
404 0 : let dir_path = get_timeline_dir(TIMELINES_STATE.lock().unwrap().get_conf(), ttid);
405 0 : let dir_existed = delete_dir(dir_path)?;
406 :
407 0 : Ok(TimelineDeleteForceResult {
408 0 : dir_existed,
409 0 : was_active: false,
410 0 : })
411 : }
412 : };
413 :
414 : // Finalize deletion, by dropping Timeline objects and storing smaller tombstones. The tombstones
415 : // are used to prevent still-running computes from re-creating the same timeline when they send data,
416 : // and to speed up repeated deletion calls by avoiding re-listing objects.
417 0 : TIMELINES_STATE.lock().unwrap().delete(*ttid);
418 0 :
419 0 : result
420 0 : }
421 :
422 : /// Deactivates and deletes all timelines for the tenant. Returns map of all timelines which
423 : /// the tenant had, `true` if a timeline was active. There may be a race if new timelines are
424 : /// created simultaneously. In that case the function will return error and the caller should
425 : /// retry tenant deletion again later.
426 : ///
427 : /// If only_local, doesn't remove WAL segments in remote storage.
428 0 : pub async fn delete_force_all_for_tenant(
429 0 : tenant_id: &TenantId,
430 0 : only_local: bool,
431 0 : ) -> Result<HashMap<TenantTimelineId, TimelineDeleteForceResult>> {
432 0 : info!("deleting all timelines for tenant {}", tenant_id);
433 0 : let to_delete = Self::get_all_for_tenant(*tenant_id);
434 0 :
435 0 : let mut err = None;
436 0 :
437 0 : let mut deleted = HashMap::new();
438 0 : for tli in &to_delete {
439 0 : match Self::delete(&tli.ttid, only_local).await {
440 0 : Ok(result) => {
441 0 : deleted.insert(tli.ttid, result);
442 0 : }
443 0 : Err(e) => {
444 0 : error!("failed to delete timeline {}: {}", tli.ttid, e);
445 : // Save error to return later.
446 0 : err = Some(e);
447 : }
448 : }
449 : }
450 :
451 : // If there was an error, return it.
452 0 : if let Some(e) = err {
453 0 : return Err(e);
454 0 : }
455 0 :
456 0 : // There may be broken timelines on disk, so delete the whole tenant dir as well.
457 0 : // Note that we could concurrently create new timelines while we were deleting them,
458 0 : // so the directory may be not empty. In this case timelines will have bad state
459 0 : // and timeline background jobs can panic.
460 0 : delete_dir(get_tenant_dir(
461 0 : TIMELINES_STATE.lock().unwrap().get_conf(),
462 0 : tenant_id,
463 0 : ))?;
464 :
465 0 : Ok(deleted)
466 0 : }
467 :
468 0 : pub fn housekeeping(tombstone_ttl: &Duration) {
469 0 : let mut state = TIMELINES_STATE.lock().unwrap();
470 0 :
471 0 : // We keep tombstones long enough to have a good chance of preventing rogue computes from re-creating deleted
472 0 : // timelines. If a compute kept running for longer than this TTL (or across a safekeeper restart) then they
473 0 : // may recreate a deleted timeline.
474 0 : let now = Instant::now();
475 0 : state
476 0 : .tombstones
477 0 : .retain(|_, v| now.duration_since(*v) < *tombstone_ttl);
478 0 : }
479 : }
480 :
481 : #[derive(Clone, Copy, Serialize)]
482 : pub struct TimelineDeleteForceResult {
483 : pub dir_existed: bool,
484 : pub was_active: bool,
485 : }
486 :
487 : /// Deletes directory and it's contents. Returns false if directory does not exist.
488 0 : fn delete_dir(path: Utf8PathBuf) -> Result<bool> {
489 0 : match std::fs::remove_dir_all(path) {
490 0 : Ok(_) => Ok(true),
491 0 : Err(e) if e.kind() == std::io::ErrorKind::NotFound => Ok(false),
492 0 : Err(e) => Err(e.into()),
493 : }
494 0 : }
|