diff --git a/libs/pageserver_api/src/config.rs b/libs/pageserver_api/src/config.rs index 24474d4840..896a5d8069 100644 --- a/libs/pageserver_api/src/config.rs +++ b/libs/pageserver_api/src/config.rs @@ -102,6 +102,7 @@ pub struct ConfigToml { pub ingest_batch_size: u64, pub max_vectored_read_bytes: MaxVectoredReadBytes, pub image_compression: ImageCompressionAlgorithm, + pub timeline_offloading: bool, pub ephemeral_bytes_per_memory_kb: usize, pub l0_flush: Option, pub virtual_file_io_mode: Option, @@ -385,6 +386,7 @@ impl Default for ConfigToml { NonZeroUsize::new(DEFAULT_MAX_VECTORED_READ_BYTES).unwrap(), )), image_compression: (DEFAULT_IMAGE_COMPRESSION), + timeline_offloading: false, ephemeral_bytes_per_memory_kb: (DEFAULT_EPHEMERAL_BYTES_PER_MEMORY_KB), l0_flush: None, virtual_file_io_mode: None, diff --git a/pageserver/src/config.rs b/pageserver/src/config.rs index 8db78285e4..06d4326459 100644 --- a/pageserver/src/config.rs +++ b/pageserver/src/config.rs @@ -164,6 +164,9 @@ pub struct PageServerConf { pub image_compression: ImageCompressionAlgorithm, + /// Whether to offload archived timelines automatically + pub timeline_offloading: bool, + /// How many bytes of ephemeral layer content will we allow per kilobyte of RAM. When this /// is exceeded, we start proactively closing ephemeral layers to limit the total amount /// of ephemeral data. @@ -321,6 +324,7 @@ impl PageServerConf { ingest_batch_size, max_vectored_read_bytes, image_compression, + timeline_offloading, ephemeral_bytes_per_memory_kb, l0_flush, virtual_file_io_mode, @@ -364,6 +368,7 @@ impl PageServerConf { ingest_batch_size, max_vectored_read_bytes, image_compression, + timeline_offloading, ephemeral_bytes_per_memory_kb, // ------------------------------------------------------------ diff --git a/pageserver/src/tenant.rs b/pageserver/src/tenant.rs index 689982ddd4..baa2365658 100644 --- a/pageserver/src/tenant.rs +++ b/pageserver/src/tenant.rs @@ -2187,7 +2187,8 @@ impl Tenant { .iter() .any(|(_id, tl)| tl.get_ancestor_timeline_id() == Some(*timeline_id)) }; - let can_offload = can_offload && has_no_unoffloaded_children; + let can_offload = + can_offload && has_no_unoffloaded_children && self.conf.timeline_offloading; if (is_active, can_offload) == (false, false) { None } else { diff --git a/pageserver/src/tenant/timeline.rs b/pageserver/src/tenant/timeline.rs index 1992dee930..2b4f949c76 100644 --- a/pageserver/src/tenant/timeline.rs +++ b/pageserver/src/tenant/timeline.rs @@ -1565,6 +1565,7 @@ impl Timeline { } /// Checks if the internal state of the timeline is consistent with it being able to be offloaded. + /// /// This is neccessary but not sufficient for offloading of the timeline as it might have /// child timelines that are not offloaded yet. pub(crate) fn can_offload(&self) -> bool { diff --git a/test_runner/regress/test_timeline_archive.py b/test_runner/regress/test_timeline_archive.py index ffaed5e130..85e1077fd5 100644 --- a/test_runner/regress/test_timeline_archive.py +++ b/test_runner/regress/test_timeline_archive.py @@ -119,6 +119,10 @@ def test_timeline_archive(neon_env_builder: NeonEnvBuilder, shard_count: int): @pytest.mark.parametrize("manual_offload", [False, True]) def test_timeline_offloading(neon_env_builder: NeonEnvBuilder, manual_offload: bool): + if not manual_offload: + # (automatic) timeline offloading defaults to false for now + neon_env_builder.pageserver_config_override = "timeline_offloading = true" + env = neon_env_builder.init_start() ps_http = env.pageserver.http_client()