Line data Source code
1 : //!
2 : //! Timeline repository implementation that keeps old data in files on disk, and
3 : //! the recent changes in memory. See tenant/*_layer.rs files.
4 : //! The functions here are responsible for locating the correct layer for the
5 : //! get/put call, walking back the timeline branching history as needed.
6 : //!
7 : //! The files are stored in the .neon/tenants/<tenant_id>/timelines/<timeline_id>
8 : //! directory. See docs/pageserver-storage.md for how the files are managed.
9 : //! In addition to the layer files, there is a metadata file in the same
10 : //! directory that contains information about the timeline, in particular its
11 : //! parent timeline, and the last LSN that has been written to disk.
12 : //!
13 :
14 : use anyhow::{bail, Context};
15 : use arc_swap::ArcSwap;
16 : use camino::Utf8Path;
17 : use camino::Utf8PathBuf;
18 : use enumset::EnumSet;
19 : use futures::stream::FuturesUnordered;
20 : use futures::FutureExt;
21 : use futures::StreamExt;
22 : use pageserver_api::models;
23 : use pageserver_api::models::AuxFilePolicy;
24 : use pageserver_api::models::TimelineState;
25 : use pageserver_api::models::TopTenantShardItem;
26 : use pageserver_api::models::WalRedoManagerStatus;
27 : use pageserver_api::shard::ShardIdentity;
28 : use pageserver_api::shard::ShardStripeSize;
29 : use pageserver_api::shard::TenantShardId;
30 : use remote_storage::DownloadError;
31 : use remote_storage::GenericRemoteStorage;
32 : use remote_storage::TimeoutOrCancel;
33 : use std::fmt;
34 : use std::time::SystemTime;
35 : use storage_broker::BrokerClientChannel;
36 : use tokio::io::BufReader;
37 : use tokio::sync::watch;
38 : use tokio::task::JoinSet;
39 : use tokio_util::sync::CancellationToken;
40 : use tracing::*;
41 : use utils::backoff;
42 : use utils::completion;
43 : use utils::crashsafe::path_with_suffix_extension;
44 : use utils::failpoint_support;
45 : use utils::fs_ext;
46 : use utils::pausable_failpoint;
47 : use utils::sync::gate::Gate;
48 : use utils::sync::gate::GateGuard;
49 : use utils::timeout::timeout_cancellable;
50 : use utils::timeout::TimeoutCancellableError;
51 : use utils::zstd::create_zst_tarball;
52 : use utils::zstd::extract_zst_tarball;
53 :
54 : use self::config::AttachedLocationConfig;
55 : use self::config::AttachmentMode;
56 : use self::config::LocationConf;
57 : use self::config::TenantConf;
58 : use self::metadata::TimelineMetadata;
59 : use self::mgr::GetActiveTenantError;
60 : use self::mgr::GetTenantError;
61 : use self::remote_timeline_client::upload::upload_index_part;
62 : use self::remote_timeline_client::RemoteTimelineClient;
63 : use self::timeline::uninit::TimelineCreateGuard;
64 : use self::timeline::uninit::TimelineExclusionError;
65 : use self::timeline::uninit::UninitializedTimeline;
66 : use self::timeline::EvictionTaskTenantState;
67 : use self::timeline::GcCutoffs;
68 : use self::timeline::TimelineResources;
69 : use self::timeline::WaitLsnError;
70 : use crate::config::PageServerConf;
71 : use crate::context::{DownloadBehavior, RequestContext};
72 : use crate::deletion_queue::DeletionQueueClient;
73 : use crate::deletion_queue::DeletionQueueError;
74 : use crate::import_datadir;
75 : use crate::is_uninit_mark;
76 : use crate::metrics::TENANT;
77 : use crate::metrics::{
78 : remove_tenant_metrics, BROKEN_TENANTS_SET, TENANT_STATE_METRIC, TENANT_SYNTHETIC_SIZE_METRIC,
79 : };
80 : use crate::repository::GcResult;
81 : use crate::task_mgr;
82 : use crate::task_mgr::TaskKind;
83 : use crate::tenant::config::LocationMode;
84 : use crate::tenant::config::TenantConfOpt;
85 : pub use crate::tenant::remote_timeline_client::index::IndexPart;
86 : use crate::tenant::remote_timeline_client::remote_initdb_archive_path;
87 : use crate::tenant::remote_timeline_client::MaybeDeletedIndexPart;
88 : use crate::tenant::remote_timeline_client::INITDB_PATH;
89 : use crate::tenant::storage_layer::DeltaLayer;
90 : use crate::tenant::storage_layer::ImageLayer;
91 : use crate::InitializationOrder;
92 : use std::collections::hash_map::Entry;
93 : use std::collections::BTreeSet;
94 : use std::collections::HashMap;
95 : use std::collections::HashSet;
96 : use std::fmt::Debug;
97 : use std::fmt::Display;
98 : use std::fs;
99 : use std::fs::File;
100 : use std::ops::Bound::Included;
101 : use std::sync::atomic::AtomicU64;
102 : use std::sync::atomic::Ordering;
103 : use std::sync::Arc;
104 : use std::sync::Mutex;
105 : use std::time::{Duration, Instant};
106 :
107 : use crate::span;
108 : use crate::tenant::timeline::delete::DeleteTimelineFlow;
109 : use crate::tenant::timeline::uninit::cleanup_timeline_directory;
110 : use crate::virtual_file::VirtualFile;
111 : use crate::walredo::PostgresRedoManager;
112 : use crate::TEMP_FILE_SUFFIX;
113 : use once_cell::sync::Lazy;
114 : pub use pageserver_api::models::TenantState;
115 : use tokio::sync::Semaphore;
116 :
117 0 : static INIT_DB_SEMAPHORE: Lazy<Semaphore> = Lazy::new(|| Semaphore::new(8));
118 : use utils::{
119 : crashsafe,
120 : generation::Generation,
121 : id::TimelineId,
122 : lsn::{Lsn, RecordLsn},
123 : };
124 :
125 : pub mod blob_io;
126 : pub mod block_io;
127 : pub mod vectored_blob_io;
128 :
129 : pub mod disk_btree;
130 : pub(crate) mod ephemeral_file;
131 : pub mod layer_map;
132 :
133 : pub mod metadata;
134 : pub mod remote_timeline_client;
135 : pub mod storage_layer;
136 :
137 : pub mod config;
138 : pub mod mgr;
139 : pub mod secondary;
140 : pub mod tasks;
141 : pub mod upload_queue;
142 :
143 : pub(crate) mod timeline;
144 :
145 : pub mod size;
146 :
147 : pub(crate) mod throttle;
148 :
149 : pub(crate) use crate::span::debug_assert_current_span_has_tenant_and_timeline_id;
150 : pub(crate) use timeline::{LogicalSizeCalculationCause, PageReconstructError, Timeline};
151 :
152 : // re-export for use in walreceiver
153 : pub use crate::tenant::timeline::WalReceiverInfo;
154 :
155 : /// The "tenants" part of `tenants/<tenant>/timelines...`
156 : pub const TENANTS_SEGMENT_NAME: &str = "tenants";
157 :
158 : /// Parts of the `.neon/tenants/<tenant_id>/timelines/<timeline_id>` directory prefix.
159 : pub const TIMELINES_SEGMENT_NAME: &str = "timelines";
160 :
161 : /// References to shared objects that are passed into each tenant, such
162 : /// as the shared remote storage client and process initialization state.
163 : #[derive(Clone)]
164 : pub struct TenantSharedResources {
165 : pub broker_client: storage_broker::BrokerClientChannel,
166 : pub remote_storage: GenericRemoteStorage,
167 : pub deletion_queue_client: DeletionQueueClient,
168 : }
169 :
170 : /// A [`Tenant`] is really an _attached_ tenant. The configuration
171 : /// for an attached tenant is a subset of the [`LocationConf`], represented
172 : /// in this struct.
173 : pub(super) struct AttachedTenantConf {
174 : tenant_conf: TenantConfOpt,
175 : location: AttachedLocationConfig,
176 : }
177 :
178 : impl AttachedTenantConf {
179 0 : fn new(tenant_conf: TenantConfOpt, location: AttachedLocationConfig) -> Self {
180 0 : Self {
181 0 : tenant_conf,
182 0 : location,
183 0 : }
184 0 : }
185 :
186 169 : fn try_from(location_conf: LocationConf) -> anyhow::Result<Self> {
187 169 : match &location_conf.mode {
188 169 : LocationMode::Attached(attach_conf) => Ok(Self {
189 169 : tenant_conf: location_conf.tenant_conf,
190 169 : location: *attach_conf,
191 169 : }),
192 : LocationMode::Secondary(_) => {
193 0 : anyhow::bail!("Attempted to construct AttachedTenantConf from a LocationConf in secondary mode")
194 : }
195 : }
196 169 : }
197 : }
198 : struct TimelinePreload {
199 : timeline_id: TimelineId,
200 : client: RemoteTimelineClient,
201 : index_part: Result<MaybeDeletedIndexPart, DownloadError>,
202 : }
203 :
204 : pub(crate) struct TenantPreload {
205 : timelines: HashMap<TimelineId, TimelinePreload>,
206 : }
207 :
208 : /// When we spawn a tenant, there is a special mode for tenant creation that
209 : /// avoids trying to read anything from remote storage.
210 : pub(crate) enum SpawnMode {
211 : /// Activate as soon as possible
212 : Eager,
213 : /// Lazy activation in the background, with the option to skip the queue if the need comes up
214 : Lazy,
215 : /// Tenant has been created during the lifetime of this process
216 : Create,
217 : }
218 :
219 : ///
220 : /// Tenant consists of multiple timelines. Keep them in a hash table.
221 : ///
222 : pub struct Tenant {
223 : // Global pageserver config parameters
224 : pub conf: &'static PageServerConf,
225 :
226 : /// The value creation timestamp, used to measure activation delay, see:
227 : /// <https://github.com/neondatabase/neon/issues/4025>
228 : constructed_at: Instant,
229 :
230 : state: watch::Sender<TenantState>,
231 :
232 : // Overridden tenant-specific config parameters.
233 : // We keep TenantConfOpt sturct here to preserve the information
234 : // about parameters that are not set.
235 : // This is necessary to allow global config updates.
236 : tenant_conf: Arc<ArcSwap<AttachedTenantConf>>,
237 :
238 : tenant_shard_id: TenantShardId,
239 :
240 : // The detailed sharding information, beyond the number/count in tenant_shard_id
241 : shard_identity: ShardIdentity,
242 :
243 : /// The remote storage generation, used to protect S3 objects from split-brain.
244 : /// Does not change over the lifetime of the [`Tenant`] object.
245 : ///
246 : /// This duplicates the generation stored in LocationConf, but that structure is mutable:
247 : /// this copy enforces the invariant that generatio doesn't change during a Tenant's lifetime.
248 : generation: Generation,
249 :
250 : timelines: Mutex<HashMap<TimelineId, Arc<Timeline>>>,
251 :
252 : /// During timeline creation, we first insert the TimelineId to the
253 : /// creating map, then `timelines`, then remove it from the creating map.
254 : /// **Lock order**: if acquring both, acquire`timelines` before `timelines_creating`
255 : timelines_creating: std::sync::Mutex<HashSet<TimelineId>>,
256 :
257 : // This mutex prevents creation of new timelines during GC.
258 : // Adding yet another mutex (in addition to `timelines`) is needed because holding
259 : // `timelines` mutex during all GC iteration
260 : // may block for a long time `get_timeline`, `get_timelines_state`,... and other operations
261 : // with timelines, which in turn may cause dropping replication connection, expiration of wait_for_lsn
262 : // timeout...
263 : gc_cs: tokio::sync::Mutex<()>,
264 : walredo_mgr: Option<Arc<WalRedoManager>>,
265 :
266 : // provides access to timeline data sitting in the remote storage
267 : pub(crate) remote_storage: GenericRemoteStorage,
268 :
269 : // Access to global deletion queue for when this tenant wants to schedule a deletion
270 : deletion_queue_client: DeletionQueueClient,
271 :
272 : /// Cached logical sizes updated updated on each [`Tenant::gather_size_inputs`].
273 : cached_logical_sizes: tokio::sync::Mutex<HashMap<(TimelineId, Lsn), u64>>,
274 : cached_synthetic_tenant_size: Arc<AtomicU64>,
275 :
276 : eviction_task_tenant_state: tokio::sync::Mutex<EvictionTaskTenantState>,
277 :
278 : /// If the tenant is in Activating state, notify this to encourage it
279 : /// to proceed to Active as soon as possible, rather than waiting for lazy
280 : /// background warmup.
281 : pub(crate) activate_now_sem: tokio::sync::Semaphore,
282 :
283 : // Cancellation token fires when we have entered shutdown(). This is a parent of
284 : // Timelines' cancellation token.
285 : pub(crate) cancel: CancellationToken,
286 :
287 : // Users of the Tenant such as the page service must take this Gate to avoid
288 : // trying to use a Tenant which is shutting down.
289 : pub(crate) gate: Gate,
290 :
291 : /// Throttle applied at the top of [`Timeline::get`].
292 : /// All [`Tenant::timelines`] of a given [`Tenant`] instance share the same [`throttle::Throttle`] instance.
293 : pub(crate) timeline_get_throttle:
294 : Arc<throttle::Throttle<&'static crate::metrics::tenant_throttling::TimelineGet>>,
295 :
296 : /// An ongoing timeline detach must be checked during attempts to GC or compact a timeline.
297 : ongoing_timeline_detach: std::sync::Mutex<Option<(TimelineId, utils::completion::Barrier)>>,
298 : }
299 :
300 : impl std::fmt::Debug for Tenant {
301 0 : fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
302 0 : write!(f, "{} ({})", self.tenant_shard_id, self.current_state())
303 0 : }
304 : }
305 :
306 : pub(crate) enum WalRedoManager {
307 : Prod(PostgresRedoManager),
308 : #[cfg(test)]
309 : Test(harness::TestRedoManager),
310 : }
311 :
312 : impl From<PostgresRedoManager> for WalRedoManager {
313 0 : fn from(mgr: PostgresRedoManager) -> Self {
314 0 : Self::Prod(mgr)
315 0 : }
316 : }
317 :
318 : #[cfg(test)]
319 : impl From<harness::TestRedoManager> for WalRedoManager {
320 161 : fn from(mgr: harness::TestRedoManager) -> Self {
321 161 : Self::Test(mgr)
322 161 : }
323 : }
324 :
325 : impl WalRedoManager {
326 0 : pub(crate) fn maybe_quiesce(&self, idle_timeout: Duration) {
327 0 : match self {
328 0 : Self::Prod(mgr) => mgr.maybe_quiesce(idle_timeout),
329 0 : #[cfg(test)]
330 0 : Self::Test(_) => {
331 0 : // Not applicable to test redo manager
332 0 : }
333 0 : }
334 0 : }
335 :
336 : /// # Cancel-Safety
337 : ///
338 : /// This method is cancellation-safe.
339 58 : pub async fn request_redo(
340 58 : &self,
341 58 : key: crate::repository::Key,
342 58 : lsn: Lsn,
343 58 : base_img: Option<(Lsn, bytes::Bytes)>,
344 58 : records: Vec<(Lsn, crate::walrecord::NeonWalRecord)>,
345 58 : pg_version: u32,
346 58 : ) -> anyhow::Result<bytes::Bytes> {
347 58 : match self {
348 0 : Self::Prod(mgr) => {
349 0 : mgr.request_redo(key, lsn, base_img, records, pg_version)
350 0 : .await
351 : }
352 : #[cfg(test)]
353 58 : Self::Test(mgr) => {
354 58 : mgr.request_redo(key, lsn, base_img, records, pg_version)
355 0 : .await
356 : }
357 : }
358 58 : }
359 :
360 0 : pub(crate) fn status(&self) -> Option<WalRedoManagerStatus> {
361 0 : match self {
362 0 : WalRedoManager::Prod(m) => Some(m.status()),
363 0 : #[cfg(test)]
364 0 : WalRedoManager::Test(_) => None,
365 0 : }
366 0 : }
367 : }
368 :
369 0 : #[derive(Debug, thiserror::Error, PartialEq, Eq)]
370 : pub enum GetTimelineError {
371 : #[error("Timeline {tenant_id}/{timeline_id} is not active, state: {state:?}")]
372 : NotActive {
373 : tenant_id: TenantShardId,
374 : timeline_id: TimelineId,
375 : state: TimelineState,
376 : },
377 : #[error("Timeline {tenant_id}/{timeline_id} was not found")]
378 : NotFound {
379 : tenant_id: TenantShardId,
380 : timeline_id: TimelineId,
381 : },
382 : }
383 :
384 0 : #[derive(Debug, thiserror::Error)]
385 : pub enum LoadLocalTimelineError {
386 : #[error("FailedToLoad")]
387 : Load(#[source] anyhow::Error),
388 : #[error("FailedToResumeDeletion")]
389 : ResumeDeletion(#[source] anyhow::Error),
390 : }
391 :
392 0 : #[derive(thiserror::Error)]
393 : pub enum DeleteTimelineError {
394 : #[error("NotFound")]
395 : NotFound,
396 :
397 : #[error("HasChildren")]
398 : HasChildren(Vec<TimelineId>),
399 :
400 : #[error("Timeline deletion is already in progress")]
401 : AlreadyInProgress(Arc<tokio::sync::Mutex<DeleteTimelineFlow>>),
402 :
403 : #[error(transparent)]
404 : Other(#[from] anyhow::Error),
405 : }
406 :
407 : impl Debug for DeleteTimelineError {
408 0 : fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
409 0 : match self {
410 0 : Self::NotFound => write!(f, "NotFound"),
411 0 : Self::HasChildren(c) => f.debug_tuple("HasChildren").field(c).finish(),
412 0 : Self::AlreadyInProgress(_) => f.debug_tuple("AlreadyInProgress").finish(),
413 0 : Self::Other(e) => f.debug_tuple("Other").field(e).finish(),
414 : }
415 0 : }
416 : }
417 :
418 : pub enum SetStoppingError {
419 : AlreadyStopping(completion::Barrier),
420 : Broken,
421 : }
422 :
423 : impl Debug for SetStoppingError {
424 0 : fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
425 0 : match self {
426 0 : Self::AlreadyStopping(_) => f.debug_tuple("AlreadyStopping").finish(),
427 0 : Self::Broken => write!(f, "Broken"),
428 : }
429 0 : }
430 : }
431 :
432 0 : #[derive(thiserror::Error, Debug)]
433 : pub enum CreateTimelineError {
434 : #[error("creation of timeline with the given ID is in progress")]
435 : AlreadyCreating,
436 : #[error("timeline already exists with different parameters")]
437 : Conflict,
438 : #[error(transparent)]
439 : AncestorLsn(anyhow::Error),
440 : #[error("ancestor timeline is not active")]
441 : AncestorNotActive,
442 : #[error("tenant shutting down")]
443 : ShuttingDown,
444 : #[error(transparent)]
445 : Other(#[from] anyhow::Error),
446 : }
447 :
448 : #[derive(thiserror::Error, Debug)]
449 : enum InitdbError {
450 : Other(anyhow::Error),
451 : Cancelled,
452 : Spawn(std::io::Result<()>),
453 : Failed(std::process::ExitStatus, Vec<u8>),
454 : }
455 :
456 : impl fmt::Display for InitdbError {
457 0 : fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
458 0 : match self {
459 0 : InitdbError::Cancelled => write!(f, "Operation was cancelled"),
460 0 : InitdbError::Spawn(e) => write!(f, "Spawn error: {:?}", e),
461 0 : InitdbError::Failed(status, stderr) => write!(
462 0 : f,
463 0 : "Command failed with status {:?}: {}",
464 0 : status,
465 0 : String::from_utf8_lossy(stderr)
466 0 : ),
467 0 : InitdbError::Other(e) => write!(f, "Error: {:?}", e),
468 : }
469 0 : }
470 : }
471 :
472 : impl From<std::io::Error> for InitdbError {
473 0 : fn from(error: std::io::Error) -> Self {
474 0 : InitdbError::Spawn(Err(error))
475 0 : }
476 : }
477 :
478 : enum CreateTimelineCause {
479 : Load,
480 : Delete,
481 : }
482 :
483 0 : #[derive(thiserror::Error, Debug)]
484 : pub(crate) enum GcError {
485 : // The tenant is shutting down
486 : #[error("tenant shutting down")]
487 : TenantCancelled,
488 :
489 : // The tenant is shutting down
490 : #[error("timeline shutting down")]
491 : TimelineCancelled,
492 :
493 : // The tenant is in a state inelegible to run GC
494 : #[error("not active")]
495 : NotActive,
496 :
497 : // A requested GC cutoff LSN was invalid, for example it tried to move backwards
498 : #[error("not active")]
499 : BadLsn { why: String },
500 :
501 : // A remote storage error while scheduling updates after compaction
502 : #[error(transparent)]
503 : Remote(anyhow::Error),
504 :
505 : // An error reading while calculating GC cutoffs
506 : #[error(transparent)]
507 : GcCutoffs(PageReconstructError),
508 :
509 : // If GC was invoked for a particular timeline, this error means it didn't exist
510 : #[error("timeline not found")]
511 : TimelineNotFound,
512 : }
513 :
514 : impl From<PageReconstructError> for GcError {
515 0 : fn from(value: PageReconstructError) -> Self {
516 0 : match value {
517 0 : PageReconstructError::Cancelled => Self::TimelineCancelled,
518 0 : other => Self::GcCutoffs(other),
519 : }
520 0 : }
521 : }
522 :
523 : impl Tenant {
524 : /// Yet another helper for timeline initialization.
525 : ///
526 : /// - Initializes the Timeline struct and inserts it into the tenant's hash map
527 : /// - Scans the local timeline directory for layer files and builds the layer map
528 : /// - Downloads remote index file and adds remote files to the layer map
529 : /// - Schedules remote upload tasks for any files that are present locally but missing from remote storage.
530 : ///
531 : /// If the operation fails, the timeline is left in the tenant's hash map in Broken state. On success,
532 : /// it is marked as Active.
533 : #[allow(clippy::too_many_arguments)]
534 6 : async fn timeline_init_and_sync(
535 6 : &self,
536 6 : timeline_id: TimelineId,
537 6 : resources: TimelineResources,
538 6 : index_part: Option<IndexPart>,
539 6 : metadata: TimelineMetadata,
540 6 : ancestor: Option<Arc<Timeline>>,
541 6 : last_aux_file_policy: Option<AuxFilePolicy>,
542 6 : _ctx: &RequestContext,
543 6 : ) -> anyhow::Result<()> {
544 6 : let tenant_id = self.tenant_shard_id;
545 :
546 6 : let timeline = self.create_timeline_struct(
547 6 : timeline_id,
548 6 : &metadata,
549 6 : ancestor.clone(),
550 6 : resources,
551 6 : CreateTimelineCause::Load,
552 6 : // This could be derived from ancestor branch + index part. Though the only caller of `timeline_init_and_sync` is `load_remote_timeline`,
553 6 : // there will potentially be other caller of this function in the future, and we don't know whether `index_part` or `ancestor` takes precedence.
554 6 : // Therefore, we pass this field explicitly for now, and remove it once we fully migrate to aux file v2.
555 6 : last_aux_file_policy,
556 6 : )?;
557 6 : let disk_consistent_lsn = timeline.get_disk_consistent_lsn();
558 6 : anyhow::ensure!(
559 6 : disk_consistent_lsn.is_valid(),
560 0 : "Timeline {tenant_id}/{timeline_id} has invalid disk_consistent_lsn"
561 : );
562 6 : assert_eq!(
563 6 : disk_consistent_lsn,
564 6 : metadata.disk_consistent_lsn(),
565 0 : "these are used interchangeably"
566 : );
567 :
568 6 : if let Some(index_part) = index_part.as_ref() {
569 6 : timeline.remote_client.init_upload_queue(index_part)?;
570 :
571 6 : timeline
572 6 : .last_aux_file_policy
573 6 : .store(index_part.last_aux_file_policy());
574 : } else {
575 : // No data on the remote storage, but we have local metadata file. We can end up
576 : // here with timeline_create being interrupted before finishing index part upload.
577 : // By doing what we do here, the index part upload is retried.
578 : // If control plane retries timeline creation in the meantime, the mgmt API handler
579 : // for timeline creation will coalesce on the upload we queue here.
580 :
581 : // FIXME: this branch should be dead code as we no longer write local metadata.
582 :
583 0 : timeline
584 0 : .remote_client
585 0 : .init_upload_queue_for_empty_remote(&metadata)?;
586 0 : timeline
587 0 : .remote_client
588 0 : .schedule_index_upload_for_full_metadata_update(&metadata)?;
589 : }
590 :
591 6 : timeline
592 6 : .load_layer_map(disk_consistent_lsn, index_part)
593 6 : .await
594 6 : .with_context(|| {
595 0 : format!("Failed to load layermap for timeline {tenant_id}/{timeline_id}")
596 6 : })?;
597 :
598 : {
599 : // avoiding holding it across awaits
600 6 : let mut timelines_accessor = self.timelines.lock().unwrap();
601 6 : match timelines_accessor.entry(timeline_id) {
602 : // We should never try and load the same timeline twice during startup
603 : Entry::Occupied(_) => {
604 0 : unreachable!(
605 0 : "Timeline {tenant_id}/{timeline_id} already exists in the tenant map"
606 0 : );
607 : }
608 6 : Entry::Vacant(v) => {
609 6 : v.insert(Arc::clone(&timeline));
610 6 : timeline.maybe_spawn_flush_loop();
611 6 : }
612 6 : }
613 6 : };
614 6 :
615 6 : // Sanity check: a timeline should have some content.
616 6 : anyhow::ensure!(
617 6 : ancestor.is_some()
618 4 : || timeline
619 4 : .layers
620 4 : .read()
621 0 : .await
622 4 : .layer_map()
623 4 : .iter_historic_layers()
624 4 : .next()
625 4 : .is_some(),
626 0 : "Timeline has no ancestor and no layer files"
627 : );
628 :
629 6 : Ok(())
630 6 : }
631 :
632 : /// Attach a tenant that's available in cloud storage.
633 : ///
634 : /// This returns quickly, after just creating the in-memory object
635 : /// Tenant struct and launching a background task to download
636 : /// the remote index files. On return, the tenant is most likely still in
637 : /// Attaching state, and it will become Active once the background task
638 : /// finishes. You can use wait_until_active() to wait for the task to
639 : /// complete.
640 : ///
641 : #[allow(clippy::too_many_arguments)]
642 0 : pub(crate) fn spawn(
643 0 : conf: &'static PageServerConf,
644 0 : tenant_shard_id: TenantShardId,
645 0 : resources: TenantSharedResources,
646 0 : attached_conf: AttachedTenantConf,
647 0 : shard_identity: ShardIdentity,
648 0 : init_order: Option<InitializationOrder>,
649 0 : mode: SpawnMode,
650 0 : ctx: &RequestContext,
651 0 : ) -> Arc<Tenant> {
652 0 : let wal_redo_manager = Arc::new(WalRedoManager::from(PostgresRedoManager::new(
653 0 : conf,
654 0 : tenant_shard_id,
655 0 : )));
656 0 :
657 0 : let TenantSharedResources {
658 0 : broker_client,
659 0 : remote_storage,
660 0 : deletion_queue_client,
661 0 : } = resources;
662 0 :
663 0 : let attach_mode = attached_conf.location.attach_mode;
664 0 : let generation = attached_conf.location.generation;
665 0 :
666 0 : let tenant = Arc::new(Tenant::new(
667 0 : TenantState::Attaching,
668 0 : conf,
669 0 : attached_conf,
670 0 : shard_identity,
671 0 : Some(wal_redo_manager),
672 0 : tenant_shard_id,
673 0 : remote_storage.clone(),
674 0 : deletion_queue_client,
675 0 : ));
676 0 :
677 0 : // The attach task will carry a GateGuard, so that shutdown() reliably waits for it to drop out if
678 0 : // we shut down while attaching.
679 0 : let attach_gate_guard = tenant
680 0 : .gate
681 0 : .enter()
682 0 : .expect("We just created the Tenant: nothing else can have shut it down yet");
683 0 :
684 0 : // Do all the hard work in the background
685 0 : let tenant_clone = Arc::clone(&tenant);
686 0 : let ctx = ctx.detached_child(TaskKind::Attach, DownloadBehavior::Warn);
687 0 : task_mgr::spawn(
688 0 : &tokio::runtime::Handle::current(),
689 0 : TaskKind::Attach,
690 0 : Some(tenant_shard_id),
691 0 : None,
692 0 : "attach tenant",
693 : false,
694 0 : async move {
695 0 :
696 0 : info!(
697 : ?attach_mode,
698 0 : "Attaching tenant"
699 : );
700 :
701 0 : let _gate_guard = attach_gate_guard;
702 0 :
703 0 : // Is this tenant being spawned as part of process startup?
704 0 : let starting_up = init_order.is_some();
705 : scopeguard::defer! {
706 : if starting_up {
707 : TENANT.startup_complete.inc();
708 : }
709 : }
710 :
711 : // Ideally we should use Tenant::set_broken_no_wait, but it is not supposed to be used when tenant is in loading state.
712 : enum BrokenVerbosity {
713 : Error,
714 : Info
715 : }
716 0 : let make_broken =
717 0 : |t: &Tenant, err: anyhow::Error, verbosity: BrokenVerbosity| {
718 0 : match verbosity {
719 : BrokenVerbosity::Info => {
720 0 : info!("attach cancelled, setting tenant state to Broken: {err}");
721 : },
722 : BrokenVerbosity::Error => {
723 0 : error!("attach failed, setting tenant state to Broken: {err:?}");
724 : }
725 : }
726 0 : t.state.send_modify(|state| {
727 0 : // The Stopping case is for when we have passed control on to DeleteTenantFlow:
728 0 : // if it errors, we will call make_broken when tenant is already in Stopping.
729 0 : assert!(
730 0 : matches!(*state, TenantState::Attaching | TenantState::Stopping { .. }),
731 0 : "the attach task owns the tenant state until activation is complete"
732 : );
733 :
734 0 : *state = TenantState::broken_from_reason(err.to_string());
735 0 : });
736 0 : };
737 :
738 0 : let mut init_order = init_order;
739 0 : // take the completion because initial tenant loading will complete when all of
740 0 : // these tasks complete.
741 0 : let _completion = init_order
742 0 : .as_mut()
743 0 : .and_then(|x| x.initial_tenant_load.take());
744 0 : let remote_load_completion = init_order
745 0 : .as_mut()
746 0 : .and_then(|x| x.initial_tenant_load_remote.take());
747 :
748 : enum AttachType<'a> {
749 : /// We are attaching this tenant lazily in the background.
750 : Warmup {
751 : _permit: tokio::sync::SemaphorePermit<'a>,
752 : during_startup: bool
753 : },
754 : /// We are attaching this tenant as soon as we can, because for example an
755 : /// endpoint tried to access it.
756 : OnDemand,
757 : /// During normal operations after startup, we are attaching a tenant, and
758 : /// eager attach was requested.
759 : Normal,
760 : }
761 :
762 0 : let attach_type = if matches!(mode, SpawnMode::Lazy) {
763 : // Before doing any I/O, wait for at least one of:
764 : // - A client attempting to access to this tenant (on-demand loading)
765 : // - A permit becoming available in the warmup semaphore (background warmup)
766 :
767 : tokio::select!(
768 : permit = tenant_clone.activate_now_sem.acquire() => {
769 : let _ = permit.expect("activate_now_sem is never closed");
770 : tracing::info!("Activating tenant (on-demand)");
771 : AttachType::OnDemand
772 : },
773 : permit = conf.concurrent_tenant_warmup.inner().acquire() => {
774 : let _permit = permit.expect("concurrent_tenant_warmup semaphore is never closed");
775 : tracing::info!("Activating tenant (warmup)");
776 : AttachType::Warmup {
777 : _permit,
778 : during_startup: init_order.is_some()
779 : }
780 : }
781 : _ = tenant_clone.cancel.cancelled() => {
782 : // This is safe, but should be pretty rare: it is interesting if a tenant
783 : // stayed in Activating for such a long time that shutdown found it in
784 : // that state.
785 : tracing::info!(state=%tenant_clone.current_state(), "Tenant shut down before activation");
786 : // Make the tenant broken so that set_stopping will not hang waiting for it to leave
787 : // the Attaching state. This is an over-reaction (nothing really broke, the tenant is
788 : // just shutting down), but ensures progress.
789 : make_broken(&tenant_clone, anyhow::anyhow!("Shut down while Attaching"), BrokenVerbosity::Info);
790 : return Ok(());
791 : },
792 : )
793 : } else {
794 : // SpawnMode::{Create,Eager} always cause jumping ahead of the
795 : // concurrent_tenant_warmup queue
796 0 : AttachType::Normal
797 : };
798 :
799 0 : let preload = match &mode {
800 : SpawnMode::Create => {
801 0 : None
802 : },
803 : SpawnMode::Eager | SpawnMode::Lazy => {
804 0 : let _preload_timer = TENANT.preload.start_timer();
805 0 : let res = tenant_clone
806 0 : .preload(&remote_storage, task_mgr::shutdown_token())
807 0 : .await;
808 0 : match res {
809 0 : Ok(p) => Some(p),
810 0 : Err(e) => {
811 0 : make_broken(&tenant_clone, anyhow::anyhow!(e), BrokenVerbosity::Error);
812 0 : return Ok(());
813 : }
814 : }
815 : }
816 :
817 : };
818 :
819 : // Remote preload is complete.
820 0 : drop(remote_load_completion);
821 :
822 : // We will time the duration of the attach phase unless this is a creation (attach will do no work)
823 0 : let attached = {
824 0 : let _attach_timer = match mode {
825 0 : SpawnMode::Create => None,
826 0 : SpawnMode::Eager | SpawnMode::Lazy => Some(TENANT.attach.start_timer()),
827 : };
828 0 : tenant_clone.attach(preload, mode, &ctx).await
829 : };
830 :
831 0 : match attached {
832 : Ok(()) => {
833 0 : info!("attach finished, activating");
834 0 : tenant_clone.activate(broker_client, None, &ctx);
835 : }
836 0 : Err(e) => {
837 0 : make_broken(&tenant_clone, anyhow::anyhow!(e), BrokenVerbosity::Error);
838 0 : }
839 : }
840 :
841 : // If we are doing an opportunistic warmup attachment at startup, initialize
842 : // logical size at the same time. This is better than starting a bunch of idle tenants
843 : // with cold caches and then coming back later to initialize their logical sizes.
844 : //
845 : // It also prevents the warmup proccess competing with the concurrency limit on
846 : // logical size calculations: if logical size calculation semaphore is saturated,
847 : // then warmup will wait for that before proceeding to the next tenant.
848 0 : if matches!(attach_type, AttachType::Warmup { during_startup: true, .. }) {
849 0 : let mut futs: FuturesUnordered<_> = tenant_clone.timelines.lock().unwrap().values().cloned().map(|t| t.await_initial_logical_size()).collect();
850 0 : tracing::info!("Waiting for initial logical sizes while warming up...");
851 0 : while futs.next().await.is_some() {}
852 0 : tracing::info!("Warm-up complete");
853 0 : }
854 :
855 0 : Ok(())
856 0 : }
857 0 : .instrument(tracing::info_span!(parent: None, "attach", tenant_id=%tenant_shard_id.tenant_id, shard_id=%tenant_shard_id.shard_slug(), gen=?generation)),
858 : );
859 0 : tenant
860 0 : }
861 :
862 322 : #[instrument(skip_all)]
863 : pub(crate) async fn preload(
864 : self: &Arc<Self>,
865 : remote_storage: &GenericRemoteStorage,
866 : cancel: CancellationToken,
867 : ) -> anyhow::Result<TenantPreload> {
868 : span::debug_assert_current_span_has_tenant_id();
869 : // Get list of remote timelines
870 : // download index files for every tenant timeline
871 : info!("listing remote timelines");
872 : let (remote_timeline_ids, other_keys) = remote_timeline_client::list_remote_timelines(
873 : remote_storage,
874 : self.tenant_shard_id,
875 : cancel.clone(),
876 : )
877 : .await?;
878 :
879 : info!("found {} timelines", remote_timeline_ids.len(),);
880 :
881 : for k in other_keys {
882 : warn!("Unexpected non timeline key {k}");
883 : }
884 :
885 : Ok(TenantPreload {
886 : timelines: Self::load_timeline_metadata(
887 : self,
888 : remote_timeline_ids,
889 : remote_storage,
890 : cancel,
891 : )
892 : .await?,
893 : })
894 : }
895 :
896 : ///
897 : /// Background task that downloads all data for a tenant and brings it to Active state.
898 : ///
899 : /// No background tasks are started as part of this routine.
900 : ///
901 161 : async fn attach(
902 161 : self: &Arc<Tenant>,
903 161 : preload: Option<TenantPreload>,
904 161 : mode: SpawnMode,
905 161 : ctx: &RequestContext,
906 161 : ) -> anyhow::Result<()> {
907 161 : span::debug_assert_current_span_has_tenant_id();
908 161 :
909 161 : failpoint_support::sleep_millis_async!("before-attaching-tenant");
910 :
911 161 : let preload = match (preload, mode) {
912 161 : (Some(p), _) => p,
913 0 : (None, SpawnMode::Create) => TenantPreload {
914 0 : timelines: HashMap::new(),
915 0 : },
916 : (None, _) => {
917 0 : anyhow::bail!("local-only deployment is no longer supported, https://github.com/neondatabase/neon/issues/5624");
918 : }
919 : };
920 :
921 161 : let mut timelines_to_resume_deletions = vec![];
922 161 :
923 161 : let mut remote_index_and_client = HashMap::new();
924 161 : let mut timeline_ancestors = HashMap::new();
925 161 : let mut existent_timelines = HashSet::new();
926 167 : for (timeline_id, preload) in preload.timelines {
927 6 : let index_part = match preload.index_part {
928 6 : Ok(i) => {
929 6 : debug!("remote index part exists for timeline {timeline_id}");
930 : // We found index_part on the remote, this is the standard case.
931 6 : existent_timelines.insert(timeline_id);
932 6 : i
933 : }
934 : Err(DownloadError::NotFound) => {
935 : // There is no index_part on the remote. We only get here
936 : // if there is some prefix for the timeline in the remote storage.
937 : // This can e.g. be the initdb.tar.zst archive, maybe a
938 : // remnant from a prior incomplete creation or deletion attempt.
939 : // Delete the local directory as the deciding criterion for a
940 : // timeline's existence is presence of index_part.
941 0 : info!(%timeline_id, "index_part not found on remote");
942 0 : continue;
943 : }
944 0 : Err(e) => {
945 0 : // Some (possibly ephemeral) error happened during index_part download.
946 0 : // Pretend the timeline exists to not delete the timeline directory,
947 0 : // as it might be a temporary issue and we don't want to re-download
948 0 : // everything after it resolves.
949 0 : warn!(%timeline_id, "Failed to load index_part from remote storage, failed creation? ({e})");
950 :
951 0 : existent_timelines.insert(timeline_id);
952 0 : continue;
953 : }
954 : };
955 6 : match index_part {
956 6 : MaybeDeletedIndexPart::IndexPart(index_part) => {
957 6 : timeline_ancestors.insert(timeline_id, index_part.metadata.clone());
958 6 : remote_index_and_client.insert(timeline_id, (index_part, preload.client));
959 6 : }
960 0 : MaybeDeletedIndexPart::Deleted(index_part) => {
961 0 : info!(
962 0 : "timeline {} is deleted, picking to resume deletion",
963 : timeline_id
964 : );
965 0 : timelines_to_resume_deletions.push((timeline_id, index_part, preload.client));
966 : }
967 : }
968 : }
969 :
970 : // For every timeline, download the metadata file, scan the local directory,
971 : // and build a layer map that contains an entry for each remote and local
972 : // layer file.
973 161 : let sorted_timelines = tree_sort_timelines(timeline_ancestors, |m| m.ancestor_timeline())?;
974 167 : for (timeline_id, remote_metadata) in sorted_timelines {
975 6 : let (index_part, remote_client) = remote_index_and_client
976 6 : .remove(&timeline_id)
977 6 : .expect("just put it in above");
978 6 :
979 6 : // TODO again handle early failure
980 6 : self.load_remote_timeline(
981 6 : timeline_id,
982 6 : index_part,
983 6 : remote_metadata,
984 6 : TimelineResources {
985 6 : remote_client,
986 6 : timeline_get_throttle: self.timeline_get_throttle.clone(),
987 6 : },
988 6 : ctx,
989 6 : )
990 11 : .await
991 6 : .with_context(|| {
992 0 : format!(
993 0 : "failed to load remote timeline {} for tenant {}",
994 0 : timeline_id, self.tenant_shard_id
995 0 : )
996 6 : })?;
997 : }
998 :
999 : // Walk through deleted timelines, resume deletion
1000 161 : for (timeline_id, index_part, remote_timeline_client) in timelines_to_resume_deletions {
1001 0 : remote_timeline_client
1002 0 : .init_upload_queue_stopped_to_continue_deletion(&index_part)
1003 0 : .context("init queue stopped")
1004 0 : .map_err(LoadLocalTimelineError::ResumeDeletion)?;
1005 :
1006 0 : DeleteTimelineFlow::resume_deletion(
1007 0 : Arc::clone(self),
1008 0 : timeline_id,
1009 0 : &index_part.metadata,
1010 0 : remote_timeline_client,
1011 0 : )
1012 0 : .instrument(tracing::info_span!("timeline_delete", %timeline_id))
1013 0 : .await
1014 0 : .context("resume_deletion")
1015 0 : .map_err(LoadLocalTimelineError::ResumeDeletion)?;
1016 : }
1017 :
1018 : // The local filesystem contents are a cache of what's in the remote IndexPart;
1019 : // IndexPart is the source of truth.
1020 161 : self.clean_up_timelines(&existent_timelines)?;
1021 :
1022 161 : fail::fail_point!("attach-before-activate", |_| {
1023 0 : anyhow::bail!("attach-before-activate");
1024 161 : });
1025 161 : failpoint_support::sleep_millis_async!("attach-before-activate-sleep", &self.cancel);
1026 :
1027 161 : info!("Done");
1028 :
1029 161 : Ok(())
1030 161 : }
1031 :
1032 : /// Check for any local timeline directories that are temporary, or do not correspond to a
1033 : /// timeline that still exists: this can happen if we crashed during a deletion/creation, or
1034 : /// if a timeline was deleted while the tenant was attached to a different pageserver.
1035 161 : fn clean_up_timelines(&self, existent_timelines: &HashSet<TimelineId>) -> anyhow::Result<()> {
1036 161 : let timelines_dir = self.conf.timelines_path(&self.tenant_shard_id);
1037 :
1038 161 : let entries = match timelines_dir.read_dir_utf8() {
1039 161 : Ok(d) => d,
1040 0 : Err(e) => {
1041 0 : if e.kind() == std::io::ErrorKind::NotFound {
1042 0 : return Ok(());
1043 : } else {
1044 0 : return Err(e).context("list timelines directory for tenant");
1045 : }
1046 : }
1047 : };
1048 :
1049 169 : for entry in entries {
1050 8 : let entry = entry.context("read timeline dir entry")?;
1051 8 : let entry_path = entry.path();
1052 :
1053 8 : let purge = if crate::is_temporary(entry_path)
1054 : // TODO: remove uninit mark code (https://github.com/neondatabase/neon/issues/5718)
1055 8 : || is_uninit_mark(entry_path)
1056 8 : || crate::is_delete_mark(entry_path)
1057 : {
1058 0 : true
1059 : } else {
1060 8 : match TimelineId::try_from(entry_path.file_name()) {
1061 8 : Ok(i) => {
1062 8 : // Purge if the timeline ID does not exist in remote storage: remote storage is the authority.
1063 8 : !existent_timelines.contains(&i)
1064 : }
1065 0 : Err(e) => {
1066 0 : tracing::warn!(
1067 0 : "Unparseable directory in timelines directory: {entry_path}, ignoring ({e})"
1068 : );
1069 : // Do not purge junk: if we don't recognize it, be cautious and leave it for a human.
1070 0 : false
1071 : }
1072 : }
1073 : };
1074 :
1075 8 : if purge {
1076 2 : tracing::info!("Purging stale timeline dentry {entry_path}");
1077 2 : if let Err(e) = match entry.file_type() {
1078 2 : Ok(t) => if t.is_dir() {
1079 2 : std::fs::remove_dir_all(entry_path)
1080 : } else {
1081 0 : std::fs::remove_file(entry_path)
1082 : }
1083 2 : .or_else(fs_ext::ignore_not_found),
1084 0 : Err(e) => Err(e),
1085 : } {
1086 0 : tracing::warn!("Failed to purge stale timeline dentry {entry_path}: {e}");
1087 2 : }
1088 6 : }
1089 : }
1090 :
1091 161 : Ok(())
1092 161 : }
1093 :
1094 : /// Get sum of all remote timelines sizes
1095 : ///
1096 : /// This function relies on the index_part instead of listing the remote storage
1097 0 : pub fn remote_size(&self) -> u64 {
1098 0 : let mut size = 0;
1099 :
1100 0 : for timeline in self.list_timelines() {
1101 0 : size += timeline.remote_client.get_remote_physical_size();
1102 0 : }
1103 :
1104 0 : size
1105 0 : }
1106 :
1107 12 : #[instrument(skip_all, fields(timeline_id=%timeline_id))]
1108 : async fn load_remote_timeline(
1109 : &self,
1110 : timeline_id: TimelineId,
1111 : index_part: IndexPart,
1112 : remote_metadata: TimelineMetadata,
1113 : resources: TimelineResources,
1114 : ctx: &RequestContext,
1115 : ) -> anyhow::Result<()> {
1116 : span::debug_assert_current_span_has_tenant_id();
1117 :
1118 : info!("downloading index file for timeline {}", timeline_id);
1119 : tokio::fs::create_dir_all(self.conf.timeline_path(&self.tenant_shard_id, &timeline_id))
1120 : .await
1121 : .context("Failed to create new timeline directory")?;
1122 :
1123 : let ancestor = if let Some(ancestor_id) = remote_metadata.ancestor_timeline() {
1124 : let timelines = self.timelines.lock().unwrap();
1125 : Some(Arc::clone(timelines.get(&ancestor_id).ok_or_else(
1126 0 : || {
1127 0 : anyhow::anyhow!(
1128 0 : "cannot find ancestor timeline {ancestor_id} for timeline {timeline_id}"
1129 0 : )
1130 0 : },
1131 : )?))
1132 : } else {
1133 : None
1134 : };
1135 :
1136 : let last_aux_file_policy = index_part.last_aux_file_policy();
1137 :
1138 : self.timeline_init_and_sync(
1139 : timeline_id,
1140 : resources,
1141 : Some(index_part),
1142 : remote_metadata,
1143 : ancestor,
1144 : last_aux_file_policy,
1145 : ctx,
1146 : )
1147 : .await
1148 : }
1149 :
1150 161 : async fn load_timeline_metadata(
1151 161 : self: &Arc<Tenant>,
1152 161 : timeline_ids: HashSet<TimelineId>,
1153 161 : remote_storage: &GenericRemoteStorage,
1154 161 : cancel: CancellationToken,
1155 161 : ) -> anyhow::Result<HashMap<TimelineId, TimelinePreload>> {
1156 161 : let mut part_downloads = JoinSet::new();
1157 167 : for timeline_id in timeline_ids {
1158 6 : let client = RemoteTimelineClient::new(
1159 6 : remote_storage.clone(),
1160 6 : self.deletion_queue_client.clone(),
1161 6 : self.conf,
1162 6 : self.tenant_shard_id,
1163 6 : timeline_id,
1164 6 : self.generation,
1165 6 : );
1166 6 : let cancel_clone = cancel.clone();
1167 6 : part_downloads.spawn(
1168 6 : async move {
1169 6 : debug!("starting index part download");
1170 :
1171 18 : let index_part = client.download_index_file(&cancel_clone).await;
1172 :
1173 6 : debug!("finished index part download");
1174 :
1175 6 : Result::<_, anyhow::Error>::Ok(TimelinePreload {
1176 6 : client,
1177 6 : timeline_id,
1178 6 : index_part,
1179 6 : })
1180 6 : }
1181 6 : .map(move |res| {
1182 6 : res.with_context(|| format!("download index part for timeline {timeline_id}"))
1183 6 : })
1184 6 : .instrument(info_span!("download_index_part", %timeline_id)),
1185 : );
1186 : }
1187 :
1188 161 : let mut timeline_preloads: HashMap<TimelineId, TimelinePreload> = HashMap::new();
1189 :
1190 167 : loop {
1191 167 : tokio::select!(
1192 : next = part_downloads.join_next() => {
1193 : match next {
1194 : Some(result) => {
1195 : let preload_result = result.context("join preload task")?;
1196 : let preload = preload_result?;
1197 : timeline_preloads.insert(preload.timeline_id, preload);
1198 : },
1199 : None => {
1200 : break;
1201 : }
1202 : }
1203 : },
1204 : _ = cancel.cancelled() => {
1205 : anyhow::bail!("Cancelled while waiting for remote index download")
1206 : }
1207 167 : )
1208 167 : }
1209 :
1210 161 : Ok(timeline_preloads)
1211 161 : }
1212 :
1213 4 : pub(crate) fn tenant_shard_id(&self) -> TenantShardId {
1214 4 : self.tenant_shard_id
1215 4 : }
1216 :
1217 : /// Get Timeline handle for given Neon timeline ID.
1218 : /// This function is idempotent. It doesn't change internal state in any way.
1219 222 : pub fn get_timeline(
1220 222 : &self,
1221 222 : timeline_id: TimelineId,
1222 222 : active_only: bool,
1223 222 : ) -> Result<Arc<Timeline>, GetTimelineError> {
1224 222 : let timelines_accessor = self.timelines.lock().unwrap();
1225 222 : let timeline = timelines_accessor
1226 222 : .get(&timeline_id)
1227 222 : .ok_or(GetTimelineError::NotFound {
1228 222 : tenant_id: self.tenant_shard_id,
1229 222 : timeline_id,
1230 222 : })?;
1231 :
1232 220 : if active_only && !timeline.is_active() {
1233 0 : Err(GetTimelineError::NotActive {
1234 0 : tenant_id: self.tenant_shard_id,
1235 0 : timeline_id,
1236 0 : state: timeline.current_state(),
1237 0 : })
1238 : } else {
1239 220 : Ok(Arc::clone(timeline))
1240 : }
1241 222 : }
1242 :
1243 : /// Lists timelines the tenant contains.
1244 : /// Up to tenant's implementation to omit certain timelines that ar not considered ready for use.
1245 8 : pub fn list_timelines(&self) -> Vec<Arc<Timeline>> {
1246 8 : self.timelines
1247 8 : .lock()
1248 8 : .unwrap()
1249 8 : .values()
1250 8 : .map(Arc::clone)
1251 8 : .collect()
1252 8 : }
1253 :
1254 0 : pub fn list_timeline_ids(&self) -> Vec<TimelineId> {
1255 0 : self.timelines.lock().unwrap().keys().cloned().collect()
1256 0 : }
1257 :
1258 : /// This is used to create the initial 'main' timeline during bootstrapping,
1259 : /// or when importing a new base backup. The caller is expected to load an
1260 : /// initial image of the datadir to the new timeline after this.
1261 : ///
1262 : /// Until that happens, the on-disk state is invalid (disk_consistent_lsn=Lsn(0))
1263 : /// and the timeline will fail to load at a restart.
1264 : ///
1265 : /// For tests, use `DatadirModification::init_empty_test_timeline` + `commit` to setup the
1266 : /// minimum amount of keys required to get a writable timeline.
1267 : /// (Without it, `put` might fail due to `repartition` failing.)
1268 153 : pub(crate) async fn create_empty_timeline(
1269 153 : &self,
1270 153 : new_timeline_id: TimelineId,
1271 153 : initdb_lsn: Lsn,
1272 153 : pg_version: u32,
1273 153 : _ctx: &RequestContext,
1274 153 : ) -> anyhow::Result<UninitializedTimeline> {
1275 153 : anyhow::ensure!(
1276 153 : self.is_active(),
1277 0 : "Cannot create empty timelines on inactive tenant"
1278 : );
1279 :
1280 : // Protect against concurrent attempts to use this TimelineId
1281 153 : let create_guard = self.create_timeline_create_guard(new_timeline_id)?;
1282 :
1283 151 : let new_metadata = TimelineMetadata::new(
1284 151 : // Initialize disk_consistent LSN to 0, The caller must import some data to
1285 151 : // make it valid, before calling finish_creation()
1286 151 : Lsn(0),
1287 151 : None,
1288 151 : None,
1289 151 : Lsn(0),
1290 151 : initdb_lsn,
1291 151 : initdb_lsn,
1292 151 : pg_version,
1293 151 : );
1294 151 : self.prepare_new_timeline(
1295 151 : new_timeline_id,
1296 151 : &new_metadata,
1297 151 : create_guard,
1298 151 : initdb_lsn,
1299 151 : None,
1300 151 : None,
1301 151 : )
1302 0 : .await
1303 153 : }
1304 :
1305 : /// Helper for unit tests to create an empty timeline.
1306 : ///
1307 : /// The timeline is has state value `Active` but its background loops are not running.
1308 : // This makes the various functions which anyhow::ensure! for Active state work in tests.
1309 : // Our current tests don't need the background loops.
1310 : #[cfg(test)]
1311 143 : pub async fn create_test_timeline(
1312 143 : &self,
1313 143 : new_timeline_id: TimelineId,
1314 143 : initdb_lsn: Lsn,
1315 143 : pg_version: u32,
1316 143 : ctx: &RequestContext,
1317 143 : ) -> anyhow::Result<Arc<Timeline>> {
1318 143 : let uninit_tl = self
1319 143 : .create_empty_timeline(new_timeline_id, initdb_lsn, pg_version, ctx)
1320 1 : .await?;
1321 142 : let tline = uninit_tl.raw_timeline().expect("we just created it");
1322 142 : assert_eq!(tline.get_last_record_lsn(), Lsn(0));
1323 :
1324 : // Setup minimum keys required for the timeline to be usable.
1325 142 : let mut modification = tline.begin_modification(initdb_lsn);
1326 142 : modification
1327 142 : .init_empty_test_timeline()
1328 142 : .context("init_empty_test_timeline")?;
1329 142 : modification
1330 142 : .commit(ctx)
1331 136 : .await
1332 142 : .context("commit init_empty_test_timeline modification")?;
1333 :
1334 : // Flush to disk so that uninit_tl's check for valid disk_consistent_lsn passes.
1335 142 : tline.maybe_spawn_flush_loop();
1336 142 : tline.freeze_and_flush().await.context("freeze_and_flush")?;
1337 :
1338 : // Make sure the freeze_and_flush reaches remote storage.
1339 142 : tline.remote_client.wait_completion().await.unwrap();
1340 :
1341 142 : let tl = uninit_tl.finish_creation()?;
1342 : // The non-test code would call tl.activate() here.
1343 142 : tl.set_state(TimelineState::Active);
1344 142 : Ok(tl)
1345 143 : }
1346 :
1347 : /// Helper for unit tests to create a timeline with some pre-loaded states.
1348 : #[cfg(test)]
1349 : #[allow(clippy::too_many_arguments)]
1350 19 : pub async fn create_test_timeline_with_layers(
1351 19 : &self,
1352 19 : new_timeline_id: TimelineId,
1353 19 : initdb_lsn: Lsn,
1354 19 : pg_version: u32,
1355 19 : ctx: &RequestContext,
1356 19 : delta_layer_desc: Vec<Vec<(pageserver_api::key::Key, Lsn, crate::repository::Value)>>,
1357 19 : image_layer_desc: Vec<(Lsn, Vec<(pageserver_api::key::Key, bytes::Bytes)>)>,
1358 19 : end_lsn: Lsn,
1359 19 : ) -> anyhow::Result<Arc<Timeline>> {
1360 19 : let tline = self
1361 19 : .create_test_timeline(new_timeline_id, initdb_lsn, pg_version, ctx)
1362 53 : .await?;
1363 18 : tline.force_advance_lsn(end_lsn);
1364 52 : for deltas in delta_layer_desc {
1365 34 : tline
1366 34 : .force_create_delta_layer(deltas, Some(initdb_lsn), ctx)
1367 102 : .await?;
1368 : }
1369 52 : for (lsn, images) in image_layer_desc {
1370 34 : tline
1371 34 : .force_create_image_layer(lsn, images, Some(initdb_lsn), ctx)
1372 157 : .await?;
1373 : }
1374 18 : Ok(tline)
1375 19 : }
1376 :
1377 : /// Create a new timeline.
1378 : ///
1379 : /// Returns the new timeline ID and reference to its Timeline object.
1380 : ///
1381 : /// If the caller specified the timeline ID to use (`new_timeline_id`), and timeline with
1382 : /// the same timeline ID already exists, returns CreateTimelineError::AlreadyExists.
1383 : #[allow(clippy::too_many_arguments)]
1384 0 : pub(crate) async fn create_timeline(
1385 0 : self: &Arc<Tenant>,
1386 0 : new_timeline_id: TimelineId,
1387 0 : ancestor_timeline_id: Option<TimelineId>,
1388 0 : mut ancestor_start_lsn: Option<Lsn>,
1389 0 : pg_version: u32,
1390 0 : load_existing_initdb: Option<TimelineId>,
1391 0 : broker_client: storage_broker::BrokerClientChannel,
1392 0 : ctx: &RequestContext,
1393 0 : ) -> Result<Arc<Timeline>, CreateTimelineError> {
1394 0 : if !self.is_active() {
1395 0 : if matches!(self.current_state(), TenantState::Stopping { .. }) {
1396 0 : return Err(CreateTimelineError::ShuttingDown);
1397 : } else {
1398 0 : return Err(CreateTimelineError::Other(anyhow::anyhow!(
1399 0 : "Cannot create timelines on inactive tenant"
1400 0 : )));
1401 : }
1402 0 : }
1403 :
1404 0 : let _gate = self
1405 0 : .gate
1406 0 : .enter()
1407 0 : .map_err(|_| CreateTimelineError::ShuttingDown)?;
1408 :
1409 : // Get exclusive access to the timeline ID: this ensures that it does not already exist,
1410 : // and that no other creation attempts will be allowed in while we are working.
1411 0 : let create_guard = match self.create_timeline_create_guard(new_timeline_id) {
1412 0 : Ok(m) => m,
1413 : Err(TimelineExclusionError::AlreadyCreating) => {
1414 : // Creation is in progress, we cannot create it again, and we cannot
1415 : // check if this request matches the existing one, so caller must try
1416 : // again later.
1417 0 : return Err(CreateTimelineError::AlreadyCreating);
1418 : }
1419 0 : Err(TimelineExclusionError::Other(e)) => {
1420 0 : return Err(CreateTimelineError::Other(e));
1421 : }
1422 0 : Err(TimelineExclusionError::AlreadyExists(existing)) => {
1423 0 : debug!("timeline {new_timeline_id} already exists");
1424 :
1425 : // Idempotency: creating the same timeline twice is not an error, unless
1426 : // the second creation has different parameters.
1427 0 : if existing.get_ancestor_timeline_id() != ancestor_timeline_id
1428 0 : || existing.pg_version != pg_version
1429 0 : || (ancestor_start_lsn.is_some()
1430 0 : && ancestor_start_lsn != Some(existing.get_ancestor_lsn()))
1431 : {
1432 0 : return Err(CreateTimelineError::Conflict);
1433 0 : }
1434 0 :
1435 0 : // Wait for uploads to complete, so that when we return Ok, the timeline
1436 0 : // is known to be durable on remote storage. Just like we do at the end of
1437 0 : // this function, after we have created the timeline ourselves.
1438 0 : //
1439 0 : // We only really care that the initial version of `index_part.json` has
1440 0 : // been uploaded. That's enough to remember that the timeline
1441 0 : // exists. However, there is no function to wait specifically for that so
1442 0 : // we just wait for all in-progress uploads to finish.
1443 0 : existing
1444 0 : .remote_client
1445 0 : .wait_completion()
1446 0 : .await
1447 0 : .context("wait for timeline uploads to complete")?;
1448 :
1449 0 : return Ok(existing);
1450 : }
1451 : };
1452 :
1453 : pausable_failpoint!("timeline-creation-after-uninit");
1454 :
1455 0 : let loaded_timeline = match ancestor_timeline_id {
1456 0 : Some(ancestor_timeline_id) => {
1457 0 : let ancestor_timeline = self
1458 0 : .get_timeline(ancestor_timeline_id, false)
1459 0 : .context("Cannot branch off the timeline that's not present in pageserver")?;
1460 :
1461 : // instead of waiting around, just deny the request because ancestor is not yet
1462 : // ready for other purposes either.
1463 0 : if !ancestor_timeline.is_active() {
1464 0 : return Err(CreateTimelineError::AncestorNotActive);
1465 0 : }
1466 :
1467 0 : if let Some(lsn) = ancestor_start_lsn.as_mut() {
1468 0 : *lsn = lsn.align();
1469 0 :
1470 0 : let ancestor_ancestor_lsn = ancestor_timeline.get_ancestor_lsn();
1471 0 : if ancestor_ancestor_lsn > *lsn {
1472 : // can we safely just branch from the ancestor instead?
1473 0 : return Err(CreateTimelineError::AncestorLsn(anyhow::anyhow!(
1474 0 : "invalid start lsn {} for ancestor timeline {}: less than timeline ancestor lsn {}",
1475 0 : lsn,
1476 0 : ancestor_timeline_id,
1477 0 : ancestor_ancestor_lsn,
1478 0 : )));
1479 0 : }
1480 0 :
1481 0 : // Wait for the WAL to arrive and be processed on the parent branch up
1482 0 : // to the requested branch point. The repository code itself doesn't
1483 0 : // require it, but if we start to receive WAL on the new timeline,
1484 0 : // decoding the new WAL might need to look up previous pages, relation
1485 0 : // sizes etc. and that would get confused if the previous page versions
1486 0 : // are not in the repository yet.
1487 0 : ancestor_timeline
1488 0 : .wait_lsn(*lsn, timeline::WaitLsnWaiter::Tenant, ctx)
1489 0 : .await
1490 0 : .map_err(|e| match e {
1491 0 : e @ (WaitLsnError::Timeout(_) | WaitLsnError::BadState { .. }) => {
1492 0 : CreateTimelineError::AncestorLsn(anyhow::anyhow!(e))
1493 : }
1494 0 : WaitLsnError::Shutdown => CreateTimelineError::ShuttingDown,
1495 0 : })?;
1496 0 : }
1497 :
1498 0 : self.branch_timeline(
1499 0 : &ancestor_timeline,
1500 0 : new_timeline_id,
1501 0 : ancestor_start_lsn,
1502 0 : create_guard,
1503 0 : ctx,
1504 0 : )
1505 0 : .await?
1506 : }
1507 : None => {
1508 0 : self.bootstrap_timeline(
1509 0 : new_timeline_id,
1510 0 : pg_version,
1511 0 : load_existing_initdb,
1512 0 : create_guard,
1513 0 : ctx,
1514 0 : )
1515 0 : .await?
1516 : }
1517 : };
1518 :
1519 : // At this point we have dropped our guard on [`Self::timelines_creating`], and
1520 : // the timeline is visible in [`Self::timelines`], but it is _not_ durable yet. We must
1521 : // not send a success to the caller until it is. The same applies to handling retries,
1522 : // see the handling of [`TimelineExclusionError::AlreadyExists`] above.
1523 0 : let kind = ancestor_timeline_id
1524 0 : .map(|_| "branched")
1525 0 : .unwrap_or("bootstrapped");
1526 0 : loaded_timeline
1527 0 : .remote_client
1528 0 : .wait_completion()
1529 0 : .await
1530 0 : .with_context(|| format!("wait for {} timeline initial uploads to complete", kind))?;
1531 :
1532 0 : loaded_timeline.activate(self.clone(), broker_client, None, ctx);
1533 0 :
1534 0 : Ok(loaded_timeline)
1535 0 : }
1536 :
1537 0 : pub(crate) async fn delete_timeline(
1538 0 : self: Arc<Self>,
1539 0 : timeline_id: TimelineId,
1540 0 : ) -> Result<(), DeleteTimelineError> {
1541 0 : DeleteTimelineFlow::run(&self, timeline_id, false).await?;
1542 :
1543 0 : Ok(())
1544 0 : }
1545 :
1546 : /// perform one garbage collection iteration, removing old data files from disk.
1547 : /// this function is periodically called by gc task.
1548 : /// also it can be explicitly requested through page server api 'do_gc' command.
1549 : ///
1550 : /// `target_timeline_id` specifies the timeline to GC, or None for all.
1551 : ///
1552 : /// The `horizon` an `pitr` parameters determine how much WAL history needs to be retained.
1553 : /// Also known as the retention period, or the GC cutoff point. `horizon` specifies
1554 : /// the amount of history, as LSN difference from current latest LSN on each timeline.
1555 : /// `pitr` specifies the same as a time difference from the current time. The effective
1556 : /// GC cutoff point is determined conservatively by either `horizon` and `pitr`, whichever
1557 : /// requires more history to be retained.
1558 : //
1559 754 : pub(crate) async fn gc_iteration(
1560 754 : &self,
1561 754 : target_timeline_id: Option<TimelineId>,
1562 754 : horizon: u64,
1563 754 : pitr: Duration,
1564 754 : cancel: &CancellationToken,
1565 754 : ctx: &RequestContext,
1566 754 : ) -> Result<GcResult, GcError> {
1567 754 : // Don't start doing work during shutdown
1568 754 : if let TenantState::Stopping { .. } = self.current_state() {
1569 0 : return Ok(GcResult::default());
1570 754 : }
1571 754 :
1572 754 : // there is a global allowed_error for this
1573 754 : if !self.is_active() {
1574 0 : return Err(GcError::NotActive);
1575 754 : }
1576 754 :
1577 754 : {
1578 754 : let conf = self.tenant_conf.load();
1579 754 :
1580 754 : if !conf.location.may_delete_layers_hint() {
1581 0 : info!("Skipping GC in location state {:?}", conf.location);
1582 0 : return Ok(GcResult::default());
1583 754 : }
1584 754 : }
1585 754 :
1586 754 : self.gc_iteration_internal(target_timeline_id, horizon, pitr, cancel, ctx)
1587 738 : .await
1588 754 : }
1589 :
1590 : /// Perform one compaction iteration.
1591 : /// This function is periodically called by compactor task.
1592 : /// Also it can be explicitly requested per timeline through page server
1593 : /// api's 'compact' command.
1594 0 : async fn compaction_iteration(
1595 0 : &self,
1596 0 : cancel: &CancellationToken,
1597 0 : ctx: &RequestContext,
1598 0 : ) -> anyhow::Result<(), timeline::CompactionError> {
1599 0 : // Don't start doing work during shutdown, or when broken, we do not need those in the logs
1600 0 : if !self.is_active() {
1601 0 : return Ok(());
1602 0 : }
1603 0 :
1604 0 : {
1605 0 : let conf = self.tenant_conf.load();
1606 0 : if !conf.location.may_delete_layers_hint() || !conf.location.may_upload_layers_hint() {
1607 0 : info!("Skipping compaction in location state {:?}", conf.location);
1608 0 : return Ok(());
1609 0 : }
1610 0 : }
1611 0 :
1612 0 : // Scan through the hashmap and collect a list of all the timelines,
1613 0 : // while holding the lock. Then drop the lock and actually perform the
1614 0 : // compactions. We don't want to block everything else while the
1615 0 : // compaction runs.
1616 0 : let timelines_to_compact = {
1617 0 : let timelines = self.timelines.lock().unwrap();
1618 0 : let timelines_to_compact = timelines
1619 0 : .iter()
1620 0 : .filter_map(|(timeline_id, timeline)| {
1621 0 : if timeline.is_active() {
1622 0 : Some((*timeline_id, timeline.clone()))
1623 : } else {
1624 0 : None
1625 : }
1626 0 : })
1627 0 : .collect::<Vec<_>>();
1628 0 : drop(timelines);
1629 0 : timelines_to_compact
1630 : };
1631 :
1632 0 : for (timeline_id, timeline) in &timelines_to_compact {
1633 0 : timeline
1634 0 : .compact(cancel, EnumSet::empty(), ctx)
1635 0 : .instrument(info_span!("compact_timeline", %timeline_id))
1636 0 : .await?;
1637 : }
1638 :
1639 0 : Ok(())
1640 0 : }
1641 :
1642 : // Call through to all timelines to freeze ephemeral layers if needed. Usually
1643 : // this happens during ingest: this background housekeeping is for freezing layers
1644 : // that are open but haven't been written to for some time.
1645 0 : async fn ingest_housekeeping(&self) {
1646 0 : // Scan through the hashmap and collect a list of all the timelines,
1647 0 : // while holding the lock. Then drop the lock and actually perform the
1648 0 : // compactions. We don't want to block everything else while the
1649 0 : // compaction runs.
1650 0 : let timelines = {
1651 0 : self.timelines
1652 0 : .lock()
1653 0 : .unwrap()
1654 0 : .values()
1655 0 : .filter_map(|timeline| {
1656 0 : if timeline.is_active() {
1657 0 : Some(timeline.clone())
1658 : } else {
1659 0 : None
1660 : }
1661 0 : })
1662 0 : .collect::<Vec<_>>()
1663 : };
1664 :
1665 0 : for timeline in &timelines {
1666 0 : timeline.maybe_freeze_ephemeral_layer().await;
1667 : }
1668 0 : }
1669 :
1670 2421 : pub fn current_state(&self) -> TenantState {
1671 2421 : self.state.borrow().clone()
1672 2421 : }
1673 :
1674 1661 : pub fn is_active(&self) -> bool {
1675 1661 : self.current_state() == TenantState::Active
1676 1661 : }
1677 :
1678 0 : pub fn generation(&self) -> Generation {
1679 0 : self.generation
1680 0 : }
1681 :
1682 0 : pub(crate) fn wal_redo_manager_status(&self) -> Option<WalRedoManagerStatus> {
1683 0 : self.walredo_mgr.as_ref().and_then(|mgr| mgr.status())
1684 0 : }
1685 :
1686 : /// Changes tenant status to active, unless shutdown was already requested.
1687 : ///
1688 : /// `background_jobs_can_start` is an optional barrier set to a value during pageserver startup
1689 : /// to delay background jobs. Background jobs can be started right away when None is given.
1690 0 : fn activate(
1691 0 : self: &Arc<Self>,
1692 0 : broker_client: BrokerClientChannel,
1693 0 : background_jobs_can_start: Option<&completion::Barrier>,
1694 0 : ctx: &RequestContext,
1695 0 : ) {
1696 0 : span::debug_assert_current_span_has_tenant_id();
1697 0 :
1698 0 : let mut activating = false;
1699 0 : self.state.send_modify(|current_state| {
1700 0 : use pageserver_api::models::ActivatingFrom;
1701 0 : match &*current_state {
1702 : TenantState::Activating(_) | TenantState::Active | TenantState::Broken { .. } | TenantState::Stopping { .. } => {
1703 0 : panic!("caller is responsible for calling activate() only on Loading / Attaching tenants, got {state:?}", state = current_state);
1704 : }
1705 0 : TenantState::Loading => {
1706 0 : *current_state = TenantState::Activating(ActivatingFrom::Loading);
1707 0 : }
1708 0 : TenantState::Attaching => {
1709 0 : *current_state = TenantState::Activating(ActivatingFrom::Attaching);
1710 0 : }
1711 : }
1712 0 : debug!(tenant_id = %self.tenant_shard_id.tenant_id, shard_id = %self.tenant_shard_id.shard_slug(), "Activating tenant");
1713 0 : activating = true;
1714 0 : // Continue outside the closure. We need to grab timelines.lock()
1715 0 : // and we plan to turn it into a tokio::sync::Mutex in a future patch.
1716 0 : });
1717 0 :
1718 0 : if activating {
1719 0 : let timelines_accessor = self.timelines.lock().unwrap();
1720 0 : let timelines_to_activate = timelines_accessor
1721 0 : .values()
1722 0 : .filter(|timeline| !(timeline.is_broken() || timeline.is_stopping()));
1723 0 :
1724 0 : // Spawn gc and compaction loops. The loops will shut themselves
1725 0 : // down when they notice that the tenant is inactive.
1726 0 : tasks::start_background_loops(self, background_jobs_can_start);
1727 0 :
1728 0 : let mut activated_timelines = 0;
1729 :
1730 0 : for timeline in timelines_to_activate {
1731 0 : timeline.activate(
1732 0 : self.clone(),
1733 0 : broker_client.clone(),
1734 0 : background_jobs_can_start,
1735 0 : ctx,
1736 0 : );
1737 0 : activated_timelines += 1;
1738 0 : }
1739 :
1740 0 : self.state.send_modify(move |current_state| {
1741 0 : assert!(
1742 0 : matches!(current_state, TenantState::Activating(_)),
1743 0 : "set_stopping and set_broken wait for us to leave Activating state",
1744 : );
1745 0 : *current_state = TenantState::Active;
1746 0 :
1747 0 : let elapsed = self.constructed_at.elapsed();
1748 0 : let total_timelines = timelines_accessor.len();
1749 0 :
1750 0 : // log a lot of stuff, because some tenants sometimes suffer from user-visible
1751 0 : // times to activate. see https://github.com/neondatabase/neon/issues/4025
1752 0 : info!(
1753 0 : since_creation_millis = elapsed.as_millis(),
1754 0 : tenant_id = %self.tenant_shard_id.tenant_id,
1755 0 : shard_id = %self.tenant_shard_id.shard_slug(),
1756 0 : activated_timelines,
1757 0 : total_timelines,
1758 0 : post_state = <&'static str>::from(&*current_state),
1759 0 : "activation attempt finished"
1760 : );
1761 :
1762 0 : TENANT.activation.observe(elapsed.as_secs_f64());
1763 0 : });
1764 0 : }
1765 0 : }
1766 :
1767 : /// Shutdown the tenant and join all of the spawned tasks.
1768 : ///
1769 : /// The method caters for all use-cases:
1770 : /// - pageserver shutdown (freeze_and_flush == true)
1771 : /// - detach + ignore (freeze_and_flush == false)
1772 : ///
1773 : /// This will attempt to shutdown even if tenant is broken.
1774 : ///
1775 : /// `shutdown_progress` is a [`completion::Barrier`] for the shutdown initiated by this call.
1776 : /// If the tenant is already shutting down, we return a clone of the first shutdown call's
1777 : /// `Barrier` as an `Err`. This not-first caller can use the returned barrier to join with
1778 : /// the ongoing shutdown.
1779 6 : async fn shutdown(
1780 6 : &self,
1781 6 : shutdown_progress: completion::Barrier,
1782 6 : shutdown_mode: timeline::ShutdownMode,
1783 6 : ) -> Result<(), completion::Barrier> {
1784 6 : span::debug_assert_current_span_has_tenant_id();
1785 :
1786 : // Set tenant (and its timlines) to Stoppping state.
1787 : //
1788 : // Since we can only transition into Stopping state after activation is complete,
1789 : // run it in a JoinSet so all tenants have a chance to stop before we get SIGKILLed.
1790 : //
1791 : // Transitioning tenants to Stopping state has a couple of non-obvious side effects:
1792 : // 1. Lock out any new requests to the tenants.
1793 : // 2. Signal cancellation to WAL receivers (we wait on it below).
1794 : // 3. Signal cancellation for other tenant background loops.
1795 : // 4. ???
1796 : //
1797 : // The waiting for the cancellation is not done uniformly.
1798 : // We certainly wait for WAL receivers to shut down.
1799 : // That is necessary so that no new data comes in before the freeze_and_flush.
1800 : // But the tenant background loops are joined-on in our caller.
1801 : // It's mesed up.
1802 : // we just ignore the failure to stop
1803 :
1804 : // If we're still attaching, fire the cancellation token early to drop out: this
1805 : // will prevent us flushing, but ensures timely shutdown if some I/O during attach
1806 : // is very slow.
1807 6 : if matches!(self.current_state(), TenantState::Attaching) {
1808 0 : self.cancel.cancel();
1809 6 : }
1810 :
1811 6 : match self.set_stopping(shutdown_progress, false, false).await {
1812 6 : Ok(()) => {}
1813 0 : Err(SetStoppingError::Broken) => {
1814 0 : // assume that this is acceptable
1815 0 : }
1816 0 : Err(SetStoppingError::AlreadyStopping(other)) => {
1817 0 : // give caller the option to wait for this this shutdown
1818 0 : info!("Tenant::shutdown: AlreadyStopping");
1819 0 : return Err(other);
1820 : }
1821 : };
1822 :
1823 6 : let mut js = tokio::task::JoinSet::new();
1824 6 : {
1825 6 : let timelines = self.timelines.lock().unwrap();
1826 6 : timelines.values().for_each(|timeline| {
1827 6 : let timeline = Arc::clone(timeline);
1828 6 : let timeline_id = timeline.timeline_id;
1829 6 : let span = tracing::info_span!("timeline_shutdown", %timeline_id, ?shutdown_mode);
1830 14 : js.spawn(async move { timeline.shutdown(shutdown_mode).instrument(span).await });
1831 6 : })
1832 6 : };
1833 6 : // test_long_timeline_create_then_tenant_delete is leaning on this message
1834 6 : tracing::info!("Waiting for timelines...");
1835 12 : while let Some(res) = js.join_next().await {
1836 0 : match res {
1837 6 : Ok(()) => {}
1838 0 : Err(je) if je.is_cancelled() => unreachable!("no cancelling used"),
1839 0 : Err(je) if je.is_panic() => { /* logged already */ }
1840 0 : Err(je) => warn!("unexpected JoinError: {je:?}"),
1841 : }
1842 : }
1843 :
1844 : // We cancel the Tenant's cancellation token _after_ the timelines have all shut down. This permits
1845 : // them to continue to do work during their shutdown methods, e.g. flushing data.
1846 6 : tracing::debug!("Cancelling CancellationToken");
1847 6 : self.cancel.cancel();
1848 6 :
1849 6 : // shutdown all tenant and timeline tasks: gc, compaction, page service
1850 6 : // No new tasks will be started for this tenant because it's in `Stopping` state.
1851 6 : //
1852 6 : // this will additionally shutdown and await all timeline tasks.
1853 6 : tracing::debug!("Waiting for tasks...");
1854 6 : task_mgr::shutdown_tasks(None, Some(self.tenant_shard_id), None).await;
1855 :
1856 : // Wait for any in-flight operations to complete
1857 6 : self.gate.close().await;
1858 :
1859 6 : remove_tenant_metrics(&self.tenant_shard_id);
1860 6 :
1861 6 : Ok(())
1862 6 : }
1863 :
1864 : /// Change tenant status to Stopping, to mark that it is being shut down.
1865 : ///
1866 : /// This function waits for the tenant to become active if it isn't already, before transitioning it into Stopping state.
1867 : ///
1868 : /// This function is not cancel-safe!
1869 : ///
1870 : /// `allow_transition_from_loading` is needed for the special case of loading task deleting the tenant.
1871 : /// `allow_transition_from_attaching` is needed for the special case of attaching deleted tenant.
1872 6 : async fn set_stopping(
1873 6 : &self,
1874 6 : progress: completion::Barrier,
1875 6 : allow_transition_from_loading: bool,
1876 6 : allow_transition_from_attaching: bool,
1877 6 : ) -> Result<(), SetStoppingError> {
1878 6 : let mut rx = self.state.subscribe();
1879 6 :
1880 6 : // cannot stop before we're done activating, so wait out until we're done activating
1881 6 : rx.wait_for(|state| match state {
1882 0 : TenantState::Attaching if allow_transition_from_attaching => true,
1883 : TenantState::Activating(_) | TenantState::Attaching => {
1884 0 : info!(
1885 0 : "waiting for {} to turn Active|Broken|Stopping",
1886 0 : <&'static str>::from(state)
1887 : );
1888 0 : false
1889 : }
1890 0 : TenantState::Loading => allow_transition_from_loading,
1891 6 : TenantState::Active | TenantState::Broken { .. } | TenantState::Stopping { .. } => true,
1892 6 : })
1893 0 : .await
1894 6 : .expect("cannot drop self.state while on a &self method");
1895 6 :
1896 6 : // we now know we're done activating, let's see whether this task is the winner to transition into Stopping
1897 6 : let mut err = None;
1898 6 : let stopping = self.state.send_if_modified(|current_state| match current_state {
1899 : TenantState::Activating(_) => {
1900 0 : unreachable!("1we ensured above that we're done with activation, and, there is no re-activation")
1901 : }
1902 : TenantState::Attaching => {
1903 0 : if !allow_transition_from_attaching {
1904 0 : unreachable!("2we ensured above that we're done with activation, and, there is no re-activation")
1905 0 : };
1906 0 : *current_state = TenantState::Stopping { progress };
1907 0 : true
1908 : }
1909 : TenantState::Loading => {
1910 0 : if !allow_transition_from_loading {
1911 0 : unreachable!("3we ensured above that we're done with activation, and, there is no re-activation")
1912 0 : };
1913 0 : *current_state = TenantState::Stopping { progress };
1914 0 : true
1915 : }
1916 : TenantState::Active => {
1917 : // FIXME: due to time-of-check vs time-of-use issues, it can happen that new timelines
1918 : // are created after the transition to Stopping. That's harmless, as the Timelines
1919 : // won't be accessible to anyone afterwards, because the Tenant is in Stopping state.
1920 6 : *current_state = TenantState::Stopping { progress };
1921 6 : // Continue stopping outside the closure. We need to grab timelines.lock()
1922 6 : // and we plan to turn it into a tokio::sync::Mutex in a future patch.
1923 6 : true
1924 : }
1925 0 : TenantState::Broken { reason, .. } => {
1926 0 : info!(
1927 0 : "Cannot set tenant to Stopping state, it is in Broken state due to: {reason}"
1928 : );
1929 0 : err = Some(SetStoppingError::Broken);
1930 0 : false
1931 : }
1932 0 : TenantState::Stopping { progress } => {
1933 0 : info!("Tenant is already in Stopping state");
1934 0 : err = Some(SetStoppingError::AlreadyStopping(progress.clone()));
1935 0 : false
1936 : }
1937 6 : });
1938 6 : match (stopping, err) {
1939 6 : (true, None) => {} // continue
1940 0 : (false, Some(err)) => return Err(err),
1941 0 : (true, Some(_)) => unreachable!(
1942 0 : "send_if_modified closure must error out if not transitioning to Stopping"
1943 0 : ),
1944 0 : (false, None) => unreachable!(
1945 0 : "send_if_modified closure must return true if transitioning to Stopping"
1946 0 : ),
1947 : }
1948 :
1949 6 : let timelines_accessor = self.timelines.lock().unwrap();
1950 6 : let not_broken_timelines = timelines_accessor
1951 6 : .values()
1952 6 : .filter(|timeline| !timeline.is_broken());
1953 12 : for timeline in not_broken_timelines {
1954 6 : timeline.set_state(TimelineState::Stopping);
1955 6 : }
1956 6 : Ok(())
1957 6 : }
1958 :
1959 : /// Method for tenant::mgr to transition us into Broken state in case of a late failure in
1960 : /// `remove_tenant_from_memory`
1961 : ///
1962 : /// This function waits for the tenant to become active if it isn't already, before transitioning it into Stopping state.
1963 : ///
1964 : /// In tests, we also use this to set tenants to Broken state on purpose.
1965 0 : pub(crate) async fn set_broken(&self, reason: String) {
1966 0 : let mut rx = self.state.subscribe();
1967 0 :
1968 0 : // The load & attach routines own the tenant state until it has reached `Active`.
1969 0 : // So, wait until it's done.
1970 0 : rx.wait_for(|state| match state {
1971 : TenantState::Activating(_) | TenantState::Loading | TenantState::Attaching => {
1972 0 : info!(
1973 0 : "waiting for {} to turn Active|Broken|Stopping",
1974 0 : <&'static str>::from(state)
1975 : );
1976 0 : false
1977 : }
1978 0 : TenantState::Active | TenantState::Broken { .. } | TenantState::Stopping { .. } => true,
1979 0 : })
1980 0 : .await
1981 0 : .expect("cannot drop self.state while on a &self method");
1982 0 :
1983 0 : // we now know we're done activating, let's see whether this task is the winner to transition into Broken
1984 0 : self.set_broken_no_wait(reason)
1985 0 : }
1986 :
1987 0 : pub(crate) fn set_broken_no_wait(&self, reason: impl Display) {
1988 0 : let reason = reason.to_string();
1989 0 : self.state.send_modify(|current_state| {
1990 0 : match *current_state {
1991 : TenantState::Activating(_) | TenantState::Loading | TenantState::Attaching => {
1992 0 : unreachable!("we ensured above that we're done with activation, and, there is no re-activation")
1993 : }
1994 : TenantState::Active => {
1995 0 : if cfg!(feature = "testing") {
1996 0 : warn!("Changing Active tenant to Broken state, reason: {}", reason);
1997 0 : *current_state = TenantState::broken_from_reason(reason);
1998 : } else {
1999 0 : unreachable!("not allowed to call set_broken on Active tenants in non-testing builds")
2000 : }
2001 : }
2002 : TenantState::Broken { .. } => {
2003 0 : warn!("Tenant is already in Broken state");
2004 : }
2005 : // This is the only "expected" path, any other path is a bug.
2006 : TenantState::Stopping { .. } => {
2007 0 : warn!(
2008 0 : "Marking Stopping tenant as Broken state, reason: {}",
2009 : reason
2010 : );
2011 0 : *current_state = TenantState::broken_from_reason(reason);
2012 : }
2013 : }
2014 0 : });
2015 0 : }
2016 :
2017 0 : pub fn subscribe_for_state_updates(&self) -> watch::Receiver<TenantState> {
2018 0 : self.state.subscribe()
2019 0 : }
2020 :
2021 : /// The activate_now semaphore is initialized with zero units. As soon as
2022 : /// we add a unit, waiters will be able to acquire a unit and proceed.
2023 0 : pub(crate) fn activate_now(&self) {
2024 0 : self.activate_now_sem.add_permits(1);
2025 0 : }
2026 :
2027 0 : pub(crate) async fn wait_to_become_active(
2028 0 : &self,
2029 0 : timeout: Duration,
2030 0 : ) -> Result<(), GetActiveTenantError> {
2031 0 : let mut receiver = self.state.subscribe();
2032 0 : loop {
2033 0 : let current_state = receiver.borrow_and_update().clone();
2034 0 : match current_state {
2035 : TenantState::Loading | TenantState::Attaching | TenantState::Activating(_) => {
2036 : // in these states, there's a chance that we can reach ::Active
2037 0 : self.activate_now();
2038 0 : match timeout_cancellable(timeout, &self.cancel, receiver.changed()).await {
2039 0 : Ok(r) => {
2040 0 : r.map_err(
2041 0 : |_e: tokio::sync::watch::error::RecvError|
2042 : // Tenant existed but was dropped: report it as non-existent
2043 0 : GetActiveTenantError::NotFound(GetTenantError::NotFound(self.tenant_shard_id.tenant_id))
2044 0 : )?
2045 : }
2046 : Err(TimeoutCancellableError::Cancelled) => {
2047 0 : return Err(GetActiveTenantError::Cancelled);
2048 : }
2049 : Err(TimeoutCancellableError::Timeout) => {
2050 0 : return Err(GetActiveTenantError::WaitForActiveTimeout {
2051 0 : latest_state: Some(self.current_state()),
2052 0 : wait_time: timeout,
2053 0 : });
2054 : }
2055 : }
2056 : }
2057 : TenantState::Active { .. } => {
2058 0 : return Ok(());
2059 : }
2060 0 : TenantState::Broken { reason, .. } => {
2061 0 : // This is fatal, and reported distinctly from the general case of "will never be active" because
2062 0 : // it's logically a 500 to external API users (broken is always a bug).
2063 0 : return Err(GetActiveTenantError::Broken(reason));
2064 : }
2065 : TenantState::Stopping { .. } => {
2066 : // There's no chance the tenant can transition back into ::Active
2067 0 : return Err(GetActiveTenantError::WillNotBecomeActive(current_state));
2068 : }
2069 : }
2070 : }
2071 0 : }
2072 :
2073 0 : pub(crate) fn get_attach_mode(&self) -> AttachmentMode {
2074 0 : self.tenant_conf.load().location.attach_mode
2075 0 : }
2076 :
2077 : /// For API access: generate a LocationConfig equivalent to the one that would be used to
2078 : /// create a Tenant in the same state. Do not use this in hot paths: it's for relatively
2079 : /// rare external API calls, like a reconciliation at startup.
2080 0 : pub(crate) fn get_location_conf(&self) -> models::LocationConfig {
2081 0 : let conf = self.tenant_conf.load();
2082 :
2083 0 : let location_config_mode = match conf.location.attach_mode {
2084 0 : AttachmentMode::Single => models::LocationConfigMode::AttachedSingle,
2085 0 : AttachmentMode::Multi => models::LocationConfigMode::AttachedMulti,
2086 0 : AttachmentMode::Stale => models::LocationConfigMode::AttachedStale,
2087 : };
2088 :
2089 : // We have a pageserver TenantConf, we need the API-facing TenantConfig.
2090 0 : let tenant_config: models::TenantConfig = conf.tenant_conf.clone().into();
2091 0 :
2092 0 : models::LocationConfig {
2093 0 : mode: location_config_mode,
2094 0 : generation: self.generation.into(),
2095 0 : secondary_conf: None,
2096 0 : shard_number: self.shard_identity.number.0,
2097 0 : shard_count: self.shard_identity.count.literal(),
2098 0 : shard_stripe_size: self.shard_identity.stripe_size.0,
2099 0 : tenant_conf: tenant_config,
2100 0 : }
2101 0 : }
2102 :
2103 0 : pub(crate) fn get_tenant_shard_id(&self) -> &TenantShardId {
2104 0 : &self.tenant_shard_id
2105 0 : }
2106 :
2107 0 : pub(crate) fn get_shard_stripe_size(&self) -> ShardStripeSize {
2108 0 : self.shard_identity.stripe_size
2109 0 : }
2110 :
2111 0 : pub(crate) fn get_generation(&self) -> Generation {
2112 0 : self.generation
2113 0 : }
2114 :
2115 : /// This function partially shuts down the tenant (it shuts down the Timelines) and is fallible,
2116 : /// and can leave the tenant in a bad state if it fails. The caller is responsible for
2117 : /// resetting this tenant to a valid state if we fail.
2118 0 : pub(crate) async fn split_prepare(
2119 0 : &self,
2120 0 : child_shards: &Vec<TenantShardId>,
2121 0 : ) -> anyhow::Result<()> {
2122 0 : let timelines = self.timelines.lock().unwrap().clone();
2123 0 : for timeline in timelines.values() {
2124 : // We do not block timeline creation/deletion during splits inside the pageserver: it is up to higher levels
2125 : // to ensure that they do not start a split if currently in the process of doing these.
2126 :
2127 : // Upload an index from the parent: this is partly to provide freshness for the
2128 : // child tenants that will copy it, and partly for general ease-of-debugging: there will
2129 : // always be a parent shard index in the same generation as we wrote the child shard index.
2130 0 : tracing::info!(timeline_id=%timeline.timeline_id, "Uploading index");
2131 0 : timeline
2132 0 : .remote_client
2133 0 : .schedule_index_upload_for_file_changes()?;
2134 0 : timeline.remote_client.wait_completion().await?;
2135 :
2136 : // Shut down the timeline's remote client: this means that the indices we write
2137 : // for child shards will not be invalidated by the parent shard deleting layers.
2138 0 : tracing::info!(timeline_id=%timeline.timeline_id, "Shutting down remote storage client");
2139 0 : timeline.remote_client.shutdown().await;
2140 :
2141 : // Download methods can still be used after shutdown, as they don't flow through the remote client's
2142 : // queue. In principal the RemoteTimelineClient could provide this without downloading it, but this
2143 : // operation is rare, so it's simpler to just download it (and robustly guarantees that the index
2144 : // we use here really is the remotely persistent one).
2145 0 : tracing::info!(timeline_id=%timeline.timeline_id, "Downloading index_part from parent");
2146 0 : let result = timeline.remote_client
2147 0 : .download_index_file(&self.cancel)
2148 0 : .instrument(info_span!("download_index_file", tenant_id=%self.tenant_shard_id.tenant_id, shard_id=%self.tenant_shard_id.shard_slug(), timeline_id=%timeline.timeline_id))
2149 0 : .await?;
2150 0 : let index_part = match result {
2151 : MaybeDeletedIndexPart::Deleted(_) => {
2152 0 : anyhow::bail!("Timeline deletion happened concurrently with split")
2153 : }
2154 0 : MaybeDeletedIndexPart::IndexPart(p) => p,
2155 : };
2156 :
2157 0 : for child_shard in child_shards {
2158 0 : tracing::info!(timeline_id=%timeline.timeline_id, "Uploading index_part for child {}", child_shard.to_index());
2159 0 : upload_index_part(
2160 0 : &self.remote_storage,
2161 0 : child_shard,
2162 0 : &timeline.timeline_id,
2163 0 : self.generation,
2164 0 : &index_part,
2165 0 : &self.cancel,
2166 0 : )
2167 0 : .await?;
2168 : }
2169 : }
2170 :
2171 0 : Ok(())
2172 0 : }
2173 :
2174 0 : pub(crate) fn get_sizes(&self) -> TopTenantShardItem {
2175 0 : let mut result = TopTenantShardItem {
2176 0 : id: self.tenant_shard_id,
2177 0 : resident_size: 0,
2178 0 : physical_size: 0,
2179 0 : max_logical_size: 0,
2180 0 : };
2181 :
2182 0 : for timeline in self.timelines.lock().unwrap().values() {
2183 0 : result.resident_size += timeline.metrics.resident_physical_size_gauge.get();
2184 0 :
2185 0 : result.physical_size += timeline
2186 0 : .remote_client
2187 0 : .metrics
2188 0 : .remote_physical_size_gauge
2189 0 : .get();
2190 0 : result.max_logical_size = std::cmp::max(
2191 0 : result.max_logical_size,
2192 0 : timeline.metrics.current_logical_size_gauge.get(),
2193 0 : );
2194 0 : }
2195 :
2196 0 : result
2197 0 : }
2198 : }
2199 :
2200 : /// Given a Vec of timelines and their ancestors (timeline_id, ancestor_id),
2201 : /// perform a topological sort, so that the parent of each timeline comes
2202 : /// before the children.
2203 : /// E extracts the ancestor from T
2204 : /// This allows for T to be different. It can be TimelineMetadata, can be Timeline itself, etc.
2205 161 : fn tree_sort_timelines<T, E>(
2206 161 : timelines: HashMap<TimelineId, T>,
2207 161 : extractor: E,
2208 161 : ) -> anyhow::Result<Vec<(TimelineId, T)>>
2209 161 : where
2210 161 : E: Fn(&T) -> Option<TimelineId>,
2211 161 : {
2212 161 : let mut result = Vec::with_capacity(timelines.len());
2213 161 :
2214 161 : let mut now = Vec::with_capacity(timelines.len());
2215 161 : // (ancestor, children)
2216 161 : let mut later: HashMap<TimelineId, Vec<(TimelineId, T)>> =
2217 161 : HashMap::with_capacity(timelines.len());
2218 :
2219 167 : for (timeline_id, value) in timelines {
2220 6 : if let Some(ancestor_id) = extractor(&value) {
2221 2 : let children = later.entry(ancestor_id).or_default();
2222 2 : children.push((timeline_id, value));
2223 4 : } else {
2224 4 : now.push((timeline_id, value));
2225 4 : }
2226 : }
2227 :
2228 167 : while let Some((timeline_id, metadata)) = now.pop() {
2229 6 : result.push((timeline_id, metadata));
2230 : // All children of this can be loaded now
2231 6 : if let Some(mut children) = later.remove(&timeline_id) {
2232 2 : now.append(&mut children);
2233 4 : }
2234 : }
2235 :
2236 : // All timelines should be visited now. Unless there were timelines with missing ancestors.
2237 161 : if !later.is_empty() {
2238 0 : for (missing_id, orphan_ids) in later {
2239 0 : for (orphan_id, _) in orphan_ids {
2240 0 : error!("could not load timeline {orphan_id} because its ancestor timeline {missing_id} could not be loaded");
2241 : }
2242 : }
2243 0 : bail!("could not load tenant because some timelines are missing ancestors");
2244 161 : }
2245 161 :
2246 161 : Ok(result)
2247 161 : }
2248 :
2249 : impl Tenant {
2250 0 : pub fn tenant_specific_overrides(&self) -> TenantConfOpt {
2251 0 : self.tenant_conf.load().tenant_conf.clone()
2252 0 : }
2253 :
2254 0 : pub fn effective_config(&self) -> TenantConf {
2255 0 : self.tenant_specific_overrides()
2256 0 : .merge(self.conf.default_tenant_conf.clone())
2257 0 : }
2258 :
2259 0 : pub fn get_checkpoint_distance(&self) -> u64 {
2260 0 : let tenant_conf = self.tenant_conf.load().tenant_conf.clone();
2261 0 : tenant_conf
2262 0 : .checkpoint_distance
2263 0 : .unwrap_or(self.conf.default_tenant_conf.checkpoint_distance)
2264 0 : }
2265 :
2266 0 : pub fn get_checkpoint_timeout(&self) -> Duration {
2267 0 : let tenant_conf = self.tenant_conf.load().tenant_conf.clone();
2268 0 : tenant_conf
2269 0 : .checkpoint_timeout
2270 0 : .unwrap_or(self.conf.default_tenant_conf.checkpoint_timeout)
2271 0 : }
2272 :
2273 0 : pub fn get_compaction_target_size(&self) -> u64 {
2274 0 : let tenant_conf = self.tenant_conf.load().tenant_conf.clone();
2275 0 : tenant_conf
2276 0 : .compaction_target_size
2277 0 : .unwrap_or(self.conf.default_tenant_conf.compaction_target_size)
2278 0 : }
2279 :
2280 0 : pub fn get_compaction_period(&self) -> Duration {
2281 0 : let tenant_conf = self.tenant_conf.load().tenant_conf.clone();
2282 0 : tenant_conf
2283 0 : .compaction_period
2284 0 : .unwrap_or(self.conf.default_tenant_conf.compaction_period)
2285 0 : }
2286 :
2287 0 : pub fn get_compaction_threshold(&self) -> usize {
2288 0 : let tenant_conf = self.tenant_conf.load().tenant_conf.clone();
2289 0 : tenant_conf
2290 0 : .compaction_threshold
2291 0 : .unwrap_or(self.conf.default_tenant_conf.compaction_threshold)
2292 0 : }
2293 :
2294 0 : pub fn get_gc_horizon(&self) -> u64 {
2295 0 : let tenant_conf = self.tenant_conf.load().tenant_conf.clone();
2296 0 : tenant_conf
2297 0 : .gc_horizon
2298 0 : .unwrap_or(self.conf.default_tenant_conf.gc_horizon)
2299 0 : }
2300 :
2301 0 : pub fn get_gc_period(&self) -> Duration {
2302 0 : let tenant_conf = self.tenant_conf.load().tenant_conf.clone();
2303 0 : tenant_conf
2304 0 : .gc_period
2305 0 : .unwrap_or(self.conf.default_tenant_conf.gc_period)
2306 0 : }
2307 :
2308 0 : pub fn get_image_creation_threshold(&self) -> usize {
2309 0 : let tenant_conf = self.tenant_conf.load().tenant_conf.clone();
2310 0 : tenant_conf
2311 0 : .image_creation_threshold
2312 0 : .unwrap_or(self.conf.default_tenant_conf.image_creation_threshold)
2313 0 : }
2314 :
2315 0 : pub fn get_pitr_interval(&self) -> Duration {
2316 0 : let tenant_conf = self.tenant_conf.load().tenant_conf.clone();
2317 0 : tenant_conf
2318 0 : .pitr_interval
2319 0 : .unwrap_or(self.conf.default_tenant_conf.pitr_interval)
2320 0 : }
2321 :
2322 0 : pub fn get_trace_read_requests(&self) -> bool {
2323 0 : let tenant_conf = self.tenant_conf.load().tenant_conf.clone();
2324 0 : tenant_conf
2325 0 : .trace_read_requests
2326 0 : .unwrap_or(self.conf.default_tenant_conf.trace_read_requests)
2327 0 : }
2328 :
2329 0 : pub fn get_min_resident_size_override(&self) -> Option<u64> {
2330 0 : let tenant_conf = self.tenant_conf.load().tenant_conf.clone();
2331 0 : tenant_conf
2332 0 : .min_resident_size_override
2333 0 : .or(self.conf.default_tenant_conf.min_resident_size_override)
2334 0 : }
2335 :
2336 0 : pub fn get_heatmap_period(&self) -> Option<Duration> {
2337 0 : let tenant_conf = self.tenant_conf.load().tenant_conf.clone();
2338 0 : let heatmap_period = tenant_conf
2339 0 : .heatmap_period
2340 0 : .unwrap_or(self.conf.default_tenant_conf.heatmap_period);
2341 0 : if heatmap_period.is_zero() {
2342 0 : None
2343 : } else {
2344 0 : Some(heatmap_period)
2345 : }
2346 0 : }
2347 :
2348 0 : pub fn get_lsn_lease_length(&self) -> Duration {
2349 0 : let tenant_conf = self.tenant_conf.load().tenant_conf.clone();
2350 0 : tenant_conf
2351 0 : .lsn_lease_length
2352 0 : .unwrap_or(self.conf.default_tenant_conf.lsn_lease_length)
2353 0 : }
2354 :
2355 0 : pub fn set_new_tenant_config(&self, new_tenant_conf: TenantConfOpt) {
2356 0 : // Use read-copy-update in order to avoid overwriting the location config
2357 0 : // state if this races with [`Tenant::set_new_location_config`]. Note that
2358 0 : // this race is not possible if both request types come from the storage
2359 0 : // controller (as they should!) because an exclusive op lock is required
2360 0 : // on the storage controller side.
2361 0 : self.tenant_conf.rcu(|inner| {
2362 0 : Arc::new(AttachedTenantConf {
2363 0 : tenant_conf: new_tenant_conf.clone(),
2364 0 : location: inner.location,
2365 0 : })
2366 0 : });
2367 0 :
2368 0 : self.tenant_conf_updated(&new_tenant_conf);
2369 0 : // Don't hold self.timelines.lock() during the notifies.
2370 0 : // There's no risk of deadlock right now, but there could be if we consolidate
2371 0 : // mutexes in struct Timeline in the future.
2372 0 : let timelines = self.list_timelines();
2373 0 : for timeline in timelines {
2374 0 : timeline.tenant_conf_updated(&new_tenant_conf);
2375 0 : }
2376 0 : }
2377 :
2378 8 : pub(crate) fn set_new_location_config(&self, new_conf: AttachedTenantConf) {
2379 8 : let new_tenant_conf = new_conf.tenant_conf.clone();
2380 8 :
2381 8 : self.tenant_conf.store(Arc::new(new_conf));
2382 8 :
2383 8 : self.tenant_conf_updated(&new_tenant_conf);
2384 8 : // Don't hold self.timelines.lock() during the notifies.
2385 8 : // There's no risk of deadlock right now, but there could be if we consolidate
2386 8 : // mutexes in struct Timeline in the future.
2387 8 : let timelines = self.list_timelines();
2388 16 : for timeline in timelines {
2389 8 : timeline.tenant_conf_updated(&new_tenant_conf);
2390 8 : }
2391 8 : }
2392 :
2393 169 : fn get_timeline_get_throttle_config(
2394 169 : psconf: &'static PageServerConf,
2395 169 : overrides: &TenantConfOpt,
2396 169 : ) -> throttle::Config {
2397 169 : overrides
2398 169 : .timeline_get_throttle
2399 169 : .clone()
2400 169 : .unwrap_or(psconf.default_tenant_conf.timeline_get_throttle.clone())
2401 169 : }
2402 :
2403 8 : pub(crate) fn tenant_conf_updated(&self, new_conf: &TenantConfOpt) {
2404 8 : let conf = Self::get_timeline_get_throttle_config(self.conf, new_conf);
2405 8 : self.timeline_get_throttle.reconfigure(conf)
2406 8 : }
2407 :
2408 : /// Helper function to create a new Timeline struct.
2409 : ///
2410 : /// The returned Timeline is in Loading state. The caller is responsible for
2411 : /// initializing any on-disk state, and for inserting the Timeline to the 'timelines'
2412 : /// map.
2413 : ///
2414 : /// `validate_ancestor == false` is used when a timeline is created for deletion
2415 : /// and we might not have the ancestor present anymore which is fine for to be
2416 : /// deleted timelines.
2417 383 : fn create_timeline_struct(
2418 383 : &self,
2419 383 : new_timeline_id: TimelineId,
2420 383 : new_metadata: &TimelineMetadata,
2421 383 : ancestor: Option<Arc<Timeline>>,
2422 383 : resources: TimelineResources,
2423 383 : cause: CreateTimelineCause,
2424 383 : last_aux_file_policy: Option<AuxFilePolicy>,
2425 383 : ) -> anyhow::Result<Arc<Timeline>> {
2426 383 : let state = match cause {
2427 : CreateTimelineCause::Load => {
2428 383 : let ancestor_id = new_metadata.ancestor_timeline();
2429 383 : anyhow::ensure!(
2430 383 : ancestor_id == ancestor.as_ref().map(|t| t.timeline_id),
2431 0 : "Timeline's {new_timeline_id} ancestor {ancestor_id:?} was not found"
2432 : );
2433 383 : TimelineState::Loading
2434 : }
2435 0 : CreateTimelineCause::Delete => TimelineState::Stopping,
2436 : };
2437 :
2438 383 : let pg_version = new_metadata.pg_version();
2439 383 :
2440 383 : let timeline = Timeline::new(
2441 383 : self.conf,
2442 383 : Arc::clone(&self.tenant_conf),
2443 383 : new_metadata,
2444 383 : ancestor,
2445 383 : new_timeline_id,
2446 383 : self.tenant_shard_id,
2447 383 : self.generation,
2448 383 : self.shard_identity,
2449 383 : self.walredo_mgr.clone(),
2450 383 : resources,
2451 383 : pg_version,
2452 383 : state,
2453 383 : last_aux_file_policy,
2454 383 : self.cancel.child_token(),
2455 383 : );
2456 383 :
2457 383 : Ok(timeline)
2458 383 : }
2459 :
2460 : // Allow too_many_arguments because a constructor's argument list naturally grows with the
2461 : // number of attributes in the struct: breaking these out into a builder wouldn't be helpful.
2462 : #[allow(clippy::too_many_arguments)]
2463 161 : fn new(
2464 161 : state: TenantState,
2465 161 : conf: &'static PageServerConf,
2466 161 : attached_conf: AttachedTenantConf,
2467 161 : shard_identity: ShardIdentity,
2468 161 : walredo_mgr: Option<Arc<WalRedoManager>>,
2469 161 : tenant_shard_id: TenantShardId,
2470 161 : remote_storage: GenericRemoteStorage,
2471 161 : deletion_queue_client: DeletionQueueClient,
2472 161 : ) -> Tenant {
2473 161 : debug_assert!(
2474 161 : !attached_conf.location.generation.is_none() || conf.control_plane_api.is_none()
2475 : );
2476 :
2477 161 : let (state, mut rx) = watch::channel(state);
2478 161 :
2479 161 : tokio::spawn(async move {
2480 161 : // reflect tenant state in metrics:
2481 161 : // - global per tenant state: TENANT_STATE_METRIC
2482 161 : // - "set" of broken tenants: BROKEN_TENANTS_SET
2483 161 : //
2484 161 : // set of broken tenants should not have zero counts so that it remains accessible for
2485 161 : // alerting.
2486 161 :
2487 161 : let tid = tenant_shard_id.to_string();
2488 161 : let shard_id = tenant_shard_id.shard_slug().to_string();
2489 161 : let set_key = &[tid.as_str(), shard_id.as_str()][..];
2490 161 :
2491 319 : fn inspect_state(state: &TenantState) -> ([&'static str; 1], bool) {
2492 319 : ([state.into()], matches!(state, TenantState::Broken { .. }))
2493 319 : }
2494 161 :
2495 161 : let mut tuple = inspect_state(&rx.borrow_and_update());
2496 161 :
2497 161 : let is_broken = tuple.1;
2498 161 : let mut counted_broken = if is_broken {
2499 : // add the id to the set right away, there should not be any updates on the channel
2500 : // after before tenant is removed, if ever
2501 0 : BROKEN_TENANTS_SET.with_label_values(set_key).set(1);
2502 0 : true
2503 : } else {
2504 161 : false
2505 : };
2506 :
2507 319 : loop {
2508 319 : let labels = &tuple.0;
2509 319 : let current = TENANT_STATE_METRIC.with_label_values(labels);
2510 319 : current.inc();
2511 319 :
2512 319 : if rx.changed().await.is_err() {
2513 : // tenant has been dropped
2514 16 : current.dec();
2515 16 : drop(BROKEN_TENANTS_SET.remove_label_values(set_key));
2516 16 : break;
2517 158 : }
2518 158 :
2519 158 : current.dec();
2520 158 : tuple = inspect_state(&rx.borrow_and_update());
2521 158 :
2522 158 : let is_broken = tuple.1;
2523 158 : if is_broken && !counted_broken {
2524 0 : counted_broken = true;
2525 0 : // insert the tenant_id (back) into the set while avoiding needless counter
2526 0 : // access
2527 0 : BROKEN_TENANTS_SET.with_label_values(set_key).set(1);
2528 158 : }
2529 : }
2530 161 : });
2531 161 :
2532 161 : Tenant {
2533 161 : tenant_shard_id,
2534 161 : shard_identity,
2535 161 : generation: attached_conf.location.generation,
2536 161 : conf,
2537 161 : // using now here is good enough approximation to catch tenants with really long
2538 161 : // activation times.
2539 161 : constructed_at: Instant::now(),
2540 161 : timelines: Mutex::new(HashMap::new()),
2541 161 : timelines_creating: Mutex::new(HashSet::new()),
2542 161 : gc_cs: tokio::sync::Mutex::new(()),
2543 161 : walredo_mgr,
2544 161 : remote_storage,
2545 161 : deletion_queue_client,
2546 161 : state,
2547 161 : cached_logical_sizes: tokio::sync::Mutex::new(HashMap::new()),
2548 161 : cached_synthetic_tenant_size: Arc::new(AtomicU64::new(0)),
2549 161 : eviction_task_tenant_state: tokio::sync::Mutex::new(EvictionTaskTenantState::default()),
2550 161 : activate_now_sem: tokio::sync::Semaphore::new(0),
2551 161 : cancel: CancellationToken::default(),
2552 161 : gate: Gate::default(),
2553 161 : timeline_get_throttle: Arc::new(throttle::Throttle::new(
2554 161 : Tenant::get_timeline_get_throttle_config(conf, &attached_conf.tenant_conf),
2555 161 : &crate::metrics::tenant_throttling::TIMELINE_GET,
2556 161 : )),
2557 161 : tenant_conf: Arc::new(ArcSwap::from_pointee(attached_conf)),
2558 161 : ongoing_timeline_detach: std::sync::Mutex::default(),
2559 161 : }
2560 161 : }
2561 :
2562 : /// Locate and load config
2563 0 : pub(super) fn load_tenant_config(
2564 0 : conf: &'static PageServerConf,
2565 0 : tenant_shard_id: &TenantShardId,
2566 0 : ) -> anyhow::Result<LocationConf> {
2567 0 : let config_path = conf.tenant_location_config_path(tenant_shard_id);
2568 0 :
2569 0 : if config_path.exists() {
2570 : // New-style config takes precedence
2571 0 : let deserialized = Self::read_config(&config_path)?;
2572 0 : Ok(toml_edit::de::from_document::<LocationConf>(deserialized)?)
2573 : } else {
2574 : // The config should almost always exist for a tenant directory:
2575 : // - When attaching a tenant, the config is the first thing we write
2576 : // - When detaching a tenant, we atomically move the directory to a tmp location
2577 : // before deleting contents.
2578 : //
2579 : // The very rare edge case that can result in a missing config is if we crash during attach
2580 : // between creating directory and writing config. Callers should handle that as if the
2581 : // directory didn't exist.
2582 0 : anyhow::bail!("tenant config not found in {}", config_path);
2583 : }
2584 0 : }
2585 :
2586 0 : fn read_config(path: &Utf8Path) -> anyhow::Result<toml_edit::Document> {
2587 0 : info!("loading tenant configuration from {path}");
2588 :
2589 : // load and parse file
2590 0 : let config = fs::read_to_string(path)
2591 0 : .with_context(|| format!("Failed to load config from path '{path}'"))?;
2592 :
2593 0 : config
2594 0 : .parse::<toml_edit::Document>()
2595 0 : .with_context(|| format!("Failed to parse config from file '{path}' as toml file"))
2596 0 : }
2597 :
2598 0 : #[tracing::instrument(skip_all, fields(tenant_id=%tenant_shard_id.tenant_id, shard_id=%tenant_shard_id.shard_slug()))]
2599 : pub(super) async fn persist_tenant_config(
2600 : conf: &'static PageServerConf,
2601 : tenant_shard_id: &TenantShardId,
2602 : location_conf: &LocationConf,
2603 : ) -> anyhow::Result<()> {
2604 : let config_path = conf.tenant_location_config_path(tenant_shard_id);
2605 :
2606 : Self::persist_tenant_config_at(tenant_shard_id, &config_path, location_conf).await
2607 : }
2608 :
2609 0 : #[tracing::instrument(skip_all, fields(tenant_id=%tenant_shard_id.tenant_id, shard_id=%tenant_shard_id.shard_slug()))]
2610 : pub(super) async fn persist_tenant_config_at(
2611 : tenant_shard_id: &TenantShardId,
2612 : config_path: &Utf8Path,
2613 : location_conf: &LocationConf,
2614 : ) -> anyhow::Result<()> {
2615 : debug!("persisting tenantconf to {config_path}");
2616 :
2617 : let mut conf_content = r#"# This file contains a specific per-tenant's config.
2618 : # It is read in case of pageserver restart.
2619 : "#
2620 : .to_string();
2621 :
2622 0 : fail::fail_point!("tenant-config-before-write", |_| {
2623 0 : anyhow::bail!("tenant-config-before-write");
2624 0 : });
2625 :
2626 : // Convert the config to a toml file.
2627 : conf_content += &toml_edit::ser::to_string_pretty(&location_conf)?;
2628 :
2629 : let temp_path = path_with_suffix_extension(config_path, TEMP_FILE_SUFFIX);
2630 :
2631 : let tenant_shard_id = *tenant_shard_id;
2632 : let config_path = config_path.to_owned();
2633 : let conf_content = conf_content.into_bytes();
2634 : VirtualFile::crashsafe_overwrite(config_path.clone(), temp_path, conf_content)
2635 : .await
2636 0 : .with_context(|| format!("write tenant {tenant_shard_id} config to {config_path}"))?;
2637 :
2638 : Ok(())
2639 : }
2640 :
2641 : //
2642 : // How garbage collection works:
2643 : //
2644 : // +--bar------------->
2645 : // /
2646 : // +----+-----foo---------------->
2647 : // /
2648 : // ----main--+-------------------------->
2649 : // \
2650 : // +-----baz-------->
2651 : //
2652 : //
2653 : // 1. Grab 'gc_cs' mutex to prevent new timelines from being created while Timeline's
2654 : // `gc_infos` are being refreshed
2655 : // 2. Scan collected timelines, and on each timeline, make note of the
2656 : // all the points where other timelines have been branched off.
2657 : // We will refrain from removing page versions at those LSNs.
2658 : // 3. For each timeline, scan all layer files on the timeline.
2659 : // Remove all files for which a newer file exists and which
2660 : // don't cover any branch point LSNs.
2661 : //
2662 : // TODO:
2663 : // - if a relation has a non-incremental persistent layer on a child branch, then we
2664 : // don't need to keep that in the parent anymore. But currently
2665 : // we do.
2666 754 : async fn gc_iteration_internal(
2667 754 : &self,
2668 754 : target_timeline_id: Option<TimelineId>,
2669 754 : horizon: u64,
2670 754 : pitr: Duration,
2671 754 : cancel: &CancellationToken,
2672 754 : ctx: &RequestContext,
2673 754 : ) -> Result<GcResult, GcError> {
2674 754 : let mut totals: GcResult = Default::default();
2675 754 : let now = Instant::now();
2676 :
2677 754 : let gc_timelines = self
2678 754 : .refresh_gc_info_internal(target_timeline_id, horizon, pitr, cancel, ctx)
2679 738 : .await?;
2680 :
2681 754 : failpoint_support::sleep_millis_async!("gc_iteration_internal_after_getting_gc_timelines");
2682 :
2683 : // If there is nothing to GC, we don't want any messages in the INFO log.
2684 754 : if !gc_timelines.is_empty() {
2685 754 : info!("{} timelines need GC", gc_timelines.len());
2686 : } else {
2687 0 : debug!("{} timelines need GC", gc_timelines.len());
2688 : }
2689 :
2690 : // Perform GC for each timeline.
2691 : //
2692 : // Note that we don't hold the `Tenant::gc_cs` lock here because we don't want to delay the
2693 : // branch creation task, which requires the GC lock. A GC iteration can run concurrently
2694 : // with branch creation.
2695 : //
2696 : // See comments in [`Tenant::branch_timeline`] for more information about why branch
2697 : // creation task can run concurrently with timeline's GC iteration.
2698 1508 : for timeline in gc_timelines {
2699 754 : if cancel.is_cancelled() {
2700 : // We were requested to shut down. Stop and return with the progress we
2701 : // made.
2702 0 : break;
2703 754 : }
2704 754 : let result = match timeline.gc().await {
2705 : Err(GcError::TimelineCancelled) => {
2706 0 : if target_timeline_id.is_some() {
2707 : // If we were targetting this specific timeline, surface cancellation to caller
2708 0 : return Err(GcError::TimelineCancelled);
2709 : } else {
2710 : // A timeline may be shutting down independently of the tenant's lifecycle: we should
2711 : // skip past this and proceed to try GC on other timelines.
2712 0 : continue;
2713 : }
2714 : }
2715 754 : r => r?,
2716 : };
2717 754 : totals += result;
2718 : }
2719 :
2720 754 : totals.elapsed = now.elapsed();
2721 754 : Ok(totals)
2722 754 : }
2723 :
2724 : /// Refreshes the Timeline::gc_info for all timelines, returning the
2725 : /// vector of timelines which have [`Timeline::get_last_record_lsn`] past
2726 : /// [`Tenant::get_gc_horizon`].
2727 : ///
2728 : /// This is usually executed as part of periodic gc, but can now be triggered more often.
2729 0 : pub(crate) async fn refresh_gc_info(
2730 0 : &self,
2731 0 : cancel: &CancellationToken,
2732 0 : ctx: &RequestContext,
2733 0 : ) -> Result<Vec<Arc<Timeline>>, GcError> {
2734 0 : // since this method can now be called at different rates than the configured gc loop, it
2735 0 : // might be that these configuration values get applied faster than what it was previously,
2736 0 : // since these were only read from the gc task.
2737 0 : let horizon = self.get_gc_horizon();
2738 0 : let pitr = self.get_pitr_interval();
2739 0 :
2740 0 : // refresh all timelines
2741 0 : let target_timeline_id = None;
2742 0 :
2743 0 : self.refresh_gc_info_internal(target_timeline_id, horizon, pitr, cancel, ctx)
2744 0 : .await
2745 0 : }
2746 :
2747 754 : async fn refresh_gc_info_internal(
2748 754 : &self,
2749 754 : target_timeline_id: Option<TimelineId>,
2750 754 : horizon: u64,
2751 754 : pitr: Duration,
2752 754 : cancel: &CancellationToken,
2753 754 : ctx: &RequestContext,
2754 754 : ) -> Result<Vec<Arc<Timeline>>, GcError> {
2755 754 : // before taking the gc_cs lock, do the heavier weight finding of gc_cutoff points for
2756 754 : // currently visible timelines.
2757 754 : let timelines = self
2758 754 : .timelines
2759 754 : .lock()
2760 754 : .unwrap()
2761 754 : .values()
2762 3310 : .filter(|tl| match target_timeline_id.as_ref() {
2763 3310 : Some(target) => &tl.timeline_id == target,
2764 0 : None => true,
2765 3310 : })
2766 754 : .cloned()
2767 754 : .collect::<Vec<_>>();
2768 754 :
2769 754 : let mut gc_cutoffs: HashMap<TimelineId, GcCutoffs> =
2770 754 : HashMap::with_capacity(timelines.len());
2771 :
2772 754 : for timeline in timelines.iter() {
2773 754 : let cutoff = timeline
2774 754 : .get_last_record_lsn()
2775 754 : .checked_sub(horizon)
2776 754 : .unwrap_or(Lsn(0));
2777 :
2778 754 : let cutoffs = timeline.find_gc_cutoffs(cutoff, pitr, cancel, ctx).await?;
2779 754 : let old = gc_cutoffs.insert(timeline.timeline_id, cutoffs);
2780 754 : assert!(old.is_none());
2781 : }
2782 :
2783 754 : if !self.is_active() || self.cancel.is_cancelled() {
2784 0 : return Err(GcError::TenantCancelled);
2785 754 : }
2786 :
2787 : // grab mutex to prevent new timelines from being created here; avoid doing long operations
2788 : // because that will stall branch creation.
2789 754 : let gc_cs = self.gc_cs.lock().await;
2790 :
2791 : // Scan all timelines. For each timeline, remember the timeline ID and
2792 : // the branch point where it was created.
2793 754 : let (all_branchpoints, timelines): (BTreeSet<(TimelineId, Lsn)>, _) = {
2794 754 : let timelines = self.timelines.lock().unwrap();
2795 754 : let mut all_branchpoints = BTreeSet::new();
2796 754 : let timelines = {
2797 754 : if let Some(target_timeline_id) = target_timeline_id.as_ref() {
2798 754 : if timelines.get(target_timeline_id).is_none() {
2799 0 : return Err(GcError::TimelineNotFound);
2800 754 : }
2801 0 : };
2802 :
2803 754 : timelines
2804 754 : .iter()
2805 3310 : .map(|(_timeline_id, timeline_entry)| {
2806 2556 : if let Some(ancestor_timeline_id) =
2807 3310 : &timeline_entry.get_ancestor_timeline_id()
2808 : {
2809 : // If target_timeline is specified, we only need to know branchpoints of its children
2810 2556 : if let Some(timeline_id) = target_timeline_id {
2811 2556 : if ancestor_timeline_id == &timeline_id {
2812 6 : all_branchpoints.insert((
2813 6 : *ancestor_timeline_id,
2814 6 : timeline_entry.get_ancestor_lsn(),
2815 6 : ));
2816 2550 : }
2817 : }
2818 : // Collect branchpoints for all timelines
2819 0 : else {
2820 0 : all_branchpoints.insert((
2821 0 : *ancestor_timeline_id,
2822 0 : timeline_entry.get_ancestor_lsn(),
2823 0 : ));
2824 0 : }
2825 754 : }
2826 :
2827 3310 : timeline_entry.clone()
2828 3310 : })
2829 754 : .collect::<Vec<_>>()
2830 754 : };
2831 754 : (all_branchpoints, timelines)
2832 754 : };
2833 754 :
2834 754 : // Ok, we now know all the branch points.
2835 754 : // Update the GC information for each timeline.
2836 754 : let mut gc_timelines = Vec::with_capacity(timelines.len());
2837 4064 : for timeline in timelines {
2838 : // If target_timeline is specified, ignore all other timelines
2839 3310 : if let Some(target_timeline_id) = target_timeline_id {
2840 3310 : if timeline.timeline_id != target_timeline_id {
2841 2556 : continue;
2842 754 : }
2843 0 : }
2844 :
2845 754 : let branchpoints: Vec<Lsn> = all_branchpoints
2846 754 : .range((
2847 754 : Included((timeline.timeline_id, Lsn(0))),
2848 754 : Included((timeline.timeline_id, Lsn(u64::MAX))),
2849 754 : ))
2850 754 : .map(|&x| x.1)
2851 754 : .collect();
2852 754 :
2853 754 : {
2854 754 : let mut target = timeline.gc_info.write().unwrap();
2855 754 :
2856 754 : let now = SystemTime::now();
2857 754 : target.leases.retain(|_, lease| !lease.is_expired(&now));
2858 754 :
2859 754 : timeline
2860 754 : .metrics
2861 754 : .valid_lsn_lease_count_gauge
2862 754 : .set(target.leases.len() as u64);
2863 754 :
2864 754 : match gc_cutoffs.remove(&timeline.timeline_id) {
2865 754 : Some(cutoffs) => {
2866 754 : target.retain_lsns = branchpoints;
2867 754 : target.cutoffs = cutoffs;
2868 754 : }
2869 0 : None => {
2870 0 : // reasons for this being unavailable:
2871 0 : // - this timeline was created while we were finding cutoffs
2872 0 : // - lsn for timestamp search fails for this timeline repeatedly
2873 0 : //
2874 0 : // in both cases, refreshing the branchpoints is correct.
2875 0 : target.retain_lsns = branchpoints;
2876 0 : }
2877 : };
2878 : }
2879 :
2880 754 : gc_timelines.push(timeline);
2881 : }
2882 754 : drop(gc_cs);
2883 754 : Ok(gc_timelines)
2884 754 : }
2885 :
2886 : /// A substitute for `branch_timeline` for use in unit tests.
2887 : /// The returned timeline will have state value `Active` to make various `anyhow::ensure!()`
2888 : /// calls pass, but, we do not actually call `.activate()` under the hood. So, none of the
2889 : /// timeline background tasks are launched, except the flush loop.
2890 : #[cfg(test)]
2891 228 : async fn branch_timeline_test(
2892 228 : &self,
2893 228 : src_timeline: &Arc<Timeline>,
2894 228 : dst_id: TimelineId,
2895 228 : ancestor_lsn: Option<Lsn>,
2896 228 : ctx: &RequestContext,
2897 228 : ) -> Result<Arc<Timeline>, CreateTimelineError> {
2898 228 : let create_guard = self.create_timeline_create_guard(dst_id).unwrap();
2899 228 : let tl = self
2900 228 : .branch_timeline_impl(src_timeline, dst_id, ancestor_lsn, create_guard, ctx)
2901 4 : .await?;
2902 224 : tl.set_state(TimelineState::Active);
2903 224 : Ok(tl)
2904 228 : }
2905 :
2906 : /// Helper for unit tests to branch a timeline with some pre-loaded states.
2907 : #[cfg(test)]
2908 : #[allow(clippy::too_many_arguments)]
2909 4 : pub async fn branch_timeline_test_with_layers(
2910 4 : &self,
2911 4 : src_timeline: &Arc<Timeline>,
2912 4 : dst_id: TimelineId,
2913 4 : ancestor_lsn: Option<Lsn>,
2914 4 : ctx: &RequestContext,
2915 4 : delta_layer_desc: Vec<Vec<(pageserver_api::key::Key, Lsn, crate::repository::Value)>>,
2916 4 : image_layer_desc: Vec<(Lsn, Vec<(pageserver_api::key::Key, bytes::Bytes)>)>,
2917 4 : end_lsn: Lsn,
2918 4 : ) -> anyhow::Result<Arc<Timeline>> {
2919 4 : let tline = self
2920 4 : .branch_timeline_test(src_timeline, dst_id, ancestor_lsn, ctx)
2921 0 : .await?;
2922 4 : let ancestor_lsn = if let Some(ancestor_lsn) = ancestor_lsn {
2923 4 : ancestor_lsn
2924 : } else {
2925 0 : tline.get_last_record_lsn()
2926 : };
2927 4 : assert!(end_lsn >= ancestor_lsn);
2928 4 : tline.force_advance_lsn(end_lsn);
2929 4 : for deltas in delta_layer_desc {
2930 0 : tline
2931 0 : .force_create_delta_layer(deltas, Some(ancestor_lsn), ctx)
2932 0 : .await?;
2933 : }
2934 8 : for (lsn, images) in image_layer_desc {
2935 4 : tline
2936 4 : .force_create_image_layer(lsn, images, Some(ancestor_lsn), ctx)
2937 14 : .await?;
2938 : }
2939 4 : Ok(tline)
2940 4 : }
2941 :
2942 : /// Branch an existing timeline.
2943 : ///
2944 : /// The caller is responsible for activating the returned timeline.
2945 0 : async fn branch_timeline(
2946 0 : &self,
2947 0 : src_timeline: &Arc<Timeline>,
2948 0 : dst_id: TimelineId,
2949 0 : start_lsn: Option<Lsn>,
2950 0 : timeline_create_guard: TimelineCreateGuard<'_>,
2951 0 : ctx: &RequestContext,
2952 0 : ) -> Result<Arc<Timeline>, CreateTimelineError> {
2953 0 : self.branch_timeline_impl(src_timeline, dst_id, start_lsn, timeline_create_guard, ctx)
2954 0 : .await
2955 0 : }
2956 :
2957 228 : async fn branch_timeline_impl(
2958 228 : &self,
2959 228 : src_timeline: &Arc<Timeline>,
2960 228 : dst_id: TimelineId,
2961 228 : start_lsn: Option<Lsn>,
2962 228 : timeline_create_guard: TimelineCreateGuard<'_>,
2963 228 : _ctx: &RequestContext,
2964 228 : ) -> Result<Arc<Timeline>, CreateTimelineError> {
2965 228 : let src_id = src_timeline.timeline_id;
2966 :
2967 : // We will validate our ancestor LSN in this function. Acquire the GC lock so that
2968 : // this check cannot race with GC, and the ancestor LSN is guaranteed to remain
2969 : // valid while we are creating the branch.
2970 228 : let _gc_cs = self.gc_cs.lock().await;
2971 :
2972 : // If no start LSN is specified, we branch the new timeline from the source timeline's last record LSN
2973 228 : let start_lsn = start_lsn.unwrap_or_else(|| {
2974 2 : let lsn = src_timeline.get_last_record_lsn();
2975 2 : info!("branching timeline {dst_id} from timeline {src_id} at last record LSN: {lsn}");
2976 2 : lsn
2977 228 : });
2978 228 :
2979 228 : // Ensure that `start_lsn` is valid, i.e. the LSN is within the PITR
2980 228 : // horizon on the source timeline
2981 228 : //
2982 228 : // We check it against both the planned GC cutoff stored in 'gc_info',
2983 228 : // and the 'latest_gc_cutoff' of the last GC that was performed. The
2984 228 : // planned GC cutoff in 'gc_info' is normally larger than
2985 228 : // 'latest_gc_cutoff_lsn', but beware of corner cases like if you just
2986 228 : // changed the GC settings for the tenant to make the PITR window
2987 228 : // larger, but some of the data was already removed by an earlier GC
2988 228 : // iteration.
2989 228 :
2990 228 : // check against last actual 'latest_gc_cutoff' first
2991 228 : let latest_gc_cutoff_lsn = src_timeline.get_latest_gc_cutoff_lsn();
2992 228 : src_timeline
2993 228 : .check_lsn_is_in_scope(start_lsn, &latest_gc_cutoff_lsn)
2994 228 : .context(format!(
2995 228 : "invalid branch start lsn: less than latest GC cutoff {}",
2996 228 : *latest_gc_cutoff_lsn,
2997 228 : ))
2998 228 : .map_err(CreateTimelineError::AncestorLsn)?;
2999 :
3000 : // and then the planned GC cutoff
3001 : {
3002 224 : let gc_info = src_timeline.gc_info.read().unwrap();
3003 224 : let cutoff = gc_info.min_cutoff();
3004 224 : if start_lsn < cutoff {
3005 0 : return Err(CreateTimelineError::AncestorLsn(anyhow::anyhow!(
3006 0 : "invalid branch start lsn: less than planned GC cutoff {cutoff}"
3007 0 : )));
3008 224 : }
3009 224 : }
3010 224 :
3011 224 : //
3012 224 : // The branch point is valid, and we are still holding the 'gc_cs' lock
3013 224 : // so that GC cannot advance the GC cutoff until we are finished.
3014 224 : // Proceed with the branch creation.
3015 224 : //
3016 224 :
3017 224 : // Determine prev-LSN for the new timeline. We can only determine it if
3018 224 : // the timeline was branched at the current end of the source timeline.
3019 224 : let RecordLsn {
3020 224 : last: src_last,
3021 224 : prev: src_prev,
3022 224 : } = src_timeline.get_last_record_rlsn();
3023 224 : let dst_prev = if src_last == start_lsn {
3024 214 : Some(src_prev)
3025 : } else {
3026 10 : None
3027 : };
3028 :
3029 : // Create the metadata file, noting the ancestor of the new timeline.
3030 : // There is initially no data in it, but all the read-calls know to look
3031 : // into the ancestor.
3032 224 : let metadata = TimelineMetadata::new(
3033 224 : start_lsn,
3034 224 : dst_prev,
3035 224 : Some(src_id),
3036 224 : start_lsn,
3037 224 : *src_timeline.latest_gc_cutoff_lsn.read(), // FIXME: should we hold onto this guard longer?
3038 224 : src_timeline.initdb_lsn,
3039 224 : src_timeline.pg_version,
3040 224 : );
3041 :
3042 224 : let uninitialized_timeline = self
3043 224 : .prepare_new_timeline(
3044 224 : dst_id,
3045 224 : &metadata,
3046 224 : timeline_create_guard,
3047 224 : start_lsn + 1,
3048 224 : Some(Arc::clone(src_timeline)),
3049 224 : src_timeline.last_aux_file_policy.load(),
3050 224 : )
3051 0 : .await?;
3052 :
3053 224 : let new_timeline = uninitialized_timeline.finish_creation()?;
3054 :
3055 : // Root timeline gets its layers during creation and uploads them along with the metadata.
3056 : // A branch timeline though, when created, can get no writes for some time, hence won't get any layers created.
3057 : // We still need to upload its metadata eagerly: if other nodes `attach` the tenant and miss this timeline, their GC
3058 : // could get incorrect information and remove more layers, than needed.
3059 : // See also https://github.com/neondatabase/neon/issues/3865
3060 224 : new_timeline
3061 224 : .remote_client
3062 224 : .schedule_index_upload_for_full_metadata_update(&metadata)
3063 224 : .context("branch initial metadata upload")?;
3064 :
3065 224 : Ok(new_timeline)
3066 228 : }
3067 :
3068 : /// For unit tests, make this visible so that other modules can directly create timelines
3069 : #[cfg(test)]
3070 4 : #[tracing::instrument(skip_all, fields(tenant_id=%self.tenant_shard_id.tenant_id, shard_id=%self.tenant_shard_id.shard_slug(), %timeline_id))]
3071 : pub(crate) async fn bootstrap_timeline_test(
3072 : &self,
3073 : timeline_id: TimelineId,
3074 : pg_version: u32,
3075 : load_existing_initdb: Option<TimelineId>,
3076 : ctx: &RequestContext,
3077 : ) -> anyhow::Result<Arc<Timeline>> {
3078 : let create_guard = self.create_timeline_create_guard(timeline_id).unwrap();
3079 : self.bootstrap_timeline(
3080 : timeline_id,
3081 : pg_version,
3082 : load_existing_initdb,
3083 : create_guard,
3084 : ctx,
3085 : )
3086 : .await
3087 : }
3088 :
3089 0 : async fn upload_initdb(
3090 0 : &self,
3091 0 : timelines_path: &Utf8PathBuf,
3092 0 : pgdata_path: &Utf8PathBuf,
3093 0 : timeline_id: &TimelineId,
3094 0 : ) -> anyhow::Result<()> {
3095 0 : let temp_path = timelines_path.join(format!(
3096 0 : "{INITDB_PATH}.upload-{timeline_id}.{TEMP_FILE_SUFFIX}"
3097 0 : ));
3098 :
3099 : scopeguard::defer! {
3100 : if let Err(e) = fs::remove_file(&temp_path) {
3101 : error!("Failed to remove temporary initdb archive '{temp_path}': {e}");
3102 : }
3103 : }
3104 :
3105 0 : let (pgdata_zstd, tar_zst_size) = create_zst_tarball(pgdata_path, &temp_path).await?;
3106 : const INITDB_TAR_ZST_WARN_LIMIT: u64 = 2 * 1024 * 1024;
3107 0 : if tar_zst_size > INITDB_TAR_ZST_WARN_LIMIT {
3108 0 : warn!(
3109 0 : "compressed {temp_path} size of {tar_zst_size} is above limit {INITDB_TAR_ZST_WARN_LIMIT}."
3110 : );
3111 0 : }
3112 :
3113 : pausable_failpoint!("before-initdb-upload");
3114 :
3115 0 : backoff::retry(
3116 0 : || async {
3117 0 : self::remote_timeline_client::upload_initdb_dir(
3118 0 : &self.remote_storage,
3119 0 : &self.tenant_shard_id.tenant_id,
3120 0 : timeline_id,
3121 0 : pgdata_zstd.try_clone().await?,
3122 0 : tar_zst_size,
3123 0 : &self.cancel,
3124 0 : )
3125 0 : .await
3126 0 : },
3127 0 : |_| false,
3128 0 : 3,
3129 0 : u32::MAX,
3130 0 : "persist_initdb_tar_zst",
3131 0 : &self.cancel,
3132 0 : )
3133 0 : .await
3134 0 : .ok_or_else(|| anyhow::Error::new(TimeoutOrCancel::Cancel))
3135 0 : .and_then(|x| x)
3136 0 : }
3137 :
3138 : /// - run initdb to init temporary instance and get bootstrap data
3139 : /// - after initialization completes, tar up the temp dir and upload it to S3.
3140 : ///
3141 : /// The caller is responsible for activating the returned timeline.
3142 2 : async fn bootstrap_timeline(
3143 2 : &self,
3144 2 : timeline_id: TimelineId,
3145 2 : pg_version: u32,
3146 2 : load_existing_initdb: Option<TimelineId>,
3147 2 : timeline_create_guard: TimelineCreateGuard<'_>,
3148 2 : ctx: &RequestContext,
3149 2 : ) -> anyhow::Result<Arc<Timeline>> {
3150 2 : // create a `tenant/{tenant_id}/timelines/basebackup-{timeline_id}.{TEMP_FILE_SUFFIX}/`
3151 2 : // temporary directory for basebackup files for the given timeline.
3152 2 :
3153 2 : let timelines_path = self.conf.timelines_path(&self.tenant_shard_id);
3154 2 : let pgdata_path = path_with_suffix_extension(
3155 2 : timelines_path.join(format!("basebackup-{timeline_id}")),
3156 2 : TEMP_FILE_SUFFIX,
3157 2 : );
3158 2 :
3159 2 : // Remove whatever was left from the previous runs: safe because TimelineCreateGuard guarantees
3160 2 : // we won't race with other creations or existent timelines with the same path.
3161 2 : if pgdata_path.exists() {
3162 0 : fs::remove_dir_all(&pgdata_path).with_context(|| {
3163 0 : format!("Failed to remove already existing initdb directory: {pgdata_path}")
3164 0 : })?;
3165 2 : }
3166 :
3167 : // this new directory is very temporary, set to remove it immediately after bootstrap, we don't need it
3168 : scopeguard::defer! {
3169 : if let Err(e) = fs::remove_dir_all(&pgdata_path) {
3170 : // this is unlikely, but we will remove the directory on pageserver restart or another bootstrap call
3171 : error!("Failed to remove temporary initdb directory '{pgdata_path}': {e}");
3172 : }
3173 : }
3174 2 : if let Some(existing_initdb_timeline_id) = load_existing_initdb {
3175 2 : if existing_initdb_timeline_id != timeline_id {
3176 0 : let source_path = &remote_initdb_archive_path(
3177 0 : &self.tenant_shard_id.tenant_id,
3178 0 : &existing_initdb_timeline_id,
3179 0 : );
3180 0 : let dest_path =
3181 0 : &remote_initdb_archive_path(&self.tenant_shard_id.tenant_id, &timeline_id);
3182 0 :
3183 0 : // if this fails, it will get retried by retried control plane requests
3184 0 : self.remote_storage
3185 0 : .copy_object(source_path, dest_path, &self.cancel)
3186 0 : .await
3187 0 : .context("copy initdb tar")?;
3188 2 : }
3189 2 : let (initdb_tar_zst_path, initdb_tar_zst) =
3190 2 : self::remote_timeline_client::download_initdb_tar_zst(
3191 2 : self.conf,
3192 2 : &self.remote_storage,
3193 2 : &self.tenant_shard_id,
3194 2 : &existing_initdb_timeline_id,
3195 2 : &self.cancel,
3196 2 : )
3197 735 : .await
3198 2 : .context("download initdb tar")?;
3199 :
3200 : scopeguard::defer! {
3201 : if let Err(e) = fs::remove_file(&initdb_tar_zst_path) {
3202 : error!("Failed to remove temporary initdb archive '{initdb_tar_zst_path}': {e}");
3203 : }
3204 : }
3205 :
3206 2 : let buf_read =
3207 2 : BufReader::with_capacity(remote_timeline_client::BUFFER_SIZE, initdb_tar_zst);
3208 2 : extract_zst_tarball(&pgdata_path, buf_read)
3209 11166 : .await
3210 2 : .context("extract initdb tar")?;
3211 : } else {
3212 : // Init temporarily repo to get bootstrap data, this creates a directory in the `pgdata_path` path
3213 0 : run_initdb(self.conf, &pgdata_path, pg_version, &self.cancel).await?;
3214 :
3215 : // Upload the created data dir to S3
3216 0 : if self.tenant_shard_id().is_shard_zero() {
3217 0 : self.upload_initdb(&timelines_path, &pgdata_path, &timeline_id)
3218 0 : .await?;
3219 0 : }
3220 : }
3221 2 : let pgdata_lsn = import_datadir::get_lsn_from_controlfile(&pgdata_path)?.align();
3222 2 :
3223 2 : // Import the contents of the data directory at the initial checkpoint
3224 2 : // LSN, and any WAL after that.
3225 2 : // Initdb lsn will be equal to last_record_lsn which will be set after import.
3226 2 : // Because we know it upfront avoid having an option or dummy zero value by passing it to the metadata.
3227 2 : let new_metadata = TimelineMetadata::new(
3228 2 : Lsn(0),
3229 2 : None,
3230 2 : None,
3231 2 : Lsn(0),
3232 2 : pgdata_lsn,
3233 2 : pgdata_lsn,
3234 2 : pg_version,
3235 2 : );
3236 2 : let raw_timeline = self
3237 2 : .prepare_new_timeline(
3238 2 : timeline_id,
3239 2 : &new_metadata,
3240 2 : timeline_create_guard,
3241 2 : pgdata_lsn,
3242 2 : None,
3243 2 : None,
3244 2 : )
3245 0 : .await?;
3246 :
3247 2 : let tenant_shard_id = raw_timeline.owning_tenant.tenant_shard_id;
3248 2 : let unfinished_timeline = raw_timeline.raw_timeline()?;
3249 :
3250 : // Flush the new layer files to disk, before we make the timeline as available to
3251 : // the outside world.
3252 : //
3253 : // Flush loop needs to be spawned in order to be able to flush.
3254 2 : unfinished_timeline.maybe_spawn_flush_loop();
3255 2 :
3256 2 : import_datadir::import_timeline_from_postgres_datadir(
3257 2 : unfinished_timeline,
3258 2 : &pgdata_path,
3259 2 : pgdata_lsn,
3260 2 : ctx,
3261 2 : )
3262 8776 : .await
3263 2 : .with_context(|| {
3264 0 : format!("Failed to import pgdatadir for timeline {tenant_shard_id}/{timeline_id}")
3265 2 : })?;
3266 :
3267 2 : fail::fail_point!("before-checkpoint-new-timeline", |_| {
3268 0 : anyhow::bail!("failpoint before-checkpoint-new-timeline");
3269 2 : });
3270 :
3271 2 : unfinished_timeline
3272 2 : .freeze_and_flush()
3273 2 : .await
3274 2 : .with_context(|| {
3275 0 : format!(
3276 0 : "Failed to flush after pgdatadir import for timeline {tenant_shard_id}/{timeline_id}"
3277 0 : )
3278 2 : })?;
3279 :
3280 : // All done!
3281 2 : let timeline = raw_timeline.finish_creation()?;
3282 :
3283 2 : Ok(timeline)
3284 2 : }
3285 :
3286 : /// Call this before constructing a timeline, to build its required structures
3287 377 : fn build_timeline_resources(&self, timeline_id: TimelineId) -> TimelineResources {
3288 377 : let remote_client = RemoteTimelineClient::new(
3289 377 : self.remote_storage.clone(),
3290 377 : self.deletion_queue_client.clone(),
3291 377 : self.conf,
3292 377 : self.tenant_shard_id,
3293 377 : timeline_id,
3294 377 : self.generation,
3295 377 : );
3296 377 : TimelineResources {
3297 377 : remote_client,
3298 377 : timeline_get_throttle: self.timeline_get_throttle.clone(),
3299 377 : }
3300 377 : }
3301 :
3302 : /// Creates intermediate timeline structure and its files.
3303 : ///
3304 : /// An empty layer map is initialized, and new data and WAL can be imported starting
3305 : /// at 'disk_consistent_lsn'. After any initial data has been imported, call
3306 : /// `finish_creation` to insert the Timeline into the timelines map.
3307 377 : async fn prepare_new_timeline<'a>(
3308 377 : &'a self,
3309 377 : new_timeline_id: TimelineId,
3310 377 : new_metadata: &TimelineMetadata,
3311 377 : create_guard: TimelineCreateGuard<'a>,
3312 377 : start_lsn: Lsn,
3313 377 : ancestor: Option<Arc<Timeline>>,
3314 377 : last_aux_file_policy: Option<AuxFilePolicy>,
3315 377 : ) -> anyhow::Result<UninitializedTimeline> {
3316 377 : let tenant_shard_id = self.tenant_shard_id;
3317 377 :
3318 377 : let resources = self.build_timeline_resources(new_timeline_id);
3319 377 : resources
3320 377 : .remote_client
3321 377 : .init_upload_queue_for_empty_remote(new_metadata)?;
3322 :
3323 377 : let timeline_struct = self
3324 377 : .create_timeline_struct(
3325 377 : new_timeline_id,
3326 377 : new_metadata,
3327 377 : ancestor,
3328 377 : resources,
3329 377 : CreateTimelineCause::Load,
3330 377 : last_aux_file_policy,
3331 377 : )
3332 377 : .context("Failed to create timeline data structure")?;
3333 :
3334 377 : timeline_struct.init_empty_layer_map(start_lsn);
3335 :
3336 377 : if let Err(e) = self
3337 377 : .create_timeline_files(&create_guard.timeline_path)
3338 0 : .await
3339 : {
3340 1 : error!("Failed to create initial files for timeline {tenant_shard_id}/{new_timeline_id}, cleaning up: {e:?}");
3341 1 : cleanup_timeline_directory(create_guard);
3342 1 : return Err(e);
3343 376 : }
3344 376 :
3345 376 : debug!(
3346 0 : "Successfully created initial files for timeline {tenant_shard_id}/{new_timeline_id}"
3347 : );
3348 :
3349 376 : Ok(UninitializedTimeline::new(
3350 376 : self,
3351 376 : new_timeline_id,
3352 376 : Some((timeline_struct, create_guard)),
3353 376 : ))
3354 377 : }
3355 :
3356 377 : async fn create_timeline_files(&self, timeline_path: &Utf8Path) -> anyhow::Result<()> {
3357 377 : crashsafe::create_dir(timeline_path).context("Failed to create timeline directory")?;
3358 :
3359 376 : fail::fail_point!("after-timeline-dir-creation", |_| {
3360 0 : anyhow::bail!("failpoint after-timeline-dir-creation");
3361 376 : });
3362 :
3363 376 : Ok(())
3364 377 : }
3365 :
3366 : /// Get a guard that provides exclusive access to the timeline directory, preventing
3367 : /// concurrent attempts to create the same timeline.
3368 383 : fn create_timeline_create_guard(
3369 383 : &self,
3370 383 : timeline_id: TimelineId,
3371 383 : ) -> Result<TimelineCreateGuard, TimelineExclusionError> {
3372 383 : let tenant_shard_id = self.tenant_shard_id;
3373 383 :
3374 383 : let timeline_path = self.conf.timeline_path(&tenant_shard_id, &timeline_id);
3375 :
3376 383 : let create_guard = TimelineCreateGuard::new(self, timeline_id, timeline_path.clone())?;
3377 :
3378 : // At this stage, we have got exclusive access to in-memory state for this timeline ID
3379 : // for creation.
3380 : // A timeline directory should never exist on disk already:
3381 : // - a previous failed creation would have cleaned up after itself
3382 : // - a pageserver restart would clean up timeline directories that don't have valid remote state
3383 : //
3384 : // Therefore it is an unexpected internal error to encounter a timeline directory already existing here,
3385 : // this error may indicate a bug in cleanup on failed creations.
3386 381 : if timeline_path.exists() {
3387 0 : return Err(TimelineExclusionError::Other(anyhow::anyhow!(
3388 0 : "Timeline directory already exists! This is a bug."
3389 0 : )));
3390 381 : }
3391 381 :
3392 381 : Ok(create_guard)
3393 383 : }
3394 :
3395 : /// Gathers inputs from all of the timelines to produce a sizing model input.
3396 : ///
3397 : /// Future is cancellation safe. Only one calculation can be running at once per tenant.
3398 0 : #[instrument(skip_all, fields(tenant_id=%self.tenant_shard_id.tenant_id, shard_id=%self.tenant_shard_id.shard_slug()))]
3399 : pub async fn gather_size_inputs(
3400 : &self,
3401 : // `max_retention_period` overrides the cutoff that is used to calculate the size
3402 : // (only if it is shorter than the real cutoff).
3403 : max_retention_period: Option<u64>,
3404 : cause: LogicalSizeCalculationCause,
3405 : cancel: &CancellationToken,
3406 : ctx: &RequestContext,
3407 : ) -> Result<size::ModelInputs, size::CalculateSyntheticSizeError> {
3408 : let logical_sizes_at_once = self
3409 : .conf
3410 : .concurrent_tenant_size_logical_size_queries
3411 : .inner();
3412 :
3413 : // TODO: Having a single mutex block concurrent reads is not great for performance.
3414 : //
3415 : // But the only case where we need to run multiple of these at once is when we
3416 : // request a size for a tenant manually via API, while another background calculation
3417 : // is in progress (which is not a common case).
3418 : //
3419 : // See more for on the issue #2748 condenced out of the initial PR review.
3420 : let mut shared_cache = tokio::select! {
3421 : locked = self.cached_logical_sizes.lock() => locked,
3422 : _ = cancel.cancelled() => return Err(size::CalculateSyntheticSizeError::Cancelled),
3423 : _ = self.cancel.cancelled() => return Err(size::CalculateSyntheticSizeError::Cancelled),
3424 : };
3425 :
3426 : size::gather_inputs(
3427 : self,
3428 : logical_sizes_at_once,
3429 : max_retention_period,
3430 : &mut shared_cache,
3431 : cause,
3432 : cancel,
3433 : ctx,
3434 : )
3435 : .await
3436 : }
3437 :
3438 : /// Calculate synthetic tenant size and cache the result.
3439 : /// This is periodically called by background worker.
3440 : /// result is cached in tenant struct
3441 0 : #[instrument(skip_all, fields(tenant_id=%self.tenant_shard_id.tenant_id, shard_id=%self.tenant_shard_id.shard_slug()))]
3442 : pub async fn calculate_synthetic_size(
3443 : &self,
3444 : cause: LogicalSizeCalculationCause,
3445 : cancel: &CancellationToken,
3446 : ctx: &RequestContext,
3447 : ) -> Result<u64, size::CalculateSyntheticSizeError> {
3448 : let inputs = self.gather_size_inputs(None, cause, cancel, ctx).await?;
3449 :
3450 : let size = inputs.calculate();
3451 :
3452 : self.set_cached_synthetic_size(size);
3453 :
3454 : Ok(size)
3455 : }
3456 :
3457 : /// Cache given synthetic size and update the metric value
3458 0 : pub fn set_cached_synthetic_size(&self, size: u64) {
3459 0 : self.cached_synthetic_tenant_size
3460 0 : .store(size, Ordering::Relaxed);
3461 0 :
3462 0 : // Only shard zero should be calculating synthetic sizes
3463 0 : debug_assert!(self.shard_identity.is_shard_zero());
3464 :
3465 0 : TENANT_SYNTHETIC_SIZE_METRIC
3466 0 : .get_metric_with_label_values(&[&self.tenant_shard_id.tenant_id.to_string()])
3467 0 : .unwrap()
3468 0 : .set(size);
3469 0 : }
3470 :
3471 0 : pub fn cached_synthetic_size(&self) -> u64 {
3472 0 : self.cached_synthetic_tenant_size.load(Ordering::Relaxed)
3473 0 : }
3474 :
3475 : /// Flush any in-progress layers, schedule uploads, and wait for uploads to complete.
3476 : ///
3477 : /// This function can take a long time: callers should wrap it in a timeout if calling
3478 : /// from an external API handler.
3479 : ///
3480 : /// Cancel-safety: cancelling this function may leave I/O running, but such I/O is
3481 : /// still bounded by tenant/timeline shutdown.
3482 0 : #[tracing::instrument(skip_all)]
3483 : pub(crate) async fn flush_remote(&self) -> anyhow::Result<()> {
3484 : let timelines = self.timelines.lock().unwrap().clone();
3485 :
3486 0 : async fn flush_timeline(_gate: GateGuard, timeline: Arc<Timeline>) -> anyhow::Result<()> {
3487 0 : tracing::info!(timeline_id=%timeline.timeline_id, "Flushing...");
3488 0 : timeline.freeze_and_flush().await?;
3489 0 : tracing::info!(timeline_id=%timeline.timeline_id, "Waiting for uploads...");
3490 0 : timeline.remote_client.wait_completion().await?;
3491 :
3492 0 : Ok(())
3493 0 : }
3494 :
3495 : // We do not use a JoinSet for these tasks, because we don't want them to be
3496 : // aborted when this function's future is cancelled: they should stay alive
3497 : // holding their GateGuard until they complete, to ensure their I/Os complete
3498 : // before Timeline shutdown completes.
3499 : let mut results = FuturesUnordered::new();
3500 :
3501 : for (_timeline_id, timeline) in timelines {
3502 : // Run each timeline's flush in a task holding the timeline's gate: this
3503 : // means that if this function's future is cancelled, the Timeline shutdown
3504 : // will still wait for any I/O in here to complete.
3505 : let Ok(gate) = timeline.gate.enter() else {
3506 : continue;
3507 : };
3508 0 : let jh = tokio::task::spawn(async move { flush_timeline(gate, timeline).await });
3509 : results.push(jh);
3510 : }
3511 :
3512 : while let Some(r) = results.next().await {
3513 : if let Err(e) = r {
3514 : if !e.is_cancelled() && !e.is_panic() {
3515 : tracing::error!("unexpected join error: {e:?}");
3516 : }
3517 : }
3518 : }
3519 :
3520 : // The flushes we did above were just writes, but the Tenant might have had
3521 : // pending deletions as well from recent compaction/gc: we want to flush those
3522 : // as well. This requires flushing the global delete queue. This is cheap
3523 : // because it's typically a no-op.
3524 : match self.deletion_queue_client.flush_execute().await {
3525 : Ok(_) => {}
3526 : Err(DeletionQueueError::ShuttingDown) => {}
3527 : }
3528 :
3529 : Ok(())
3530 : }
3531 :
3532 0 : pub(crate) fn get_tenant_conf(&self) -> TenantConfOpt {
3533 0 : self.tenant_conf.load().tenant_conf.clone()
3534 0 : }
3535 : }
3536 :
3537 : /// Create the cluster temporarily in 'initdbpath' directory inside the repository
3538 : /// to get bootstrap data for timeline initialization.
3539 0 : async fn run_initdb(
3540 0 : conf: &'static PageServerConf,
3541 0 : initdb_target_dir: &Utf8Path,
3542 0 : pg_version: u32,
3543 0 : cancel: &CancellationToken,
3544 0 : ) -> Result<(), InitdbError> {
3545 0 : let initdb_bin_path = conf
3546 0 : .pg_bin_dir(pg_version)
3547 0 : .map_err(InitdbError::Other)?
3548 0 : .join("initdb");
3549 0 : let initdb_lib_dir = conf.pg_lib_dir(pg_version).map_err(InitdbError::Other)?;
3550 0 : info!(
3551 0 : "running {} in {}, libdir: {}",
3552 : initdb_bin_path, initdb_target_dir, initdb_lib_dir,
3553 : );
3554 :
3555 0 : let _permit = INIT_DB_SEMAPHORE.acquire().await;
3556 :
3557 0 : let initdb_command = tokio::process::Command::new(&initdb_bin_path)
3558 0 : .args(["-D", initdb_target_dir.as_ref()])
3559 0 : .args(["-U", &conf.superuser])
3560 0 : .args(["-E", "utf8"])
3561 0 : .arg("--no-instructions")
3562 0 : .arg("--no-sync")
3563 0 : .env_clear()
3564 0 : .env("LD_LIBRARY_PATH", &initdb_lib_dir)
3565 0 : .env("DYLD_LIBRARY_PATH", &initdb_lib_dir)
3566 0 : .stdin(std::process::Stdio::null())
3567 0 : // stdout invocation produces the same output every time, we don't need it
3568 0 : .stdout(std::process::Stdio::null())
3569 0 : // we would be interested in the stderr output, if there was any
3570 0 : .stderr(std::process::Stdio::piped())
3571 0 : .spawn()?;
3572 :
3573 : // Ideally we'd select here with the cancellation token, but the problem is that
3574 : // we can't safely terminate initdb: it launches processes of its own, and killing
3575 : // initdb doesn't kill them. After we return from this function, we want the target
3576 : // directory to be able to be cleaned up.
3577 : // See https://github.com/neondatabase/neon/issues/6385
3578 0 : let initdb_output = initdb_command.wait_with_output().await?;
3579 0 : if !initdb_output.status.success() {
3580 0 : return Err(InitdbError::Failed(
3581 0 : initdb_output.status,
3582 0 : initdb_output.stderr,
3583 0 : ));
3584 0 : }
3585 0 :
3586 0 : // This isn't true cancellation support, see above. Still return an error to
3587 0 : // excercise the cancellation code path.
3588 0 : if cancel.is_cancelled() {
3589 0 : return Err(InitdbError::Cancelled);
3590 0 : }
3591 0 :
3592 0 : Ok(())
3593 0 : }
3594 :
3595 : /// Dump contents of a layer file to stdout.
3596 0 : pub async fn dump_layerfile_from_path(
3597 0 : path: &Utf8Path,
3598 0 : verbose: bool,
3599 0 : ctx: &RequestContext,
3600 0 : ) -> anyhow::Result<()> {
3601 : use std::os::unix::fs::FileExt;
3602 :
3603 : // All layer files start with a two-byte "magic" value, to identify the kind of
3604 : // file.
3605 0 : let file = File::open(path)?;
3606 0 : let mut header_buf = [0u8; 2];
3607 0 : file.read_exact_at(&mut header_buf, 0)?;
3608 :
3609 0 : match u16::from_be_bytes(header_buf) {
3610 : crate::IMAGE_FILE_MAGIC => {
3611 0 : ImageLayer::new_for_path(path, file)?
3612 0 : .dump(verbose, ctx)
3613 0 : .await?
3614 : }
3615 : crate::DELTA_FILE_MAGIC => {
3616 0 : DeltaLayer::new_for_path(path, file)?
3617 0 : .dump(verbose, ctx)
3618 0 : .await?
3619 : }
3620 0 : magic => bail!("unrecognized magic identifier: {:?}", magic),
3621 : }
3622 :
3623 0 : Ok(())
3624 0 : }
3625 :
3626 : #[cfg(test)]
3627 : pub(crate) mod harness {
3628 : use bytes::{Bytes, BytesMut};
3629 : use once_cell::sync::OnceCell;
3630 : use pageserver_api::models::ShardParameters;
3631 : use pageserver_api::shard::ShardIndex;
3632 : use utils::logging;
3633 :
3634 : use crate::deletion_queue::mock::MockDeletionQueue;
3635 : use crate::walredo::apply_neon;
3636 : use crate::{repository::Key, walrecord::NeonWalRecord};
3637 :
3638 : use super::*;
3639 : use hex_literal::hex;
3640 : use utils::id::TenantId;
3641 :
3642 : pub const TIMELINE_ID: TimelineId =
3643 : TimelineId::from_array(hex!("11223344556677881122334455667788"));
3644 : pub const NEW_TIMELINE_ID: TimelineId =
3645 : TimelineId::from_array(hex!("AA223344556677881122334455667788"));
3646 :
3647 : /// Convenience function to create a page image with given string as the only content
3648 5028378 : pub fn test_img(s: &str) -> Bytes {
3649 5028378 : let mut buf = BytesMut::new();
3650 5028378 : buf.extend_from_slice(s.as_bytes());
3651 5028378 : buf.resize(64, 0);
3652 5028378 :
3653 5028378 : buf.freeze()
3654 5028378 : }
3655 :
3656 : impl From<TenantConf> for TenantConfOpt {
3657 161 : fn from(tenant_conf: TenantConf) -> Self {
3658 161 : Self {
3659 161 : checkpoint_distance: Some(tenant_conf.checkpoint_distance),
3660 161 : checkpoint_timeout: Some(tenant_conf.checkpoint_timeout),
3661 161 : compaction_target_size: Some(tenant_conf.compaction_target_size),
3662 161 : compaction_period: Some(tenant_conf.compaction_period),
3663 161 : compaction_threshold: Some(tenant_conf.compaction_threshold),
3664 161 : compaction_algorithm: Some(tenant_conf.compaction_algorithm),
3665 161 : gc_horizon: Some(tenant_conf.gc_horizon),
3666 161 : gc_period: Some(tenant_conf.gc_period),
3667 161 : image_creation_threshold: Some(tenant_conf.image_creation_threshold),
3668 161 : pitr_interval: Some(tenant_conf.pitr_interval),
3669 161 : walreceiver_connect_timeout: Some(tenant_conf.walreceiver_connect_timeout),
3670 161 : lagging_wal_timeout: Some(tenant_conf.lagging_wal_timeout),
3671 161 : max_lsn_wal_lag: Some(tenant_conf.max_lsn_wal_lag),
3672 161 : trace_read_requests: Some(tenant_conf.trace_read_requests),
3673 161 : eviction_policy: Some(tenant_conf.eviction_policy),
3674 161 : min_resident_size_override: tenant_conf.min_resident_size_override,
3675 161 : evictions_low_residence_duration_metric_threshold: Some(
3676 161 : tenant_conf.evictions_low_residence_duration_metric_threshold,
3677 161 : ),
3678 161 : heatmap_period: Some(tenant_conf.heatmap_period),
3679 161 : lazy_slru_download: Some(tenant_conf.lazy_slru_download),
3680 161 : timeline_get_throttle: Some(tenant_conf.timeline_get_throttle),
3681 161 : image_layer_creation_check_threshold: Some(
3682 161 : tenant_conf.image_layer_creation_check_threshold,
3683 161 : ),
3684 161 : switch_aux_file_policy: Some(tenant_conf.switch_aux_file_policy),
3685 161 : lsn_lease_length: Some(tenant_conf.lsn_lease_length),
3686 161 : lsn_lease_length_for_ts: Some(tenant_conf.lsn_lease_length_for_ts),
3687 161 : }
3688 161 : }
3689 : }
3690 :
3691 : pub struct TenantHarness {
3692 : pub conf: &'static PageServerConf,
3693 : pub tenant_conf: TenantConf,
3694 : pub tenant_shard_id: TenantShardId,
3695 : pub generation: Generation,
3696 : pub shard: ShardIndex,
3697 : pub remote_storage: GenericRemoteStorage,
3698 : pub remote_fs_dir: Utf8PathBuf,
3699 : pub deletion_queue: MockDeletionQueue,
3700 : }
3701 :
3702 : static LOG_HANDLE: OnceCell<()> = OnceCell::new();
3703 :
3704 167 : pub(crate) fn setup_logging() {
3705 167 : LOG_HANDLE.get_or_init(|| {
3706 155 : logging::init(
3707 155 : logging::LogFormat::Test,
3708 155 : // enable it in case the tests exercise code paths that use
3709 155 : // debug_assert_current_span_has_tenant_and_timeline_id
3710 155 : logging::TracingErrorLayerEnablement::EnableWithRustLogFilter,
3711 155 : logging::Output::Stdout,
3712 155 : )
3713 155 : .expect("Failed to init test logging")
3714 167 : });
3715 167 : }
3716 :
3717 : impl TenantHarness {
3718 161 : pub fn create_custom(
3719 161 : test_name: &'static str,
3720 161 : tenant_conf: TenantConf,
3721 161 : tenant_id: TenantId,
3722 161 : shard_identity: ShardIdentity,
3723 161 : generation: Generation,
3724 161 : ) -> anyhow::Result<Self> {
3725 161 : setup_logging();
3726 161 :
3727 161 : let repo_dir = PageServerConf::test_repo_dir(test_name);
3728 161 : let _ = fs::remove_dir_all(&repo_dir);
3729 161 : fs::create_dir_all(&repo_dir)?;
3730 :
3731 161 : let conf = PageServerConf::dummy_conf(repo_dir);
3732 161 : // Make a static copy of the config. This can never be free'd, but that's
3733 161 : // OK in a test.
3734 161 : let conf: &'static PageServerConf = Box::leak(Box::new(conf));
3735 161 :
3736 161 : let shard = shard_identity.shard_index();
3737 161 : let tenant_shard_id = TenantShardId {
3738 161 : tenant_id,
3739 161 : shard_number: shard.shard_number,
3740 161 : shard_count: shard.shard_count,
3741 161 : };
3742 161 : fs::create_dir_all(conf.tenant_path(&tenant_shard_id))?;
3743 161 : fs::create_dir_all(conf.timelines_path(&tenant_shard_id))?;
3744 :
3745 : use remote_storage::{RemoteStorageConfig, RemoteStorageKind};
3746 161 : let remote_fs_dir = conf.workdir.join("localfs");
3747 161 : std::fs::create_dir_all(&remote_fs_dir).unwrap();
3748 161 : let config = RemoteStorageConfig {
3749 161 : storage: RemoteStorageKind::LocalFs {
3750 161 : local_path: remote_fs_dir.clone(),
3751 161 : },
3752 161 : timeout: RemoteStorageConfig::DEFAULT_TIMEOUT,
3753 161 : };
3754 161 : let remote_storage = GenericRemoteStorage::from_config(&config).unwrap();
3755 161 : let deletion_queue = MockDeletionQueue::new(Some(remote_storage.clone()));
3756 161 :
3757 161 : Ok(Self {
3758 161 : conf,
3759 161 : tenant_conf,
3760 161 : tenant_shard_id,
3761 161 : generation,
3762 161 : shard,
3763 161 : remote_storage,
3764 161 : remote_fs_dir,
3765 161 : deletion_queue,
3766 161 : })
3767 161 : }
3768 :
3769 149 : pub fn create(test_name: &'static str) -> anyhow::Result<Self> {
3770 149 : // Disable automatic GC and compaction to make the unit tests more deterministic.
3771 149 : // The tests perform them manually if needed.
3772 149 : let tenant_conf = TenantConf {
3773 149 : gc_period: Duration::ZERO,
3774 149 : compaction_period: Duration::ZERO,
3775 149 : ..TenantConf::default()
3776 149 : };
3777 149 : let tenant_id = TenantId::generate();
3778 149 : let shard = ShardIdentity::unsharded();
3779 149 : Self::create_custom(
3780 149 : test_name,
3781 149 : tenant_conf,
3782 149 : tenant_id,
3783 149 : shard,
3784 149 : Generation::new(0xdeadbeef),
3785 149 : )
3786 149 : }
3787 :
3788 18 : pub fn span(&self) -> tracing::Span {
3789 18 : info_span!("TenantHarness", tenant_id=%self.tenant_shard_id.tenant_id, shard_id=%self.tenant_shard_id.shard_slug())
3790 18 : }
3791 :
3792 161 : pub(crate) async fn load(&self) -> (Arc<Tenant>, RequestContext) {
3793 161 : let ctx = RequestContext::new(TaskKind::UnitTest, DownloadBehavior::Error);
3794 161 : (
3795 161 : self.do_try_load(&ctx)
3796 647 : .await
3797 161 : .expect("failed to load test tenant"),
3798 161 : ctx,
3799 161 : )
3800 161 : }
3801 :
3802 322 : #[instrument(skip_all, fields(tenant_id=%self.tenant_shard_id.tenant_id, shard_id=%self.tenant_shard_id.shard_slug()))]
3803 : pub(crate) async fn do_try_load(
3804 : &self,
3805 : ctx: &RequestContext,
3806 : ) -> anyhow::Result<Arc<Tenant>> {
3807 : let walredo_mgr = Arc::new(WalRedoManager::from(TestRedoManager));
3808 :
3809 : let tenant = Arc::new(Tenant::new(
3810 : TenantState::Loading,
3811 : self.conf,
3812 : AttachedTenantConf::try_from(LocationConf::attached_single(
3813 : TenantConfOpt::from(self.tenant_conf.clone()),
3814 : self.generation,
3815 : &ShardParameters::default(),
3816 : ))
3817 : .unwrap(),
3818 : // This is a legacy/test code path: sharding isn't supported here.
3819 : ShardIdentity::unsharded(),
3820 : Some(walredo_mgr),
3821 : self.tenant_shard_id,
3822 : self.remote_storage.clone(),
3823 : self.deletion_queue.new_client(),
3824 : ));
3825 :
3826 : let preload = tenant
3827 : .preload(&self.remote_storage, CancellationToken::new())
3828 : .await?;
3829 : tenant.attach(Some(preload), SpawnMode::Eager, ctx).await?;
3830 :
3831 : tenant.state.send_replace(TenantState::Active);
3832 : for timeline in tenant.timelines.lock().unwrap().values() {
3833 : timeline.set_state(TimelineState::Active);
3834 : }
3835 : Ok(tenant)
3836 : }
3837 :
3838 2 : pub fn timeline_path(&self, timeline_id: &TimelineId) -> Utf8PathBuf {
3839 2 : self.conf.timeline_path(&self.tenant_shard_id, timeline_id)
3840 2 : }
3841 : }
3842 :
3843 : // Mock WAL redo manager that doesn't do much
3844 : pub(crate) struct TestRedoManager;
3845 :
3846 : impl TestRedoManager {
3847 : /// # Cancel-Safety
3848 : ///
3849 : /// This method is cancellation-safe.
3850 58 : pub async fn request_redo(
3851 58 : &self,
3852 58 : key: Key,
3853 58 : lsn: Lsn,
3854 58 : base_img: Option<(Lsn, Bytes)>,
3855 58 : records: Vec<(Lsn, NeonWalRecord)>,
3856 58 : _pg_version: u32,
3857 58 : ) -> anyhow::Result<Bytes> {
3858 76 : let records_neon = records.iter().all(|r| apply_neon::can_apply_in_neon(&r.1));
3859 58 : if records_neon {
3860 : // For Neon wal records, we can decode without spawning postgres, so do so.
3861 58 : let base_img = base_img.expect("Neon WAL redo requires base image").1;
3862 58 : let mut page = BytesMut::new();
3863 58 : page.extend_from_slice(&base_img);
3864 134 : for (record_lsn, record) in records {
3865 76 : apply_neon::apply_in_neon(&record, record_lsn, key, &mut page)?;
3866 : }
3867 58 : Ok(page.freeze())
3868 : } else {
3869 : // We never spawn a postgres walredo process in unit tests: just log what we might have done.
3870 0 : let s = format!(
3871 0 : "redo for {} to get to {}, with {} and {} records",
3872 0 : key,
3873 0 : lsn,
3874 0 : if base_img.is_some() {
3875 0 : "base image"
3876 : } else {
3877 0 : "no base image"
3878 : },
3879 0 : records.len()
3880 0 : );
3881 0 : println!("{s}");
3882 0 :
3883 0 : Ok(test_img(&s))
3884 : }
3885 58 : }
3886 : }
3887 : }
3888 :
3889 : #[cfg(test)]
3890 : mod tests {
3891 : use std::collections::BTreeMap;
3892 :
3893 : use super::*;
3894 : use crate::keyspace::KeySpaceAccum;
3895 : use crate::pgdatadir_mapping::AuxFilesDirectory;
3896 : use crate::repository::{Key, Value};
3897 : use crate::tenant::harness::*;
3898 : use crate::tenant::timeline::CompactFlags;
3899 : use crate::walrecord::NeonWalRecord;
3900 : use crate::DEFAULT_PG_VERSION;
3901 : use bytes::{Bytes, BytesMut};
3902 : use hex_literal::hex;
3903 : use itertools::Itertools;
3904 : use pageserver_api::key::{AUX_FILES_KEY, AUX_KEY_PREFIX, NON_INHERITED_RANGE};
3905 : use pageserver_api::keyspace::KeySpace;
3906 : use pageserver_api::models::{CompactionAlgorithm, CompactionAlgorithmSettings};
3907 : use rand::{thread_rng, Rng};
3908 : use storage_layer::PersistentLayerKey;
3909 : use tests::storage_layer::ValuesReconstructState;
3910 : use tests::timeline::{GetVectoredError, ShutdownMode};
3911 : use timeline::GcInfo;
3912 : use utils::bin_ser::BeSer;
3913 : use utils::id::TenantId;
3914 :
3915 : static TEST_KEY: Lazy<Key> =
3916 18 : Lazy::new(|| Key::from_slice(&hex!("010000000033333333444444445500000001")));
3917 :
3918 : #[tokio::test]
3919 2 : async fn test_basic() -> anyhow::Result<()> {
3920 8 : let (tenant, ctx) = TenantHarness::create("test_basic")?.load().await;
3921 2 : let tline = tenant
3922 2 : .create_test_timeline(TIMELINE_ID, Lsn(0x08), DEFAULT_PG_VERSION, &ctx)
3923 6 : .await?;
3924 2 :
3925 2 : let mut writer = tline.writer().await;
3926 2 : writer
3927 2 : .put(
3928 2 : *TEST_KEY,
3929 2 : Lsn(0x10),
3930 2 : &Value::Image(test_img("foo at 0x10")),
3931 2 : &ctx,
3932 2 : )
3933 2 : .await?;
3934 2 : writer.finish_write(Lsn(0x10));
3935 2 : drop(writer);
3936 2 :
3937 2 : let mut writer = tline.writer().await;
3938 2 : writer
3939 2 : .put(
3940 2 : *TEST_KEY,
3941 2 : Lsn(0x20),
3942 2 : &Value::Image(test_img("foo at 0x20")),
3943 2 : &ctx,
3944 2 : )
3945 2 : .await?;
3946 2 : writer.finish_write(Lsn(0x20));
3947 2 : drop(writer);
3948 2 :
3949 2 : assert_eq!(
3950 2 : tline.get(*TEST_KEY, Lsn(0x10), &ctx).await?,
3951 2 : test_img("foo at 0x10")
3952 2 : );
3953 2 : assert_eq!(
3954 2 : tline.get(*TEST_KEY, Lsn(0x1f), &ctx).await?,
3955 2 : test_img("foo at 0x10")
3956 2 : );
3957 2 : assert_eq!(
3958 2 : tline.get(*TEST_KEY, Lsn(0x20), &ctx).await?,
3959 2 : test_img("foo at 0x20")
3960 2 : );
3961 2 :
3962 2 : Ok(())
3963 2 : }
3964 :
3965 : #[tokio::test]
3966 2 : async fn no_duplicate_timelines() -> anyhow::Result<()> {
3967 2 : let (tenant, ctx) = TenantHarness::create("no_duplicate_timelines")?
3968 2 : .load()
3969 8 : .await;
3970 2 : let _ = tenant
3971 2 : .create_test_timeline(TIMELINE_ID, Lsn(0x10), DEFAULT_PG_VERSION, &ctx)
3972 6 : .await?;
3973 2 :
3974 2 : match tenant
3975 2 : .create_empty_timeline(TIMELINE_ID, Lsn(0x10), DEFAULT_PG_VERSION, &ctx)
3976 2 : .await
3977 2 : {
3978 2 : Ok(_) => panic!("duplicate timeline creation should fail"),
3979 2 : Err(e) => assert_eq!(e.to_string(), "Already exists".to_string()),
3980 2 : }
3981 2 :
3982 2 : Ok(())
3983 2 : }
3984 :
3985 : /// Convenience function to create a page image with given string as the only content
3986 10 : pub fn test_value(s: &str) -> Value {
3987 10 : let mut buf = BytesMut::new();
3988 10 : buf.extend_from_slice(s.as_bytes());
3989 10 : Value::Image(buf.freeze())
3990 10 : }
3991 :
3992 : ///
3993 : /// Test branch creation
3994 : ///
3995 : #[tokio::test]
3996 2 : async fn test_branch() -> anyhow::Result<()> {
3997 2 : use std::str::from_utf8;
3998 2 :
3999 8 : let (tenant, ctx) = TenantHarness::create("test_branch")?.load().await;
4000 2 : let tline = tenant
4001 2 : .create_test_timeline(TIMELINE_ID, Lsn(0x10), DEFAULT_PG_VERSION, &ctx)
4002 6 : .await?;
4003 2 : let mut writer = tline.writer().await;
4004 2 :
4005 2 : #[allow(non_snake_case)]
4006 2 : let TEST_KEY_A: Key = Key::from_hex("110000000033333333444444445500000001").unwrap();
4007 2 : #[allow(non_snake_case)]
4008 2 : let TEST_KEY_B: Key = Key::from_hex("110000000033333333444444445500000002").unwrap();
4009 2 :
4010 2 : // Insert a value on the timeline
4011 2 : writer
4012 2 : .put(TEST_KEY_A, Lsn(0x20), &test_value("foo at 0x20"), &ctx)
4013 2 : .await?;
4014 2 : writer
4015 2 : .put(TEST_KEY_B, Lsn(0x20), &test_value("foobar at 0x20"), &ctx)
4016 2 : .await?;
4017 2 : writer.finish_write(Lsn(0x20));
4018 2 :
4019 2 : writer
4020 2 : .put(TEST_KEY_A, Lsn(0x30), &test_value("foo at 0x30"), &ctx)
4021 2 : .await?;
4022 2 : writer.finish_write(Lsn(0x30));
4023 2 : writer
4024 2 : .put(TEST_KEY_A, Lsn(0x40), &test_value("foo at 0x40"), &ctx)
4025 2 : .await?;
4026 2 : writer.finish_write(Lsn(0x40));
4027 2 :
4028 2 : //assert_current_logical_size(&tline, Lsn(0x40));
4029 2 :
4030 2 : // Branch the history, modify relation differently on the new timeline
4031 2 : tenant
4032 2 : .branch_timeline_test(&tline, NEW_TIMELINE_ID, Some(Lsn(0x30)), &ctx)
4033 2 : .await?;
4034 2 : let newtline = tenant
4035 2 : .get_timeline(NEW_TIMELINE_ID, true)
4036 2 : .expect("Should have a local timeline");
4037 2 : let mut new_writer = newtline.writer().await;
4038 2 : new_writer
4039 2 : .put(TEST_KEY_A, Lsn(0x40), &test_value("bar at 0x40"), &ctx)
4040 2 : .await?;
4041 2 : new_writer.finish_write(Lsn(0x40));
4042 2 :
4043 2 : // Check page contents on both branches
4044 2 : assert_eq!(
4045 2 : from_utf8(&tline.get(TEST_KEY_A, Lsn(0x40), &ctx).await?)?,
4046 2 : "foo at 0x40"
4047 2 : );
4048 2 : assert_eq!(
4049 2 : from_utf8(&newtline.get(TEST_KEY_A, Lsn(0x40), &ctx).await?)?,
4050 2 : "bar at 0x40"
4051 2 : );
4052 2 : assert_eq!(
4053 2 : from_utf8(&newtline.get(TEST_KEY_B, Lsn(0x40), &ctx).await?)?,
4054 2 : "foobar at 0x20"
4055 2 : );
4056 2 :
4057 2 : //assert_current_logical_size(&tline, Lsn(0x40));
4058 2 :
4059 2 : Ok(())
4060 2 : }
4061 :
4062 20 : async fn make_some_layers(
4063 20 : tline: &Timeline,
4064 20 : start_lsn: Lsn,
4065 20 : ctx: &RequestContext,
4066 20 : ) -> anyhow::Result<()> {
4067 20 : let mut lsn = start_lsn;
4068 : {
4069 20 : let mut writer = tline.writer().await;
4070 : // Create a relation on the timeline
4071 20 : writer
4072 20 : .put(
4073 20 : *TEST_KEY,
4074 20 : lsn,
4075 20 : &Value::Image(test_img(&format!("foo at {}", lsn))),
4076 20 : ctx,
4077 20 : )
4078 10 : .await?;
4079 20 : writer.finish_write(lsn);
4080 20 : lsn += 0x10;
4081 20 : writer
4082 20 : .put(
4083 20 : *TEST_KEY,
4084 20 : lsn,
4085 20 : &Value::Image(test_img(&format!("foo at {}", lsn))),
4086 20 : ctx,
4087 20 : )
4088 0 : .await?;
4089 20 : writer.finish_write(lsn);
4090 20 : lsn += 0x10;
4091 20 : }
4092 20 : tline.freeze_and_flush().await?;
4093 : {
4094 20 : let mut writer = tline.writer().await;
4095 20 : writer
4096 20 : .put(
4097 20 : *TEST_KEY,
4098 20 : lsn,
4099 20 : &Value::Image(test_img(&format!("foo at {}", lsn))),
4100 20 : ctx,
4101 20 : )
4102 10 : .await?;
4103 20 : writer.finish_write(lsn);
4104 20 : lsn += 0x10;
4105 20 : writer
4106 20 : .put(
4107 20 : *TEST_KEY,
4108 20 : lsn,
4109 20 : &Value::Image(test_img(&format!("foo at {}", lsn))),
4110 20 : ctx,
4111 20 : )
4112 0 : .await?;
4113 20 : writer.finish_write(lsn);
4114 20 : }
4115 20 : tline.freeze_and_flush().await.map_err(|e| e.into())
4116 20 : }
4117 :
4118 : #[tokio::test]
4119 2 : async fn test_prohibit_branch_creation_on_garbage_collected_data() -> anyhow::Result<()> {
4120 2 : let (tenant, ctx) =
4121 2 : TenantHarness::create("test_prohibit_branch_creation_on_garbage_collected_data")?
4122 2 : .load()
4123 8 : .await;
4124 2 : let tline = tenant
4125 2 : .create_test_timeline(TIMELINE_ID, Lsn(0x10), DEFAULT_PG_VERSION, &ctx)
4126 6 : .await?;
4127 6 : make_some_layers(tline.as_ref(), Lsn(0x20), &ctx).await?;
4128 2 :
4129 2 : // this removes layers before lsn 40 (50 minus 10), so there are two remaining layers, image and delta for 31-50
4130 2 : // FIXME: this doesn't actually remove any layer currently, given how the flushing
4131 2 : // and compaction works. But it does set the 'cutoff' point so that the cross check
4132 2 : // below should fail.
4133 2 : tenant
4134 2 : .gc_iteration(
4135 2 : Some(TIMELINE_ID),
4136 2 : 0x10,
4137 2 : Duration::ZERO,
4138 2 : &CancellationToken::new(),
4139 2 : &ctx,
4140 2 : )
4141 2 : .await?;
4142 2 :
4143 2 : // try to branch at lsn 25, should fail because we already garbage collected the data
4144 2 : match tenant
4145 2 : .branch_timeline_test(&tline, NEW_TIMELINE_ID, Some(Lsn(0x25)), &ctx)
4146 2 : .await
4147 2 : {
4148 2 : Ok(_) => panic!("branching should have failed"),
4149 2 : Err(err) => {
4150 2 : let CreateTimelineError::AncestorLsn(err) = err else {
4151 2 : panic!("wrong error type")
4152 2 : };
4153 2 : assert!(err.to_string().contains("invalid branch start lsn"));
4154 2 : assert!(err
4155 2 : .source()
4156 2 : .unwrap()
4157 2 : .to_string()
4158 2 : .contains("we might've already garbage collected needed data"))
4159 2 : }
4160 2 : }
4161 2 :
4162 2 : Ok(())
4163 2 : }
4164 :
4165 : #[tokio::test]
4166 2 : async fn test_prohibit_branch_creation_on_pre_initdb_lsn() -> anyhow::Result<()> {
4167 2 : let (tenant, ctx) =
4168 2 : TenantHarness::create("test_prohibit_branch_creation_on_pre_initdb_lsn")?
4169 2 : .load()
4170 8 : .await;
4171 2 :
4172 2 : let tline = tenant
4173 2 : .create_test_timeline(TIMELINE_ID, Lsn(0x50), DEFAULT_PG_VERSION, &ctx)
4174 6 : .await?;
4175 2 : // try to branch at lsn 0x25, should fail because initdb lsn is 0x50
4176 2 : match tenant
4177 2 : .branch_timeline_test(&tline, NEW_TIMELINE_ID, Some(Lsn(0x25)), &ctx)
4178 2 : .await
4179 2 : {
4180 2 : Ok(_) => panic!("branching should have failed"),
4181 2 : Err(err) => {
4182 2 : let CreateTimelineError::AncestorLsn(err) = err else {
4183 2 : panic!("wrong error type");
4184 2 : };
4185 2 : assert!(&err.to_string().contains("invalid branch start lsn"));
4186 2 : assert!(&err
4187 2 : .source()
4188 2 : .unwrap()
4189 2 : .to_string()
4190 2 : .contains("is earlier than latest GC horizon"));
4191 2 : }
4192 2 : }
4193 2 :
4194 2 : Ok(())
4195 2 : }
4196 :
4197 : /*
4198 : // FIXME: This currently fails to error out. Calling GC doesn't currently
4199 : // remove the old value, we'd need to work a little harder
4200 : #[tokio::test]
4201 : async fn test_prohibit_get_for_garbage_collected_data() -> anyhow::Result<()> {
4202 : let repo =
4203 : RepoHarness::create("test_prohibit_get_for_garbage_collected_data")?
4204 : .load();
4205 :
4206 : let tline = repo.create_empty_timeline(TIMELINE_ID, Lsn(0), DEFAULT_PG_VERSION)?;
4207 : make_some_layers(tline.as_ref(), Lsn(0x20), &ctx).await?;
4208 :
4209 : repo.gc_iteration(Some(TIMELINE_ID), 0x10, Duration::ZERO)?;
4210 : let latest_gc_cutoff_lsn = tline.get_latest_gc_cutoff_lsn();
4211 : assert!(*latest_gc_cutoff_lsn > Lsn(0x25));
4212 : match tline.get(*TEST_KEY, Lsn(0x25)) {
4213 : Ok(_) => panic!("request for page should have failed"),
4214 : Err(err) => assert!(err.to_string().contains("not found at")),
4215 : }
4216 : Ok(())
4217 : }
4218 : */
4219 :
4220 : #[tokio::test]
4221 2 : async fn test_get_branchpoints_from_an_inactive_timeline() -> anyhow::Result<()> {
4222 2 : let (tenant, ctx) =
4223 2 : TenantHarness::create("test_get_branchpoints_from_an_inactive_timeline")?
4224 2 : .load()
4225 8 : .await;
4226 2 : let tline = tenant
4227 2 : .create_test_timeline(TIMELINE_ID, Lsn(0x10), DEFAULT_PG_VERSION, &ctx)
4228 6 : .await?;
4229 6 : make_some_layers(tline.as_ref(), Lsn(0x20), &ctx).await?;
4230 2 :
4231 2 : tenant
4232 2 : .branch_timeline_test(&tline, NEW_TIMELINE_ID, Some(Lsn(0x40)), &ctx)
4233 2 : .await?;
4234 2 : let newtline = tenant
4235 2 : .get_timeline(NEW_TIMELINE_ID, true)
4236 2 : .expect("Should have a local timeline");
4237 2 :
4238 6 : make_some_layers(newtline.as_ref(), Lsn(0x60), &ctx).await?;
4239 2 :
4240 2 : tline.set_broken("test".to_owned());
4241 2 :
4242 2 : tenant
4243 2 : .gc_iteration(
4244 2 : Some(TIMELINE_ID),
4245 2 : 0x10,
4246 2 : Duration::ZERO,
4247 2 : &CancellationToken::new(),
4248 2 : &ctx,
4249 2 : )
4250 2 : .await?;
4251 2 :
4252 2 : // The branchpoints should contain all timelines, even ones marked
4253 2 : // as Broken.
4254 2 : {
4255 2 : let branchpoints = &tline.gc_info.read().unwrap().retain_lsns;
4256 2 : assert_eq!(branchpoints.len(), 1);
4257 2 : assert_eq!(branchpoints[0], Lsn(0x40));
4258 2 : }
4259 2 :
4260 2 : // You can read the key from the child branch even though the parent is
4261 2 : // Broken, as long as you don't need to access data from the parent.
4262 2 : assert_eq!(
4263 4 : newtline.get(*TEST_KEY, Lsn(0x70), &ctx).await?,
4264 2 : test_img(&format!("foo at {}", Lsn(0x70)))
4265 2 : );
4266 2 :
4267 2 : // This needs to traverse to the parent, and fails.
4268 2 : let err = newtline.get(*TEST_KEY, Lsn(0x50), &ctx).await.unwrap_err();
4269 2 : assert!(err.to_string().starts_with(&format!(
4270 2 : "Bad state on timeline {}: Broken",
4271 2 : tline.timeline_id
4272 2 : )));
4273 2 :
4274 2 : Ok(())
4275 2 : }
4276 :
4277 : #[tokio::test]
4278 2 : async fn test_retain_data_in_parent_which_is_needed_for_child() -> anyhow::Result<()> {
4279 2 : let (tenant, ctx) =
4280 2 : TenantHarness::create("test_retain_data_in_parent_which_is_needed_for_child")?
4281 2 : .load()
4282 8 : .await;
4283 2 : let tline = tenant
4284 2 : .create_test_timeline(TIMELINE_ID, Lsn(0x10), DEFAULT_PG_VERSION, &ctx)
4285 6 : .await?;
4286 6 : make_some_layers(tline.as_ref(), Lsn(0x20), &ctx).await?;
4287 2 :
4288 2 : tenant
4289 2 : .branch_timeline_test(&tline, NEW_TIMELINE_ID, Some(Lsn(0x40)), &ctx)
4290 2 : .await?;
4291 2 : let newtline = tenant
4292 2 : .get_timeline(NEW_TIMELINE_ID, true)
4293 2 : .expect("Should have a local timeline");
4294 2 : // this removes layers before lsn 40 (50 minus 10), so there are two remaining layers, image and delta for 31-50
4295 2 : tenant
4296 2 : .gc_iteration(
4297 2 : Some(TIMELINE_ID),
4298 2 : 0x10,
4299 2 : Duration::ZERO,
4300 2 : &CancellationToken::new(),
4301 2 : &ctx,
4302 2 : )
4303 2 : .await?;
4304 4 : assert!(newtline.get(*TEST_KEY, Lsn(0x25), &ctx).await.is_ok());
4305 2 :
4306 2 : Ok(())
4307 2 : }
4308 : #[tokio::test]
4309 2 : async fn test_parent_keeps_data_forever_after_branching() -> anyhow::Result<()> {
4310 2 : let (tenant, ctx) =
4311 2 : TenantHarness::create("test_parent_keeps_data_forever_after_branching")?
4312 2 : .load()
4313 8 : .await;
4314 2 : let tline = tenant
4315 2 : .create_test_timeline(TIMELINE_ID, Lsn(0x10), DEFAULT_PG_VERSION, &ctx)
4316 6 : .await?;
4317 6 : make_some_layers(tline.as_ref(), Lsn(0x20), &ctx).await?;
4318 2 :
4319 2 : tenant
4320 2 : .branch_timeline_test(&tline, NEW_TIMELINE_ID, Some(Lsn(0x40)), &ctx)
4321 2 : .await?;
4322 2 : let newtline = tenant
4323 2 : .get_timeline(NEW_TIMELINE_ID, true)
4324 2 : .expect("Should have a local timeline");
4325 2 :
4326 6 : make_some_layers(newtline.as_ref(), Lsn(0x60), &ctx).await?;
4327 2 :
4328 2 : // run gc on parent
4329 2 : tenant
4330 2 : .gc_iteration(
4331 2 : Some(TIMELINE_ID),
4332 2 : 0x10,
4333 2 : Duration::ZERO,
4334 2 : &CancellationToken::new(),
4335 2 : &ctx,
4336 2 : )
4337 2 : .await?;
4338 2 :
4339 2 : // Check that the data is still accessible on the branch.
4340 2 : assert_eq!(
4341 7 : newtline.get(*TEST_KEY, Lsn(0x50), &ctx).await?,
4342 2 : test_img(&format!("foo at {}", Lsn(0x40)))
4343 2 : );
4344 2 :
4345 2 : Ok(())
4346 2 : }
4347 :
4348 : #[tokio::test]
4349 2 : async fn timeline_load() -> anyhow::Result<()> {
4350 2 : const TEST_NAME: &str = "timeline_load";
4351 2 : let harness = TenantHarness::create(TEST_NAME)?;
4352 2 : {
4353 8 : let (tenant, ctx) = harness.load().await;
4354 2 : let tline = tenant
4355 2 : .create_test_timeline(TIMELINE_ID, Lsn(0x7000), DEFAULT_PG_VERSION, &ctx)
4356 6 : .await?;
4357 6 : make_some_layers(tline.as_ref(), Lsn(0x8000), &ctx).await?;
4358 2 : // so that all uploads finish & we can call harness.load() below again
4359 2 : tenant
4360 2 : .shutdown(Default::default(), ShutdownMode::FreezeAndFlush)
4361 2 : .instrument(harness.span())
4362 2 : .await
4363 2 : .ok()
4364 2 : .unwrap();
4365 2 : }
4366 2 :
4367 7 : let (tenant, _ctx) = harness.load().await;
4368 2 : tenant
4369 2 : .get_timeline(TIMELINE_ID, true)
4370 2 : .expect("cannot load timeline");
4371 2 :
4372 2 : Ok(())
4373 2 : }
4374 :
4375 : #[tokio::test]
4376 2 : async fn timeline_load_with_ancestor() -> anyhow::Result<()> {
4377 2 : const TEST_NAME: &str = "timeline_load_with_ancestor";
4378 2 : let harness = TenantHarness::create(TEST_NAME)?;
4379 2 : // create two timelines
4380 2 : {
4381 8 : let (tenant, ctx) = harness.load().await;
4382 2 : let tline = tenant
4383 2 : .create_test_timeline(TIMELINE_ID, Lsn(0x10), DEFAULT_PG_VERSION, &ctx)
4384 6 : .await?;
4385 2 :
4386 6 : make_some_layers(tline.as_ref(), Lsn(0x20), &ctx).await?;
4387 2 :
4388 2 : let child_tline = tenant
4389 2 : .branch_timeline_test(&tline, NEW_TIMELINE_ID, Some(Lsn(0x40)), &ctx)
4390 2 : .await?;
4391 2 : child_tline.set_state(TimelineState::Active);
4392 2 :
4393 2 : let newtline = tenant
4394 2 : .get_timeline(NEW_TIMELINE_ID, true)
4395 2 : .expect("Should have a local timeline");
4396 2 :
4397 6 : make_some_layers(newtline.as_ref(), Lsn(0x60), &ctx).await?;
4398 2 :
4399 2 : // so that all uploads finish & we can call harness.load() below again
4400 2 : tenant
4401 2 : .shutdown(Default::default(), ShutdownMode::FreezeAndFlush)
4402 2 : .instrument(harness.span())
4403 4 : .await
4404 2 : .ok()
4405 2 : .unwrap();
4406 2 : }
4407 2 :
4408 2 : // check that both of them are initially unloaded
4409 13 : let (tenant, _ctx) = harness.load().await;
4410 2 :
4411 2 : // check that both, child and ancestor are loaded
4412 2 : let _child_tline = tenant
4413 2 : .get_timeline(NEW_TIMELINE_ID, true)
4414 2 : .expect("cannot get child timeline loaded");
4415 2 :
4416 2 : let _ancestor_tline = tenant
4417 2 : .get_timeline(TIMELINE_ID, true)
4418 2 : .expect("cannot get ancestor timeline loaded");
4419 2 :
4420 2 : Ok(())
4421 2 : }
4422 :
4423 : #[tokio::test]
4424 2 : async fn delta_layer_dumping() -> anyhow::Result<()> {
4425 2 : use storage_layer::AsLayerDesc;
4426 7 : let (tenant, ctx) = TenantHarness::create("test_layer_dumping")?.load().await;
4427 2 : let tline = tenant
4428 2 : .create_test_timeline(TIMELINE_ID, Lsn(0x10), DEFAULT_PG_VERSION, &ctx)
4429 6 : .await?;
4430 6 : make_some_layers(tline.as_ref(), Lsn(0x20), &ctx).await?;
4431 2 :
4432 2 : let layer_map = tline.layers.read().await;
4433 2 : let level0_deltas = layer_map
4434 2 : .layer_map()
4435 2 : .get_level0_deltas()?
4436 2 : .into_iter()
4437 4 : .map(|desc| layer_map.get_from_desc(&desc))
4438 2 : .collect::<Vec<_>>();
4439 2 :
4440 2 : assert!(!level0_deltas.is_empty());
4441 2 :
4442 6 : for delta in level0_deltas {
4443 2 : // Ensure we are dumping a delta layer here
4444 4 : assert!(delta.layer_desc().is_delta);
4445 8 : delta.dump(true, &ctx).await.unwrap();
4446 2 : }
4447 2 :
4448 2 : Ok(())
4449 2 : }
4450 :
4451 : #[tokio::test]
4452 2 : async fn test_images() -> anyhow::Result<()> {
4453 8 : let (tenant, ctx) = TenantHarness::create("test_images")?.load().await;
4454 2 : let tline = tenant
4455 2 : .create_test_timeline(TIMELINE_ID, Lsn(0x08), DEFAULT_PG_VERSION, &ctx)
4456 6 : .await?;
4457 2 :
4458 2 : let mut writer = tline.writer().await;
4459 2 : writer
4460 2 : .put(
4461 2 : *TEST_KEY,
4462 2 : Lsn(0x10),
4463 2 : &Value::Image(test_img("foo at 0x10")),
4464 2 : &ctx,
4465 2 : )
4466 2 : .await?;
4467 2 : writer.finish_write(Lsn(0x10));
4468 2 : drop(writer);
4469 2 :
4470 2 : tline.freeze_and_flush().await?;
4471 2 : tline
4472 2 : .compact(&CancellationToken::new(), EnumSet::empty(), &ctx)
4473 2 : .await?;
4474 2 :
4475 2 : let mut writer = tline.writer().await;
4476 2 : writer
4477 2 : .put(
4478 2 : *TEST_KEY,
4479 2 : Lsn(0x20),
4480 2 : &Value::Image(test_img("foo at 0x20")),
4481 2 : &ctx,
4482 2 : )
4483 2 : .await?;
4484 2 : writer.finish_write(Lsn(0x20));
4485 2 : drop(writer);
4486 2 :
4487 2 : tline.freeze_and_flush().await?;
4488 2 : tline
4489 2 : .compact(&CancellationToken::new(), EnumSet::empty(), &ctx)
4490 2 : .await?;
4491 2 :
4492 2 : let mut writer = tline.writer().await;
4493 2 : writer
4494 2 : .put(
4495 2 : *TEST_KEY,
4496 2 : Lsn(0x30),
4497 2 : &Value::Image(test_img("foo at 0x30")),
4498 2 : &ctx,
4499 2 : )
4500 2 : .await?;
4501 2 : writer.finish_write(Lsn(0x30));
4502 2 : drop(writer);
4503 2 :
4504 2 : tline.freeze_and_flush().await?;
4505 2 : tline
4506 2 : .compact(&CancellationToken::new(), EnumSet::empty(), &ctx)
4507 2 : .await?;
4508 2 :
4509 2 : let mut writer = tline.writer().await;
4510 2 : writer
4511 2 : .put(
4512 2 : *TEST_KEY,
4513 2 : Lsn(0x40),
4514 2 : &Value::Image(test_img("foo at 0x40")),
4515 2 : &ctx,
4516 2 : )
4517 2 : .await?;
4518 2 : writer.finish_write(Lsn(0x40));
4519 2 : drop(writer);
4520 2 :
4521 2 : tline.freeze_and_flush().await?;
4522 2 : tline
4523 2 : .compact(&CancellationToken::new(), EnumSet::empty(), &ctx)
4524 2 : .await?;
4525 2 :
4526 2 : assert_eq!(
4527 4 : tline.get(*TEST_KEY, Lsn(0x10), &ctx).await?,
4528 2 : test_img("foo at 0x10")
4529 2 : );
4530 2 : assert_eq!(
4531 3 : tline.get(*TEST_KEY, Lsn(0x1f), &ctx).await?,
4532 2 : test_img("foo at 0x10")
4533 2 : );
4534 2 : assert_eq!(
4535 2 : tline.get(*TEST_KEY, Lsn(0x20), &ctx).await?,
4536 2 : test_img("foo at 0x20")
4537 2 : );
4538 2 : assert_eq!(
4539 4 : tline.get(*TEST_KEY, Lsn(0x30), &ctx).await?,
4540 2 : test_img("foo at 0x30")
4541 2 : );
4542 2 : assert_eq!(
4543 4 : tline.get(*TEST_KEY, Lsn(0x40), &ctx).await?,
4544 2 : test_img("foo at 0x40")
4545 2 : );
4546 2 :
4547 2 : Ok(())
4548 2 : }
4549 :
4550 4 : async fn bulk_insert_compact_gc(
4551 4 : tenant: &Tenant,
4552 4 : timeline: &Arc<Timeline>,
4553 4 : ctx: &RequestContext,
4554 4 : lsn: Lsn,
4555 4 : repeat: usize,
4556 4 : key_count: usize,
4557 4 : ) -> anyhow::Result<()> {
4558 4 : let compact = true;
4559 72774 : bulk_insert_maybe_compact_gc(tenant, timeline, ctx, lsn, repeat, key_count, compact).await
4560 4 : }
4561 :
4562 8 : async fn bulk_insert_maybe_compact_gc(
4563 8 : tenant: &Tenant,
4564 8 : timeline: &Arc<Timeline>,
4565 8 : ctx: &RequestContext,
4566 8 : mut lsn: Lsn,
4567 8 : repeat: usize,
4568 8 : key_count: usize,
4569 8 : compact: bool,
4570 8 : ) -> anyhow::Result<()> {
4571 8 : let mut test_key = Key::from_hex("010000000033333333444444445500000000").unwrap();
4572 8 : let mut blknum = 0;
4573 8 :
4574 8 : // Enforce that key range is monotonously increasing
4575 8 : let mut keyspace = KeySpaceAccum::new();
4576 8 :
4577 8 : let cancel = CancellationToken::new();
4578 8 :
4579 8 : for _ in 0..repeat {
4580 400 : for _ in 0..key_count {
4581 4000000 : test_key.field6 = blknum;
4582 4000000 : let mut writer = timeline.writer().await;
4583 4000000 : writer
4584 4000000 : .put(
4585 4000000 : test_key,
4586 4000000 : lsn,
4587 4000000 : &Value::Image(test_img(&format!("{} at {}", blknum, lsn))),
4588 4000000 : ctx,
4589 4000000 : )
4590 2600 : .await?;
4591 4000000 : writer.finish_write(lsn);
4592 4000000 : drop(writer);
4593 4000000 :
4594 4000000 : keyspace.add_key(test_key);
4595 4000000 :
4596 4000000 : lsn = Lsn(lsn.0 + 0x10);
4597 4000000 : blknum += 1;
4598 : }
4599 :
4600 400 : timeline.freeze_and_flush().await?;
4601 400 : if compact {
4602 : // this requires timeline to be &Arc<Timeline>
4603 40174 : timeline.compact(&cancel, EnumSet::empty(), ctx).await?;
4604 200 : }
4605 :
4606 : // this doesn't really need to use the timeline_id target, but it is closer to what it
4607 : // originally was.
4608 400 : let res = tenant
4609 400 : .gc_iteration(Some(timeline.timeline_id), 0, Duration::ZERO, &cancel, ctx)
4610 400 : .await?;
4611 :
4612 400 : assert_eq!(res.layers_removed, 0, "this never removes anything");
4613 : }
4614 :
4615 8 : Ok(())
4616 8 : }
4617 :
4618 : //
4619 : // Insert 1000 key-value pairs with increasing keys, flush, compact, GC.
4620 : // Repeat 50 times.
4621 : //
4622 : #[tokio::test]
4623 2 : async fn test_bulk_insert() -> anyhow::Result<()> {
4624 2 : let harness = TenantHarness::create("test_bulk_insert")?;
4625 8 : let (tenant, ctx) = harness.load().await;
4626 2 : let tline = tenant
4627 2 : .create_test_timeline(TIMELINE_ID, Lsn(0x08), DEFAULT_PG_VERSION, &ctx)
4628 6 : .await?;
4629 2 :
4630 2 : let lsn = Lsn(0x10);
4631 36387 : bulk_insert_compact_gc(&tenant, &tline, &ctx, lsn, 50, 10000).await?;
4632 2 :
4633 2 : Ok(())
4634 2 : }
4635 :
4636 : // Test the vectored get real implementation against a simple sequential implementation.
4637 : //
4638 : // The test generates a keyspace by repeatedly flushing the in-memory layer and compacting.
4639 : // Projected to 2D the key space looks like below. Lsn grows upwards on the Y axis and keys
4640 : // grow to the right on the X axis.
4641 : // [Delta]
4642 : // [Delta]
4643 : // [Delta]
4644 : // [Delta]
4645 : // ------------ Image ---------------
4646 : //
4647 : // After layer generation we pick the ranges to query as follows:
4648 : // 1. The beginning of each delta layer
4649 : // 2. At the seam between two adjacent delta layers
4650 : //
4651 : // There's one major downside to this test: delta layers only contains images,
4652 : // so the search can stop at the first delta layer and doesn't traverse any deeper.
4653 : #[tokio::test]
4654 2 : async fn test_get_vectored() -> anyhow::Result<()> {
4655 2 : let harness = TenantHarness::create("test_get_vectored")?;
4656 8 : let (tenant, ctx) = harness.load().await;
4657 2 : let tline = tenant
4658 2 : .create_test_timeline(TIMELINE_ID, Lsn(0x08), DEFAULT_PG_VERSION, &ctx)
4659 6 : .await?;
4660 2 :
4661 2 : let lsn = Lsn(0x10);
4662 36387 : bulk_insert_compact_gc(&tenant, &tline, &ctx, lsn, 50, 10000).await?;
4663 2 :
4664 2 : let guard = tline.layers.read().await;
4665 2 : guard.layer_map().dump(true, &ctx).await?;
4666 2 :
4667 2 : let mut reads = Vec::new();
4668 2 : let mut prev = None;
4669 12 : guard.layer_map().iter_historic_layers().for_each(|desc| {
4670 12 : if !desc.is_delta() {
4671 2 : prev = Some(desc.clone());
4672 2 : return;
4673 10 : }
4674 10 :
4675 10 : let start = desc.key_range.start;
4676 10 : let end = desc
4677 10 : .key_range
4678 10 : .start
4679 10 : .add(Timeline::MAX_GET_VECTORED_KEYS.try_into().unwrap());
4680 10 : reads.push(KeySpace {
4681 10 : ranges: vec![start..end],
4682 10 : });
4683 2 :
4684 10 : if let Some(prev) = &prev {
4685 10 : if !prev.is_delta() {
4686 10 : return;
4687 2 : }
4688 0 :
4689 0 : let first_range = Key {
4690 0 : field6: prev.key_range.end.field6 - 4,
4691 0 : ..prev.key_range.end
4692 0 : }..prev.key_range.end;
4693 0 :
4694 0 : let second_range = desc.key_range.start..Key {
4695 0 : field6: desc.key_range.start.field6 + 4,
4696 0 : ..desc.key_range.start
4697 0 : };
4698 0 :
4699 0 : reads.push(KeySpace {
4700 0 : ranges: vec![first_range, second_range],
4701 0 : });
4702 2 : };
4703 2 :
4704 2 : prev = Some(desc.clone());
4705 12 : });
4706 2 :
4707 2 : drop(guard);
4708 2 :
4709 2 : // Pick a big LSN such that we query over all the changes.
4710 2 : let reads_lsn = Lsn(u64::MAX - 1);
4711 2 :
4712 12 : for read in reads {
4713 10 : info!("Doing vectored read on {:?}", read);
4714 2 :
4715 10 : let vectored_res = tline
4716 10 : .get_vectored_impl(
4717 10 : read.clone(),
4718 10 : reads_lsn,
4719 10 : &mut ValuesReconstructState::new(),
4720 10 : &ctx,
4721 10 : )
4722 25 : .await;
4723 10 : tline
4724 10 : .validate_get_vectored_impl(&vectored_res, read, reads_lsn, &ctx)
4725 19 : .await;
4726 2 : }
4727 2 :
4728 2 : Ok(())
4729 2 : }
4730 :
4731 : #[tokio::test]
4732 2 : async fn test_get_vectored_aux_files() -> anyhow::Result<()> {
4733 2 : let harness = TenantHarness::create("test_get_vectored_aux_files")?;
4734 2 :
4735 8 : let (tenant, ctx) = harness.load().await;
4736 2 : let tline = tenant
4737 2 : .create_empty_timeline(TIMELINE_ID, Lsn(0), DEFAULT_PG_VERSION, &ctx)
4738 2 : .await?;
4739 2 : let tline = tline.raw_timeline().unwrap();
4740 2 :
4741 2 : let mut modification = tline.begin_modification(Lsn(0x1000));
4742 2 : modification.put_file("foo/bar1", b"content1", &ctx).await?;
4743 2 : modification.set_lsn(Lsn(0x1008))?;
4744 2 : modification.put_file("foo/bar2", b"content2", &ctx).await?;
4745 2 : modification.commit(&ctx).await?;
4746 2 :
4747 2 : let child_timeline_id = TimelineId::generate();
4748 2 : tenant
4749 2 : .branch_timeline_test(
4750 2 : tline,
4751 2 : child_timeline_id,
4752 2 : Some(tline.get_last_record_lsn()),
4753 2 : &ctx,
4754 2 : )
4755 2 : .await?;
4756 2 :
4757 2 : let child_timeline = tenant
4758 2 : .get_timeline(child_timeline_id, true)
4759 2 : .expect("Should have the branched timeline");
4760 2 :
4761 2 : let aux_keyspace = KeySpace {
4762 2 : ranges: vec![NON_INHERITED_RANGE],
4763 2 : };
4764 2 : let read_lsn = child_timeline.get_last_record_lsn();
4765 2 :
4766 2 : let vectored_res = child_timeline
4767 2 : .get_vectored_impl(
4768 2 : aux_keyspace.clone(),
4769 2 : read_lsn,
4770 2 : &mut ValuesReconstructState::new(),
4771 2 : &ctx,
4772 2 : )
4773 2 : .await;
4774 2 :
4775 2 : child_timeline
4776 2 : .validate_get_vectored_impl(&vectored_res, aux_keyspace, read_lsn, &ctx)
4777 2 : .await;
4778 2 :
4779 2 : let images = vectored_res?;
4780 2 : assert!(images.is_empty());
4781 2 : Ok(())
4782 2 : }
4783 :
4784 : // Test that vectored get handles layer gaps correctly
4785 : // by advancing into the next ancestor timeline if required.
4786 : //
4787 : // The test generates timelines that look like the diagram below.
4788 : // We leave a gap in one of the L1 layers at `gap_at_key` (`/` in the diagram).
4789 : // The reconstruct data for that key lies in the ancestor timeline (`X` in the diagram).
4790 : //
4791 : // ```
4792 : //-------------------------------+
4793 : // ... |
4794 : // [ L1 ] |
4795 : // [ / L1 ] | Child Timeline
4796 : // ... |
4797 : // ------------------------------+
4798 : // [ X L1 ] | Parent Timeline
4799 : // ------------------------------+
4800 : // ```
4801 : #[tokio::test]
4802 2 : async fn test_get_vectored_key_gap() -> anyhow::Result<()> {
4803 2 : let tenant_conf = TenantConf {
4804 2 : // Make compaction deterministic
4805 2 : gc_period: Duration::ZERO,
4806 2 : compaction_period: Duration::ZERO,
4807 2 : // Encourage creation of L1 layers
4808 2 : checkpoint_distance: 16 * 1024,
4809 2 : compaction_target_size: 8 * 1024,
4810 2 : ..TenantConf::default()
4811 2 : };
4812 2 :
4813 2 : let harness = TenantHarness::create_custom(
4814 2 : "test_get_vectored_key_gap",
4815 2 : tenant_conf,
4816 2 : TenantId::generate(),
4817 2 : ShardIdentity::unsharded(),
4818 2 : Generation::new(0xdeadbeef),
4819 2 : )?;
4820 8 : let (tenant, ctx) = harness.load().await;
4821 2 :
4822 2 : let mut current_key = Key::from_hex("010000000033333333444444445500000000").unwrap();
4823 2 : let gap_at_key = current_key.add(100);
4824 2 : let mut current_lsn = Lsn(0x10);
4825 2 :
4826 2 : const KEY_COUNT: usize = 10_000;
4827 2 :
4828 2 : let timeline_id = TimelineId::generate();
4829 2 : let current_timeline = tenant
4830 2 : .create_test_timeline(timeline_id, current_lsn, DEFAULT_PG_VERSION, &ctx)
4831 6 : .await?;
4832 2 :
4833 2 : current_lsn += 0x100;
4834 2 :
4835 2 : let mut writer = current_timeline.writer().await;
4836 2 : writer
4837 2 : .put(
4838 2 : gap_at_key,
4839 2 : current_lsn,
4840 2 : &Value::Image(test_img(&format!("{} at {}", gap_at_key, current_lsn))),
4841 2 : &ctx,
4842 2 : )
4843 2 : .await?;
4844 2 : writer.finish_write(current_lsn);
4845 2 : drop(writer);
4846 2 :
4847 2 : let mut latest_lsns = HashMap::new();
4848 2 : latest_lsns.insert(gap_at_key, current_lsn);
4849 2 :
4850 2 : current_timeline.freeze_and_flush().await?;
4851 2 :
4852 2 : let child_timeline_id = TimelineId::generate();
4853 2 :
4854 2 : tenant
4855 2 : .branch_timeline_test(
4856 2 : ¤t_timeline,
4857 2 : child_timeline_id,
4858 2 : Some(current_lsn),
4859 2 : &ctx,
4860 2 : )
4861 2 : .await?;
4862 2 : let child_timeline = tenant
4863 2 : .get_timeline(child_timeline_id, true)
4864 2 : .expect("Should have the branched timeline");
4865 2 :
4866 20002 : for i in 0..KEY_COUNT {
4867 20000 : if current_key == gap_at_key {
4868 2 : current_key = current_key.next();
4869 2 : continue;
4870 19998 : }
4871 19998 :
4872 19998 : current_lsn += 0x10;
4873 2 :
4874 19998 : let mut writer = child_timeline.writer().await;
4875 19998 : writer
4876 19998 : .put(
4877 19998 : current_key,
4878 19998 : current_lsn,
4879 19998 : &Value::Image(test_img(&format!("{} at {}", current_key, current_lsn))),
4880 19998 : &ctx,
4881 19998 : )
4882 77 : .await?;
4883 19998 : writer.finish_write(current_lsn);
4884 19998 : drop(writer);
4885 19998 :
4886 19998 : latest_lsns.insert(current_key, current_lsn);
4887 19998 : current_key = current_key.next();
4888 19998 :
4889 19998 : // Flush every now and then to encourage layer file creation.
4890 19998 : if i % 500 == 0 {
4891 42 : child_timeline.freeze_and_flush().await?;
4892 19958 : }
4893 2 : }
4894 2 :
4895 2 : child_timeline.freeze_and_flush().await?;
4896 2 : let mut flags = EnumSet::new();
4897 2 : flags.insert(CompactFlags::ForceRepartition);
4898 2 : child_timeline
4899 2 : .compact(&CancellationToken::new(), flags, &ctx)
4900 2184 : .await?;
4901 2 :
4902 2 : let key_near_end = {
4903 2 : let mut tmp = current_key;
4904 2 : tmp.field6 -= 10;
4905 2 : tmp
4906 2 : };
4907 2 :
4908 2 : let key_near_gap = {
4909 2 : let mut tmp = gap_at_key;
4910 2 : tmp.field6 -= 10;
4911 2 : tmp
4912 2 : };
4913 2 :
4914 2 : let read = KeySpace {
4915 2 : ranges: vec![key_near_gap..gap_at_key.next(), key_near_end..current_key],
4916 2 : };
4917 2 : let results = child_timeline
4918 2 : .get_vectored_impl(
4919 2 : read.clone(),
4920 2 : current_lsn,
4921 2 : &mut ValuesReconstructState::new(),
4922 2 : &ctx,
4923 2 : )
4924 15 : .await?;
4925 2 :
4926 44 : for (key, img_res) in results {
4927 42 : let expected = test_img(&format!("{} at {}", key, latest_lsns[&key]));
4928 42 : assert_eq!(img_res?, expected);
4929 2 : }
4930 2 :
4931 2 : Ok(())
4932 2 : }
4933 :
4934 : // Test that vectored get descends into ancestor timelines correctly and
4935 : // does not return an image that's newer than requested.
4936 : //
4937 : // The diagram below ilustrates an interesting case. We have a parent timeline
4938 : // (top of the Lsn range) and a child timeline. The request key cannot be reconstructed
4939 : // from the child timeline, so the parent timeline must be visited. When advacing into
4940 : // the child timeline, the read path needs to remember what the requested Lsn was in
4941 : // order to avoid returning an image that's too new. The test below constructs such
4942 : // a timeline setup and does a few queries around the Lsn of each page image.
4943 : // ```
4944 : // LSN
4945 : // ^
4946 : // |
4947 : // |
4948 : // 500 | --------------------------------------> branch point
4949 : // 400 | X
4950 : // 300 | X
4951 : // 200 | --------------------------------------> requested lsn
4952 : // 100 | X
4953 : // |---------------------------------------> Key
4954 : // |
4955 : // ------> requested key
4956 : //
4957 : // Legend:
4958 : // * X - page images
4959 : // ```
4960 : #[tokio::test]
4961 2 : async fn test_get_vectored_ancestor_descent() -> anyhow::Result<()> {
4962 2 : let harness = TenantHarness::create("test_get_vectored_on_lsn_axis")?;
4963 8 : let (tenant, ctx) = harness.load().await;
4964 2 :
4965 2 : let start_key = Key::from_hex("010000000033333333444444445500000000").unwrap();
4966 2 : let end_key = start_key.add(1000);
4967 2 : let child_gap_at_key = start_key.add(500);
4968 2 : let mut parent_gap_lsns: BTreeMap<Lsn, String> = BTreeMap::new();
4969 2 :
4970 2 : let mut current_lsn = Lsn(0x10);
4971 2 :
4972 2 : let timeline_id = TimelineId::generate();
4973 2 : let parent_timeline = tenant
4974 2 : .create_test_timeline(timeline_id, current_lsn, DEFAULT_PG_VERSION, &ctx)
4975 6 : .await?;
4976 2 :
4977 2 : current_lsn += 0x100;
4978 2 :
4979 8 : for _ in 0..3 {
4980 6 : let mut key = start_key;
4981 6006 : while key < end_key {
4982 6000 : current_lsn += 0x10;
4983 6000 :
4984 6000 : let image_value = format!("{} at {}", child_gap_at_key, current_lsn);
4985 2 :
4986 6000 : let mut writer = parent_timeline.writer().await;
4987 6000 : writer
4988 6000 : .put(
4989 6000 : key,
4990 6000 : current_lsn,
4991 6000 : &Value::Image(test_img(&image_value)),
4992 6000 : &ctx,
4993 6000 : )
4994 6 : .await?;
4995 6000 : writer.finish_write(current_lsn);
4996 6000 :
4997 6000 : if key == child_gap_at_key {
4998 6 : parent_gap_lsns.insert(current_lsn, image_value);
4999 5994 : }
5000 2 :
5001 6000 : key = key.next();
5002 2 : }
5003 2 :
5004 6 : parent_timeline.freeze_and_flush().await?;
5005 2 : }
5006 2 :
5007 2 : let child_timeline_id = TimelineId::generate();
5008 2 :
5009 2 : let child_timeline = tenant
5010 2 : .branch_timeline_test(&parent_timeline, child_timeline_id, Some(current_lsn), &ctx)
5011 2 : .await?;
5012 2 :
5013 2 : let mut key = start_key;
5014 2002 : while key < end_key {
5015 2000 : if key == child_gap_at_key {
5016 2 : key = key.next();
5017 2 : continue;
5018 1998 : }
5019 1998 :
5020 1998 : current_lsn += 0x10;
5021 2 :
5022 1998 : let mut writer = child_timeline.writer().await;
5023 1998 : writer
5024 1998 : .put(
5025 1998 : key,
5026 1998 : current_lsn,
5027 1998 : &Value::Image(test_img(&format!("{} at {}", key, current_lsn))),
5028 1998 : &ctx,
5029 1998 : )
5030 17 : .await?;
5031 1998 : writer.finish_write(current_lsn);
5032 1998 :
5033 1998 : key = key.next();
5034 2 : }
5035 2 :
5036 2 : child_timeline.freeze_and_flush().await?;
5037 2 :
5038 2 : let lsn_offsets: [i64; 5] = [-10, -1, 0, 1, 10];
5039 2 : let mut query_lsns = Vec::new();
5040 6 : for image_lsn in parent_gap_lsns.keys().rev() {
5041 36 : for offset in lsn_offsets {
5042 30 : query_lsns.push(Lsn(image_lsn
5043 30 : .0
5044 30 : .checked_add_signed(offset)
5045 30 : .expect("Shouldn't overflow")));
5046 30 : }
5047 2 : }
5048 2 :
5049 32 : for query_lsn in query_lsns {
5050 30 : let results = child_timeline
5051 30 : .get_vectored_impl(
5052 30 : KeySpace {
5053 30 : ranges: vec![child_gap_at_key..child_gap_at_key.next()],
5054 30 : },
5055 30 : query_lsn,
5056 30 : &mut ValuesReconstructState::new(),
5057 30 : &ctx,
5058 30 : )
5059 29 : .await;
5060 2 :
5061 30 : let expected_item = parent_gap_lsns
5062 30 : .iter()
5063 30 : .rev()
5064 68 : .find(|(lsn, _)| **lsn <= query_lsn);
5065 30 :
5066 30 : info!(
5067 2 : "Doing vectored read at LSN {}. Expecting image to be: {:?}",
5068 2 : query_lsn, expected_item
5069 2 : );
5070 2 :
5071 30 : match expected_item {
5072 26 : Some((_, img_value)) => {
5073 26 : let key_results = results.expect("No vectored get error expected");
5074 26 : let key_result = &key_results[&child_gap_at_key];
5075 26 : let returned_img = key_result
5076 26 : .as_ref()
5077 26 : .expect("No page reconstruct error expected");
5078 26 :
5079 26 : info!(
5080 2 : "Vectored read at LSN {} returned image {}",
5081 0 : query_lsn,
5082 0 : std::str::from_utf8(returned_img)?
5083 2 : );
5084 26 : assert_eq!(*returned_img, test_img(img_value));
5085 2 : }
5086 2 : None => {
5087 4 : assert!(matches!(results, Err(GetVectoredError::MissingKey(_))));
5088 2 : }
5089 2 : }
5090 2 : }
5091 2 :
5092 2 : Ok(())
5093 2 : }
5094 :
5095 : #[tokio::test]
5096 2 : async fn test_random_updates() -> anyhow::Result<()> {
5097 2 : let names_algorithms = [
5098 2 : ("test_random_updates_legacy", CompactionAlgorithm::Legacy),
5099 2 : ("test_random_updates_tiered", CompactionAlgorithm::Tiered),
5100 2 : ];
5101 6 : for (name, algorithm) in names_algorithms {
5102 17749 : test_random_updates_algorithm(name, algorithm).await?;
5103 2 : }
5104 2 : Ok(())
5105 2 : }
5106 :
5107 4 : async fn test_random_updates_algorithm(
5108 4 : name: &'static str,
5109 4 : compaction_algorithm: CompactionAlgorithm,
5110 4 : ) -> anyhow::Result<()> {
5111 4 : let mut harness = TenantHarness::create(name)?;
5112 4 : harness.tenant_conf.compaction_algorithm = CompactionAlgorithmSettings {
5113 4 : kind: compaction_algorithm,
5114 4 : };
5115 16 : let (tenant, ctx) = harness.load().await;
5116 4 : let tline = tenant
5117 4 : .create_test_timeline(TIMELINE_ID, Lsn(0x10), DEFAULT_PG_VERSION, &ctx)
5118 11 : .await?;
5119 :
5120 : const NUM_KEYS: usize = 1000;
5121 4 : let cancel = CancellationToken::new();
5122 4 :
5123 4 : let mut test_key = Key::from_hex("010000000033333333444444445500000000").unwrap();
5124 4 : let mut test_key_end = test_key;
5125 4 : test_key_end.field6 = NUM_KEYS as u32;
5126 4 : tline.add_extra_test_dense_keyspace(KeySpace::single(test_key..test_key_end));
5127 4 :
5128 4 : let mut keyspace = KeySpaceAccum::new();
5129 4 :
5130 4 : // Track when each page was last modified. Used to assert that
5131 4 : // a read sees the latest page version.
5132 4 : let mut updated = [Lsn(0); NUM_KEYS];
5133 4 :
5134 4 : let mut lsn = Lsn(0x10);
5135 : #[allow(clippy::needless_range_loop)]
5136 4004 : for blknum in 0..NUM_KEYS {
5137 4000 : lsn = Lsn(lsn.0 + 0x10);
5138 4000 : test_key.field6 = blknum as u32;
5139 4000 : let mut writer = tline.writer().await;
5140 4000 : writer
5141 4000 : .put(
5142 4000 : test_key,
5143 4000 : lsn,
5144 4000 : &Value::Image(test_img(&format!("{} at {}", blknum, lsn))),
5145 4000 : &ctx,
5146 4000 : )
5147 4 : .await?;
5148 4000 : writer.finish_write(lsn);
5149 4000 : updated[blknum] = lsn;
5150 4000 : drop(writer);
5151 4000 :
5152 4000 : keyspace.add_key(test_key);
5153 : }
5154 :
5155 204 : for _ in 0..50 {
5156 200200 : for _ in 0..NUM_KEYS {
5157 200000 : lsn = Lsn(lsn.0 + 0x10);
5158 200000 : let blknum = thread_rng().gen_range(0..NUM_KEYS);
5159 200000 : test_key.field6 = blknum as u32;
5160 200000 : let mut writer = tline.writer().await;
5161 200000 : writer
5162 200000 : .put(
5163 200000 : test_key,
5164 200000 : lsn,
5165 200000 : &Value::Image(test_img(&format!("{} at {}", blknum, lsn))),
5166 200000 : &ctx,
5167 200000 : )
5168 198 : .await?;
5169 200000 : writer.finish_write(lsn);
5170 200000 : drop(writer);
5171 200000 : updated[blknum] = lsn;
5172 : }
5173 :
5174 : // Read all the blocks
5175 200000 : for (blknum, last_lsn) in updated.iter().enumerate() {
5176 200000 : test_key.field6 = blknum as u32;
5177 200000 : assert_eq!(
5178 200000 : tline.get(test_key, lsn, &ctx).await?,
5179 200000 : test_img(&format!("{} at {}", blknum, last_lsn))
5180 : );
5181 : }
5182 :
5183 : // Perform a cycle of flush, and GC
5184 206 : tline.freeze_and_flush().await?;
5185 200 : tenant
5186 200 : .gc_iteration(Some(tline.timeline_id), 0, Duration::ZERO, &cancel, &ctx)
5187 195 : .await?;
5188 : }
5189 :
5190 4 : Ok(())
5191 4 : }
5192 :
5193 : #[tokio::test]
5194 2 : async fn test_traverse_branches() -> anyhow::Result<()> {
5195 2 : let (tenant, ctx) = TenantHarness::create("test_traverse_branches")?
5196 2 : .load()
5197 8 : .await;
5198 2 : let mut tline = tenant
5199 2 : .create_test_timeline(TIMELINE_ID, Lsn(0x10), DEFAULT_PG_VERSION, &ctx)
5200 6 : .await?;
5201 2 :
5202 2 : const NUM_KEYS: usize = 1000;
5203 2 :
5204 2 : let mut test_key = Key::from_hex("010000000033333333444444445500000000").unwrap();
5205 2 :
5206 2 : let mut keyspace = KeySpaceAccum::new();
5207 2 :
5208 2 : let cancel = CancellationToken::new();
5209 2 :
5210 2 : // Track when each page was last modified. Used to assert that
5211 2 : // a read sees the latest page version.
5212 2 : let mut updated = [Lsn(0); NUM_KEYS];
5213 2 :
5214 2 : let mut lsn = Lsn(0x10);
5215 2 : #[allow(clippy::needless_range_loop)]
5216 2002 : for blknum in 0..NUM_KEYS {
5217 2000 : lsn = Lsn(lsn.0 + 0x10);
5218 2000 : test_key.field6 = blknum as u32;
5219 2000 : let mut writer = tline.writer().await;
5220 2000 : writer
5221 2000 : .put(
5222 2000 : test_key,
5223 2000 : lsn,
5224 2000 : &Value::Image(test_img(&format!("{} at {}", blknum, lsn))),
5225 2000 : &ctx,
5226 2000 : )
5227 2 : .await?;
5228 2000 : writer.finish_write(lsn);
5229 2000 : updated[blknum] = lsn;
5230 2000 : drop(writer);
5231 2000 :
5232 2000 : keyspace.add_key(test_key);
5233 2 : }
5234 2 :
5235 102 : for _ in 0..50 {
5236 100 : let new_tline_id = TimelineId::generate();
5237 100 : tenant
5238 100 : .branch_timeline_test(&tline, new_tline_id, Some(lsn), &ctx)
5239 2 : .await?;
5240 100 : tline = tenant
5241 100 : .get_timeline(new_tline_id, true)
5242 100 : .expect("Should have the branched timeline");
5243 2 :
5244 100100 : for _ in 0..NUM_KEYS {
5245 100000 : lsn = Lsn(lsn.0 + 0x10);
5246 100000 : let blknum = thread_rng().gen_range(0..NUM_KEYS);
5247 100000 : test_key.field6 = blknum as u32;
5248 100000 : let mut writer = tline.writer().await;
5249 100000 : writer
5250 100000 : .put(
5251 100000 : test_key,
5252 100000 : lsn,
5253 100000 : &Value::Image(test_img(&format!("{} at {}", blknum, lsn))),
5254 100000 : &ctx,
5255 100000 : )
5256 851 : .await?;
5257 100000 : println!("updating {} at {}", blknum, lsn);
5258 100000 : writer.finish_write(lsn);
5259 100000 : drop(writer);
5260 100000 : updated[blknum] = lsn;
5261 2 : }
5262 2 :
5263 2 : // Read all the blocks
5264 100000 : for (blknum, last_lsn) in updated.iter().enumerate() {
5265 100000 : test_key.field6 = blknum as u32;
5266 100000 : assert_eq!(
5267 100000 : tline.get(test_key, lsn, &ctx).await?,
5268 100000 : test_img(&format!("{} at {}", blknum, last_lsn))
5269 2 : );
5270 2 : }
5271 2 :
5272 2 : // Perform a cycle of flush, compact, and GC
5273 103 : tline.freeze_and_flush().await?;
5274 13222 : tline.compact(&cancel, EnumSet::empty(), &ctx).await?;
5275 100 : tenant
5276 100 : .gc_iteration(Some(tline.timeline_id), 0, Duration::ZERO, &cancel, &ctx)
5277 94 : .await?;
5278 2 : }
5279 2 :
5280 2 : Ok(())
5281 2 : }
5282 :
5283 : #[tokio::test]
5284 2 : async fn test_traverse_ancestors() -> anyhow::Result<()> {
5285 2 : let (tenant, ctx) = TenantHarness::create("test_traverse_ancestors")?
5286 2 : .load()
5287 8 : .await;
5288 2 : let mut tline = tenant
5289 2 : .create_test_timeline(TIMELINE_ID, Lsn(0x10), DEFAULT_PG_VERSION, &ctx)
5290 6 : .await?;
5291 2 :
5292 2 : const NUM_KEYS: usize = 100;
5293 2 : const NUM_TLINES: usize = 50;
5294 2 :
5295 2 : let mut test_key = Key::from_hex("010000000033333333444444445500000000").unwrap();
5296 2 : // Track page mutation lsns across different timelines.
5297 2 : let mut updated = [[Lsn(0); NUM_KEYS]; NUM_TLINES];
5298 2 :
5299 2 : let mut lsn = Lsn(0x10);
5300 2 :
5301 2 : #[allow(clippy::needless_range_loop)]
5302 102 : for idx in 0..NUM_TLINES {
5303 100 : let new_tline_id = TimelineId::generate();
5304 100 : tenant
5305 100 : .branch_timeline_test(&tline, new_tline_id, Some(lsn), &ctx)
5306 2 : .await?;
5307 100 : tline = tenant
5308 100 : .get_timeline(new_tline_id, true)
5309 100 : .expect("Should have the branched timeline");
5310 2 :
5311 10100 : for _ in 0..NUM_KEYS {
5312 10000 : lsn = Lsn(lsn.0 + 0x10);
5313 10000 : let blknum = thread_rng().gen_range(0..NUM_KEYS);
5314 10000 : test_key.field6 = blknum as u32;
5315 10000 : let mut writer = tline.writer().await;
5316 10000 : writer
5317 10000 : .put(
5318 10000 : test_key,
5319 10000 : lsn,
5320 10000 : &Value::Image(test_img(&format!("{} {} at {}", idx, blknum, lsn))),
5321 10000 : &ctx,
5322 10000 : )
5323 88 : .await?;
5324 10000 : println!("updating [{}][{}] at {}", idx, blknum, lsn);
5325 10000 : writer.finish_write(lsn);
5326 10000 : drop(writer);
5327 10000 : updated[idx][blknum] = lsn;
5328 2 : }
5329 2 : }
5330 2 :
5331 2 : // Read pages from leaf timeline across all ancestors.
5332 100 : for (idx, lsns) in updated.iter().enumerate() {
5333 10000 : for (blknum, lsn) in lsns.iter().enumerate() {
5334 2 : // Skip empty mutations.
5335 10000 : if lsn.0 == 0 {
5336 3689 : continue;
5337 6311 : }
5338 6311 : println!("checking [{idx}][{blknum}] at {lsn}");
5339 6311 : test_key.field6 = blknum as u32;
5340 6311 : assert_eq!(
5341 6311 : tline.get(test_key, *lsn, &ctx).await?,
5342 6311 : test_img(&format!("{idx} {blknum} at {lsn}"))
5343 2 : );
5344 2 : }
5345 2 : }
5346 2 : Ok(())
5347 2 : }
5348 :
5349 : #[tokio::test]
5350 2 : async fn test_write_at_initdb_lsn_takes_optimization_code_path() -> anyhow::Result<()> {
5351 2 : let (tenant, ctx) = TenantHarness::create("test_empty_test_timeline_is_usable")?
5352 2 : .load()
5353 8 : .await;
5354 2 :
5355 2 : let initdb_lsn = Lsn(0x20);
5356 2 : let utline = tenant
5357 2 : .create_empty_timeline(TIMELINE_ID, initdb_lsn, DEFAULT_PG_VERSION, &ctx)
5358 2 : .await?;
5359 2 : let tline = utline.raw_timeline().unwrap();
5360 2 :
5361 2 : // Spawn flush loop now so that we can set the `expect_initdb_optimization`
5362 2 : tline.maybe_spawn_flush_loop();
5363 2 :
5364 2 : // Make sure the timeline has the minimum set of required keys for operation.
5365 2 : // The only operation you can always do on an empty timeline is to `put` new data.
5366 2 : // Except if you `put` at `initdb_lsn`.
5367 2 : // In that case, there's an optimization to directly create image layers instead of delta layers.
5368 2 : // It uses `repartition()`, which assumes some keys to be present.
5369 2 : // Let's make sure the test timeline can handle that case.
5370 2 : {
5371 2 : let mut state = tline.flush_loop_state.lock().unwrap();
5372 2 : assert_eq!(
5373 2 : timeline::FlushLoopState::Running {
5374 2 : expect_initdb_optimization: false,
5375 2 : initdb_optimization_count: 0,
5376 2 : },
5377 2 : *state
5378 2 : );
5379 2 : *state = timeline::FlushLoopState::Running {
5380 2 : expect_initdb_optimization: true,
5381 2 : initdb_optimization_count: 0,
5382 2 : };
5383 2 : }
5384 2 :
5385 2 : // Make writes at the initdb_lsn. When we flush it below, it should be handled by the optimization.
5386 2 : // As explained above, the optimization requires some keys to be present.
5387 2 : // As per `create_empty_timeline` documentation, use init_empty to set them.
5388 2 : // This is what `create_test_timeline` does, by the way.
5389 2 : let mut modification = tline.begin_modification(initdb_lsn);
5390 2 : modification
5391 2 : .init_empty_test_timeline()
5392 2 : .context("init_empty_test_timeline")?;
5393 2 : modification
5394 2 : .commit(&ctx)
5395 2 : .await
5396 2 : .context("commit init_empty_test_timeline modification")?;
5397 2 :
5398 2 : // Do the flush. The flush code will check the expectations that we set above.
5399 2 : tline.freeze_and_flush().await?;
5400 2 :
5401 2 : // assert freeze_and_flush exercised the initdb optimization
5402 2 : {
5403 2 : let state = tline.flush_loop_state.lock().unwrap();
5404 2 : let timeline::FlushLoopState::Running {
5405 2 : expect_initdb_optimization,
5406 2 : initdb_optimization_count,
5407 2 : } = *state
5408 2 : else {
5409 2 : panic!("unexpected state: {:?}", *state);
5410 2 : };
5411 2 : assert!(expect_initdb_optimization);
5412 2 : assert!(initdb_optimization_count > 0);
5413 2 : }
5414 2 : Ok(())
5415 2 : }
5416 :
5417 : #[tokio::test]
5418 2 : async fn test_create_guard_crash() -> anyhow::Result<()> {
5419 2 : let name = "test_create_guard_crash";
5420 2 : let harness = TenantHarness::create(name)?;
5421 2 : {
5422 8 : let (tenant, ctx) = harness.load().await;
5423 2 : let tline = tenant
5424 2 : .create_empty_timeline(TIMELINE_ID, Lsn(0), DEFAULT_PG_VERSION, &ctx)
5425 2 : .await?;
5426 2 : // Leave the timeline ID in [`Tenant::timelines_creating`] to exclude attempting to create it again
5427 2 : let raw_tline = tline.raw_timeline().unwrap();
5428 2 : raw_tline
5429 2 : .shutdown(super::timeline::ShutdownMode::Hard)
5430 2 : .instrument(info_span!("test_shutdown", tenant_id=%raw_tline.tenant_shard_id, shard_id=%raw_tline.tenant_shard_id.shard_slug(), timeline_id=%TIMELINE_ID))
5431 2 : .await;
5432 2 : std::mem::forget(tline);
5433 2 : }
5434 2 :
5435 8 : let (tenant, _) = harness.load().await;
5436 2 : match tenant.get_timeline(TIMELINE_ID, false) {
5437 2 : Ok(_) => panic!("timeline should've been removed during load"),
5438 2 : Err(e) => {
5439 2 : assert_eq!(
5440 2 : e,
5441 2 : GetTimelineError::NotFound {
5442 2 : tenant_id: tenant.tenant_shard_id,
5443 2 : timeline_id: TIMELINE_ID,
5444 2 : }
5445 2 : )
5446 2 : }
5447 2 : }
5448 2 :
5449 2 : assert!(!harness
5450 2 : .conf
5451 2 : .timeline_path(&tenant.tenant_shard_id, &TIMELINE_ID)
5452 2 : .exists());
5453 2 :
5454 2 : Ok(())
5455 2 : }
5456 :
5457 : #[tokio::test]
5458 2 : async fn test_read_at_max_lsn() -> anyhow::Result<()> {
5459 2 : let names_algorithms = [
5460 2 : ("test_read_at_max_lsn_legacy", CompactionAlgorithm::Legacy),
5461 2 : ("test_read_at_max_lsn_tiered", CompactionAlgorithm::Tiered),
5462 2 : ];
5463 6 : for (name, algorithm) in names_algorithms {
5464 32937 : test_read_at_max_lsn_algorithm(name, algorithm).await?;
5465 2 : }
5466 2 : Ok(())
5467 2 : }
5468 :
5469 4 : async fn test_read_at_max_lsn_algorithm(
5470 4 : name: &'static str,
5471 4 : compaction_algorithm: CompactionAlgorithm,
5472 4 : ) -> anyhow::Result<()> {
5473 4 : let mut harness = TenantHarness::create(name)?;
5474 4 : harness.tenant_conf.compaction_algorithm = CompactionAlgorithmSettings {
5475 4 : kind: compaction_algorithm,
5476 4 : };
5477 16 : let (tenant, ctx) = harness.load().await;
5478 4 : let tline = tenant
5479 4 : .create_test_timeline(TIMELINE_ID, Lsn(0x08), DEFAULT_PG_VERSION, &ctx)
5480 11 : .await?;
5481 :
5482 4 : let lsn = Lsn(0x10);
5483 4 : let compact = false;
5484 32600 : bulk_insert_maybe_compact_gc(&tenant, &tline, &ctx, lsn, 50, 10000, compact).await?;
5485 :
5486 4 : let test_key = Key::from_hex("010000000033333333444444445500000000").unwrap();
5487 4 : let read_lsn = Lsn(u64::MAX - 1);
5488 :
5489 310 : let result = tline.get(test_key, read_lsn, &ctx).await;
5490 4 : assert!(result.is_ok(), "result is not Ok: {}", result.unwrap_err());
5491 :
5492 4 : Ok(())
5493 4 : }
5494 :
5495 : #[tokio::test]
5496 2 : async fn test_metadata_scan() -> anyhow::Result<()> {
5497 2 : let harness = TenantHarness::create("test_metadata_scan")?;
5498 8 : let (tenant, ctx) = harness.load().await;
5499 2 : let tline = tenant
5500 2 : .create_test_timeline(TIMELINE_ID, Lsn(0x10), DEFAULT_PG_VERSION, &ctx)
5501 6 : .await?;
5502 2 :
5503 2 : const NUM_KEYS: usize = 1000;
5504 2 : const STEP: usize = 10000; // random update + scan base_key + idx * STEP
5505 2 :
5506 2 : let cancel = CancellationToken::new();
5507 2 :
5508 2 : let mut base_key = Key::from_hex("000000000033333333444444445500000000").unwrap();
5509 2 : base_key.field1 = AUX_KEY_PREFIX;
5510 2 : let mut test_key = base_key;
5511 2 :
5512 2 : // Track when each page was last modified. Used to assert that
5513 2 : // a read sees the latest page version.
5514 2 : let mut updated = [Lsn(0); NUM_KEYS];
5515 2 :
5516 2 : let mut lsn = Lsn(0x10);
5517 2 : #[allow(clippy::needless_range_loop)]
5518 2002 : for blknum in 0..NUM_KEYS {
5519 2000 : lsn = Lsn(lsn.0 + 0x10);
5520 2000 : test_key.field6 = (blknum * STEP) as u32;
5521 2000 : let mut writer = tline.writer().await;
5522 2000 : writer
5523 2000 : .put(
5524 2000 : test_key,
5525 2000 : lsn,
5526 2000 : &Value::Image(test_img(&format!("{} at {}", blknum, lsn))),
5527 2000 : &ctx,
5528 2000 : )
5529 2 : .await?;
5530 2000 : writer.finish_write(lsn);
5531 2000 : updated[blknum] = lsn;
5532 2000 : drop(writer);
5533 2 : }
5534 2 :
5535 2 : let keyspace = KeySpace::single(base_key..base_key.add((NUM_KEYS * STEP) as u32));
5536 2 :
5537 24 : for iter in 0..=10 {
5538 2 : // Read all the blocks
5539 22000 : for (blknum, last_lsn) in updated.iter().enumerate() {
5540 22000 : test_key.field6 = (blknum * STEP) as u32;
5541 22000 : assert_eq!(
5542 22000 : tline.get(test_key, lsn, &ctx).await?,
5543 22000 : test_img(&format!("{} at {}", blknum, last_lsn))
5544 2 : );
5545 2 : }
5546 2 :
5547 22 : let mut cnt = 0;
5548 22000 : for (key, value) in tline
5549 22 : .get_vectored_impl(
5550 22 : keyspace.clone(),
5551 22 : lsn,
5552 22 : &mut ValuesReconstructState::default(),
5553 22 : &ctx,
5554 22 : )
5555 5561 : .await?
5556 2 : {
5557 22000 : let blknum = key.field6 as usize;
5558 22000 : let value = value?;
5559 22000 : assert!(blknum % STEP == 0);
5560 22000 : let blknum = blknum / STEP;
5561 22000 : assert_eq!(
5562 22000 : value,
5563 22000 : test_img(&format!("{} at {}", blknum, updated[blknum]))
5564 22000 : );
5565 22000 : cnt += 1;
5566 2 : }
5567 2 :
5568 22 : assert_eq!(cnt, NUM_KEYS);
5569 2 :
5570 22022 : for _ in 0..NUM_KEYS {
5571 22000 : lsn = Lsn(lsn.0 + 0x10);
5572 22000 : let blknum = thread_rng().gen_range(0..NUM_KEYS);
5573 22000 : test_key.field6 = (blknum * STEP) as u32;
5574 22000 : let mut writer = tline.writer().await;
5575 22000 : writer
5576 22000 : .put(
5577 22000 : test_key,
5578 22000 : lsn,
5579 22000 : &Value::Image(test_img(&format!("{} at {}", blknum, lsn))),
5580 22000 : &ctx,
5581 22000 : )
5582 111 : .await?;
5583 22000 : writer.finish_write(lsn);
5584 22000 : drop(writer);
5585 22000 : updated[blknum] = lsn;
5586 2 : }
5587 2 :
5588 2 : // Perform two cycles of flush, compact, and GC
5589 66 : for round in 0..2 {
5590 44 : tline.freeze_and_flush().await?;
5591 44 : tline
5592 44 : .compact(
5593 44 : &cancel,
5594 44 : if iter % 5 == 0 && round == 0 {
5595 6 : let mut flags = EnumSet::new();
5596 6 : flags.insert(CompactFlags::ForceImageLayerCreation);
5597 6 : flags.insert(CompactFlags::ForceRepartition);
5598 6 : flags
5599 2 : } else {
5600 38 : EnumSet::empty()
5601 2 : },
5602 44 : &ctx,
5603 2 : )
5604 8926 : .await?;
5605 44 : tenant
5606 44 : .gc_iteration(Some(tline.timeline_id), 0, Duration::ZERO, &cancel, &ctx)
5607 40 : .await?;
5608 2 : }
5609 2 : }
5610 2 :
5611 2 : Ok(())
5612 2 : }
5613 :
5614 : #[tokio::test]
5615 2 : async fn test_metadata_compaction_trigger() -> anyhow::Result<()> {
5616 2 : let harness = TenantHarness::create("test_metadata_compaction_trigger")?;
5617 8 : let (tenant, ctx) = harness.load().await;
5618 2 : let tline = tenant
5619 2 : .create_test_timeline(TIMELINE_ID, Lsn(0x10), DEFAULT_PG_VERSION, &ctx)
5620 6 : .await?;
5621 2 :
5622 2 : let cancel = CancellationToken::new();
5623 2 :
5624 2 : let mut base_key = Key::from_hex("000000000033333333444444445500000000").unwrap();
5625 2 : base_key.field1 = AUX_KEY_PREFIX;
5626 2 : let test_key = base_key;
5627 2 : let mut lsn = Lsn(0x10);
5628 2 :
5629 42 : for _ in 0..20 {
5630 40 : lsn = Lsn(lsn.0 + 0x10);
5631 40 : let mut writer = tline.writer().await;
5632 40 : writer
5633 40 : .put(
5634 40 : test_key,
5635 40 : lsn,
5636 40 : &Value::Image(test_img(&format!("{} at {}", 0, lsn))),
5637 40 : &ctx,
5638 40 : )
5639 20 : .await?;
5640 40 : writer.finish_write(lsn);
5641 40 : drop(writer);
5642 40 : tline.freeze_and_flush().await?; // force create a delta layer
5643 2 : }
5644 2 :
5645 2 : let before_num_l0_delta_files = tline
5646 2 : .layers
5647 2 : .read()
5648 2 : .await
5649 2 : .layer_map()
5650 2 : .get_level0_deltas()?
5651 2 : .len();
5652 2 :
5653 110 : tline.compact(&cancel, EnumSet::empty(), &ctx).await?;
5654 2 :
5655 2 : let after_num_l0_delta_files = tline
5656 2 : .layers
5657 2 : .read()
5658 2 : .await
5659 2 : .layer_map()
5660 2 : .get_level0_deltas()?
5661 2 : .len();
5662 2 :
5663 2 : assert!(after_num_l0_delta_files < before_num_l0_delta_files, "after_num_l0_delta_files={after_num_l0_delta_files}, before_num_l0_delta_files={before_num_l0_delta_files}");
5664 2 :
5665 2 : assert_eq!(
5666 4 : tline.get(test_key, lsn, &ctx).await?,
5667 2 : test_img(&format!("{} at {}", 0, lsn))
5668 2 : );
5669 2 :
5670 2 : Ok(())
5671 2 : }
5672 :
5673 : #[tokio::test]
5674 2 : async fn test_branch_copies_dirty_aux_file_flag() {
5675 2 : let harness = TenantHarness::create("test_branch_copies_dirty_aux_file_flag").unwrap();
5676 2 :
5677 2 : // the default aux file policy to switch is v1 if not set by the admins
5678 2 : assert_eq!(
5679 2 : harness.tenant_conf.switch_aux_file_policy,
5680 2 : AuxFilePolicy::V1
5681 2 : );
5682 8 : let (tenant, ctx) = harness.load().await;
5683 2 :
5684 2 : let mut lsn = Lsn(0x08);
5685 2 :
5686 2 : let tline: Arc<Timeline> = tenant
5687 2 : .create_test_timeline(TIMELINE_ID, lsn, DEFAULT_PG_VERSION, &ctx)
5688 6 : .await
5689 2 : .unwrap();
5690 2 :
5691 2 : // no aux file is written at this point, so the persistent flag should be unset
5692 2 : assert_eq!(tline.last_aux_file_policy.load(), None);
5693 2 :
5694 2 : {
5695 2 : lsn += 8;
5696 2 : let mut modification = tline.begin_modification(lsn);
5697 2 : modification
5698 2 : .put_file("pg_logical/mappings/test1", b"first", &ctx)
5699 4 : .await
5700 2 : .unwrap();
5701 2 : modification.commit(&ctx).await.unwrap();
5702 2 : }
5703 2 :
5704 2 : // there is no tenant manager to pass the configuration through, so lets mimic it
5705 2 : tenant.set_new_location_config(
5706 2 : AttachedTenantConf::try_from(LocationConf::attached_single(
5707 2 : TenantConfOpt {
5708 2 : switch_aux_file_policy: Some(AuxFilePolicy::V2),
5709 2 : ..Default::default()
5710 2 : },
5711 2 : tenant.generation,
5712 2 : &pageserver_api::models::ShardParameters::default(),
5713 2 : ))
5714 2 : .unwrap(),
5715 2 : );
5716 2 :
5717 2 : assert_eq!(
5718 2 : tline.get_switch_aux_file_policy(),
5719 2 : AuxFilePolicy::V2,
5720 2 : "wanted state has been updated"
5721 2 : );
5722 2 : assert_eq!(
5723 2 : tline.last_aux_file_policy.load(),
5724 2 : Some(AuxFilePolicy::V1),
5725 2 : "aux file is written with switch_aux_file_policy unset (which is v1), so we should keep v1"
5726 2 : );
5727 2 :
5728 2 : // we can read everything from the storage
5729 2 : let files = tline.list_aux_files(lsn, &ctx).await.unwrap();
5730 2 : assert_eq!(
5731 2 : files.get("pg_logical/mappings/test1"),
5732 2 : Some(&bytes::Bytes::from_static(b"first"))
5733 2 : );
5734 2 :
5735 2 : {
5736 2 : lsn += 8;
5737 2 : let mut modification = tline.begin_modification(lsn);
5738 2 : modification
5739 2 : .put_file("pg_logical/mappings/test2", b"second", &ctx)
5740 2 : .await
5741 2 : .unwrap();
5742 2 : modification.commit(&ctx).await.unwrap();
5743 2 : }
5744 2 :
5745 2 : assert_eq!(
5746 2 : tline.last_aux_file_policy.load(),
5747 2 : Some(AuxFilePolicy::V1),
5748 2 : "keep v1 storage format when new files are written"
5749 2 : );
5750 2 :
5751 2 : let files = tline.list_aux_files(lsn, &ctx).await.unwrap();
5752 2 : assert_eq!(
5753 2 : files.get("pg_logical/mappings/test2"),
5754 2 : Some(&bytes::Bytes::from_static(b"second"))
5755 2 : );
5756 2 :
5757 2 : let child = tenant
5758 2 : .branch_timeline_test(&tline, NEW_TIMELINE_ID, Some(lsn), &ctx)
5759 2 : .await
5760 2 : .unwrap();
5761 2 :
5762 2 : // child copies the last flag even if that is not on remote storage yet
5763 2 : assert_eq!(child.get_switch_aux_file_policy(), AuxFilePolicy::V2);
5764 2 : assert_eq!(child.last_aux_file_policy.load(), Some(AuxFilePolicy::V1));
5765 2 :
5766 2 : let files = child.list_aux_files(lsn, &ctx).await.unwrap();
5767 2 : assert_eq!(files.get("pg_logical/mappings/test1"), None);
5768 2 : assert_eq!(files.get("pg_logical/mappings/test2"), None);
5769 2 :
5770 2 : // even if we crash here without flushing parent timeline with it's new
5771 2 : // last_aux_file_policy we are safe, because child was never meant to access ancestor's
5772 2 : // files. the ancestor can even switch back to V1 because of a migration safely.
5773 2 : }
5774 :
5775 : #[tokio::test]
5776 2 : async fn aux_file_policy_switch() {
5777 2 : let mut harness = TenantHarness::create("aux_file_policy_switch").unwrap();
5778 2 : harness.tenant_conf.switch_aux_file_policy = AuxFilePolicy::CrossValidation; // set to cross-validation mode
5779 8 : let (tenant, ctx) = harness.load().await;
5780 2 :
5781 2 : let mut lsn = Lsn(0x08);
5782 2 :
5783 2 : let tline: Arc<Timeline> = tenant
5784 2 : .create_test_timeline(TIMELINE_ID, lsn, DEFAULT_PG_VERSION, &ctx)
5785 6 : .await
5786 2 : .unwrap();
5787 2 :
5788 2 : assert_eq!(
5789 2 : tline.last_aux_file_policy.load(),
5790 2 : None,
5791 2 : "no aux file is written so it should be unset"
5792 2 : );
5793 2 :
5794 2 : {
5795 2 : lsn += 8;
5796 2 : let mut modification = tline.begin_modification(lsn);
5797 2 : modification
5798 2 : .put_file("pg_logical/mappings/test1", b"first", &ctx)
5799 4 : .await
5800 2 : .unwrap();
5801 2 : modification.commit(&ctx).await.unwrap();
5802 2 : }
5803 2 :
5804 2 : // there is no tenant manager to pass the configuration through, so lets mimic it
5805 2 : tenant.set_new_location_config(
5806 2 : AttachedTenantConf::try_from(LocationConf::attached_single(
5807 2 : TenantConfOpt {
5808 2 : switch_aux_file_policy: Some(AuxFilePolicy::V2),
5809 2 : ..Default::default()
5810 2 : },
5811 2 : tenant.generation,
5812 2 : &pageserver_api::models::ShardParameters::default(),
5813 2 : ))
5814 2 : .unwrap(),
5815 2 : );
5816 2 :
5817 2 : assert_eq!(
5818 2 : tline.get_switch_aux_file_policy(),
5819 2 : AuxFilePolicy::V2,
5820 2 : "wanted state has been updated"
5821 2 : );
5822 2 : assert_eq!(
5823 2 : tline.last_aux_file_policy.load(),
5824 2 : Some(AuxFilePolicy::CrossValidation),
5825 2 : "dirty index_part.json reflected state is yet to be updated"
5826 2 : );
5827 2 :
5828 2 : // we can still read the auxfile v1 before we ingest anything new
5829 2 : let files = tline.list_aux_files(lsn, &ctx).await.unwrap();
5830 2 : assert_eq!(
5831 2 : files.get("pg_logical/mappings/test1"),
5832 2 : Some(&bytes::Bytes::from_static(b"first"))
5833 2 : );
5834 2 :
5835 2 : {
5836 2 : lsn += 8;
5837 2 : let mut modification = tline.begin_modification(lsn);
5838 2 : modification
5839 2 : .put_file("pg_logical/mappings/test2", b"second", &ctx)
5840 2 : .await
5841 2 : .unwrap();
5842 2 : modification.commit(&ctx).await.unwrap();
5843 2 : }
5844 2 :
5845 2 : assert_eq!(
5846 2 : tline.last_aux_file_policy.load(),
5847 2 : Some(AuxFilePolicy::V2),
5848 2 : "ingesting a file should apply the wanted switch state when applicable"
5849 2 : );
5850 2 :
5851 2 : let files = tline.list_aux_files(lsn, &ctx).await.unwrap();
5852 2 : assert_eq!(
5853 2 : files.get("pg_logical/mappings/test1"),
5854 2 : Some(&bytes::Bytes::from_static(b"first")),
5855 2 : "cross validation writes to both v1 and v2 so this should be available in v2"
5856 2 : );
5857 2 : assert_eq!(
5858 2 : files.get("pg_logical/mappings/test2"),
5859 2 : Some(&bytes::Bytes::from_static(b"second"))
5860 2 : );
5861 2 :
5862 2 : // mimic again by trying to flip it from V2 to V1 (not switched to while ingesting a file)
5863 2 : tenant.set_new_location_config(
5864 2 : AttachedTenantConf::try_from(LocationConf::attached_single(
5865 2 : TenantConfOpt {
5866 2 : switch_aux_file_policy: Some(AuxFilePolicy::V1),
5867 2 : ..Default::default()
5868 2 : },
5869 2 : tenant.generation,
5870 2 : &pageserver_api::models::ShardParameters::default(),
5871 2 : ))
5872 2 : .unwrap(),
5873 2 : );
5874 2 :
5875 2 : {
5876 2 : lsn += 8;
5877 2 : let mut modification = tline.begin_modification(lsn);
5878 2 : modification
5879 2 : .put_file("pg_logical/mappings/test2", b"third", &ctx)
5880 2 : .await
5881 2 : .unwrap();
5882 2 : modification.commit(&ctx).await.unwrap();
5883 2 : }
5884 2 :
5885 2 : assert_eq!(
5886 2 : tline.get_switch_aux_file_policy(),
5887 2 : AuxFilePolicy::V1,
5888 2 : "wanted state has been updated again, even if invalid request"
5889 2 : );
5890 2 :
5891 2 : assert_eq!(
5892 2 : tline.last_aux_file_policy.load(),
5893 2 : Some(AuxFilePolicy::V2),
5894 2 : "ingesting a file should apply the wanted switch state when applicable"
5895 2 : );
5896 2 :
5897 2 : let files = tline.list_aux_files(lsn, &ctx).await.unwrap();
5898 2 : assert_eq!(
5899 2 : files.get("pg_logical/mappings/test1"),
5900 2 : Some(&bytes::Bytes::from_static(b"first"))
5901 2 : );
5902 2 : assert_eq!(
5903 2 : files.get("pg_logical/mappings/test2"),
5904 2 : Some(&bytes::Bytes::from_static(b"third"))
5905 2 : );
5906 2 :
5907 2 : // mimic again by trying to flip it from from V1 to V2 (not switched to while ingesting a file)
5908 2 : tenant.set_new_location_config(
5909 2 : AttachedTenantConf::try_from(LocationConf::attached_single(
5910 2 : TenantConfOpt {
5911 2 : switch_aux_file_policy: Some(AuxFilePolicy::V2),
5912 2 : ..Default::default()
5913 2 : },
5914 2 : tenant.generation,
5915 2 : &pageserver_api::models::ShardParameters::default(),
5916 2 : ))
5917 2 : .unwrap(),
5918 2 : );
5919 2 :
5920 2 : {
5921 2 : lsn += 8;
5922 2 : let mut modification = tline.begin_modification(lsn);
5923 2 : modification
5924 2 : .put_file("pg_logical/mappings/test3", b"last", &ctx)
5925 2 : .await
5926 2 : .unwrap();
5927 2 : modification.commit(&ctx).await.unwrap();
5928 2 : }
5929 2 :
5930 2 : assert_eq!(tline.get_switch_aux_file_policy(), AuxFilePolicy::V2);
5931 2 :
5932 2 : assert_eq!(tline.last_aux_file_policy.load(), Some(AuxFilePolicy::V2));
5933 2 :
5934 2 : let files = tline.list_aux_files(lsn, &ctx).await.unwrap();
5935 2 : assert_eq!(
5936 2 : files.get("pg_logical/mappings/test1"),
5937 2 : Some(&bytes::Bytes::from_static(b"first"))
5938 2 : );
5939 2 : assert_eq!(
5940 2 : files.get("pg_logical/mappings/test2"),
5941 2 : Some(&bytes::Bytes::from_static(b"third"))
5942 2 : );
5943 2 : assert_eq!(
5944 2 : files.get("pg_logical/mappings/test3"),
5945 2 : Some(&bytes::Bytes::from_static(b"last"))
5946 2 : );
5947 2 : }
5948 :
5949 : #[tokio::test]
5950 2 : async fn aux_file_policy_force_switch() {
5951 2 : let mut harness = TenantHarness::create("aux_file_policy_force_switch").unwrap();
5952 2 : harness.tenant_conf.switch_aux_file_policy = AuxFilePolicy::V1;
5953 8 : let (tenant, ctx) = harness.load().await;
5954 2 :
5955 2 : let mut lsn = Lsn(0x08);
5956 2 :
5957 2 : let tline: Arc<Timeline> = tenant
5958 2 : .create_test_timeline(TIMELINE_ID, lsn, DEFAULT_PG_VERSION, &ctx)
5959 5 : .await
5960 2 : .unwrap();
5961 2 :
5962 2 : assert_eq!(
5963 2 : tline.last_aux_file_policy.load(),
5964 2 : None,
5965 2 : "no aux file is written so it should be unset"
5966 2 : );
5967 2 :
5968 2 : {
5969 2 : lsn += 8;
5970 2 : let mut modification = tline.begin_modification(lsn);
5971 2 : modification
5972 2 : .put_file("pg_logical/mappings/test1", b"first", &ctx)
5973 4 : .await
5974 2 : .unwrap();
5975 2 : modification.commit(&ctx).await.unwrap();
5976 2 : }
5977 2 :
5978 2 : tline.do_switch_aux_policy(AuxFilePolicy::V2).unwrap();
5979 2 :
5980 2 : assert_eq!(
5981 2 : tline.last_aux_file_policy.load(),
5982 2 : Some(AuxFilePolicy::V2),
5983 2 : "dirty index_part.json reflected state is yet to be updated"
5984 2 : );
5985 2 :
5986 2 : // lose all data from v1
5987 2 : let files = tline.list_aux_files(lsn, &ctx).await.unwrap();
5988 2 : assert_eq!(files.get("pg_logical/mappings/test1"), None);
5989 2 :
5990 2 : {
5991 2 : lsn += 8;
5992 2 : let mut modification = tline.begin_modification(lsn);
5993 2 : modification
5994 2 : .put_file("pg_logical/mappings/test2", b"second", &ctx)
5995 2 : .await
5996 2 : .unwrap();
5997 2 : modification.commit(&ctx).await.unwrap();
5998 2 : }
5999 2 :
6000 2 : // read data ingested in v2
6001 2 : let files = tline.list_aux_files(lsn, &ctx).await.unwrap();
6002 2 : assert_eq!(
6003 2 : files.get("pg_logical/mappings/test2"),
6004 2 : Some(&bytes::Bytes::from_static(b"second"))
6005 2 : );
6006 2 : // lose all data from v1
6007 2 : assert_eq!(files.get("pg_logical/mappings/test1"), None);
6008 2 : }
6009 :
6010 : #[tokio::test]
6011 2 : async fn aux_file_policy_auto_detect() {
6012 2 : let mut harness = TenantHarness::create("aux_file_policy_auto_detect").unwrap();
6013 2 : harness.tenant_conf.switch_aux_file_policy = AuxFilePolicy::V2; // set to cross-validation mode
6014 8 : let (tenant, ctx) = harness.load().await;
6015 2 :
6016 2 : let mut lsn = Lsn(0x08);
6017 2 :
6018 2 : let tline: Arc<Timeline> = tenant
6019 2 : .create_test_timeline(TIMELINE_ID, lsn, DEFAULT_PG_VERSION, &ctx)
6020 5 : .await
6021 2 : .unwrap();
6022 2 :
6023 2 : assert_eq!(
6024 2 : tline.last_aux_file_policy.load(),
6025 2 : None,
6026 2 : "no aux file is written so it should be unset"
6027 2 : );
6028 2 :
6029 2 : {
6030 2 : lsn += 8;
6031 2 : let mut modification = tline.begin_modification(lsn);
6032 2 : let buf = AuxFilesDirectory::ser(&AuxFilesDirectory {
6033 2 : files: vec![(
6034 2 : "test_file".to_string(),
6035 2 : Bytes::copy_from_slice(b"test_file"),
6036 2 : )]
6037 2 : .into_iter()
6038 2 : .collect(),
6039 2 : })
6040 2 : .unwrap();
6041 2 : modification.put_for_test(AUX_FILES_KEY, Value::Image(Bytes::from(buf)));
6042 2 : modification.commit(&ctx).await.unwrap();
6043 2 : }
6044 2 :
6045 2 : {
6046 2 : lsn += 8;
6047 2 : let mut modification = tline.begin_modification(lsn);
6048 2 : modification
6049 2 : .put_file("pg_logical/mappings/test1", b"first", &ctx)
6050 2 : .await
6051 2 : .unwrap();
6052 2 : modification.commit(&ctx).await.unwrap();
6053 2 : }
6054 2 :
6055 2 : assert_eq!(
6056 2 : tline.last_aux_file_policy.load(),
6057 2 : Some(AuxFilePolicy::V1),
6058 2 : "keep using v1 because there are aux files writting with v1"
6059 2 : );
6060 2 :
6061 2 : // we can still read the auxfile v1
6062 2 : let files = tline.list_aux_files(lsn, &ctx).await.unwrap();
6063 2 : assert_eq!(
6064 2 : files.get("pg_logical/mappings/test1"),
6065 2 : Some(&bytes::Bytes::from_static(b"first"))
6066 2 : );
6067 2 : assert_eq!(
6068 2 : files.get("test_file"),
6069 2 : Some(&bytes::Bytes::from_static(b"test_file"))
6070 2 : );
6071 2 : }
6072 :
6073 : #[tokio::test]
6074 2 : async fn test_metadata_image_creation() -> anyhow::Result<()> {
6075 2 : let harness = TenantHarness::create("test_metadata_image_creation")?;
6076 8 : let (tenant, ctx) = harness.load().await;
6077 2 : let tline = tenant
6078 2 : .create_test_timeline(TIMELINE_ID, Lsn(0x10), DEFAULT_PG_VERSION, &ctx)
6079 6 : .await?;
6080 2 :
6081 2 : const NUM_KEYS: usize = 1000;
6082 2 : const STEP: usize = 10000; // random update + scan base_key + idx * STEP
6083 2 :
6084 2 : let cancel = CancellationToken::new();
6085 2 :
6086 2 : let base_key = Key::from_hex("620000000033333333444444445500000000").unwrap();
6087 2 : assert_eq!(base_key.field1, AUX_KEY_PREFIX); // in case someone accidentally changed the prefix...
6088 2 : let mut test_key = base_key;
6089 2 : let mut lsn = Lsn(0x10);
6090 2 :
6091 8 : async fn scan_with_statistics(
6092 8 : tline: &Timeline,
6093 8 : keyspace: &KeySpace,
6094 8 : lsn: Lsn,
6095 8 : ctx: &RequestContext,
6096 8 : ) -> anyhow::Result<(BTreeMap<Key, Result<Bytes, PageReconstructError>>, usize)> {
6097 8 : let mut reconstruct_state = ValuesReconstructState::default();
6098 8 : let res = tline
6099 8 : .get_vectored_impl(keyspace.clone(), lsn, &mut reconstruct_state, ctx)
6100 1705 : .await?;
6101 8 : Ok((res, reconstruct_state.get_delta_layers_visited() as usize))
6102 8 : }
6103 2 :
6104 2 : #[allow(clippy::needless_range_loop)]
6105 2002 : for blknum in 0..NUM_KEYS {
6106 2000 : lsn = Lsn(lsn.0 + 0x10);
6107 2000 : test_key.field6 = (blknum * STEP) as u32;
6108 2000 : let mut writer = tline.writer().await;
6109 2000 : writer
6110 2000 : .put(
6111 2000 : test_key,
6112 2000 : lsn,
6113 2000 : &Value::Image(test_img(&format!("{} at {}", blknum, lsn))),
6114 2000 : &ctx,
6115 2000 : )
6116 2 : .await?;
6117 2000 : writer.finish_write(lsn);
6118 2000 : drop(writer);
6119 2 : }
6120 2 :
6121 2 : let keyspace = KeySpace::single(base_key..base_key.add((NUM_KEYS * STEP) as u32));
6122 2 :
6123 22 : for iter in 1..=10 {
6124 20020 : for _ in 0..NUM_KEYS {
6125 20000 : lsn = Lsn(lsn.0 + 0x10);
6126 20000 : let blknum = thread_rng().gen_range(0..NUM_KEYS);
6127 20000 : test_key.field6 = (blknum * STEP) as u32;
6128 20000 : let mut writer = tline.writer().await;
6129 20000 : writer
6130 20000 : .put(
6131 20000 : test_key,
6132 20000 : lsn,
6133 20000 : &Value::Image(test_img(&format!("{} at {}", blknum, lsn))),
6134 20000 : &ctx,
6135 20000 : )
6136 19 : .await?;
6137 20000 : writer.finish_write(lsn);
6138 20000 : drop(writer);
6139 2 : }
6140 2 :
6141 20 : tline.freeze_and_flush().await?;
6142 2 :
6143 20 : if iter % 5 == 0 {
6144 4 : let (_, before_delta_file_accessed) =
6145 1697 : scan_with_statistics(&tline, &keyspace, lsn, &ctx).await?;
6146 4 : tline
6147 4 : .compact(
6148 4 : &cancel,
6149 4 : {
6150 4 : let mut flags = EnumSet::new();
6151 4 : flags.insert(CompactFlags::ForceImageLayerCreation);
6152 4 : flags.insert(CompactFlags::ForceRepartition);
6153 4 : flags
6154 4 : },
6155 4 : &ctx,
6156 4 : )
6157 6531 : .await?;
6158 4 : let (_, after_delta_file_accessed) =
6159 8 : scan_with_statistics(&tline, &keyspace, lsn, &ctx).await?;
6160 4 : assert!(after_delta_file_accessed < before_delta_file_accessed, "after_delta_file_accessed={after_delta_file_accessed}, before_delta_file_accessed={before_delta_file_accessed}");
6161 2 : // Given that we already produced an image layer, there should be no delta layer needed for the scan, but still setting a low threshold there for unforeseen circumstances.
6162 4 : assert!(
6163 4 : after_delta_file_accessed <= 2,
6164 2 : "after_delta_file_accessed={after_delta_file_accessed}"
6165 2 : );
6166 16 : }
6167 2 : }
6168 2 :
6169 2 : Ok(())
6170 2 : }
6171 :
6172 : #[tokio::test]
6173 3 : async fn test_vectored_missing_data_key_reads() -> anyhow::Result<()> {
6174 3 : let harness = TenantHarness::create("test_vectored_missing_data_key_reads")?;
6175 12 : let (tenant, ctx) = harness.load().await;
6176 3 :
6177 3 : let base_key = Key::from_hex("000000000033333333444444445500000000").unwrap();
6178 3 : let base_key_child = Key::from_hex("000000000033333333444444445500000001").unwrap();
6179 3 : let base_key_nonexist = Key::from_hex("000000000033333333444444445500000002").unwrap();
6180 3 :
6181 3 : let tline = tenant
6182 3 : .create_test_timeline_with_layers(
6183 3 : TIMELINE_ID,
6184 3 : Lsn(0x10),
6185 3 : DEFAULT_PG_VERSION,
6186 3 : &ctx,
6187 3 : Vec::new(), // delta layers
6188 3 : vec![(Lsn(0x20), vec![(base_key, test_img("data key 1"))])], // image layers
6189 3 : Lsn(0x20), // it's fine to not advance LSN to 0x30 while using 0x30 to get below because `get_vectored_impl` does not wait for LSN
6190 3 : )
6191 13 : .await?;
6192 3 : tline.add_extra_test_dense_keyspace(KeySpace::single(base_key..(base_key_nonexist.next())));
6193 3 :
6194 3 : let child = tenant
6195 2 : .branch_timeline_test_with_layers(
6196 2 : &tline,
6197 2 : NEW_TIMELINE_ID,
6198 2 : Some(Lsn(0x20)),
6199 2 : &ctx,
6200 2 : Vec::new(), // delta layers
6201 2 : vec![(Lsn(0x30), vec![(base_key_child, test_img("data key 2"))])], // image layers
6202 2 : Lsn(0x30),
6203 2 : )
6204 7 : .await
6205 3 : .unwrap();
6206 2 :
6207 12 : async fn get_vectored_impl_wrapper(
6208 12 : tline: &Arc<Timeline>,
6209 12 : key: Key,
6210 12 : lsn: Lsn,
6211 12 : ctx: &RequestContext,
6212 12 : ) -> Result<Option<Bytes>, GetVectoredError> {
6213 12 : let mut reconstruct_state = ValuesReconstructState::new();
6214 12 : let mut res = tline
6215 12 : .get_vectored_impl(
6216 12 : KeySpace::single(key..key.next()),
6217 12 : lsn,
6218 12 : &mut reconstruct_state,
6219 12 : ctx,
6220 12 : )
6221 12 : .await?;
6222 6 : Ok(res.pop_last().map(|(k, v)| {
6223 6 : assert_eq!(k, key);
6224 6 : v.unwrap()
6225 6 : }))
6226 12 : }
6227 2 :
6228 2 : let lsn = Lsn(0x30);
6229 3 :
6230 3 : // test vectored get on parent timeline
6231 3 : assert_eq!(
6232 4 : get_vectored_impl_wrapper(&tline, base_key, lsn, &ctx).await?,
6233 3 : Some(test_img("data key 1"))
6234 3 : );
6235 3 : assert!(get_vectored_impl_wrapper(&tline, base_key_child, lsn, &ctx)
6236 3 : .await
6237 3 : .unwrap_err()
6238 2 : .is_missing_key_error());
6239 3 : assert!(
6240 2 : get_vectored_impl_wrapper(&tline, base_key_nonexist, lsn, &ctx)
6241 3 : .await
6242 3 : .unwrap_err()
6243 2 : .is_missing_key_error()
6244 3 : );
6245 3 :
6246 3 : // test vectored get on child timeline
6247 3 : assert_eq!(
6248 3 : get_vectored_impl_wrapper(&child, base_key, lsn, &ctx).await?,
6249 3 : Some(test_img("data key 1"))
6250 3 : );
6251 3 : assert_eq!(
6252 4 : get_vectored_impl_wrapper(&child, base_key_child, lsn, &ctx).await?,
6253 3 : Some(test_img("data key 2"))
6254 3 : );
6255 3 : assert!(
6256 2 : get_vectored_impl_wrapper(&child, base_key_nonexist, lsn, &ctx)
6257 3 : .await
6258 3 : .unwrap_err()
6259 2 : .is_missing_key_error()
6260 3 : );
6261 3 :
6262 3 : Ok(())
6263 3 : }
6264 :
6265 : #[tokio::test]
6266 2 : async fn test_vectored_missing_metadata_key_reads() -> anyhow::Result<()> {
6267 2 : let harness = TenantHarness::create("test_vectored_missing_data_key_reads")?;
6268 8 : let (tenant, ctx) = harness.load().await;
6269 2 :
6270 2 : let base_key = Key::from_hex("620000000033333333444444445500000000").unwrap();
6271 2 : let base_key_child = Key::from_hex("620000000033333333444444445500000001").unwrap();
6272 2 : let base_key_nonexist = Key::from_hex("620000000033333333444444445500000002").unwrap();
6273 2 : assert_eq!(base_key.field1, AUX_KEY_PREFIX); // in case someone accidentally changed the prefix...
6274 2 :
6275 2 : let tline = tenant
6276 2 : .create_test_timeline_with_layers(
6277 2 : TIMELINE_ID,
6278 2 : Lsn(0x10),
6279 2 : DEFAULT_PG_VERSION,
6280 2 : &ctx,
6281 2 : Vec::new(), // delta layers
6282 2 : vec![(Lsn(0x20), vec![(base_key, test_img("metadata key 1"))])], // image layers
6283 2 : Lsn(0x20), // it's fine to not advance LSN to 0x30 while using 0x30 to get below because `get_vectored_impl` does not wait for LSN
6284 2 : )
6285 13 : .await?;
6286 2 :
6287 2 : let child = tenant
6288 2 : .branch_timeline_test_with_layers(
6289 2 : &tline,
6290 2 : NEW_TIMELINE_ID,
6291 2 : Some(Lsn(0x20)),
6292 2 : &ctx,
6293 2 : Vec::new(), // delta layers
6294 2 : vec![(
6295 2 : Lsn(0x30),
6296 2 : vec![(base_key_child, test_img("metadata key 2"))],
6297 2 : )], // image layers
6298 2 : Lsn(0x30),
6299 2 : )
6300 7 : .await
6301 2 : .unwrap();
6302 2 :
6303 12 : async fn get_vectored_impl_wrapper(
6304 12 : tline: &Arc<Timeline>,
6305 12 : key: Key,
6306 12 : lsn: Lsn,
6307 12 : ctx: &RequestContext,
6308 12 : ) -> Result<Option<Bytes>, GetVectoredError> {
6309 12 : let mut reconstruct_state = ValuesReconstructState::new();
6310 12 : let mut res = tline
6311 12 : .get_vectored_impl(
6312 12 : KeySpace::single(key..key.next()),
6313 12 : lsn,
6314 12 : &mut reconstruct_state,
6315 12 : ctx,
6316 12 : )
6317 8 : .await?;
6318 12 : Ok(res.pop_last().map(|(k, v)| {
6319 4 : assert_eq!(k, key);
6320 4 : v.unwrap()
6321 12 : }))
6322 12 : }
6323 2 :
6324 2 : let lsn = Lsn(0x30);
6325 2 :
6326 2 : // test vectored get on parent timeline
6327 2 : assert_eq!(
6328 4 : get_vectored_impl_wrapper(&tline, base_key, lsn, &ctx).await?,
6329 2 : Some(test_img("metadata key 1"))
6330 2 : );
6331 2 : assert_eq!(
6332 2 : get_vectored_impl_wrapper(&tline, base_key_child, lsn, &ctx).await?,
6333 2 : None
6334 2 : );
6335 2 : assert_eq!(
6336 2 : get_vectored_impl_wrapper(&tline, base_key_nonexist, lsn, &ctx).await?,
6337 2 : None
6338 2 : );
6339 2 :
6340 2 : // test vectored get on child timeline
6341 2 : assert_eq!(
6342 2 : get_vectored_impl_wrapper(&child, base_key, lsn, &ctx).await?,
6343 2 : None
6344 2 : );
6345 2 : assert_eq!(
6346 4 : get_vectored_impl_wrapper(&child, base_key_child, lsn, &ctx).await?,
6347 2 : Some(test_img("metadata key 2"))
6348 2 : );
6349 2 : assert_eq!(
6350 2 : get_vectored_impl_wrapper(&child, base_key_nonexist, lsn, &ctx).await?,
6351 2 : None
6352 2 : );
6353 2 :
6354 2 : Ok(())
6355 2 : }
6356 :
6357 12 : async fn get_vectored_impl_wrapper(
6358 12 : tline: &Arc<Timeline>,
6359 12 : key: Key,
6360 12 : lsn: Lsn,
6361 12 : ctx: &RequestContext,
6362 12 : ) -> Result<Option<Bytes>, GetVectoredError> {
6363 12 : let mut reconstruct_state = ValuesReconstructState::new();
6364 12 : let mut res = tline
6365 12 : .get_vectored_impl(
6366 12 : KeySpace::single(key..key.next()),
6367 12 : lsn,
6368 12 : &mut reconstruct_state,
6369 12 : ctx,
6370 12 : )
6371 13 : .await?;
6372 12 : Ok(res.pop_last().map(|(k, v)| {
6373 8 : assert_eq!(k, key);
6374 8 : v.unwrap()
6375 12 : }))
6376 12 : }
6377 :
6378 : #[tokio::test]
6379 2 : async fn test_metadata_tombstone_reads() -> anyhow::Result<()> {
6380 2 : let harness = TenantHarness::create("test_metadata_tombstone_reads")?;
6381 8 : let (tenant, ctx) = harness.load().await;
6382 2 : let key0 = Key::from_hex("620000000033333333444444445500000000").unwrap();
6383 2 : let key1 = Key::from_hex("620000000033333333444444445500000001").unwrap();
6384 2 : let key2 = Key::from_hex("620000000033333333444444445500000002").unwrap();
6385 2 : let key3 = Key::from_hex("620000000033333333444444445500000003").unwrap();
6386 2 :
6387 2 : // We emulate the situation that the compaction algorithm creates an image layer that removes the tombstones
6388 2 : // Lsn 0x30 key0, key3, no key1+key2
6389 2 : // Lsn 0x20 key1+key2 tomestones
6390 2 : // Lsn 0x10 key1 in image, key2 in delta
6391 2 : let tline = tenant
6392 2 : .create_test_timeline_with_layers(
6393 2 : TIMELINE_ID,
6394 2 : Lsn(0x10),
6395 2 : DEFAULT_PG_VERSION,
6396 2 : &ctx,
6397 2 : // delta layers
6398 2 : vec![
6399 2 : vec![(key2, Lsn(0x10), Value::Image(test_img("metadata key 2")))],
6400 2 : vec![(key1, Lsn(0x20), Value::Image(Bytes::new()))],
6401 2 : vec![(key2, Lsn(0x20), Value::Image(Bytes::new()))],
6402 2 : ],
6403 2 : // image layers
6404 2 : vec![
6405 2 : (Lsn(0x10), vec![(key1, test_img("metadata key 1"))]),
6406 2 : (
6407 2 : Lsn(0x30),
6408 2 : vec![
6409 2 : (key0, test_img("metadata key 0")),
6410 2 : (key3, test_img("metadata key 3")),
6411 2 : ],
6412 2 : ),
6413 2 : ],
6414 2 : Lsn(0x30),
6415 2 : )
6416 40 : .await?;
6417 2 :
6418 2 : let lsn = Lsn(0x30);
6419 2 : let old_lsn = Lsn(0x20);
6420 2 :
6421 2 : assert_eq!(
6422 4 : get_vectored_impl_wrapper(&tline, key0, lsn, &ctx).await?,
6423 2 : Some(test_img("metadata key 0"))
6424 2 : );
6425 2 : assert_eq!(
6426 2 : get_vectored_impl_wrapper(&tline, key1, lsn, &ctx).await?,
6427 2 : None,
6428 2 : );
6429 2 : assert_eq!(
6430 2 : get_vectored_impl_wrapper(&tline, key2, lsn, &ctx).await?,
6431 2 : None,
6432 2 : );
6433 2 : assert_eq!(
6434 4 : get_vectored_impl_wrapper(&tline, key1, old_lsn, &ctx).await?,
6435 2 : Some(Bytes::new()),
6436 2 : );
6437 2 : assert_eq!(
6438 4 : get_vectored_impl_wrapper(&tline, key2, old_lsn, &ctx).await?,
6439 2 : Some(Bytes::new()),
6440 2 : );
6441 2 : assert_eq!(
6442 2 : get_vectored_impl_wrapper(&tline, key3, lsn, &ctx).await?,
6443 2 : Some(test_img("metadata key 3"))
6444 2 : );
6445 2 :
6446 2 : Ok(())
6447 2 : }
6448 :
6449 : #[tokio::test]
6450 2 : async fn test_metadata_tombstone_image_creation() {
6451 2 : let harness = TenantHarness::create("test_metadata_tombstone_image_creation").unwrap();
6452 8 : let (tenant, ctx) = harness.load().await;
6453 2 :
6454 2 : let key0 = Key::from_hex("620000000033333333444444445500000000").unwrap();
6455 2 : let key1 = Key::from_hex("620000000033333333444444445500000001").unwrap();
6456 2 : let key2 = Key::from_hex("620000000033333333444444445500000002").unwrap();
6457 2 : let key3 = Key::from_hex("620000000033333333444444445500000003").unwrap();
6458 2 :
6459 2 : let tline = tenant
6460 2 : .create_test_timeline_with_layers(
6461 2 : TIMELINE_ID,
6462 2 : Lsn(0x10),
6463 2 : DEFAULT_PG_VERSION,
6464 2 : &ctx,
6465 2 : // delta layers
6466 2 : vec![
6467 2 : vec![(key2, Lsn(0x10), Value::Image(test_img("metadata key 2")))],
6468 2 : vec![(key1, Lsn(0x20), Value::Image(Bytes::new()))],
6469 2 : vec![(key2, Lsn(0x20), Value::Image(Bytes::new()))],
6470 2 : vec![
6471 2 : (key0, Lsn(0x30), Value::Image(test_img("metadata key 0"))),
6472 2 : (key3, Lsn(0x30), Value::Image(test_img("metadata key 3"))),
6473 2 : ],
6474 2 : ],
6475 2 : // image layers
6476 2 : vec![(Lsn(0x10), vec![(key1, test_img("metadata key 1"))])],
6477 2 : Lsn(0x30),
6478 2 : )
6479 37 : .await
6480 2 : .unwrap();
6481 2 :
6482 2 : let cancel = CancellationToken::new();
6483 2 :
6484 2 : tline
6485 2 : .compact(
6486 2 : &cancel,
6487 2 : {
6488 2 : let mut flags = EnumSet::new();
6489 2 : flags.insert(CompactFlags::ForceImageLayerCreation);
6490 2 : flags.insert(CompactFlags::ForceRepartition);
6491 2 : flags
6492 2 : },
6493 2 : &ctx,
6494 2 : )
6495 49 : .await
6496 2 : .unwrap();
6497 2 :
6498 2 : // Image layers are created at last_record_lsn
6499 2 : let images = tline
6500 2 : .inspect_image_layers(Lsn(0x30), &ctx)
6501 8 : .await
6502 2 : .unwrap()
6503 2 : .into_iter()
6504 20 : .filter(|(k, _)| k.is_metadata_key())
6505 2 : .collect::<Vec<_>>();
6506 2 : assert_eq!(images.len(), 2); // the image layer should only contain two existing keys, tombstones should be removed.
6507 2 : }
6508 :
6509 : #[tokio::test]
6510 2 : async fn test_metadata_tombstone_empty_image_creation() {
6511 2 : let harness =
6512 2 : TenantHarness::create("test_metadata_tombstone_empty_image_creation").unwrap();
6513 8 : let (tenant, ctx) = harness.load().await;
6514 2 :
6515 2 : let key1 = Key::from_hex("620000000033333333444444445500000001").unwrap();
6516 2 : let key2 = Key::from_hex("620000000033333333444444445500000002").unwrap();
6517 2 :
6518 2 : let tline = tenant
6519 2 : .create_test_timeline_with_layers(
6520 2 : TIMELINE_ID,
6521 2 : Lsn(0x10),
6522 2 : DEFAULT_PG_VERSION,
6523 2 : &ctx,
6524 2 : // delta layers
6525 2 : vec![
6526 2 : vec![(key2, Lsn(0x10), Value::Image(test_img("metadata key 2")))],
6527 2 : vec![(key1, Lsn(0x20), Value::Image(Bytes::new()))],
6528 2 : vec![(key2, Lsn(0x20), Value::Image(Bytes::new()))],
6529 2 : ],
6530 2 : // image layers
6531 2 : vec![(Lsn(0x10), vec![(key1, test_img("metadata key 1"))])],
6532 2 : Lsn(0x30),
6533 2 : )
6534 31 : .await
6535 2 : .unwrap();
6536 2 :
6537 2 : let cancel = CancellationToken::new();
6538 2 :
6539 2 : tline
6540 2 : .compact(
6541 2 : &cancel,
6542 2 : {
6543 2 : let mut flags = EnumSet::new();
6544 2 : flags.insert(CompactFlags::ForceImageLayerCreation);
6545 2 : flags.insert(CompactFlags::ForceRepartition);
6546 2 : flags
6547 2 : },
6548 2 : &ctx,
6549 2 : )
6550 37 : .await
6551 2 : .unwrap();
6552 2 :
6553 2 : // Image layers are created at last_record_lsn
6554 2 : let images = tline
6555 2 : .inspect_image_layers(Lsn(0x30), &ctx)
6556 4 : .await
6557 2 : .unwrap()
6558 2 : .into_iter()
6559 16 : .filter(|(k, _)| k.is_metadata_key())
6560 2 : .collect::<Vec<_>>();
6561 2 : assert_eq!(images.len(), 0); // the image layer should not contain tombstones, or it is not created
6562 2 : }
6563 :
6564 : #[tokio::test]
6565 2 : async fn test_simple_bottom_most_compaction_images() -> anyhow::Result<()> {
6566 2 : let harness = TenantHarness::create("test_simple_bottom_most_compaction_images")?;
6567 8 : let (tenant, ctx) = harness.load().await;
6568 2 :
6569 104 : fn get_key(id: u32) -> Key {
6570 104 : // using aux key here b/c they are guaranteed to be inside `collect_keyspace`.
6571 104 : let mut key = Key::from_hex("620000000033333333444444445500000000").unwrap();
6572 104 : key.field6 = id;
6573 104 : key
6574 104 : }
6575 2 :
6576 2 : // We create one bottom-most image layer, a delta layer D1 crossing the GC horizon, D2 below the horizon, and D3 above the horizon.
6577 2 : //
6578 2 : // | D1 | | D3 |
6579 2 : // -| |-- gc horizon -----------------
6580 2 : // | | | D2 |
6581 2 : // --------- img layer ------------------
6582 2 : //
6583 2 : // What we should expact from this compaction is:
6584 2 : // | Part of D1 | | D3 |
6585 2 : // --------- img layer with D1+D2 at GC horizon------------------
6586 2 :
6587 2 : // img layer at 0x10
6588 2 : let img_layer = (0..10)
6589 20 : .map(|id| (get_key(id), Bytes::from(format!("value {id}@0x10"))))
6590 2 : .collect_vec();
6591 2 :
6592 2 : let delta1 = vec![
6593 2 : (
6594 2 : get_key(1),
6595 2 : Lsn(0x20),
6596 2 : Value::Image(Bytes::from("value 1@0x20")),
6597 2 : ),
6598 2 : (
6599 2 : get_key(2),
6600 2 : Lsn(0x30),
6601 2 : Value::Image(Bytes::from("value 2@0x30")),
6602 2 : ),
6603 2 : (
6604 2 : get_key(3),
6605 2 : Lsn(0x40),
6606 2 : Value::Image(Bytes::from("value 3@0x40")),
6607 2 : ),
6608 2 : ];
6609 2 : let delta2 = vec![
6610 2 : (
6611 2 : get_key(5),
6612 2 : Lsn(0x20),
6613 2 : Value::Image(Bytes::from("value 5@0x20")),
6614 2 : ),
6615 2 : (
6616 2 : get_key(6),
6617 2 : Lsn(0x20),
6618 2 : Value::Image(Bytes::from("value 6@0x20")),
6619 2 : ),
6620 2 : ];
6621 2 : let delta3 = vec![
6622 2 : (
6623 2 : get_key(8),
6624 2 : Lsn(0x40),
6625 2 : Value::Image(Bytes::from("value 8@0x40")),
6626 2 : ),
6627 2 : (
6628 2 : get_key(9),
6629 2 : Lsn(0x40),
6630 2 : Value::Image(Bytes::from("value 9@0x40")),
6631 2 : ),
6632 2 : ];
6633 2 :
6634 2 : let tline = tenant
6635 2 : .create_test_timeline_with_layers(
6636 2 : TIMELINE_ID,
6637 2 : Lsn(0x10),
6638 2 : DEFAULT_PG_VERSION,
6639 2 : &ctx,
6640 2 : vec![delta1, delta2, delta3], // delta layers
6641 2 : vec![(Lsn(0x10), img_layer)], // image layers
6642 2 : Lsn(0x50),
6643 2 : )
6644 49 : .await?;
6645 2 : {
6646 2 : // Update GC info
6647 2 : let mut guard = tline.gc_info.write().unwrap();
6648 2 : guard.cutoffs.pitr = Lsn(0x30);
6649 2 : guard.cutoffs.horizon = Lsn(0x30);
6650 2 : }
6651 2 :
6652 2 : let expected_result = [
6653 2 : Bytes::from_static(b"value 0@0x10"),
6654 2 : Bytes::from_static(b"value 1@0x20"),
6655 2 : Bytes::from_static(b"value 2@0x30"),
6656 2 : Bytes::from_static(b"value 3@0x40"),
6657 2 : Bytes::from_static(b"value 4@0x10"),
6658 2 : Bytes::from_static(b"value 5@0x20"),
6659 2 : Bytes::from_static(b"value 6@0x20"),
6660 2 : Bytes::from_static(b"value 7@0x10"),
6661 2 : Bytes::from_static(b"value 8@0x40"),
6662 2 : Bytes::from_static(b"value 9@0x40"),
6663 2 : ];
6664 2 :
6665 20 : for (idx, expected) in expected_result.iter().enumerate() {
6666 20 : assert_eq!(
6667 20 : tline
6668 20 : .get(get_key(idx as u32), Lsn(0x50), &ctx)
6669 17 : .await
6670 20 : .unwrap(),
6671 2 : expected
6672 2 : );
6673 2 : }
6674 2 :
6675 2 : let cancel = CancellationToken::new();
6676 52 : tline.compact_with_gc(&cancel, &ctx).await.unwrap();
6677 2 :
6678 20 : for (idx, expected) in expected_result.iter().enumerate() {
6679 20 : assert_eq!(
6680 20 : tline
6681 20 : .get(get_key(idx as u32), Lsn(0x50), &ctx)
6682 8 : .await
6683 20 : .unwrap(),
6684 2 : expected
6685 2 : );
6686 2 : }
6687 2 :
6688 2 : // Check if the image layer at the GC horizon contains exactly what we want
6689 2 : let image_at_gc_horizon = tline
6690 2 : .inspect_image_layers(Lsn(0x30), &ctx)
6691 2 : .await
6692 2 : .unwrap()
6693 2 : .into_iter()
6694 36 : .filter(|(k, _)| k.is_metadata_key())
6695 2 : .collect::<Vec<_>>();
6696 2 :
6697 2 : assert_eq!(image_at_gc_horizon.len(), 10);
6698 2 : let expected_result = [
6699 2 : Bytes::from_static(b"value 0@0x10"),
6700 2 : Bytes::from_static(b"value 1@0x20"),
6701 2 : Bytes::from_static(b"value 2@0x30"),
6702 2 : Bytes::from_static(b"value 3@0x10"),
6703 2 : Bytes::from_static(b"value 4@0x10"),
6704 2 : Bytes::from_static(b"value 5@0x20"),
6705 2 : Bytes::from_static(b"value 6@0x20"),
6706 2 : Bytes::from_static(b"value 7@0x10"),
6707 2 : Bytes::from_static(b"value 8@0x10"),
6708 2 : Bytes::from_static(b"value 9@0x10"),
6709 2 : ];
6710 22 : for idx in 0..10 {
6711 20 : assert_eq!(
6712 20 : image_at_gc_horizon[idx],
6713 20 : (get_key(idx as u32), expected_result[idx].clone())
6714 20 : );
6715 2 : }
6716 2 :
6717 2 : // Check if old layers are removed / new layers have the expected LSN
6718 2 : let mut all_layers = tline.inspect_historic_layers().await.unwrap();
6719 4 : all_layers.sort_by(|k1, k2| {
6720 4 : (
6721 4 : k1.is_delta,
6722 4 : k1.key_range.start,
6723 4 : k1.key_range.end,
6724 4 : k1.lsn_range.start,
6725 4 : k1.lsn_range.end,
6726 4 : )
6727 4 : .cmp(&(
6728 4 : k2.is_delta,
6729 4 : k2.key_range.start,
6730 4 : k2.key_range.end,
6731 4 : k2.lsn_range.start,
6732 4 : k2.lsn_range.end,
6733 4 : ))
6734 4 : });
6735 2 : assert_eq!(
6736 2 : all_layers,
6737 2 : vec![
6738 2 : // Image layer at GC horizon
6739 2 : PersistentLayerKey {
6740 2 : key_range: Key::MIN..get_key(10),
6741 2 : lsn_range: Lsn(0x30)..Lsn(0x31),
6742 2 : is_delta: false
6743 2 : },
6744 2 : // The delta layer that is cut in the middle
6745 2 : PersistentLayerKey {
6746 2 : key_range: get_key(3)..get_key(4),
6747 2 : lsn_range: Lsn(0x30)..Lsn(0x41),
6748 2 : is_delta: true
6749 2 : },
6750 2 : // The delta layer we created and should not be picked for the compaction
6751 2 : PersistentLayerKey {
6752 2 : key_range: get_key(8)..get_key(10),
6753 2 : lsn_range: Lsn(0x40)..Lsn(0x41),
6754 2 : is_delta: true
6755 2 : }
6756 2 : ]
6757 2 : );
6758 2 :
6759 2 : Ok(())
6760 2 : }
6761 :
6762 : #[tokio::test]
6763 2 : async fn test_neon_test_record() -> anyhow::Result<()> {
6764 2 : let harness = TenantHarness::create("test_neon_test_record")?;
6765 8 : let (tenant, ctx) = harness.load().await;
6766 2 :
6767 24 : fn get_key(id: u32) -> Key {
6768 24 : // using aux key here b/c they are guaranteed to be inside `collect_keyspace`.
6769 24 : let mut key = Key::from_hex("620000000033333333444444445500000000").unwrap();
6770 24 : key.field6 = id;
6771 24 : key
6772 24 : }
6773 2 :
6774 2 : let delta1 = vec![
6775 2 : (
6776 2 : get_key(1),
6777 2 : Lsn(0x20),
6778 2 : Value::WalRecord(NeonWalRecord::wal_append(",0x20")),
6779 2 : ),
6780 2 : (
6781 2 : get_key(1),
6782 2 : Lsn(0x30),
6783 2 : Value::WalRecord(NeonWalRecord::wal_append(",0x30")),
6784 2 : ),
6785 2 : (get_key(2), Lsn(0x10), Value::Image("0x10".into())),
6786 2 : (
6787 2 : get_key(2),
6788 2 : Lsn(0x20),
6789 2 : Value::WalRecord(NeonWalRecord::wal_append(",0x20")),
6790 2 : ),
6791 2 : (
6792 2 : get_key(2),
6793 2 : Lsn(0x30),
6794 2 : Value::WalRecord(NeonWalRecord::wal_append(",0x30")),
6795 2 : ),
6796 2 : (get_key(3), Lsn(0x10), Value::Image("0x10".into())),
6797 2 : (
6798 2 : get_key(3),
6799 2 : Lsn(0x20),
6800 2 : Value::WalRecord(NeonWalRecord::wal_clear()),
6801 2 : ),
6802 2 : (get_key(4), Lsn(0x10), Value::Image("0x10".into())),
6803 2 : (
6804 2 : get_key(4),
6805 2 : Lsn(0x20),
6806 2 : Value::WalRecord(NeonWalRecord::wal_init()),
6807 2 : ),
6808 2 : ];
6809 2 : let image1 = vec![(get_key(1), "0x10".into())];
6810 2 :
6811 2 : let tline = tenant
6812 2 : .create_test_timeline_with_layers(
6813 2 : TIMELINE_ID,
6814 2 : Lsn(0x10),
6815 2 : DEFAULT_PG_VERSION,
6816 2 : &ctx,
6817 2 : vec![delta1], // delta layers
6818 2 : vec![(Lsn(0x10), image1)], // image layers
6819 2 : Lsn(0x50),
6820 2 : )
6821 18 : .await?;
6822 2 :
6823 2 : assert_eq!(
6824 8 : tline.get(get_key(1), Lsn(0x50), &ctx).await?,
6825 2 : Bytes::from_static(b"0x10,0x20,0x30")
6826 2 : );
6827 2 : assert_eq!(
6828 2 : tline.get(get_key(2), Lsn(0x50), &ctx).await?,
6829 2 : Bytes::from_static(b"0x10,0x20,0x30")
6830 2 : );
6831 2 :
6832 2 : // Need to remove the limit of "Neon WAL redo requires base image".
6833 2 :
6834 2 : // assert_eq!(tline.get(get_key(3), Lsn(0x50), &ctx).await?, Bytes::new());
6835 2 : // assert_eq!(tline.get(get_key(4), Lsn(0x50), &ctx).await?, Bytes::new());
6836 2 :
6837 2 : Ok(())
6838 2 : }
6839 :
6840 : #[tokio::test]
6841 2 : async fn test_lsn_lease() -> anyhow::Result<()> {
6842 8 : let (tenant, ctx) = TenantHarness::create("test_lsn_lease")?.load().await;
6843 2 : let key = Key::from_hex("010000000033333333444444445500000000").unwrap();
6844 2 :
6845 2 : let end_lsn = Lsn(0x100);
6846 2 : let image_layers = (0x20..=0x90)
6847 2 : .step_by(0x10)
6848 16 : .map(|n| {
6849 16 : (
6850 16 : Lsn(n),
6851 16 : vec![(key, test_img(&format!("data key at {:x}", n)))],
6852 16 : )
6853 16 : })
6854 2 : .collect();
6855 2 :
6856 2 : let timeline = tenant
6857 2 : .create_test_timeline_with_layers(
6858 2 : TIMELINE_ID,
6859 2 : Lsn(0x10),
6860 2 : DEFAULT_PG_VERSION,
6861 2 : &ctx,
6862 2 : Vec::new(),
6863 2 : image_layers,
6864 2 : end_lsn,
6865 2 : )
6866 62 : .await?;
6867 2 :
6868 2 : let leased_lsns = [0x30, 0x50, 0x70];
6869 2 : let mut leases = Vec::new();
6870 6 : let _: anyhow::Result<_> = leased_lsns.iter().try_for_each(|n| {
6871 6 : leases.push(timeline.make_lsn_lease(Lsn(*n), timeline.get_lsn_lease_length(), &ctx)?);
6872 6 : Ok(())
6873 6 : });
6874 2 :
6875 2 : // Renewing with shorter lease should not change the lease.
6876 2 : let updated_lease_0 =
6877 2 : timeline.make_lsn_lease(Lsn(leased_lsns[0]), Duration::from_secs(0), &ctx)?;
6878 2 : assert_eq!(updated_lease_0.valid_until, leases[0].valid_until);
6879 2 :
6880 2 : // Renewing with a long lease should renew lease with later expiration time.
6881 2 : let updated_lease_1 = timeline.make_lsn_lease(
6882 2 : Lsn(leased_lsns[1]),
6883 2 : timeline.get_lsn_lease_length() * 2,
6884 2 : &ctx,
6885 2 : )?;
6886 2 :
6887 2 : assert!(updated_lease_1.valid_until > leases[1].valid_until);
6888 2 :
6889 2 : // Force set disk consistent lsn so we can get the cutoff at `end_lsn`.
6890 2 : info!(
6891 2 : "latest_gc_cutoff_lsn: {}",
6892 0 : *timeline.get_latest_gc_cutoff_lsn()
6893 2 : );
6894 2 : timeline.force_set_disk_consistent_lsn(end_lsn);
6895 2 :
6896 2 : let res = tenant
6897 2 : .gc_iteration(
6898 2 : Some(TIMELINE_ID),
6899 2 : 0,
6900 2 : Duration::ZERO,
6901 2 : &CancellationToken::new(),
6902 2 : &ctx,
6903 2 : )
6904 2 : .await?;
6905 2 :
6906 2 : // Keeping everything <= Lsn(0x80) b/c leases:
6907 2 : // 0/10: initdb layer
6908 2 : // (0/20..=0/70).step_by(0x10): image layers added when creating the timeline.
6909 2 : assert_eq!(res.layers_needed_by_leases, 7);
6910 2 : // Keeping 0/90 b/c it is the latest layer.
6911 2 : assert_eq!(res.layers_not_updated, 1);
6912 2 : // Removed 0/80.
6913 2 : assert_eq!(res.layers_removed, 1);
6914 2 :
6915 2 : // Make lease on a already GC-ed LSN.
6916 2 : // 0/80 does not have a valid lease + is below latest_gc_cutoff
6917 2 : assert!(Lsn(0x80) < *timeline.get_latest_gc_cutoff_lsn());
6918 2 : let res = timeline.make_lsn_lease(Lsn(0x80), timeline.get_lsn_lease_length(), &ctx);
6919 2 : assert!(res.is_err());
6920 2 :
6921 2 : // Should still be able to renew a currently valid lease
6922 2 : // Assumption: original lease to is still valid for 0/50.
6923 2 : let _ =
6924 2 : timeline.make_lsn_lease(Lsn(leased_lsns[1]), timeline.get_lsn_lease_length(), &ctx)?;
6925 2 :
6926 2 : Ok(())
6927 2 : }
6928 :
6929 : #[tokio::test]
6930 2 : async fn test_simple_bottom_most_compaction_deltas() -> anyhow::Result<()> {
6931 2 : let harness = TenantHarness::create("test_simple_bottom_most_compaction_deltas")?;
6932 8 : let (tenant, ctx) = harness.load().await;
6933 2 :
6934 118 : fn get_key(id: u32) -> Key {
6935 118 : // using aux key here b/c they are guaranteed to be inside `collect_keyspace`.
6936 118 : let mut key = Key::from_hex("620000000033333333444444445500000000").unwrap();
6937 118 : key.field6 = id;
6938 118 : key
6939 118 : }
6940 2 :
6941 2 : // We create one bottom-most image layer, a delta layer D1 crossing the GC horizon, D2 below the horizon, and D3 above the horizon.
6942 2 : //
6943 2 : // | D1 | | D3 |
6944 2 : // -| |-- gc horizon -----------------
6945 2 : // | | | D2 |
6946 2 : // --------- img layer ------------------
6947 2 : //
6948 2 : // What we should expact from this compaction is:
6949 2 : // | Part of D1 | | D3 |
6950 2 : // --------- img layer with D1+D2 at GC horizon------------------
6951 2 :
6952 2 : // img layer at 0x10
6953 2 : let img_layer = (0..10)
6954 20 : .map(|id| (get_key(id), Bytes::from(format!("value {id}@0x10"))))
6955 2 : .collect_vec();
6956 2 :
6957 2 : let delta1 = vec![
6958 2 : (
6959 2 : get_key(1),
6960 2 : Lsn(0x20),
6961 2 : Value::WalRecord(NeonWalRecord::wal_append("@0x20")),
6962 2 : ),
6963 2 : (
6964 2 : get_key(2),
6965 2 : Lsn(0x30),
6966 2 : Value::WalRecord(NeonWalRecord::wal_append("@0x30")),
6967 2 : ),
6968 2 : (
6969 2 : get_key(3),
6970 2 : Lsn(0x28),
6971 2 : Value::WalRecord(NeonWalRecord::wal_append("@0x28")),
6972 2 : ),
6973 2 : (
6974 2 : get_key(3),
6975 2 : Lsn(0x30),
6976 2 : Value::WalRecord(NeonWalRecord::wal_append("@0x30")),
6977 2 : ),
6978 2 : (
6979 2 : get_key(3),
6980 2 : Lsn(0x40),
6981 2 : Value::WalRecord(NeonWalRecord::wal_append("@0x40")),
6982 2 : ),
6983 2 : ];
6984 2 : let delta2 = vec![
6985 2 : (
6986 2 : get_key(5),
6987 2 : Lsn(0x20),
6988 2 : Value::WalRecord(NeonWalRecord::wal_append("@0x20")),
6989 2 : ),
6990 2 : (
6991 2 : get_key(6),
6992 2 : Lsn(0x20),
6993 2 : Value::WalRecord(NeonWalRecord::wal_append("@0x20")),
6994 2 : ),
6995 2 : ];
6996 2 : let delta3 = vec![
6997 2 : (
6998 2 : get_key(8),
6999 2 : Lsn(0x40),
7000 2 : Value::WalRecord(NeonWalRecord::wal_append("@0x40")),
7001 2 : ),
7002 2 : (
7003 2 : get_key(9),
7004 2 : Lsn(0x40),
7005 2 : Value::WalRecord(NeonWalRecord::wal_append("@0x40")),
7006 2 : ),
7007 2 : ];
7008 2 :
7009 2 : let tline = tenant
7010 2 : .create_test_timeline_with_layers(
7011 2 : TIMELINE_ID,
7012 2 : Lsn(0x10),
7013 2 : DEFAULT_PG_VERSION,
7014 2 : &ctx,
7015 2 : vec![delta1, delta2, delta3], // delta layers
7016 2 : vec![(Lsn(0x10), img_layer)], // image layers
7017 2 : Lsn(0x50),
7018 2 : )
7019 49 : .await?;
7020 2 : {
7021 2 : // Update GC info
7022 2 : let mut guard = tline.gc_info.write().unwrap();
7023 2 : *guard = GcInfo {
7024 2 : retain_lsns: vec![],
7025 2 : cutoffs: GcCutoffs {
7026 2 : pitr: Lsn(0x30),
7027 2 : horizon: Lsn(0x30),
7028 2 : },
7029 2 : leases: Default::default(),
7030 2 : };
7031 2 : }
7032 2 :
7033 2 : let expected_result = [
7034 2 : Bytes::from_static(b"value 0@0x10"),
7035 2 : Bytes::from_static(b"value 1@0x10@0x20"),
7036 2 : Bytes::from_static(b"value 2@0x10@0x30"),
7037 2 : Bytes::from_static(b"value 3@0x10@0x28@0x30@0x40"),
7038 2 : Bytes::from_static(b"value 4@0x10"),
7039 2 : Bytes::from_static(b"value 5@0x10@0x20"),
7040 2 : Bytes::from_static(b"value 6@0x10@0x20"),
7041 2 : Bytes::from_static(b"value 7@0x10"),
7042 2 : Bytes::from_static(b"value 8@0x10@0x40"),
7043 2 : Bytes::from_static(b"value 9@0x10@0x40"),
7044 2 : ];
7045 2 :
7046 2 : let expected_result_at_gc_horizon = [
7047 2 : Bytes::from_static(b"value 0@0x10"),
7048 2 : Bytes::from_static(b"value 1@0x10@0x20"),
7049 2 : Bytes::from_static(b"value 2@0x10@0x30"),
7050 2 : Bytes::from_static(b"value 3@0x10@0x28@0x30"),
7051 2 : Bytes::from_static(b"value 4@0x10"),
7052 2 : Bytes::from_static(b"value 5@0x10@0x20"),
7053 2 : Bytes::from_static(b"value 6@0x10@0x20"),
7054 2 : Bytes::from_static(b"value 7@0x10"),
7055 2 : Bytes::from_static(b"value 8@0x10"),
7056 2 : Bytes::from_static(b"value 9@0x10"),
7057 2 : ];
7058 2 :
7059 22 : for idx in 0..10 {
7060 20 : assert_eq!(
7061 20 : tline
7062 20 : .get(get_key(idx as u32), Lsn(0x50), &ctx)
7063 17 : .await
7064 20 : .unwrap(),
7065 20 : &expected_result[idx]
7066 2 : );
7067 20 : assert_eq!(
7068 20 : tline
7069 20 : .get(get_key(idx as u32), Lsn(0x30), &ctx)
7070 2 : .await
7071 20 : .unwrap(),
7072 20 : &expected_result_at_gc_horizon[idx]
7073 2 : );
7074 2 : }
7075 2 :
7076 2 : let cancel = CancellationToken::new();
7077 52 : tline.compact_with_gc(&cancel, &ctx).await.unwrap();
7078 2 :
7079 22 : for idx in 0..10 {
7080 20 : assert_eq!(
7081 20 : tline
7082 20 : .get(get_key(idx as u32), Lsn(0x50), &ctx)
7083 8 : .await
7084 20 : .unwrap(),
7085 20 : &expected_result[idx]
7086 2 : );
7087 20 : assert_eq!(
7088 20 : tline
7089 20 : .get(get_key(idx as u32), Lsn(0x30), &ctx)
7090 2 : .await
7091 20 : .unwrap(),
7092 20 : &expected_result_at_gc_horizon[idx]
7093 2 : );
7094 2 : }
7095 2 :
7096 2 : Ok(())
7097 2 : }
7098 : }
|