diff --git a/pageserver/src/http/routes.rs b/pageserver/src/http/routes.rs index eb0a04c6c40d..faa20e436e2a 100644 --- a/pageserver/src/http/routes.rs +++ b/pageserver/src/http/routes.rs @@ -1806,16 +1806,16 @@ async fn timeline_offload_handler( active_timeline_of_active_tenant(&state.tenant_manager, tenant_shard_id, timeline_id) .await?; - let has_no_unarchived_children = tenant.timeline_has_no_unarchived_children(timeline_id); + let has_no_attached_children = tenant.timeline_has_no_attached_children(timeline_id); let can_offload = timeline.can_offload(); - match (can_offload, has_no_unarchived_children) { + match (can_offload, has_no_attached_children) { (true, true) => { offload_timeline(&tenant, &timeline) .await .map_err(ApiError::InternalServerError)?; } (_, _) => { - let msg = format!("no way to offload timeline, can_offload={can_offload}, has_no_unarchived_children={has_no_unarchived_children}"); + let msg = format!("no way to offload timeline, can_offload={can_offload}, has_no_attached_children={has_no_attached_children}"); return Err(ApiError::PreconditionFailed(msg.into_boxed_str())); } } diff --git a/pageserver/src/tenant.rs b/pageserver/src/tenant.rs index a76695686c30..50cb931d7c98 100644 --- a/pageserver/src/tenant.rs +++ b/pageserver/src/tenant.rs @@ -2221,7 +2221,7 @@ impl Tenant { } } - pub fn timeline_has_no_unarchived_children(&self, timeline_id: TimelineId) -> bool { + pub fn timeline_has_no_attached_children(&self, timeline_id: TimelineId) -> bool { let timelines = self.timelines.lock().unwrap(); !timelines .iter() diff --git a/test_runner/regress/test_timeline_archive.py b/test_runner/regress/test_timeline_archive.py index e7c832cfbc58..99fea1ba13e4 100644 --- a/test_runner/regress/test_timeline_archive.py +++ b/test_runner/regress/test_timeline_archive.py @@ -117,41 +117,52 @@ def test_timeline_archive(neon_env_builder: NeonEnvBuilder, shard_count: int): ) -def test_timeline_offloading(neon_env_builder: NeonEnvBuilder): +@pytest.mark.parametrize("manual_offload", [False, True]) +def test_timeline_offloading(neon_env_builder: NeonEnvBuilder, manual_offload: bool): env = neon_env_builder.init_start() ps_http = env.pageserver.http_client() + # Turn off gc and compaction loops: we want to issue them manually for better reliability + tenant_id, _timeline = env.neon_cli.create_tenant( + conf={ + "gc_period": "0s", + "compaction_period": "0s" if manual_offload else "1s", + } + ) + # Create two branches and archive them - parent_timeline_id = env.neon_cli.create_branch("test_ancestor_branch_archive_parent") + parent_timeline_id = env.neon_cli.create_branch("test_ancestor_branch_archive_parent", tenant_id = tenant_id) leaf_timeline_id = env.neon_cli.create_branch( - "test_ancestor_branch_archive_branch1", "test_ancestor_branch_archive_parent" + "test_ancestor_branch_archive_branch1", "test_ancestor_branch_archive_parent", tenant_id = tenant_id ) ps_http.timeline_archival_config( - env.initial_tenant, + tenant_id, leaf_timeline_id, state=TimelineArchivalState.ARCHIVED, ) leaf_detail = ps_http.timeline_detail( - env.initial_tenant, + tenant_id, leaf_timeline_id, ) assert leaf_detail["is_archived"] is True ps_http.timeline_archival_config( - env.initial_tenant, + tenant_id, parent_timeline_id, state=TimelineArchivalState.ARCHIVED, ) def parent_offloaded(): - ps_http.timeline_offload(tenant_id=env.initial_tenant, timeline_id=parent_timeline_id) + if manual_offload: + ps_http.timeline_offload(tenant_id=tenant_id, timeline_id=parent_timeline_id) assert env.pageserver.log_contains( f".*{parent_timeline_id}.*offloading archived timeline.*" ) def leaf_offloaded(): - ps_http.timeline_offload(tenant_id=env.initial_tenant, timeline_id=leaf_timeline_id) + if manual_offload: + ps_http.timeline_offload(tenant_id=tenant_id, timeline_id=leaf_timeline_id) assert env.pageserver.log_contains(f".*{leaf_timeline_id}.*offloading archived timeline.*") wait_until(30, 1, leaf_offloaded)