From 548cb3be57894cbecccdb4597a6ddb812762b197 Mon Sep 17 00:00:00 2001 From: Gigi Sayfan Date: Wed, 21 Oct 2026 13:07:10 -0700 Subject: [PATCH] [restore] retire an abandoned restore's execution in the revert that abandons it --- architecture-baseline.toml | 4 +- .../sandbox/ingress/tests/kubernetes.rs | 61 ++ .../api/src/controllers/sandbox/lifecycle.rs | 14 +- .../lifecycle/kubernetes_suspend_tests.rs | 108 +++ .../controllers/sandbox/wake_completion.rs | 7 + .../kubernetes/adapter/substrate_adapter.rs | 61 +- .../substrate/kubernetes/adapter/teardown.rs | 5 +- .../adapter_characterization_tests.rs | 1 + .../create_and_teardown.rs | 182 ++++- .../restoring_access.rs | 721 ++++++++++++++++++ .../substrate/kubernetes/execution.rs | 417 +++++++++- .../controllers/substrate/kubernetes/mod.rs | 17 +- .../substrate/kubernetes/pod_ops/read.rs | 8 +- .../controllers/substrate/kubernetes/store.rs | 33 + .../kubernetes/store/execution_rows.rs | 2 +- .../store/k8s_store_postgres_tests.rs | 74 +- .../owned_restore_completion_tests.rs | 129 ++++ .../restore_revert_tests.rs | 477 ++++++++++++ .../started_recovery_observation_tests.rs | 53 ++ .../stopped_evacuation_claim_tests.rs | 10 +- .../kubernetes/store/restore_lease.rs | 282 +++++-- .../kubernetes/store/started_recovery.rs | 31 +- .../substrate/kubernetes/test_substrate.rs | 13 +- .../controllers/substrate/kubernetes/tests.rs | 166 ++++ .../kubernetes/tests/native_pod_failover.rs | 33 +- .../substrate/kubernetes/wait_ready.rs | 5 + .../libs/api/src/controllers/substrate/mod.rs | 64 ++ .../src/orphaned_restore_pod_gc/classify.rs | 28 +- .../server/src/orphaned_restore_pod_gc/mod.rs | 39 +- .../src/orphaned_restore_pod_gc/reconciler.rs | 5 + .../src/orphaned_restore_pod_gc/tests.rs | 15 +- .../src/suspended_execution_reaper/mod.rs | 30 +- .../lifecycle_writer_tests.rs | 414 +++++++++- .../src/transient_lifecycle_sweeper/mod.rs | 158 ++-- .../src/transient_lifecycle_sweeper/store.rs | 240 +++++- .../src/transient_lifecycle_sweeper/tests.rs | 15 +- .../api/server/src/data_plane_sessions.rs | 17 + .../src/data_plane_sessions/route_rows.rs | 20 +- .../src/data_plane_sessions/service_route.rs | 25 +- .../src/data_plane_sessions/session_route.rs | 16 +- .../server/src/data_plane_sessions/tests.rs | 50 ++ crates/libs/internal/api/server/src/tests.rs | 9 +- .../api/server/src/tests/restoring_routes.rs | 207 +++++ crates/libs/metrics/src/sandbox/restore.rs | 46 ++ docs/configuration.md | 1 + .../diagrams/orphaned-restore-pod-gc.mmd | 51 -- .../diagrams/orphaned-restore-pod-gc.png | Bin 389798 -> 0 bytes .../diagrams/orphaned-restore-pod-gc.svg | 1 - 48 files changed, 4039 insertions(+), 326 deletions(-) create mode 100644 crates/libs/api/src/controllers/sandbox/lifecycle/kubernetes_suspend_tests.rs create mode 100644 crates/libs/api/src/controllers/substrate/kubernetes/adapter_characterization_tests/restoring_access.rs create mode 100644 crates/libs/api/src/controllers/substrate/kubernetes/store/k8s_store_postgres_tests/owned_restore_completion_tests.rs create mode 100644 crates/libs/api/src/controllers/substrate/kubernetes/store/k8s_store_postgres_tests/restore_revert_tests.rs create mode 100644 crates/libs/api/src/controllers/substrate/kubernetes/store/k8s_store_postgres_tests/started_recovery_observation_tests.rs create mode 100644 crates/libs/internal/api/server/src/tests/restoring_routes.rs delete mode 100644 docs/space/diagrams/orphaned-restore-pod-gc.mmd delete mode 100644 docs/space/diagrams/orphaned-restore-pod-gc.png delete mode 100644 docs/space/diagrams/orphaned-restore-pod-gc.svg diff --git a/architecture-baseline.toml b/architecture-baseline.toml index 833e8a38d..92803fffa 100644 --- a/architecture-baseline.toml +++ b/architecture-baseline.toml @@ -158,7 +158,7 @@ "crates/libs/api/src/controllers/substrate/kubernetes/store/execution_rows.rs" = 1 "crates/libs/api/src/controllers/substrate/kubernetes/store/pod_placement.rs" = 1 "crates/libs/api/src/controllers/substrate/kubernetes/store/pod_tracking.rs" = 5 -"crates/libs/api/src/controllers/substrate/kubernetes/store/restore_lease.rs" = 5 +"crates/libs/api/src/controllers/substrate/kubernetes/store/restore_lease.rs" = 8 "crates/libs/api/src/controllers/substrate/kubernetes/store/suspend.rs" = 8 "crates/libs/api/src/controllers/substrate/mod.rs" = 1 "crates/libs/cluster/controller/server/src/drained_node_terminator/store.rs" = 2 @@ -169,7 +169,7 @@ "crates/libs/cluster/controller/server/src/started_sandbox_repair/store.rs" = 3 "crates/libs/cluster/controller/server/src/suspended_execution_reaper/store.rs" = 1 "crates/libs/cluster/controller/server/src/tombstone_retention.rs" = 1 -"crates/libs/cluster/controller/server/src/transient_lifecycle_sweeper/store.rs" = 10 +"crates/libs/cluster/controller/server/src/transient_lifecycle_sweeper/store.rs" = 12 "crates/libs/internal/api/server/src/data_plane_sessions/service_route.rs" = 1 "crates/libs/internal/api/server/src/published_state.rs" = 9 "crates/libs/internal/api/server/src/sandbox_quarantine_store.rs" = 3 diff --git a/crates/libs/api/src/controllers/sandbox/ingress/tests/kubernetes.rs b/crates/libs/api/src/controllers/sandbox/ingress/tests/kubernetes.rs index 162725ef1..6fea0fb0d 100644 --- a/crates/libs/api/src/controllers/sandbox/ingress/tests/kubernetes.rs +++ b/crates/libs/api/src/controllers/sandbox/ingress/tests/kubernetes.rs @@ -123,3 +123,64 @@ async fn stored_ingress_access_resumes_paused_execution_before_reporting_ready() } Ok(()) } + +/// Ingress to a `restoring` sandbox whose restore lease has lapsed while its +/// execution still owns the restore (so `observe` reports no owner) reaches +/// `access`, which must not dial the restoring pod: it joins the restore and +/// reports ready only once the sandbox is `started`. +#[tokio::test(flavor = "current_thread")] +async fn stored_ingress_access_joins_a_restore_whose_lease_lapsed() +-> pplx_space_test_env::TestResult { + use pplx_space_common_core::types::AutoLifecycleWakeSource; + use pplx_space_db_test_fixtures::try_pg_test_db; + use pplx_space_node_api_sim::SimWorkerHarness; + if !crate::test_support::enter_sim_worker_process() { + return Ok(()); + } + let Some(db) = try_pg_test_db().await? else { + return Ok(()); + }; + let worker = SimWorkerHarness::builder(crate::test_support::kubernetes::WORKER_NODE) + .with_internal_api() + .start() + .await?; + let node = insert_kubernetes_worker_node(&db.pool).await?; + let id = SandboxId::new(); + let mut policy = AutoLifecyclePolicy::disabled(); + policy.wake.on.push(AutoLifecycleWakeSource::Access); + sqlx::query(sql!("INSERT INTO sandboxes (id, user_id, org_id, managed_by, lifecycle, placement, active_node_id, template_id, content_hash, auto_lifecycle_json, network_policy_generation, restore_lease_expires_at, restore_lease_token) + VALUES ($1, '00000000-0000-0000-0000-000000000010', '00000000-0000-0000-0000-000000000001', 'k8s', 'restoring', 'on_node', $2, 'seed', 'hash-seed', $3, 1, NOW() - INTERVAL '1 second', $4)")) + .bind(id.as_uuid()).bind(node).bind(sqlx::types::Json(&policy)).bind(uuid::Uuid::now_v7()).execute(&db.pool).await?; + let LiveExecution { pod, .. } = + insert_live_execution(&db.pool, id, RouteIdentity::Published).await?; + let (substrate, _) = substrate_over_pods(&db.pool, worker.port(), vec![pod])?; + let backend = StoredAccess { + pool: db.pool.clone(), + controller: SandboxController::new(kubernetes(substrate)), + }; + assert!( + !backend.observe(id).await?.restore_owned, + "the lapsed lease leaves ingress with no owner to observe" + ); + let owner_pool = db.pool.clone(); + let owner = tokio::spawn(async move { + tokio::time::sleep(std::time::Duration::from_millis(300)).await; + sqlx::query(sql!( + "UPDATE sandboxes SET lifecycle = 'started' WHERE id = $1" + )) + .bind(id.as_uuid()) + .execute(&owner_pool) + .await + }); + + let outcome = backend.access(id).await; + let lifecycle_at_return = fetch_sandbox(&db.pool, id).await?.unwrap().lifecycle; + owner.await??; + + assert_eq!(outcome?, AccessOutcome::Ready); + assert_eq!( + lifecycle_at_return, "started", + "ingress must not reach a restoring pod" + ); + Ok(()) +} diff --git a/crates/libs/api/src/controllers/sandbox/lifecycle.rs b/crates/libs/api/src/controllers/sandbox/lifecycle.rs index 93cbb7283..e0fef71c5 100644 --- a/crates/libs/api/src/controllers/sandbox/lifecycle.rs +++ b/crates/libs/api/src/controllers/sandbox/lifecycle.rs @@ -379,7 +379,10 @@ impl PublishedSuspendFence { /// may continue, but this stale completion must not replace `suspending` with /// `started`. A substrate whose restore-shaped wakes claim `restoring` before /// the node call and leave `restoring -> started` to this CAS (Kubernetes) -/// also fences a finalized `suspended` state. Direct does not: its +/// also fences a finalized `suspended` state. On Kubernetes the restore's +/// owner usually committed `started` already (see +/// `SubstrateCapabilities::caller_completes_claimed_restore`); this CAS then +/// rewrites `started` and completes. Direct does not: its /// worker-local wake leaves its pre-wake lifecycle (`suspended`, or /// `restoring` kept by an earlier lease restore) for this CAS to advance, and /// its lease restore writes `started` before returning. @@ -2168,7 +2171,11 @@ impl SandboxController { } }; if let Some(status) = status { - maybe_update_sandbox_lifecycle(pool, id, status.as_str()).await; + if !(status == SandboxLifecycleStatus::Suspended + && self.substrate.capabilities().suspend_commits_lifecycle) + { + maybe_update_sandbox_lifecycle(pool, id, status.as_str()).await; + } if status == SandboxLifecycleStatus::Started { record_dormant_activation_if_needed(sandbox, metric_identity, action); self.reconcile_open_sandbox_port_exposures_best_effort(pool, id) @@ -2408,6 +2415,9 @@ mod wake_completion_postgres_tests; #[cfg(test)] mod characterization_tests; +#[cfg(all(test, feature = "kubernetes"))] +mod kubernetes_suspend_tests; + // Coverage-gap acknowledgment: `SandboxController::control_runtime_client`'s // ordering invariant (drain check must run before any substrate call that can // implicit-recover a pod) is not unit-testable here because the `Substrate` diff --git a/crates/libs/api/src/controllers/sandbox/lifecycle/kubernetes_suspend_tests.rs b/crates/libs/api/src/controllers/sandbox/lifecycle/kubernetes_suspend_tests.rs new file mode 100644 index 000000000..cfa54c2fe --- /dev/null +++ b/crates/libs/api/src/controllers/sandbox/lifecycle/kubernetes_suspend_tests.rs @@ -0,0 +1,108 @@ +//! The suspend action's lifecycle write on the Kubernetes substrate, whose +//! `suspend_execution` commits `suspended` itself. + +use std::sync::Arc; +use std::sync::atomic::AtomicUsize; + +use pplx_space_common_core::types::{LocalArtifactId, SandboxId}; +use pplx_space_db_test_fixtures::pg_test_db; +use pplx_space_metrics::sandbox::api::SandboxApiMetricIdentity; +use pplx_space_test_env::TestResult; + +use super::*; +use crate::controllers::substrate::kubernetes::tests::{ + deleted_pod_kubernetes_client, insert_k8s_execution, insert_test_sandbox_row, + substrate_with_store_and_client, +}; + +const POD_NAME: &str = "space-sbx-suspend-race-g1"; +const POD_UID: &str = "suspend-race-uid"; + +/// A wake that claims the sandbox right after the substrate commits its +/// suspend keeps the row. The suspend action writes no lifecycle of its own +/// afterwards: a trailing `suspended` would put the woken, routed sandbox +/// back to `suspended` while its new execution stays active, the shape whose +/// pod holds the only copy of the sandbox's state. +/// +/// A deferred trigger stands in for the wake: it moves the row to +/// `restoring` when the transaction that committed `suspended` commits, +/// which is after the substrate's write and before anything the controller +/// does next. +#[tokio::test(flavor = "current_thread")] +async fn suspend_keeps_a_wake_that_claimed_after_the_suspend_committed() -> TestResult { + let db = pg_test_db().await?; + let pool = &db.pool; + let sandbox_id = SandboxId::new(); + insert_test_sandbox_row(pool, sandbox_id, "suspending").await?; + // The node already committed the suspend, so the substrate only retires + // the pod and finalizes the row; it never reaches the node. + sqlx::query(sql!( + "UPDATE sandboxes + SET current_local_artifact_id = $2, + current_local_artifact_kind = 'full', + current_local_artifact_origin = 'suspend' + WHERE id = $1" + )) + .bind(sandbox_id) + .bind(LocalArtifactId::new()) + .execute(pool) + .await?; + insert_k8s_execution(pool, sandbox_id, 1, POD_NAME, POD_UID, None, true).await?; + sqlx::query(sql!( + "UPDATE sandbox_k8s_executions SET state = 'suspended' WHERE sandbox_id = $1" + )) + .bind(sandbox_id) + .execute(pool) + .await?; + sqlx::query(sql!( + "CREATE FUNCTION wake_claims_after_suspend() RETURNS trigger AS $$ + BEGIN + UPDATE sandboxes SET lifecycle = 'restoring' WHERE id = NEW.id; + RETURN NULL; + END; + $$ LANGUAGE plpgsql" + )) + .execute(pool) + .await?; + sqlx::query(sql!( + "CREATE CONSTRAINT TRIGGER wake_claims_after_suspend + AFTER UPDATE OF lifecycle ON sandboxes + DEFERRABLE INITIALLY DEFERRED + FOR EACH ROW + WHEN (OLD.lifecycle = 'suspending' AND NEW.lifecycle = 'suspended') + EXECUTE FUNCTION wake_claims_after_suspend()" + )) + .execute(pool) + .await?; + + let mut substrate = substrate_with_store_and_client( + pool.clone(), + deleted_pod_kubernetes_client(POD_NAME, POD_UID, Arc::new(AtomicUsize::new(0))), + ); + substrate.k8s.k8s_blocking_delete_timeout = std::time::Duration::from_millis(500); + let controller = SandboxController::new( + crate::controllers::substrate::Substrate::from_kubernetes(substrate), + ); + let sandbox = crate::controllers::sandbox::fetch_sandbox(pool, sandbox_id) + .await? + .expect("sandbox row exists"); + + controller + .action( + pool, + sandbox_id, + Some(&sandbox), + SandboxApiMetricIdentity::native(SandboxAction::Suspend.as_str()), + SandboxAction::Suspend, + None, + ) + .await?; + + let lifecycle: String = + sqlx::query_scalar(sql!("SELECT lifecycle FROM sandboxes WHERE id = $1")) + .bind(sandbox_id) + .fetch_one(pool) + .await?; + assert_eq!(lifecycle, "restoring"); + Ok(()) +} diff --git a/crates/libs/api/src/controllers/sandbox/wake_completion.rs b/crates/libs/api/src/controllers/sandbox/wake_completion.rs index e2938b05f..c25450c67 100644 --- a/crates/libs/api/src/controllers/sandbox/wake_completion.rs +++ b/crates/libs/api/src/controllers/sandbox/wake_completion.rs @@ -25,6 +25,13 @@ //! Process death during the redrive still loses the write; the sweeper //! remains the backstop, exactly as `CancelGuard` defers to the reconciler //! when it cannot clean up itself. +//! +//! On Kubernetes the restore's owner commits `started` in the substrate under +//! its own lease token before the wake returns +//! (`K8sStore::complete_owned_user_restore`, this module's CAS with the +//! lease-expiry guard replaced by that token). The shield covers the wakes +//! whose owner commit didn't land, and a redrive that finds the row already +//! `started` has nothing left to do. use pplx_space_common_core::sql; use std::sync::{Arc, LazyLock}; diff --git a/crates/libs/api/src/controllers/substrate/kubernetes/adapter/substrate_adapter.rs b/crates/libs/api/src/controllers/substrate/kubernetes/adapter/substrate_adapter.rs index 6b1551c47..ef84438d6 100644 --- a/crates/libs/api/src/controllers/substrate/kubernetes/adapter/substrate_adapter.rs +++ b/crates/libs/api/src/controllers/substrate/kubernetes/adapter/substrate_adapter.rs @@ -1497,9 +1497,22 @@ impl SubstrateAdapter for KubernetesSubstrate { .resolve_pod_instance(sandbox_id) .await .map_err(ControllerError::from)? - && let Ok(client) = self.access_kept_worker_client(sandbox_id, &pod).await { - return Ok(client); + if pod.sandbox_restoring { + return self + .join_restore_before_dial( + sandbox_id, + pplx_space_metrics::sandbox::api::SandboxApiMetricIdentity::native( + "runtime_client", + ), + Duration::from_secs(120), + charge, + ) + .await; + } + if let Ok(client) = self.access_kept_worker_client(sandbox_id, &pod).await { + return Ok(client); + } } let sandbox = self @@ -1507,11 +1520,23 @@ impl SubstrateAdapter for KubernetesSubstrate { .require_sandbox(sandbox_id) .await .map_err(ControllerError::from)?; + if is_restoring_lifecycle(&sandbox.lifecycle) { + return self + .join_restore_before_dial( + sandbox_id, + pplx_space_metrics::sandbox::api::SandboxApiMetricIdentity::native( + "runtime_client", + ), + Duration::from_secs(120), + charge, + ) + .await; + } if normalize_lifecycle(&sandbox_before_resolution.lifecycle) == "suspending" && is_suspended_lifecycle(&sandbox.lifecycle) { - let binding = self - .wake_execution_with_metric_identity( + return self + .wake_and_dial_once_started( &sandbox, Duration::from_secs(120), pplx_space_metrics::sandbox::api::SandboxApiMetricIdentity::native( @@ -1519,9 +1544,7 @@ impl SubstrateAdapter for KubernetesSubstrate { ), charge, ) - .await - .map_err(ControllerError::from)?; - return connect_worker(binding.binding.runtime_endpoint).await; + .await; } if is_wake_recovery_lifecycle(&sandbox.lifecycle) && !is_dormant_lifecycle(&sandbox.lifecycle) @@ -1556,6 +1579,12 @@ impl SubstrateAdapter for KubernetesSubstrate { else { return Ok(None); }; + if pod.sandbox_restoring { + // Never dial a restoring pod (see `join_restore_before_dial`). + // This path neither recovers nor waits: its callers are + // best-effort or turn "unreachable" into a retryable 503. + return Ok(None); + } match self.access_kept_worker_client(sandbox_id, &pod).await { Ok(client) => Ok(Some(client)), @@ -1591,16 +1620,14 @@ impl SubstrateAdapter for KubernetesSubstrate { ) -> Result { if is_suspended_lifecycle(&sandbox.lifecycle) { Self::should_allow_access_recovery(sandbox, activity)?; - let binding = self - .wake_execution_with_metric_identity( + return self + .wake_and_dial_once_started( sandbox, timeout, metric_identity, crate::controllers::substrate::RecoveryCharge::Exempt, ) - .await - .map_err(ControllerError::from)?; - return connect_worker(binding.binding.runtime_endpoint).await; + .await; } if let Some(pod) = self @@ -1608,6 +1635,11 @@ impl SubstrateAdapter for KubernetesSubstrate { .await .map_err(ControllerError::from)? { + if pod.sandbox_restoring { + return self + .join_restore_before_dial(sandbox.id, metric_identity, timeout, charge) + .await; + } let pod_ip = pod.ip; // A preserved slow create was reported `started` before its guest // command API was confirmed ready. Force a (bounded) command-ready @@ -1668,6 +1700,11 @@ impl SubstrateAdapter for KubernetesSubstrate { .await .map_err(ControllerError::from)?; Self::should_allow_access_recovery(&sandbox, activity)?; + if is_restoring_lifecycle(&sandbox.lifecycle) { + return self + .join_restore_before_dial(sandbox.id, metric_identity, timeout, charge) + .await; + } let binding = self .ensure_execution_ready_for_access(&sandbox, metric_identity, timeout, charge, None) .await?; diff --git a/crates/libs/api/src/controllers/substrate/kubernetes/adapter/teardown.rs b/crates/libs/api/src/controllers/substrate/kubernetes/adapter/teardown.rs index 762496374..2ea2b75a4 100644 --- a/crates/libs/api/src/controllers/substrate/kubernetes/adapter/teardown.rs +++ b/crates/libs/api/src/controllers/substrate/kubernetes/adapter/teardown.rs @@ -597,7 +597,10 @@ impl KubernetesSubstrate { /// Idempotent best-effort delete of each tracked pod name. An already-gone /// pod is the expected case, not a failure. - async fn delete_tracked_pods(&self, pod_names: Vec) -> Result<(), SubstrateError> { + pub(in crate::controllers::substrate::kubernetes) async fn delete_tracked_pods( + &self, + pod_names: Vec, + ) -> Result<(), SubstrateError> { for pod_name in pod_names { if let Err(err) = delete_sandbox_pod(&self.k8s, &pod_name).await && !is_not_found_error(&err) diff --git a/crates/libs/api/src/controllers/substrate/kubernetes/adapter_characterization_tests.rs b/crates/libs/api/src/controllers/substrate/kubernetes/adapter_characterization_tests.rs index ddcb71ae4..8bf2cf433 100644 --- a/crates/libs/api/src/controllers/substrate/kubernetes/adapter_characterization_tests.rs +++ b/crates/libs/api/src/controllers/substrate/kubernetes/adapter_characterization_tests.rs @@ -14,6 +14,7 @@ mod create_readiness; mod durable_snapshot; mod hydration_and_suspend; mod pooled_adoption; +mod restoring_access; use std::collections::BTreeMap; diff --git a/crates/libs/api/src/controllers/substrate/kubernetes/adapter_characterization_tests/create_and_teardown.rs b/crates/libs/api/src/controllers/substrate/kubernetes/adapter_characterization_tests/create_and_teardown.rs index f69a6eb04..83ab72ce4 100644 --- a/crates/libs/api/src/controllers/substrate/kubernetes/adapter_characterization_tests/create_and_teardown.rs +++ b/crates/libs/api/src/controllers/substrate/kubernetes/adapter_characterization_tests/create_and_teardown.rs @@ -3,8 +3,8 @@ use axum::http::{Method, StatusCode}; use super::super::scripted_test_doubles::{ - KubeLog, ScriptedReply, ScriptedWorkerConfig, kube_status, pod_path, requests_matching, - scripted_kube_client, start_scripted_worker, + KubeLog, ScriptedReply, ScriptedWorkerConfig, kube_status, pod_path, pod_reply, + requests_matching, scripted_kube_client, start_scripted_worker, }; use super::super::tests::{ insert_test_sandbox_row, node_with_network_slots, sandbox_pod_on_node, @@ -12,6 +12,8 @@ use super::super::tests::{ substrate_with_store_client_proxy_port_and_pod, }; use super::*; +use crate::controllers::substrate::{PodIdentityFence, RecordedExecution}; +use pplx_space_common_core::types::{PodName, PodResourceVersion, PodUid}; // ── create_execution ──────────────────────────────────────────────────────── @@ -467,3 +469,179 @@ async fn teardown_propagates_an_apiserver_fault_while_resolving_the_runtime() -> assert_eq!(active_execution_count(&db.pool, sandbox_id).await?, 1); Ok(()) } + +/// The shape the sweeper's lifecycle-only revert leaves with abandoned-restore +/// retirement disabled, the default until every routing replica refuses to +/// route to a `restoring` sandbox: the restore's execution still active. A +/// restore pod that came up after the sweeper's live-pod check may then have +/// served a user request through a replica without that guard. The cleanup +/// that follows the revert retires under the candidate's fence, which sees the +/// pod at a newer resourceVersion and refuses: the pod and its execution are +/// kept, so the sweeper puts the row back to `restoring` rather than losing +/// what the pod served. (That the revert leaves this shape with retirement +/// disabled is pinned in the sweeper's own store tests.) +#[tokio::test(flavor = "current_thread")] +async fn without_retirement_the_sweeper_cleanup_keeps_a_restore_pod_that_came_up_after_its_check() +-> TestResult { + let db = pplx_space_db_test_fixtures::pg_test_db().await?; + let sandbox_id = SandboxId::new(); + let pod_name = "space-sbx-late-serve-g1"; + insert_test_sandbox_row(&db.pool, sandbox_id, "suspended").await?; + // What the lifecycle-only revert left: the restore's execution still + // active and the row still placed. + insert_execution( + &db.pool, + sandbox_id, + ExecutionSeed::running(pod_name, "late-serve-uid"), + ) + .await?; + sqlx::query(sql!( + "UPDATE sandboxes SET placement = 'on_node' WHERE id = $1" + )) + .bind(sandbox_id) + .execute(&db.pool) + .await?; + // The pod bound and came up after the check: a newer resourceVersion. + let (client, log) = scripted_kube_client(move |request| match request.method { + Method::DELETE => kube_status(StatusCode::OK, "Success"), + _ => { + let mut pod = (*sandbox_pod_on_node(pod_name, WORKER_NODE, 1, "Running")).clone(); + pod.metadata.uid = Some("late-serve-uid".to_string()); + pod.metadata.resource_version = Some("2".to_string()); + pod_reply(&pod) + } + }); + let substrate = substrate_with_store_and_client(db.pool.clone(), client); + let observed = PodIdentityFence::new(PodName::parse(pod_name)?) + .with_recorded(RecordedExecution::Read { + pod_uid: Some(PodUid::parse("late-serve-uid")?), + route_activation_id: None, + }) + .with_generation(1) + .with_pod_uid(PodUid::parse("late-serve-uid")?) + .with_resource_version(PodResourceVersion::parse("1")?); + + let outcome = substrate + .retire_execution(RetireExecutionRequest::fenced(sandbox_id, observed)) + .await?; + + assert!( + matches!(outcome, RetireExecutionOutcome::PodIdentityChanged { .. }), + "the cleanup must refuse a pod that came up after the check: {outcome:?}" + ); + assert!( + requests_matching(&log, &Method::DELETE).is_empty(), + "a pod that may have served a request must not be deleted" + ); + assert_eq!(active_execution_count(&db.pool, sandbox_id).await?, 1); + Ok(()) +} + +/// The transient-lifecycle sweeper reverts an aged `restoring` row by +/// retiring its execution as `replaced` in the revert's own transaction, and +/// only after checking the candidate's live pod. A restore pod that binds and +/// comes up after that check is the abandoned restore's leftover: the cleanup +/// that follows deletes it rather than letting it recover, the row stays +/// `suspended`, and restore still selects the durable pre-restore snapshot +/// the revert proved. +#[tokio::test(flavor = "current_thread")] +async fn sweeper_revert_cleanup_deletes_a_restore_pod_that_bound_after_its_check() -> TestResult { + let db = pplx_space_db_test_fixtures::pg_test_db().await?; + let sandbox_id = SandboxId::new(); + let pod_name = "space-sbx-late-bind-g1"; + let source_uri = "s3://space-test/pre-restore?region=us-east-1"; + insert_test_sandbox_row(&db.pool, sandbox_id, "suspended").await?; + sqlx::query(sql!( + "INSERT INTO snapshots (id, sandbox_id, type, state, timestamp, org_id) + VALUES ($1, $2, 'full', 'ready', NOW() - INTERVAL '10 minutes', + '00000000-0000-0000-0000-000000000001')" + )) + .bind(Uuid::now_v7()) + .bind(sandbox_id) + .execute(&db.pool) + .await?; + sqlx::query(sql!( + "INSERT INTO snapshot_storage_locations (snapshot_id, storage_connection_string, org_id) + SELECT id, $2, org_id FROM snapshots WHERE sandbox_id = $1" + )) + .bind(sandbox_id) + .bind(source_uri) + .execute(&db.pool) + .await?; + // What the sweeper's revert left: its execution retired as an abandoned + // restore, the row still reading as placed from when the pod bound, and + // the placement clock the bind moved past the snapshot cleared. + insert_execution( + &db.pool, + sandbox_id, + ExecutionSeed { + state: "replaced", + ..ExecutionSeed::running(pod_name, "late-bind-uid") + }, + ) + .await?; + sqlx::query(sql!( + "UPDATE sandbox_k8s_executions SET active = FALSE WHERE sandbox_id = $1" + )) + .bind(sandbox_id) + .execute(&db.pool) + .await?; + sqlx::query(sql!( + "UPDATE sandboxes + SET placement = 'on_node', + placement_changed_at = NULL + WHERE id = $1" + )) + .bind(sandbox_id) + .execute(&db.pool) + .await?; + // The live pod has moved on since the candidate observed it: it bound + // and is Running at a newer resourceVersion. + let (client, log) = scripted_kube_client(move |request| match request.method { + Method::DELETE => kube_status(StatusCode::OK, "Success"), + _ => { + let mut pod = (*sandbox_pod_on_node(pod_name, WORKER_NODE, 1, "Running")).clone(); + pod.metadata.uid = Some("late-bind-uid".to_string()); + pod.metadata.resource_version = Some("2".to_string()); + pod_reply(&pod) + } + }); + let substrate = substrate_with_store_and_client(db.pool.clone(), client); + let observed = PodIdentityFence::new(PodName::parse(pod_name)?) + .with_recorded(RecordedExecution::Read { + pod_uid: Some(PodUid::parse("late-bind-uid")?), + route_activation_id: None, + }) + .with_generation(1) + .with_pod_uid(PodUid::parse("late-bind-uid")?) + .with_resource_version(PodResourceVersion::parse("1")?); + + let outcome = substrate + .retire_execution(RetireExecutionRequest::fenced(sandbox_id, observed)) + .await?; + + assert_eq!(outcome, RetireExecutionOutcome::Retired); + let deletes = requests_matching(&log, &Method::DELETE); + assert!( + deletes + .iter() + .any(|request| request.path == pod_path(pod_name)), + "the late-bound restore pod must be deleted: {deletes:?}" + ); + assert_eq!(sandbox_lifecycle(&db.pool, sandbox_id).await?, "suspended"); + assert_eq!(active_execution_count(&db.pool, sandbox_id).await?, 0); + let sandbox = crate::controllers::sandbox::fetch_sandbox(&db.pool, sandbox_id) + .await? + .expect("sandbox row exists"); + assert_eq!(sandbox.placement, "unplaced"); + assert_eq!( + substrate + .store + .selected_suspend_restore_storage_uri(&sandbox, false) + .await? + .as_deref(), + Some(source_uri), + "the next wake must restore from the durable pre-restore snapshot" + ); + Ok(()) +} diff --git a/crates/libs/api/src/controllers/substrate/kubernetes/adapter_characterization_tests/restoring_access.rs b/crates/libs/api/src/controllers/substrate/kubernetes/adapter_characterization_tests/restoring_access.rs new file mode 100644 index 000000000..e94390ae3 --- /dev/null +++ b/crates/libs/api/src/controllers/substrate/kubernetes/adapter_characterization_tests/restoring_access.rs @@ -0,0 +1,721 @@ +//! No path serving a user request dials a sandbox's pod while the sandbox is +//! `restoring`: `access_runtime_client` and `runtime_client` join the +//! in-flight restore instead, and fail retryably when it doesn't reach +//! `started` in their budget; the non-recovering `reachable_runtime_client` +//! reports the runtime unreachable. + +use axum::http::{Method, StatusCode}; + +use super::super::scripted_test_doubles::{ + KubeLog, ScriptedWorker, ScriptedWorkerConfig, kube_status, scripted_kube_client, + start_scripted_worker, +}; +use super::super::tests::{ + insert_ready_full_snapshot_relative_to_lifecycle, insert_test_sandbox_row, + substrate_with_store_client_proxy_port_and_pod, +}; +use super::*; +use crate::controllers::ControllerRetryAfterCause; + +const POD_UID: &str = "restoring-pod-uid"; +/// The restore's pod runs here, behind a live worker, so a client that +/// dialed it would connect. +const POD_IP: &str = "127.0.0.1"; + +struct Restoring { + substrate: KubernetesSubstrate, + pod_name: String, + sandbox: Sandbox, + lease_token: Uuid, + kube_log: KubeLog, + worker: ScriptedWorker, +} + +/// A `restoring` sandbox whose restore pod is up, routable and dialable: its +/// execution is `running` with the pod's IP. `lease` is how long the restore +/// lease another request holds has left; a lapsed one leaves no owner. +async fn restoring_with_live_pod( + pool: &PgPool, + sandbox_id: SandboxId, + lease: &str, +) -> Result { + insert_test_sandbox_row(pool, sandbox_id, "restoring").await?; + let lease_token = Uuid::now_v7(); + let pod_name = format!("space-sbx-{}-g1", sandbox_id.as_uuid().simple()); + sqlx::query(sql!( + "UPDATE sandboxes + SET restore_lease_expires_at = NOW() + $2::interval, + restore_lease_token = $3 + WHERE id = $1" + )) + .bind(sandbox_id) + .bind(lease) + .bind(lease_token) + .execute(pool) + .await?; + insert_execution( + pool, + sandbox_id, + ExecutionSeed { + pod_ip: Some(POD_IP), + ..ExecutionSeed::running(&pod_name, POD_UID) + }, + ) + .await?; + let worker = start_scripted_worker(ScriptedWorkerConfig::new([])).await?; + let (client, kube_log) = scripted_kube_client(|request| match request.method { + Method::GET => kube_status(StatusCode::NOT_FOUND, "NotFound"), + _ => kube_status(StatusCode::BAD_REQUEST, "BadRequest"), + }); + let substrate = substrate_with_store_client_proxy_port_and_pod( + pool.clone(), + client, + worker.port, + Some(sandbox_pod( + &pod_name, + POD_UID, + sandbox_id, + 1, + "Running", + Some(POD_IP), + )), + ); + let sandbox = crate::controllers::sandbox::fetch_sandbox(pool, sandbox_id) + .await? + .expect("sandbox row exists"); + Ok(Restoring { + pod_name, + substrate, + sandbox, + lease_token, + kube_log, + worker, + }) +} + +/// The restore's owner finishes it after `delay`. +fn complete_restore_after( + pool: &PgPool, + sandbox_id: SandboxId, + delay: Duration, +) -> tokio::task::JoinHandle> { + let pool = pool.clone(); + tokio::spawn(async move { + tokio::time::sleep(delay).await; + sqlx::query(sql!( + "UPDATE sandboxes SET lifecycle = 'started' WHERE id = $1" + )) + .bind(sandbox_id) + .execute(&pool) + .await?; + Ok(()) + }) +} + +fn access_identity() -> SandboxApiMetricIdentity { + SandboxApiMetricIdentity::native("restoring_access") +} + +fn assert_retryable_restore_in_flight(error: &ControllerError) { + assert_eq!(error.status(), StatusCode::SERVICE_UNAVAILABLE, "{error:?}"); + assert_eq!( + error.retry_after_cause(), + Some(ControllerRetryAfterCause::RestoreInFlight), + "{error:?}" + ); +} + +/// An access request on a restoring sandbox waits for the restore and gets a +/// client only once the sandbox is `started`, although its pod was dialable +/// the whole time. +#[tokio::test(flavor = "current_thread")] +async fn access_on_a_restoring_sandbox_joins_the_restore_before_dialing() -> TestResult { + // The joined client connects to the worker over internal RPC. + if !crate::test_support::enter_sim_worker_process() { + return Ok(()); + } + let db = pplx_space_db_test_fixtures::pg_test_db().await?; + let sandbox_id = SandboxId::new(); + let restoring = restoring_with_live_pod(&db.pool, sandbox_id, "120 seconds").await?; + let owner = complete_restore_after(&db.pool, sandbox_id, Duration::from_millis(300)); + + let client = restoring + .substrate + .access_runtime_client( + access_identity(), + &restoring.sandbox, + ExecutionActivityKind::Access, + Duration::from_secs(10), + crate::controllers::substrate::RecoveryCharge::Exempt, + ) + .await; + let lifecycle_at_return = sandbox_lifecycle(&db.pool, sandbox_id).await?; + owner.await??; + + assert!(client.is_ok(), "{:?}", client.err()); + assert_eq!( + lifecycle_at_return, "started", + "no client may be handed out while the sandbox is restoring" + ); + Ok(()) +} + +/// A request that wakes a `suspended` sandbox and joins an in-process +/// restore run gets the run's activation as soon as the restore's activation +/// completes, which can be before the row is `started` (the run's owner +/// commit may not have landed). The request still gets its client only once +/// the sandbox is `started`, and it keeps waiting while the row reads as +/// unowned. +/// +/// The row has no live owner, so a request that didn't join the run would +/// claim the restore itself and fail at pod creation. +#[tokio::test(flavor = "current_thread")] +async fn a_restore_run_that_completes_before_started_is_dialed_only_once_started() -> TestResult { + if !crate::test_support::enter_sim_worker_process() { + return Ok(()); + } + let db = pplx_space_db_test_fixtures::pg_test_db().await?; + let sandbox_id = SandboxId::new(); + let restoring = restoring_with_live_pod(&db.pool, sandbox_id, "-1 hour").await?; + sqlx::query(sql!( + "UPDATE sandbox_k8s_executions + SET updated_at = NOW() - INTERVAL '1 hour' + WHERE sandbox_id = $1" + )) + .bind(sandbox_id) + .execute(&db.pool) + .await?; + let binding = ExecutionBinding { + execution_object_id: restoring.pod_name.clone(), + runtime_endpoint: KubernetesSubstrate::endpoint_for_pod_ip( + PodIp::parse(POD_IP)?, + restoring.worker.port, + )?, + execution_generation: 1, + }; + let release = std::sync::Arc::new(tokio::sync::Notify::new()); + let run_release = release.clone(); + let run_substrate = restoring.substrate.clone(); + let run = tokio::spawn(async move { + run_substrate + .wake_coalescer + .join_or_start( + sandbox_id, + tokio::time::Instant::now() + Duration::from_secs(10), + move || async move { + run_release.notified().await; + Ok(crate::controllers::substrate::ExecutionActivation::joined( + binding, + )) + }, + ) + .await + .is_ok() + }); + tokio::time::sleep(Duration::from_millis(20)).await; + assert_eq!(restoring.substrate.wake_coalescer.live_runs(), 1); + + let request_substrate = restoring.substrate.clone(); + let request_sandbox = restoring.sandbox.clone(); + let request_pool = db.pool.clone(); + let request = tokio::spawn(async move { + // The caller read the row before the restore claimed it. + let mut suspended_view = request_sandbox; + suspended_view.lifecycle = "suspended".to_string(); + let client = request_substrate + .wake_and_dial_once_started( + &suspended_view, + Duration::from_secs(10), + access_identity(), + crate::controllers::substrate::RecoveryCharge::Exempt, + ) + .await; + let lifecycle_at_return = sandbox_lifecycle(&request_pool, sandbox_id).await; + (client.map(|_| ()), lifecycle_at_return) + }); + // Let the request reach the run and join it, then finish the run. + tokio::time::sleep(Duration::from_millis(300)).await; + release.notify_one(); + let owner = complete_restore_after(&db.pool, sandbox_id, Duration::from_millis(300)); + + let (client, lifecycle_at_return) = request.await?; + owner.await??; + + assert!(run.await?, "the seeded restore run completes"); + assert!(client.is_ok(), "{:?}", client.err()); + assert_eq!( + lifecycle_at_return?, "started", + "no client may be handed out while the sandbox is restoring" + ); + Ok(()) +} + +/// A request that saw the sandbox `restoring` and finds it `started` when +/// it joins dials it, rather than failing a restore that already succeeded. +#[tokio::test(flavor = "current_thread")] +async fn a_restore_that_finished_before_the_join_is_dialed() -> TestResult { + if !crate::test_support::enter_sim_worker_process() { + return Ok(()); + } + let db = pplx_space_db_test_fixtures::pg_test_db().await?; + let sandbox_id = SandboxId::new(); + let restoring = restoring_with_live_pod(&db.pool, sandbox_id, "120 seconds").await?; + complete_restore_after(&db.pool, sandbox_id, Duration::ZERO).await??; + + let client = restoring + .substrate + .join_restore_before_dial( + sandbox_id, + access_identity(), + Duration::from_secs(10), + crate::controllers::substrate::RecoveryCharge::Exempt, + ) + .await; + + assert!(client.is_ok(), "{:?}", client.err()); + Ok(()) +} + +/// An access request whose restore doesn't finish in its budget gets a +/// retryable `RestoreInFlight`, never a client for the restoring pod. +#[tokio::test(flavor = "current_thread")] +async fn access_on_a_restoring_sandbox_is_refused_retryably_when_the_restore_lingers() -> TestResult +{ + // Credentials make the pod connectable, so only the gate can refuse it. + if !crate::test_support::enter_sim_worker_process() { + return Ok(()); + } + let db = pplx_space_db_test_fixtures::pg_test_db().await?; + let sandbox_id = SandboxId::new(); + let restoring = restoring_with_live_pod(&db.pool, sandbox_id, "120 seconds").await?; + + let error = restoring + .substrate + .access_runtime_client( + access_identity(), + &restoring.sandbox, + ExecutionActivityKind::Access, + Duration::from_secs(2), + crate::controllers::substrate::RecoveryCharge::Exempt, + ) + .await + .expect_err("a restoring pod must not be dialed"); + + assert_retryable_restore_in_flight(&error); + assert_eq!(sandbox_lifecycle(&db.pool, sandbox_id).await?, "restoring"); + Ok(()) +} + +/// `runtime_client`'s user paths join the restore the same way. +#[tokio::test(flavor = "current_thread")] +async fn runtime_client_on_a_restoring_sandbox_joins_the_restore_before_dialing() -> TestResult { + // The joined client connects to the worker over internal RPC. + if !crate::test_support::enter_sim_worker_process() { + return Ok(()); + } + let db = pplx_space_db_test_fixtures::pg_test_db().await?; + let sandbox_id = SandboxId::new(); + let restoring = restoring_with_live_pod(&db.pool, sandbox_id, "120 seconds").await?; + let owner = complete_restore_after(&db.pool, sandbox_id, Duration::from_millis(300)); + + let client = restoring + .substrate + .runtime_client( + sandbox_id, + crate::controllers::substrate::RecoveryCharge::Exempt, + ) + .await; + let lifecycle_at_return = sandbox_lifecycle(&db.pool, sandbox_id).await?; + owner.await??; + + assert!(client.is_ok(), "{:?}", client.err()); + assert_eq!(lifecycle_at_return, "started"); + Ok(()) +} + +/// The non-recovering `reachable_runtime_client` neither dials a restoring +/// pod nor waits for its restore: its callers are best-effort (the ECW +/// keepalive refresh) or turn an unreachable runtime into a retryable 503, +/// so it reports the runtime unreachable at once, whether or not an owner is +/// still restoring the sandbox. +#[tokio::test(flavor = "current_thread")] +async fn reachable_runtime_client_finds_no_client_for_a_restoring_sandbox() -> TestResult { + // Credentials make the pod connectable, so only the gate can refuse it. + if !crate::test_support::enter_sim_worker_process() { + return Ok(()); + } + let db = pplx_space_db_test_fixtures::pg_test_db().await?; + for lease in ["120 seconds", "-1 hour"] { + let sandbox_id = SandboxId::new(); + let restoring = restoring_with_live_pod(&db.pool, sandbox_id, lease).await?; + let started = std::time::Instant::now(); + let client = restoring + .substrate + .reachable_runtime_client(sandbox_id) + .await?; + assert!( + client.is_none(), + "{lease}: a restoring pod must not be dialed" + ); + assert!( + started.elapsed() < Duration::from_secs(5), + "{lease}: the probe must not wait out the restore: {:?}", + started.elapsed() + ); + assert_eq!(sandbox_lifecycle(&db.pool, sandbox_id).await?, "restoring"); + } + Ok(()) +} + +/// A drain's evacuation restore owns its `restoring` row through the +/// evacuation marker rather than a lease, so the row reads as unowned. A user +/// request on it waits for the drain to move it to `started` and drives no +/// wake of its own (the scripted worker answers no wake). +#[tokio::test(flavor = "current_thread")] +async fn access_on_an_evacuating_sandbox_waits_for_the_drain() -> TestResult { + if !crate::test_support::enter_sim_worker_process() { + return Ok(()); + } + let db = pplx_space_db_test_fixtures::pg_test_db().await?; + let sandbox_id = SandboxId::new(); + let restoring = restoring_with_live_pod(&db.pool, sandbox_id, "-1 hour").await?; + sqlx::query(sql!( + "UPDATE sandboxes SET evacuation_restore_target = 'started' WHERE id = $1" + )) + .bind(sandbox_id) + .execute(&db.pool) + .await?; + let drain = complete_restore_after(&db.pool, sandbox_id, Duration::from_millis(300)); + + let client = restoring + .substrate + .access_runtime_client( + access_identity(), + &restoring.sandbox, + ExecutionActivityKind::Access, + Duration::from_secs(10), + crate::controllers::substrate::RecoveryCharge::Exempt, + ) + .await; + let lifecycle_at_return = sandbox_lifecycle(&db.pool, sandbox_id).await?; + drain.await??; + + assert!(client.is_ok(), "{:?}", client.err()); + assert_eq!(lifecycle_at_return, "started"); + Ok(()) +} + +/// A request that joins a restore whose owner then gives up starts no +/// restore of its own: one the owner rolled back to `suspended` is refused +/// retryably, and one that went to a state that can't be woken gets that +/// state's error. Either way no pod is created; a fresh restore is for a +/// caller whose access-wake policy and admission allow it. +#[tokio::test(flavor = "current_thread")] +async fn a_joined_restore_that_its_owner_abandons_starts_no_restore() -> TestResult { + if !crate::test_support::enter_sim_worker_process() { + return Ok(()); + } + let db = pplx_space_db_test_fixtures::pg_test_db().await?; + for abandoned_to in ["suspended", "failed"] { + let sandbox_id = SandboxId::new(); + let restoring = restoring_with_live_pod(&db.pool, sandbox_id, "120 seconds").await?; + // A durable source, so a fresh restore of the reverted row could + // start. + insert_ready_full_snapshot_relative_to_lifecycle( + &db.pool, + sandbox_id, + "s3://bucket/pre-restore?region=us-east-1", + -60, + Uuid::now_v7(), + ) + .await?; + sqlx::query(sql!( + "UPDATE sandbox_k8s_executions SET created_at = NOW() WHERE sandbox_id = $1" + )) + .bind(sandbox_id) + .execute(&db.pool) + .await?; + let owner_substrate = restoring.substrate.clone(); + let owner_pool = db.pool.clone(); + let lease_token = restoring.lease_token; + let owner = tokio::spawn(async move { + tokio::time::sleep(Duration::from_millis(300)).await; + if abandoned_to == "suspended" { + owner_substrate + .store + .revert_restore_claim_to_suspended( + sandbox_id, + Some(lease_token), + None, + crate::controllers::substrate::AbandonedRestoreRetirement::Enabled, + ) + .await?; + } else { + sqlx::query(sql!( + "UPDATE sandboxes SET lifecycle = 'failed' WHERE id = $1" + )) + .bind(sandbox_id) + .execute(&owner_pool) + .await?; + } + Ok::<_, BoxError>(()) + }); + + let refused = restoring + .substrate + .access_runtime_client( + access_identity(), + &restoring.sandbox, + ExecutionActivityKind::Access, + Duration::from_secs(10), + crate::controllers::substrate::RecoveryCharge::Exempt, + ) + .await + .expect_err("an abandoned restore serves no client"); + owner.await??; + + if abandoned_to == "suspended" { + assert_retryable_restore_in_flight(&refused); + } else { + assert_eq!(refused.status(), StatusCode::CONFLICT, "{refused:?}"); + } + assert_eq!(sandbox_lifecycle(&db.pool, sandbox_id).await?, abandoned_to); + let pod_writes = restoring + .kube_log + .lock() + .expect("kube log") + .iter() + .filter(|request| request.method != Method::GET) + .count(); + assert_eq!(pod_writes, 0, "{abandoned_to}: no restore may be started"); + } + Ok(()) +} + +/// A request that arrives during a restore that then fails is refused +/// without a client, so when the API rollback retires the restore's execution +/// and deletes its pod, nothing that pod served is lost: it served nothing. +#[tokio::test(flavor = "current_thread")] +async fn a_rolled_back_restore_discards_no_request_it_served() -> TestResult { + // Credentials make the pod connectable, so only the gate can refuse it. + if !crate::test_support::enter_sim_worker_process() { + return Ok(()); + } + let db = pplx_space_db_test_fixtures::pg_test_db().await?; + let sandbox_id = SandboxId::new(); + let restoring = restoring_with_live_pod(&db.pool, sandbox_id, "120 seconds").await?; + insert_ready_full_snapshot_relative_to_lifecycle( + &db.pool, + sandbox_id, + "s3://bucket/pre-restore?region=us-east-1", + -60, + Uuid::now_v7(), + ) + .await?; + sqlx::query(sql!( + "UPDATE sandbox_k8s_executions SET created_at = NOW() WHERE sandbox_id = $1" + )) + .bind(sandbox_id) + .execute(&db.pool) + .await?; + + let refused = restoring + .substrate + .access_runtime_client( + access_identity(), + &restoring.sandbox, + ExecutionActivityKind::Access, + Duration::from_secs(2), + crate::controllers::substrate::RecoveryCharge::Exempt, + ) + .await + .expect_err("a restoring pod must not be dialed"); + assert_retryable_restore_in_flight(&refused); + + let revert = restoring + .substrate + .store + .revert_restore_claim_to_suspended( + sandbox_id, + Some(restoring.lease_token), + None, + crate::controllers::substrate::AbandonedRestoreRetirement::Enabled, + ) + .await?; + + assert_eq!( + revert, + RestoreClaimRevert::ExecutionRetired { + pod_name: restoring.pod_name.clone() + } + ); + assert_eq!(sandbox_lifecycle(&db.pool, sandbox_id).await?, "suspended"); + Ok(()) +} + +/// The substrate's rollback of a failed fresh restore claim retires the +/// restore's execution and deletes its pod only with abandoned-restore +/// retirement enabled. Disabled, the default until every routing replica +/// refuses to route to a `restoring` sandbox, it reverts only the lifecycle +/// and deletes nothing, so a pod an older replica may have routed a request +/// to survives the rollback. +#[tokio::test(flavor = "current_thread")] +async fn a_failed_restore_rollback_deletes_its_pod_only_with_retirement_enabled() -> TestResult { + let db = pplx_space_db_test_fixtures::pg_test_db().await?; + for retirement in [ + crate::controllers::substrate::AbandonedRestoreRetirement::Enabled, + crate::controllers::substrate::AbandonedRestoreRetirement::Disabled, + ] { + let sandbox_id = SandboxId::new(); + let mut restoring = restoring_with_live_pod(&db.pool, sandbox_id, "120 seconds").await?; + restoring.substrate.k8s.abandoned_restore_retirement = retirement; + insert_ready_full_snapshot_relative_to_lifecycle( + &db.pool, + sandbox_id, + "s3://bucket/pre-restore?region=us-east-1", + -60, + Uuid::now_v7(), + ) + .await?; + sqlx::query(sql!( + "UPDATE sandbox_k8s_executions SET created_at = NOW() WHERE sandbox_id = $1" + )) + .bind(sandbox_id) + .execute(&db.pool) + .await?; + + restoring + .substrate + .revert_failed_restore_claim( + sandbox_id, + super::super::store::RestoreClaim::FreshFromSuspended { + restore_lease_token: Some(restoring.lease_token), + }, + None, + ) + .await; + + let deleted = restoring + .kube_log + .lock() + .expect("kube log") + .iter() + .any(|request| request.method == Method::DELETE); + let active: i64 = sqlx::query_scalar(sql!( + "SELECT COUNT(*) FROM sandbox_k8s_executions WHERE sandbox_id = $1 AND active" + )) + .bind(sandbox_id) + .fetch_one(&db.pool) + .await?; + assert_eq!(sandbox_lifecycle(&db.pool, sandbox_id).await?, "suspended"); + assert_eq!( + (deleted, active), + if retirement.is_enabled() { + (true, 0) + } else { + (false, 1) + }, + "{retirement:?}" + ); + } + Ok(()) +} + +/// When the restoring sandbox's pod isn't resolvable yet (its execution is +/// still `creating`), the fallback joins the restore too, rather than +/// materializing a pod of its own: both `access_runtime_client` and +/// `runtime_client` return only once the owner has brought the sandbox to +/// `started`. +#[tokio::test(flavor = "current_thread")] +async fn a_restoring_sandbox_without_a_resolved_pod_joins_the_restore() -> TestResult { + // The joined client connects to the worker over internal RPC. + if !crate::test_support::enter_sim_worker_process() { + return Ok(()); + } + let db = pplx_space_db_test_fixtures::pg_test_db().await?; + for via_access in [true, false] { + let sandbox_id = SandboxId::new(); + let restoring = restoring_with_live_pod(&db.pool, sandbox_id, "120 seconds").await?; + sqlx::query(sql!( + "UPDATE sandbox_k8s_executions + SET state = 'creating', pod_uid = NULL, pod_ip = NULL + WHERE sandbox_id = $1" + )) + .bind(sandbox_id) + .execute(&db.pool) + .await?; + let pool = db.pool.clone(); + let owner = tokio::spawn(async move { + tokio::time::sleep(Duration::from_millis(300)).await; + sqlx::query(sql!( + "UPDATE sandbox_k8s_executions + SET state = 'running', pod_uid = $2, pod_ip = $3::INET + WHERE sandbox_id = $1" + )) + .bind(sandbox_id) + .bind(POD_UID) + .bind(POD_IP) + .execute(&pool) + .await?; + sqlx::query(sql!( + "UPDATE sandboxes SET lifecycle = 'started' WHERE id = $1" + )) + .bind(sandbox_id) + .execute(&pool) + .await?; + Ok::<_, BoxError>(()) + }); + + let client = if via_access { + restoring + .substrate + .access_runtime_client( + access_identity(), + &restoring.sandbox, + ExecutionActivityKind::Access, + Duration::from_secs(10), + crate::controllers::substrate::RecoveryCharge::Exempt, + ) + .await + } else { + restoring + .substrate + .runtime_client( + sandbox_id, + crate::controllers::substrate::RecoveryCharge::Exempt, + ) + .await + }; + let lifecycle_at_return = sandbox_lifecycle(&db.pool, sandbox_id).await?; + owner.await??; + + assert!( + client.is_ok(), + "via_access={via_access}: {:?}", + client.err() + ); + assert_eq!(lifecycle_at_return, "started", "via_access={via_access}"); + } + Ok(()) +} + +/// The reachability probe that replays a runtime RPC on a fresh client finds +/// no client for a restoring pod. +#[tokio::test(flavor = "current_thread")] +async fn probe_runtime_client_finds_no_client_for_a_restoring_pod() -> TestResult { + // Credentials make the pod connectable, so only the gate can refuse it. + if !crate::test_support::enter_sim_worker_process() { + return Ok(()); + } + let db = pplx_space_db_test_fixtures::pg_test_db().await?; + let sandbox_id = SandboxId::new(); + let restoring = restoring_with_live_pod(&db.pool, sandbox_id, "120 seconds").await?; + assert!( + restoring + .substrate + .probe_runtime_client(sandbox_id) + .await? + .is_none() + ); + Ok(()) +} diff --git a/crates/libs/api/src/controllers/substrate/kubernetes/execution.rs b/crates/libs/api/src/controllers/substrate/kubernetes/execution.rs index 4e072c1e4..156c90186 100644 --- a/crates/libs/api/src/controllers/substrate/kubernetes/execution.rs +++ b/crates/libs/api/src/controllers/substrate/kubernetes/execution.rs @@ -176,6 +176,37 @@ pub(super) fn ensure_wake_recovery_lifecycle(sandbox: &Sandbox) -> Result<(), Su } } +/// What [`KubernetesSubstrate::reclaim_lapsed_restore`] did. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +enum LapsedRestoreReclaim { + /// This request drove the restore's wake under its own claim. + Reclaimed, + /// An owner holds the restore again; keep joining it. + OwnerReturned, + /// The row isn't this request's to reclaim (a drain owns it, it moved + /// on, or too little budget is left). + Declined, +} + +/// Below this much of its readiness budget, a request joining a restore +/// whose owner lapsed doesn't reclaim it: the claim would take the lease only +/// to fail its wake at once. +const MIN_RESTORE_RECLAIM_WINDOW: Duration = Duration::from_secs(1); + +/// The error for a request whose sandbox left `restoring` other than for +/// `started`: the error a wake would give a state that can't be woken, and +/// otherwise (a rollback to `suspended`, say) a retryable `RestoreInFlight`, +/// so the retry acts on the current state. +fn left_restoring_error(sandbox: &Sandbox) -> SubstrateError { + match ensure_wake_recovery_lifecycle(sandbox) { + Err(error) => error, + Ok(()) => SubstrateError::restore_in_flight(format!( + "sandbox {} went {} before its restore reached started; retry the same sandbox", + sandbox.id, sandbox.lifecycle + )), + } +} + #[derive(Clone)] struct RestoreClaimHandling { preclaimed: Option, @@ -368,6 +399,62 @@ impl KubernetesSubstrate { } } + /// Best-effort rollback of a failed fresh restore claim to `suspended` + /// (see [`K8sStore::revert_restore_claim_to_suspended`]). When the + /// rollback retired the claim's execution, its pod is deleted here too. + /// That delete is best-effort: a pod it misses belongs to no active + /// execution, so the orphaned-restore-pod GC and the next restore's + /// lingering-pod sweep both reap it. + pub(super) async fn revert_failed_restore_claim( + &self, + sandbox_id: SandboxId, + restore_claim: RestoreClaim, + activation_attempt: Option<(SandboxActivationAttemptId, i64)>, + ) { + let outcome = match self + .store + .revert_restore_claim_to_suspended( + sandbox_id, + restore_claim.restore_lease_token(), + activation_attempt, + self.k8s.abandoned_restore_retirement, + ) + .await + { + Ok(outcome) => outcome, + Err(err) => { + tracing::warn!( + sandbox_id = %sandbox_id, + error = %err, + "failed to restore suspended lifecycle after Kubernetes restore failure" + ); + return; + } + }; + pplx_space_metrics::sandbox::restore::claim_reverted(outcome.metric_outcome()); + match outcome { + RestoreClaimRevert::ExecutionRetired { pod_name } => { + // A retired generation is never reused, so the name can't + // belong to a successor. + if let Err(err) = self.delete_tracked_pods(vec![pod_name]).await { + tracing::warn!( + sandbox_id = %sandbox_id, + error = %err, + "failed to delete the pod of an abandoned restore; it has no active \ + execution, so orphan GC or the next restore removes it" + ); + } + } + RestoreClaimRevert::ExecutionLeftActive => tracing::warn!( + sandbox_id = %sandbox_id, + "reverted a failed restore with an execution still active: either restore \ + selection isn't proven to choose a durable pre-restore source, or the claim \ + reused that execution rather than creating it" + ), + RestoreClaimRevert::LifecycleOnly | RestoreClaimRevert::NotReverted => {} + } + } + /// Best-effort release of a restore claim whose deferred admission /// charge was rejected: a fresh suspended claim rolls back to /// `suspended`; a redrive keeps the row `restoring` and only frees the @@ -379,13 +466,9 @@ impl KubernetesSubstrate { ) { let result = match restore_claim { RestoreClaim::FreshFromSuspended { .. } => { - self.store - .revert_lifecycle_after_rollback_best_effort( - sandbox_id, - "suspended", - restore_claim.restore_lease_token(), - ) - .await + return self + .revert_failed_restore_claim(sandbox_id, restore_claim, None) + .await; } RestoreClaim::RedroveRestoring { restore_lease_token: Some(token), @@ -584,6 +667,31 @@ impl KubernetesSubstrate { }) } + /// Redrive the restore of a sandbox whose started recovery a suspend + /// superseded, for an access request. The binding is handed out only once + /// the row is `started` (see `join_restore_before_dial`): if the redrive's + /// owner couldn't commit that, its restore may still be rolled back. + pub(super) async fn redrive_suspended_recovery_for_access( + &self, + sandbox: &Sandbox, + retired_generation: Option, + join_deadline: Instant, + metric_identity: pplx_space_metrics::sandbox::api::SandboxApiMetricIdentity, + charge: RecoveryCharge, + ) -> Result { + self.redrive_started_recovery_after_suspend( + sandbox, + retired_generation, + join_deadline, + metric_identity, + charge, + None, + ) + .await + .map_err(ControllerError::from)?; + self.binding_once_started(sandbox.id, join_deadline).await + } + /// Bounded follower join on the user restore lease. The process-wide /// restore poller batch-observes every pending sandbox; this request owns /// only a watch receiver until the owner publishes a routable outcome, @@ -658,6 +766,223 @@ impl KubernetesSubstrate { } } + /// A request a user is waiting on never dials a sandbox's pod while the + /// sandbox is `restoring`: the restore can still fail and be rolled back, + /// and the rollback retires its execution and deletes the pod, discarding + /// whatever it served. Instead this joins the in-flight restore and, once + /// the sandbox is `started`, returns a client for the execution the row + /// routes to. A row the caller saw `restoring` that is already `started` + /// here is dialed the same way. + /// + /// It joins before it claims, waiting on the shared restore poller rather + /// than polling the row per request. Only a row still `restoring` once its + /// owner's lease and execution ownership have lapsed, and that no drain + /// owns, is reclaimed: this request takes the restoring lease itself and + /// drives the wake under that claim, whose owner commits `started`. A + /// follower whose owner rolled back to `suspended` gets `RestoreInFlight` + /// rather than starting a fresh restore that its caller's access-wake + /// policy and admission never approved. Past `timeout` this fails as + /// `RestoreInFlight` too, a retryable 503; a row that leaves for a state + /// that can't be woken gets that state's error. + pub(super) async fn join_restore_before_dial( + &self, + sandbox_id: SandboxId, + metric_identity: pplx_space_metrics::sandbox::api::SandboxApiMetricIdentity, + timeout: Duration, + charge: RecoveryCharge, + ) -> Result { + let (_, join_deadline) = ready_wait_bounds(timeout); + let sandbox = self + .store + .require_sandbox(sandbox_id) + .await + .map_err(ControllerError::from)?; + match parse_sandbox_lifecycle(&sandbox.lifecycle).map_err(ControllerError::from)? { + SandboxLifecycle::Started => {} + SandboxLifecycle::Restoring => { + let mut backoff = RecoveryJoinBackoff::new(self.recovery_join_backoff); + while let UserRestoreJoin::Claimable = self + .wait_for_user_restore(sandbox_id, join_deadline, None, &mut backoff) + .await + .map_err(ControllerError::from)? + { + let reclaim = self + .reclaim_lapsed_restore( + sandbox_id, + join_deadline, + metric_identity, + charge.clone(), + ) + .await?; + if reclaim != LapsedRestoreReclaim::OwnerReturned { + break; + } + } + } + _ => return Err(ControllerError::from(left_restoring_error(&sandbox))), + } + self.dial_once_started(sandbox_id, join_deadline).await + } + + /// Reclaim a `restoring` row whose owner's lease and execution ownership + /// lapsed, for [`Self::join_restore_before_dial`]. The lease claim matches + /// only a row still `restoring`, unowned and unmarked; this request then + /// drives the wake under that claim, and the wake's owner commits + /// `started`. A row a drain owns, or that moved on, is declined and left + /// to the caller's `started` wait. + async fn reclaim_lapsed_restore( + &self, + sandbox_id: SandboxId, + join_deadline: Instant, + metric_identity: pplx_space_metrics::sandbox::api::SandboxApiMetricIdentity, + charge: RecoveryCharge, + ) -> Result { + if remaining_ready_window(join_deadline) < MIN_RESTORE_RECLAIM_WINDOW { + return Ok(LapsedRestoreReclaim::Declined); + } + let restore_claim = match self + .store + .claim_markerless_restoring_restore_lease(sandbox_id) + .await + { + Ok(Some(restore_claim)) => restore_claim, + Ok(None) | Err(SubstrateError::Conflict { .. }) => { + return Ok(LapsedRestoreReclaim::Declined); + } + Err(SubstrateError::RestoreInFlight { .. }) => { + return Ok(LapsedRestoreReclaim::OwnerReturned); + } + Err(error) => return Err(ControllerError::from(error)), + }; + if let Err(error) = charge.charge_restore_claim().await { + self.release_rejected_restore_claim(sandbox_id, restore_claim) + .await; + return Err(ControllerError::from(error)); + } + let sandbox = self + .store + .require_sandbox(sandbox_id) + .await + .map_err(ControllerError::from)?; + // Only a writer that ignores the lease (the tokenless evacuation + // rollback) can move the row now; and the charge may have used up the + // budget. Either way, hand the restore back rather than drive it. + if !is_restoring_lifecycle(&sandbox.lifecycle) + || remaining_ready_window(join_deadline) < MIN_RESTORE_RECLAIM_WINDOW + { + self.release_rejected_restore_claim(sandbox_id, restore_claim) + .await; + return Ok(LapsedRestoreReclaim::Declined); + } + self.wake_execution_inner( + &sandbox, + remaining_ready_window(join_deadline), + metric_identity, + None, + WakeTarget::started(None), + RestoreClaimHandling::preclaimed_with_cleanup(restore_claim), + ) + .await + .map_err(ControllerError::from)?; + Ok(LapsedRestoreReclaim::Reclaimed) + } + + /// Wake a dormant sandbox for a user request and dial it once it is + /// `started`. A restore-shaped wake leaves the row `restoring` until its + /// owner commits `started` (or, for a joined run, its owner did), so the + /// binding the wake returns isn't dialed directly (see + /// `join_restore_before_dial`). + pub(super) async fn wake_and_dial_once_started( + &self, + sandbox: &Sandbox, + timeout: Duration, + metric_identity: pplx_space_metrics::sandbox::api::SandboxApiMetricIdentity, + charge: RecoveryCharge, + ) -> Result { + let (_, join_deadline) = ready_wait_bounds(timeout); + self.wake_execution_with_metric_identity(sandbox, timeout, metric_identity, charge) + .await + .map_err(ControllerError::from)?; + self.dial_once_started(sandbox.id, join_deadline).await + } + + /// Dial the execution a sandbox routes to once the row is `started`, + /// polling with the recovery-join backoff until `deadline`. Observes before + /// each sleep, so a row that is already `started` is dialed at once. A + /// `restoring` row that reads as unowned is still waited on: a drain's + /// evacuation restore owns its row through the marker, not the lease, and + /// only the drain moves it to `started`. + async fn dial_once_started( + &self, + sandbox_id: SandboxId, + deadline: Instant, + ) -> Result { + let binding = self.binding_once_started(sandbox_id, deadline).await?; + crate::controllers::substrate::node_api_client::connect_worker(binding.runtime_endpoint) + .await + } + + /// The binding [`Self::dial_once_started`] dials. + async fn binding_once_started( + &self, + sandbox_id: SandboxId, + deadline: Instant, + ) -> Result { + let mut backoff = RecoveryJoinBackoff::new(self.recovery_join_backoff); + loop { + match self + .store + .observe_user_restore(sandbox_id) + .await + .map_err(ControllerError::from)? + { + UserRestoreObservation::Routable { + generation, + pod_name, + pod_ip, + } => { + return self + .binding_for_routable_pod(generation, pod_name, &pod_ip) + .map_err(ControllerError::from); + } + UserRestoreObservation::InFlight { .. } => {} + UserRestoreObservation::Claimable => { + let sandbox = self + .store + .require_sandbox(sandbox_id) + .await + .map_err(ControllerError::from)?; + if normalize_lifecycle(&sandbox.lifecycle) == "started" { + // Moved on since the observation: observe again now. + continue; + } + if !is_restoring_lifecycle(&sandbox.lifecycle) { + return Err(ControllerError::from(left_restoring_error(&sandbox))); + } + } + UserRestoreObservation::StartedUnroutable => { + return Err(ControllerError::from(SubstrateError::restore_in_flight( + format!( + "sandbox {sandbox_id} is started without a routable execution; \ + retry the same sandbox" + ), + ))); + } + } + if tokio::time::timeout_at(deadline, tokio::time::sleep(backoff.next_delay())) + .await + .is_err() + { + return Err(ControllerError::from(SubstrateError::restore_in_flight( + format!( + "sandbox {sandbox_id}'s restore didn't reach started in time; \ + retry the same sandbox" + ), + ))); + } + } + } + /// Claim or join started recovery. Followers wait without spending /// admission; owners charge before Pod creation and publish completion. /// The access path takes no caller-selected `suspend_restore_source_node`. @@ -698,17 +1023,14 @@ impl KubernetesSubstrate { retired_generation, } => { return self - .redrive_started_recovery_after_suspend( + .redrive_suspended_recovery_for_access( &sandbox, retired_generation, join_deadline, metric_identity, charge, - None, ) - .await - .map(|activation| activation.binding) - .map_err(ControllerError::from); + .await; } } } else { @@ -860,17 +1182,14 @@ impl KubernetesSubstrate { .await; } return self - .redrive_started_recovery_after_suspend( + .redrive_suspended_recovery_for_access( &sandbox, Some(generation), join_deadline, metric_identity, RecoveryCharge::Exempt, - None, ) - .await - .map(|activation| activation.binding) - .map_err(ControllerError::from); + .await; } self.publish_started_recovery_result( @@ -1909,22 +2228,14 @@ impl KubernetesSubstrate { result.as_ref().err(), wake_dispatch, ) - && let Err(update_err) = self - .store - .revert_lifecycle_after_rollback_best_effort( - sandbox_id, - "suspended", - prepared - .restore_claim - .and_then(RestoreClaim::restore_lease_token), - ) - .await + && let Some(restore_claim) = prepared.restore_claim { - tracing::warn!( - sandbox_id = %sandbox_id, - error = %update_err, - "failed to restore suspended lifecycle after Kubernetes wake failure" - ); + self.revert_failed_restore_claim( + sandbox_id, + restore_claim, + activation_assignment.assigned(), + ) + .await; } if cleanup_on_failure && !restore_failure_should_revert_to_suspended( @@ -1957,6 +2268,48 @@ impl KubernetesSubstrate { ) .await; } + // The owner of a user restore commits `restoring -> started` itself, + // under its own lease, before handing out the binding: a user request + // never dials a `restoring` pod (see `join_restore_before_dial`), and + // on the access path no lifecycle-controller completion follows. A + // lost or failed CAS leaves the row to the controller's completion or + // the node's published state, which those requests then wait for. + if result.is_ok() + && target_state == SandboxLifecycle::Started + && evacuation_placement.is_none() + && let Some(restore_lease_token) = prepared + .restore_claim + .and_then(RestoreClaim::restore_lease_token) + { + match self + .store + .complete_owned_user_restore(sandbox_id, restore_lease_token) + .await + { + // Reported here, in the restore's detached task, so a caller + // that hung up can't drop it; the node's published state + // finds the row `started` and reports nothing. The lifecycle + // controller's actions report their own completion too, as + // for `WakeCompleted`. + Ok(true) => SandboxEvent::new( + sandbox_id, + SandboxEventType::RestoreCompleted, + SandboxEventSource::Substrate, + ) + .emit_with( + SandboxEventDetails::default() + .reason("restore_owner_completed") + .with_from_state("restoring") + .with_to_state("started"), + ), + Ok(false) => {} + Err(error) => tracing::warn!( + sandbox_id = %sandbox_id, + %error, + "failed to commit started after an owned restore" + ), + } + } result.map(|(binding, _)| ExecutionActivation::owned(binding)) } } diff --git a/crates/libs/api/src/controllers/substrate/kubernetes/mod.rs b/crates/libs/api/src/controllers/substrate/kubernetes/mod.rs index e459d95a5..62996cb3b 100644 --- a/crates/libs/api/src/controllers/substrate/kubernetes/mod.rs +++ b/crates/libs/api/src/controllers/substrate/kubernetes/mod.rs @@ -70,8 +70,9 @@ use cancel_rate_tracker::CancelRateTracker; use restore_waiters::RestoreWaiters; use store::{ ActiveExecutionState, K8sStore, NodeLifecycleState, NodeStateWrite, PlacementClock, - PreparedWakeSandbox, RestoreClaim, SandboxExecutionRow, StartedRecoveryClaim, - StartedRecoveryCompletion, StartedRecoveryObservation, UserRestoreObservation, + PreparedWakeSandbox, RestoreClaim, RestoreClaimRevert, SandboxExecutionRow, + StartedRecoveryClaim, StartedRecoveryCompletion, StartedRecoveryObservation, + UserRestoreObservation, }; use crate::{ @@ -370,6 +371,10 @@ struct ResolvedPodInstance { ip: PodIp, pod_uid: Option, generation: i64, + /// The sandbox row read with the execution was `restoring`. A path that + /// serves a user request must not dial such a pod (see + /// `KubernetesSubstrate::join_restore_before_dial`). + sandbox_restoring: bool, } impl ResolvedPodInstance { @@ -419,6 +424,9 @@ pub struct KubernetesState { /// plane does not publish `suspended` while kubelet/containerd teardown for /// the old sandbox pod can still run later. pub k8s_blocking_delete_timeout: Duration, + /// Whether a failed restore claim's rollback retires its execution + /// (`SPACE_ABANDONED_RESTORE_RETIREMENT_ENABLED`, default off). + pub abandoned_restore_retirement: super::AbandonedRestoreRetirement, pub caches: Arc, } @@ -471,6 +479,7 @@ impl KubernetesState { create_command_ready_timeout, create_data_plane_ready_timeout, k8s_blocking_delete_timeout: DEFAULT_K8S_BLOCKING_DELETE_TIMEOUT, + abandoned_restore_retirement: super::AbandonedRestoreRetirement::from_env(), sandbox_proxy_port: DEFAULT_SANDBOX_PROXY_PORT, node_api_port: DEFAULT_SANDBOX_PROXY_PORT, sandbox_pod_config: SandboxPodConfigValues::from_env()?, @@ -554,6 +563,10 @@ fn is_suspended_lifecycle(lifecycle: &str) -> bool { normalize_lifecycle(lifecycle) == "suspended" } +fn is_restoring_lifecycle(lifecycle: &str) -> bool { + normalize_lifecycle(lifecycle) == "restoring" +} + fn is_recoverable_live_lifecycle(lifecycle: &str) -> bool { matches!( normalize_lifecycle(lifecycle).as_str(), diff --git a/crates/libs/api/src/controllers/substrate/kubernetes/pod_ops/read.rs b/crates/libs/api/src/controllers/substrate/kubernetes/pod_ops/read.rs index f1eb5dd49..be84cfbca 100644 --- a/crates/libs/api/src/controllers/substrate/kubernetes/pod_ops/read.rs +++ b/crates/libs/api/src/controllers/substrate/kubernetes/pod_ops/read.rs @@ -196,7 +196,12 @@ impl KubernetesSubstrate { let Some(row) = self.store.active_execution_status(sandbox_id).await? else { return Ok(None); }; - if is_suspended_lifecycle(&row.lifecycle) || execution_pod_lookup_in_flight(row.state) { + // A restoring pod is not reachable for a request: see + // `join_restore_before_dial`. + if is_suspended_lifecycle(&row.lifecycle) + || is_restoring_lifecycle(&row.lifecycle) + || execution_pod_lookup_in_flight(row.state) + { return Ok(None); } let Some(pod) = self @@ -466,6 +471,7 @@ impl KubernetesSubstrate { ip, pod_uid: pod_uid.as_deref().and_then(|uid| PodUid::parse(uid).ok()), generation: row.generation, + sandbox_restoring: is_restoring_lifecycle(&row.lifecycle), })) } } diff --git a/crates/libs/api/src/controllers/substrate/kubernetes/store.rs b/crates/libs/api/src/controllers/substrate/kubernetes/store.rs index 8da2d92a2..7a3da6dc2 100644 --- a/crates/libs/api/src/controllers/substrate/kubernetes/store.rs +++ b/crates/libs/api/src/controllers/substrate/kubernetes/store.rs @@ -642,6 +642,39 @@ impl RestoreClaim { } } +/// What [`K8sStore::revert_restore_claim_to_suspended`] did with a failed +/// fresh restore claim. +#[derive(Debug, Clone, PartialEq, Eq)] +pub(super) enum RestoreClaimRevert { + /// The row reverted to `suspended` and the execution the claim created + /// was retired as `replaced`, so its pod, `pod_name`, is unadoptable. + ExecutionRetired { pod_name: String }, + /// The row reverted to `suspended` with no active execution left. + LifecycleOnly, + /// The row reverted to `suspended` with an execution still active, + /// leaving a same-generation pod on a `suspended` row: either the claim's + /// own execution, because restore selection isn't proven to choose a + /// durable pre-restore source, or one the claim reused rather than + /// created, whose pod may hold state of its own. + ExecutionLeftActive, + /// The row is no longer this claim's `restoring` row; nothing changed. + NotReverted, +} + +impl RestoreClaimRevert { + pub(super) const fn metric_outcome( + &self, + ) -> pplx_space_metrics::sandbox::restore::RevertOutcome { + use pplx_space_metrics::sandbox::restore::RevertOutcome; + match self { + Self::ExecutionRetired { .. } => RevertOutcome::ExecutionRetired, + Self::LifecycleOnly => RevertOutcome::LifecycleOnly, + Self::ExecutionLeftActive => RevertOutcome::ExecutionLeftActive, + Self::NotReverted => RevertOutcome::NotReverted, + } + } +} + #[derive(sqlx::FromRow)] struct RestoreClaimRow { lifecycle: String, diff --git a/crates/libs/api/src/controllers/substrate/kubernetes/store/execution_rows.rs b/crates/libs/api/src/controllers/substrate/kubernetes/store/execution_rows.rs index 98ca38ffd..64bd34161 100644 --- a/crates/libs/api/src/controllers/substrate/kubernetes/store/execution_rows.rs +++ b/crates/libs/api/src/controllers/substrate/kubernetes/store/execution_rows.rs @@ -948,7 +948,7 @@ impl K8sStore { /// The activation-attempt identity fences retries that reuse a generation. /// `restore_lease_token` additionally ownership-fences restore takeover /// (with the same `None`-is-unguarded contract as - /// [`Self::revert_lifecycle_after_rollback_best_effort`]). A dropped leader + /// [`Self::revert_restore_claim_to_suspended`]). A dropped leader /// can outlive its lease; once a follower adopts the still-`creating` /// generation, either changed fence makes the delayed retirement a no-op. /// diff --git a/crates/libs/api/src/controllers/substrate/kubernetes/store/k8s_store_postgres_tests.rs b/crates/libs/api/src/controllers/substrate/kubernetes/store/k8s_store_postgres_tests.rs index 7478b38fb..c3b5f25e6 100644 --- a/crates/libs/api/src/controllers/substrate/kubernetes/store/k8s_store_postgres_tests.rs +++ b/crates/libs/api/src/controllers/substrate/kubernetes/store/k8s_store_postgres_tests.rs @@ -13,10 +13,19 @@ use pplx_space_test_env::TestResult; mod cleanup_recovery_lifecycle_matrix_tests; mod lifecycle_writer_tests; mod materialization_record_tests; +mod owned_restore_completion_tests; +mod restore_revert_tests; +mod started_recovery_observation_tests; mod stopped_evacuation_claim_tests; mod stopped_restore_selector_tests; mod substrate_scope_tests; mod suspend_lifecycle_matrix_tests; +use owned_restore_completion_tests::owned_restore_completion_requires_its_lease_and_a_routable_execution; +use restore_revert_tests::{ + failed_restore_revert_retires_only_its_own_proven_execution, + without_retirement_a_failed_restore_revert_moves_only_the_lifecycle, +}; +use started_recovery_observation_tests::started_recovery_followers_succeed_only_on_a_started_sandbox; use stopped_evacuation_claim_tests::{ a_fresh_stopped_claim_is_not_rolled_back_on_a_clean_failure, evacuation_claim_takes_only_a_released_stopped_evacuation, @@ -1433,6 +1442,10 @@ async fn k8s_store_postgres_behaviors() -> TestResult { clean_redrive_failure_releases_restore_lease_for_immediate_retry(&fixture).await?; stale_restore_rollback_is_guarded_by_lease_token(&fixture).await?; restore_rollback_requires_fresh_claim_and_clean_failure(&fixture).await?; + failed_restore_revert_retires_only_its_own_proven_execution(&fixture).await?; + without_retirement_a_failed_restore_revert_moves_only_the_lifecycle(&fixture).await?; + owned_restore_completion_requires_its_lease_and_a_routable_execution(&fixture).await?; + started_recovery_followers_succeed_only_on_a_started_sandbox(&fixture).await?; prepare_wake_preserves_restore_source_boundary(&fixture).await?; prepare_wake_sandbox_restores_suspended_lifecycle(&fixture).await?; restored_generation_accepts_late_pod_metadata(&fixture).await?; @@ -7781,15 +7794,12 @@ async fn begin_restore_lifecycle_preserves_retry_and_marker_claim_rules( .expect_err("started sandbox should not enter restore lifecycle"); assert!(matches!(err, SubstrateError::Conflict { .. })); - // Test fixture setup: a fresh `started` row from `insert_sandbox` - // is never in a terminal lifecycle, so the rollback-best-effort - // helper is functionally equivalent to a plain UPDATE here. We - // reuse it rather than splitting into a separate test-only setter - // — keeping a single lifecycle-write API surface. - fixture - .store - .revert_lifecycle_after_rollback_best_effort(sandbox_id, "suspended", None) - .await?; + sqlx::query(sql!( + "UPDATE sandboxes SET lifecycle = 'suspended' WHERE id = $1" + )) + .bind(sandbox_id) + .execute(&fixture.pool) + .await?; let claim = fixture .store .begin_restore_lifecycle(sandbox_id, false) @@ -8247,8 +8257,8 @@ async fn clean_redrive_failure_releases_restore_lease_for_immediate_retry( /// A stale clean-failure rollback (delayed past the lease TTL, carrying an /// OLD token) must not flip a row that a retry has already re-claimed with a /// NEW token back to `suspended` — that would orphan the newer live restore. -/// The token-guarded `revert_lifecycle_after_rollback_best_effort` is a CAS -/// on the claim's token; `None` (evacuation / test setup) stays unguarded. +/// The token-guarded `revert_restore_claim_to_suspended` is a CAS on the +/// claim's token; `None` (the tokenless evacuation claim) is unguarded. async fn stale_restore_rollback_is_guarded_by_lease_token(fixture: &PostgresStore) -> TestResult { let id = SandboxId::new(); insert_sandbox(&fixture.pool, id, "restoring").await?; @@ -8269,7 +8279,12 @@ async fn stale_restore_rollback_is_guarded_by_lease_token(fixture: &PostgresStor let old_token = Uuid::now_v7(); fixture .store - .revert_lifecycle_after_rollback_best_effort(id, "suspended", Some(old_token)) + .revert_restore_claim_to_suspended( + id, + Some(old_token), + None, + crate::controllers::substrate::AbandonedRestoreRetirement::Enabled, + ) .await?; // A stale-token rollback must not flip the newer retry to `suspended` // nor clear its lease — the redaction asserts the token is exactly the @@ -8292,7 +8307,12 @@ async fn stale_restore_rollback_is_guarded_by_lease_token(fixture: &PostgresStor // the clear-trigger drops the lease as the row leaves `restoring`). fixture .store - .revert_lifecycle_after_rollback_best_effort(id, "suspended", Some(new_token)) + .revert_restore_claim_to_suspended( + id, + Some(new_token), + None, + crate::controllers::substrate::AbandonedRestoreRetirement::Enabled, + ) .await?; // A matching-token rollback reverts, and leaving `restoring` clears the // lease (no run-varying value survives here, so a plain snapshot pins @@ -8335,7 +8355,12 @@ async fn restore_rollback_requires_fresh_claim_and_clean_failure( ) { fixture .store - .revert_lifecycle_after_rollback_best_effort(fresh_clean_id, "suspended", None) + .revert_restore_claim_to_suspended( + fresh_clean_id, + None, + None, + crate::controllers::substrate::AbandonedRestoreRetirement::Enabled, + ) .await?; } let lifecycle: String = @@ -8415,7 +8440,12 @@ async fn restore_rollback_requires_fresh_claim_and_clean_failure( ) { fixture .store - .revert_lifecycle_after_rollback_best_effort(redrove_clean_id, "suspended", None) + .revert_restore_claim_to_suspended( + redrove_clean_id, + None, + None, + crate::controllers::substrate::AbandonedRestoreRetirement::Enabled, + ) .await?; } let lifecycle: String = @@ -8447,7 +8477,12 @@ async fn restore_rollback_requires_fresh_claim_and_clean_failure( ) { fixture .store - .revert_lifecycle_after_rollback_best_effort(fresh_timeout_id, "suspended", None) + .revert_restore_claim_to_suspended( + fresh_timeout_id, + None, + None, + crate::controllers::substrate::AbandonedRestoreRetirement::Enabled, + ) .await?; } let lifecycle: String = @@ -8492,7 +8527,12 @@ async fn restore_rollback_requires_fresh_claim_and_clean_failure( ) { fixture .store - .revert_lifecycle_after_rollback_best_effort(redrove_timeout_id, "suspended", None) + .revert_restore_claim_to_suspended( + redrove_timeout_id, + None, + None, + crate::controllers::substrate::AbandonedRestoreRetirement::Enabled, + ) .await?; } let lifecycle: String = diff --git a/crates/libs/api/src/controllers/substrate/kubernetes/store/k8s_store_postgres_tests/owned_restore_completion_tests.rs b/crates/libs/api/src/controllers/substrate/kubernetes/store/k8s_store_postgres_tests/owned_restore_completion_tests.rs new file mode 100644 index 000000000..b4fd722df --- /dev/null +++ b/crates/libs/api/src/controllers/substrate/kubernetes/store/k8s_store_postgres_tests/owned_restore_completion_tests.rs @@ -0,0 +1,129 @@ +//! Postgres behaviors for the restore owner's own `restoring -> started` +//! commit (`K8sStore::complete_owned_user_restore`). +//! +//! A sibling module rather than more lines in the parent, which is already far +//! past the repository's file-size guidance. Driven from +//! `k8s_store_postgres_behaviors` against the parent's fixture. + +use pplx_space_common_core::types::SandboxId; + +use super::*; + +/// What stands between the owner's wake and its commit. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +enum Shape { + /// The owner's restore is live behind a running execution with a pod IP. + Routable, + /// A newer claim holds the row under another lease token. + AnotherOwner, + /// A rollback moved the row back to `suspended`. + RolledBack, + /// The execution has no pod IP yet. + Unroutable, +} + +/// The owner of a user restore commits `started` only for its own lease, on +/// a row still `restoring` with a routable execution. Once it has, the API +/// rollback that would abandon the restore finds nothing to revert. +pub(super) async fn owned_restore_completion_requires_its_lease_and_a_routable_execution( + fixture: &PostgresStore, +) -> TestResult { + let mut mismatches = Vec::new(); + for shape in [ + Shape::Routable, + Shape::AnotherOwner, + Shape::RolledBack, + Shape::Unroutable, + ] { + let sandbox_id = SandboxId::new(); + insert_sandbox(&fixture.pool, sandbox_id, "suspended").await?; + let token = user_fresh_restore_token( + fixture + .store + .begin_restore_lifecycle(sandbox_id, false) + .await?, + ); + sqlx::query(sql!( + "INSERT INTO sandbox_k8s_executions + (sandbox_id, generation, pod_name, pod_namespace, pod_uid, state, active, + pod_ip) + VALUES ($1, 1, $2, 'space', 'uid-owned-g1', 'running', TRUE, + CASE WHEN $3 THEN NULL ELSE '10.80.0.7'::inet END)" + )) + .bind(sandbox_id) + .bind(format!("space-sbx-{}-g1", sandbox_id.as_uuid().simple())) + .bind(shape == Shape::Unroutable) + .execute(&fixture.pool) + .await?; + match shape { + Shape::AnotherOwner => { + sqlx::query(sql!( + "UPDATE sandboxes SET restore_lease_token = $2 WHERE id = $1" + )) + .bind(sandbox_id) + .bind(Uuid::now_v7()) + .execute(&fixture.pool) + .await?; + } + Shape::RolledBack => { + sqlx::query(sql!( + "UPDATE sandboxes SET lifecycle = 'suspended' WHERE id = $1" + )) + .bind(sandbox_id) + .execute(&fixture.pool) + .await?; + } + Shape::Routable | Shape::Unroutable => {} + } + let lifecycle_before: String = + sqlx::query_scalar(sql!("SELECT lifecycle FROM sandboxes WHERE id = $1")) + .bind(sandbox_id) + .fetch_one(&fixture.pool) + .await?; + + let completed = fixture + .store + .complete_owned_user_restore(sandbox_id, token) + .await?; + let reverted = fixture + .store + .revert_restore_claim_to_suspended( + sandbox_id, + Some(token), + None, + crate::controllers::substrate::AbandonedRestoreRetirement::Enabled, + ) + .await?; + let (lifecycle, placement): (String, String) = sqlx::query_as(sql!( + "SELECT lifecycle, placement FROM sandboxes WHERE id = $1" + )) + .bind(sandbox_id) + .fetch_one(&fixture.pool) + .await?; + + let expected_completed = shape == Shape::Routable; + if completed != expected_completed { + mismatches.push(format!( + "{shape:?}: completed {completed}, expected {expected_completed}" + )); + } + if expected_completed { + if (lifecycle.as_str(), placement.as_str()) != ("started", "on_node") { + mismatches.push(format!( + "{shape:?}: row is {lifecycle}/{placement} after the commit" + )); + } + if reverted != RestoreClaimRevert::NotReverted { + mismatches.push(format!( + "{shape:?}: a rollback after the commit reported {reverted:?}" + )); + } + } else if lifecycle == "started" { + mismatches.push(format!( + "{shape:?}: the row moved to started from {lifecycle_before}" + )); + } + } + assert!(mismatches.is_empty(), "{}", mismatches.join("\n")); + Ok(()) +} diff --git a/crates/libs/api/src/controllers/substrate/kubernetes/store/k8s_store_postgres_tests/restore_revert_tests.rs b/crates/libs/api/src/controllers/substrate/kubernetes/store/k8s_store_postgres_tests/restore_revert_tests.rs new file mode 100644 index 000000000..d262ad2a6 --- /dev/null +++ b/crates/libs/api/src/controllers/substrate/kubernetes/store/k8s_store_postgres_tests/restore_revert_tests.rs @@ -0,0 +1,477 @@ +//! Postgres behaviors for rolling a failed fresh restore claim back to +//! `suspended` (`K8sStore::revert_restore_claim_to_suspended`). +//! +//! A sibling module rather than more lines in the parent, which is already far +//! past the repository's file-size guidance. Driven from +//! `k8s_store_postgres_behaviors` against the parent's fixture. + +use pplx_space_common_core::types::{ + LocalArtifactId, NodeId, SandboxActivationAttemptId, SandboxId, +}; + +use super::*; + +/// Which execution row the sandbox carries when the revert runs. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +enum Execution { + /// The restore claim created it and its pod materialized (UID recorded, + /// placed on a node), then the restore failed before dispatch. + CreatedByClaim, + /// It was already active when the claim reused it, so this restore didn't + /// create it. + PredatesClaim, + /// The claim failed before any execution existed. + Absent, +} + +/// The activation attempt the rollback names as its claim's. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +enum ClaimAttempt { + /// The claim failed before it was assigned one. + Unassigned, + /// The attempt the execution carries. + Assigned, + /// Another attempt: a redrive took the execution over since. + Superseded, +} + +struct Case { + name: &'static str, + marker: bool, + /// A ready, stored pre-restore snapshot (of the marker's artifact on a + /// marker row), which is what proves restore selection stays durable. + durable_source: bool, + execution: Execution, + /// A node converged the row to `started` before the rollback ran. + converged_to_started: bool, + /// Revert with the claim's lease token, or tokenless as an evacuation + /// claim does. + with_token: bool, + attempt: ClaimAttempt, + expected: Expected, +} + +/// What the revert should report; the retired pod's name is per sandbox. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +enum Expected { + ExecutionRetired, + LifecycleOnly, + ExecutionLeftActive, + NotReverted, +} + +#[derive(Debug, PartialEq, Eq, sqlx::FromRow)] +struct RevertedRow { + lifecycle: String, + placement: String, + active_node_id: Option, + target_node_id: Option, + current_local_artifact_id: Option, + execution_active: Option, + execution_state: Option, +} + +async fn reverted_row(pool: &PgPool, sandbox_id: SandboxId) -> TestResult { + Ok(sqlx::query_as(sql!( + "SELECT s.lifecycle, s.placement, s.active_node_id, s.target_node_id, + s.current_local_artifact_id, + e.active AS execution_active, e.state AS execution_state + FROM sandboxes s + LEFT JOIN sandbox_k8s_executions e ON e.sandbox_id = s.id + WHERE s.id = $1" + )) + .bind(sandbox_id) + .fetch_one(pool) + .await?) +} + +const PRE_RESTORE_URI: &str = "s3://bucket/pre-restore?region=us-east-1"; + +fn execution_pod_name(sandbox_id: SandboxId) -> String { + format!("space-sbx-{}-g1", sandbox_id.as_uuid().simple()) +} + +/// `created` offsets the row's `created_at` and `updated_at` from now. An +/// older row needs both: a recently updated active execution would block the +/// restore claim itself. +async fn insert_execution( + pool: &PgPool, + sandbox_id: SandboxId, + created: &str, + activation_attempt_id: SandboxActivationAttemptId, +) -> TestResult { + sqlx::query(sql!( + "INSERT INTO sandbox_k8s_executions + (sandbox_id, generation, pod_name, pod_namespace, pod_uid, state, active, + created_at, updated_at, activation_attempt_id, activation_attempt_expires_at, + activation_attempt_target_state) + VALUES ($1, 1, $3, 'space', 'uid-revert-g1', 'created', TRUE, + NOW() + $2::interval, NOW() + $2::interval, $4, + NOW() + INTERVAL '5 minutes', 'started')" + )) + .bind(sandbox_id) + .bind(created) + .bind(execution_pod_name(sandbox_id)) + .bind(activation_attempt_id) + .execute(pool) + .await?; + Ok(()) +} + +/// A failed fresh restore claim's rollback records the abandonment on the +/// execution the claim created, retiring it as `replaced` with the row's +/// placement, while keeping the wake source (`target_node_id`, the artifact +/// marker). It retires nothing it can't prove disposable: an execution the +/// claim didn't create, or one whose sandbox has no proven durable restore +/// source, stays active; and a row that left `restoring` isn't touched. +pub(super) async fn failed_restore_revert_retires_only_its_own_proven_execution( + fixture: &PostgresStore, +) -> TestResult { + let cases = [ + Case { + name: "claimed execution, durable markerless source", + marker: false, + durable_source: true, + execution: Execution::CreatedByClaim, + converged_to_started: false, + with_token: true, + attempt: ClaimAttempt::Unassigned, + expected: Expected::ExecutionRetired, + }, + Case { + name: "claimed execution, durable marker source", + marker: true, + durable_source: true, + execution: Execution::CreatedByClaim, + converged_to_started: false, + with_token: true, + attempt: ClaimAttempt::Assigned, + expected: Expected::ExecutionRetired, + }, + Case { + name: "claimed execution, tokenless claim with its attempt", + marker: false, + durable_source: true, + execution: Execution::CreatedByClaim, + converged_to_started: false, + with_token: false, + attempt: ClaimAttempt::Assigned, + expected: Expected::ExecutionRetired, + }, + Case { + name: "claimed execution, tokenless claim with no attempt", + marker: false, + durable_source: true, + execution: Execution::CreatedByClaim, + converged_to_started: false, + with_token: false, + attempt: ClaimAttempt::Unassigned, + expected: Expected::ExecutionLeftActive, + }, + Case { + name: "execution taken over by a tokenless redrive", + marker: false, + durable_source: true, + execution: Execution::CreatedByClaim, + converged_to_started: false, + with_token: false, + attempt: ClaimAttempt::Superseded, + expected: Expected::ExecutionLeftActive, + }, + Case { + name: "claimed execution, no durable source", + marker: false, + durable_source: false, + execution: Execution::CreatedByClaim, + converged_to_started: false, + with_token: true, + attempt: ClaimAttempt::Unassigned, + expected: Expected::ExecutionLeftActive, + }, + Case { + name: "execution predates the claim", + marker: false, + durable_source: true, + execution: Execution::PredatesClaim, + converged_to_started: false, + with_token: true, + attempt: ClaimAttempt::Unassigned, + expected: Expected::ExecutionLeftActive, + }, + Case { + name: "no execution, durable source", + marker: false, + durable_source: true, + execution: Execution::Absent, + converged_to_started: false, + with_token: true, + attempt: ClaimAttempt::Unassigned, + expected: Expected::LifecycleOnly, + }, + Case { + name: "row converged to started first", + marker: false, + durable_source: true, + execution: Execution::CreatedByClaim, + converged_to_started: true, + with_token: false, + attempt: ClaimAttempt::Assigned, + expected: Expected::NotReverted, + }, + ]; + + let mut mismatches = Vec::new(); + for case in cases { + let sandbox_id = SandboxId::new(); + let node_id = NodeId::new(); + insert_sandbox(&fixture.pool, sandbox_id, "suspended").await?; + sqlx::query(sql!( + "INSERT INTO nodes (id, address, role, managed_by, state) + VALUES ($1, '10.0.0.11', 'worker', 'k8s', 'ready')" + )) + .bind(node_id) + .execute(&fixture.pool) + .await?; + let execution_attempt = SandboxActivationAttemptId::new(); + let claim_attempt = match case.attempt { + ClaimAttempt::Unassigned => None, + ClaimAttempt::Assigned => Some((execution_attempt, 1)), + ClaimAttempt::Superseded => Some((SandboxActivationAttemptId::new(), 1)), + }; + let marker = case.marker.then(LocalArtifactId::new); + sqlx::query(sql!( + "UPDATE sandboxes + SET target_node_id = $2, + current_local_artifact_id = $3, + current_local_artifact_kind = CASE WHEN $3::uuid IS NULL THEN NULL + ELSE 'full' END, + current_local_artifact_origin = CASE WHEN $3::uuid IS NULL THEN NULL + ELSE 'suspend' END + WHERE id = $1" + )) + .bind(sandbox_id) + .bind(node_id) + .bind(marker) + .execute(&fixture.pool) + .await?; + if case.durable_source { + insert_ready_full_snapshot_relative_to_lifecycle( + &fixture.pool, + sandbox_id, + PRE_RESTORE_URI, + -1, + marker, + ) + .await?; + } + if case.execution == Execution::PredatesClaim { + insert_execution(&fixture.pool, sandbox_id, "-1 hour", execution_attempt).await?; + } + + let token = user_fresh_restore_token( + fixture + .store + .begin_restore_lifecycle(sandbox_id, false) + .await?, + ); + + if case.execution == Execution::CreatedByClaim { + insert_execution(&fixture.pool, sandbox_id, "0 seconds", execution_attempt).await?; + } + // The pod bound, so the row reads as placed on that node, as of now: + // after the pre-restore snapshot was taken. + sqlx::query(sql!( + "UPDATE sandboxes + SET placement = 'on_node', + active_node_id = $2, + placement_changed_at = NOW() + WHERE id = $1" + )) + .bind(sandbox_id) + .bind(node_id) + .execute(&fixture.pool) + .await?; + if case.converged_to_started { + sqlx::query(sql!( + "UPDATE sandboxes SET lifecycle = 'started' WHERE id = $1" + )) + .bind(sandbox_id) + .execute(&fixture.pool) + .await?; + } + let before = reverted_row(&fixture.pool, sandbox_id).await?; + + let outcome = fixture + .store + .revert_restore_claim_to_suspended( + sandbox_id, + case.with_token.then_some(token), + claim_attempt, + crate::controllers::substrate::AbandonedRestoreRetirement::Enabled, + ) + .await?; + + let (expected_outcome, expected_row) = match case.expected { + Expected::ExecutionRetired => ( + RestoreClaimRevert::ExecutionRetired { + pod_name: execution_pod_name(sandbox_id), + }, + RevertedRow { + lifecycle: "suspended".to_string(), + placement: "unplaced".to_string(), + active_node_id: None, + execution_active: Some(false), + execution_state: Some("replaced".to_string()), + ..before + }, + ), + Expected::LifecycleOnly => ( + RestoreClaimRevert::LifecycleOnly, + RevertedRow { + lifecycle: "suspended".to_string(), + ..before + }, + ), + Expected::ExecutionLeftActive => ( + RestoreClaimRevert::ExecutionLeftActive, + RevertedRow { + lifecycle: "suspended".to_string(), + ..before + }, + ), + Expected::NotReverted => (RestoreClaimRevert::NotReverted, before), + }; + // The expected rows keep `before`'s wake source: `target_node_id` + // and the artifact marker. + let actual = reverted_row(&fixture.pool, sandbox_id).await?; + // A reverted row with a durable source still restores from it, + // although the bind moved its placement past that snapshot. + let selected = if case.expected == Expected::NotReverted { + None + } else { + let sandbox = crate::controllers::sandbox::fetch_sandbox(&fixture.pool, sandbox_id) + .await? + .expect("sandbox row exists"); + fixture + .store + .selected_suspend_restore_storage_uri(&sandbox, false) + .await? + }; + // A reverted, proven row whose active execution is gone has its + // placement clock cleared, so it selects its durable source. One that + // keeps an execution the claim didn't create keeps the clock: nothing + // proves that execution's pod holds no state of its own. + let clock_cleared = case.durable_source + && (case.expected == Expected::ExecutionRetired + || (case.expected == Expected::LifecycleOnly + && case.execution == Execution::Absent)); + let expected_selected = clock_cleared.then_some(PRE_RESTORE_URI); + // ...and is durably restorable at once, which is what lets a fenced + // release of the abandoned pod's runtime go ahead. + let durable = + pplx_space_sandbox_store::durably_restorable(&fixture.pool, sandbox_id).await?; + if outcome != expected_outcome + || actual != expected_row + || selected.as_deref() != expected_selected + || durable != clock_cleared + { + mismatches.push(format!( + "{}: {outcome:?}, {actual:?}, selected {selected:?}, durable {durable}", + case.name + )); + } + } + assert!(mismatches.is_empty(), "{mismatches:#?}"); + Ok(()) +} + +/// With abandoned-restore retirement disabled (the default until every +/// routing replica refuses to route to a `restoring` sandbox), a failed +/// fresh restore claim's rollback moves only the lifecycle, even with a +/// proven source: the execution the claim created stays active and the row +/// stays placed with its placement clock, so its pod isn't deleted under a +/// request an older replica may have routed to it. +pub(super) async fn without_retirement_a_failed_restore_revert_moves_only_the_lifecycle( + fixture: &PostgresStore, +) -> TestResult { + let sandbox_id = SandboxId::new(); + let node_id = NodeId::new(); + insert_sandbox(&fixture.pool, sandbox_id, "suspended").await?; + sqlx::query(sql!( + "INSERT INTO nodes (id, address, role, managed_by, state) + VALUES ($1, '10.0.0.12', 'worker', 'k8s', 'ready')" + )) + .bind(node_id) + .execute(&fixture.pool) + .await?; + sqlx::query(sql!( + "UPDATE sandboxes SET target_node_id = $2 WHERE id = $1" + )) + .bind(sandbox_id) + .bind(node_id) + .execute(&fixture.pool) + .await?; + insert_ready_full_snapshot_relative_to_lifecycle( + &fixture.pool, + sandbox_id, + PRE_RESTORE_URI, + -1, + None, + ) + .await?; + let token = user_fresh_restore_token( + fixture + .store + .begin_restore_lifecycle(sandbox_id, false) + .await?, + ); + insert_execution( + &fixture.pool, + sandbox_id, + "0 seconds", + SandboxActivationAttemptId::new(), + ) + .await?; + sqlx::query(sql!( + "UPDATE sandboxes + SET placement = 'on_node', + active_node_id = $2, + placement_changed_at = NOW() + WHERE id = $1" + )) + .bind(sandbox_id) + .bind(node_id) + .execute(&fixture.pool) + .await?; + let before = reverted_row(&fixture.pool, sandbox_id).await?; + let clock = || async { + sqlx::query_scalar::<_, Option>(sql!( + "SELECT placement_changed_at FROM sandboxes WHERE id = $1" + )) + .bind(sandbox_id) + .fetch_one(&fixture.pool) + .await + }; + let clock_before = clock().await?; + + let outcome = fixture + .store + .revert_restore_claim_to_suspended( + sandbox_id, + Some(token), + None, + crate::controllers::substrate::AbandonedRestoreRetirement::Disabled, + ) + .await?; + + assert_eq!(outcome, RestoreClaimRevert::ExecutionLeftActive); + assert_eq!( + reverted_row(&fixture.pool, sandbox_id).await?, + RevertedRow { + lifecycle: "suspended".to_string(), + ..before + } + ); + assert_eq!(clock().await?, clock_before); + Ok(()) +} diff --git a/crates/libs/api/src/controllers/substrate/kubernetes/store/k8s_store_postgres_tests/started_recovery_observation_tests.rs b/crates/libs/api/src/controllers/substrate/kubernetes/store/k8s_store_postgres_tests/started_recovery_observation_tests.rs new file mode 100644 index 000000000..d9ec80daf --- /dev/null +++ b/crates/libs/api/src/controllers/substrate/kubernetes/store/k8s_store_postgres_tests/started_recovery_observation_tests.rs @@ -0,0 +1,53 @@ +//! Postgres behaviors for a started-recovery follower's observation +//! (`K8sStore::observe_started_recovery`). +//! +//! A sibling module rather than more lines in the parent, which is already far +//! past the repository's file-size guidance. Driven from +//! `k8s_store_postgres_behaviors` against the parent's fixture. + +use pplx_space_common_core::types::SandboxId; + +use super::*; + +/// A follower of started recovery at generation 1 reports success only for a +/// running execution on a `started` sandbox. If a suspend and a restore landed +/// in between, the generation it finds running belongs to that restore, and +/// while the sandbox is still `restoring` the follower keeps waiting: handing +/// it out would route a user request to a restore that can still be rolled +/// back. +pub(super) async fn started_recovery_followers_succeed_only_on_a_started_sandbox( + fixture: &PostgresStore, +) -> TestResult { + let mut mismatches = Vec::new(); + for (lifecycle, expected_success, expected_pending) in [ + ("started", true, false), + ("restoring", false, true), + ("suspended", false, false), + ] { + let sandbox_id = SandboxId::new(); + insert_sandbox(&fixture.pool, sandbox_id, lifecycle).await?; + // The generation a later restore created, already running. + sqlx::query(sql!( + "INSERT INTO sandbox_k8s_executions + (sandbox_id, generation, pod_name, pod_namespace, pod_uid, state, active, pod_ip) + VALUES ($1, 2, $2, 'space', 'uid-observed-g2', 'running', TRUE, '10.80.0.9')" + )) + .bind(sandbox_id) + .bind(format!("space-sbx-{}-g2", sandbox_id.as_uuid().simple())) + .execute(&fixture.pool) + .await?; + + let observation = fixture + .store + .observe_started_recovery(sandbox_id, 1) + .await?; + + let succeeded = matches!(observation, StartedRecoveryObservation::Succeeded { .. }); + let pending = matches!(observation, StartedRecoveryObservation::Pending); + if (succeeded, pending) != (expected_success, expected_pending) { + mismatches.push(format!("{lifecycle}: observed {observation:?}")); + } + } + assert!(mismatches.is_empty(), "{}", mismatches.join("\n")); + Ok(()) +} diff --git a/crates/libs/api/src/controllers/substrate/kubernetes/store/k8s_store_postgres_tests/stopped_evacuation_claim_tests.rs b/crates/libs/api/src/controllers/substrate/kubernetes/store/k8s_store_postgres_tests/stopped_evacuation_claim_tests.rs index ca5241733..bcaf79798 100644 --- a/crates/libs/api/src/controllers/substrate/kubernetes/store/k8s_store_postgres_tests/stopped_evacuation_claim_tests.rs +++ b/crates/libs/api/src/controllers/substrate/kubernetes/store/k8s_store_postgres_tests/stopped_evacuation_claim_tests.rs @@ -244,10 +244,12 @@ pub(super) async fn a_fresh_stopped_claim_is_not_rolled_back_on_a_clean_failure( is no lease for the failure path to release" ); - fixture - .store - .revert_lifecycle_after_rollback_best_effort(sandbox_id, "stopped", None) - .await?; + sqlx::query(sql!( + "UPDATE sandboxes SET lifecycle = 'stopped' WHERE id = $1" + )) + .bind(sandbox_id) + .execute(pool) + .await?; assert_eq!( claim_row(pool, sandbox_id).await?, StoppedClaimRow { diff --git a/crates/libs/api/src/controllers/substrate/kubernetes/store/restore_lease.rs b/crates/libs/api/src/controllers/substrate/kubernetes/store/restore_lease.rs index b730258cd..378101433 100644 --- a/crates/libs/api/src/controllers/substrate/kubernetes/store/restore_lease.rs +++ b/crates/libs/api/src/controllers/substrate/kubernetes/store/restore_lease.rs @@ -210,57 +210,206 @@ impl K8sStore { .collect() } - /// Best-effort lifecycle revert for the restore/wake rollback paths in - /// [`super::super::execution`]. Reverts the row to `lifecycle` (in practice - /// `"suspended"`) when a Kubernetes restore or wake fails mid-flight. - /// CAS-guarded against `('failed', 'quarantined', 'deleted', 'cleanup_required')` so - /// a rollback can't overwrite a terminal lifecycle the - /// transient-lifecycle sweeper just claimed (the sweeper decided the - /// operation was wedged; the rollback's "revert to my last known - /// state" intent is wrong once the row has been retired). + /// Revert a failed fresh restore claim from `restoring` to `suspended`. /// - /// **Returns Ok regardless of whether the write applied.** The - /// rollback is best-effort by design — `failed` is the correct - /// resting state for a swept row, and callers don't act on the - /// no-write case. The function name encodes this contract; do - /// **not** call this from paths that depend on "the row is now in - /// `lifecycle` after this returns." Use a dedicated CAS-guarded - /// UPDATE inline if you need to detect race-loss (see the - /// post-restore / post-wake CAS in - /// `crates/libs/api/src/controllers/sandbox/lifecycle.rs` for the - /// pattern). - /// Best-effort revert of a sandbox's lifecycle (used by the restore - /// rollback path to put a failed restore back to `suspended`). + /// The revert is the one writer that knows the restore was abandoned, so + /// it records that on the execution the claim created: in the same + /// transaction, that execution is retired as `replaced` and the row is + /// unplaced. A pod the claim materialized then belongs to no active + /// execution, so no later restore adopts it (a new claim allocates a newer + /// generation, whose lingering-pod sweep deletes this one), and a backup + /// its runtime captures registers against a retired generation. Left + /// active instead, the execution keeps a same-generation pod adoptable on + /// a `suspended` row that no reconciler revisits, and that row looks like + /// one whose pod holds the only copy of the sandbox's state. /// - /// `restore_lease_token` token-guards the revert: when `Some`, the UPDATE - /// only matches if the row still holds that exact lease token. This closes - /// a stale-rollback race the restore lease introduced — a fresh attempt's - /// clean-failure rollback could be delayed past the lease TTL, by which - /// point a retry has claimed a *new* token and is actively restoring; - /// without the guard the stale rollback would flip the newer restore back - /// to `suspended` (and the clear-trigger would drop the newer lease), - /// orphaning live work. `None` skips the guard (evacuation reverts, which - /// are marker-gated and tokenless, and test-only lifecycle setup). - pub(in crate::controllers::substrate::kubernetes) async fn revert_lifecycle_after_rollback_best_effort( + /// The retirement needs the suspended-revert proof: restore selection on + /// the reverted row must still choose a ready, durably stored pre-restore + /// source, the state the sandbox had when it was suspended. The retired + /// runtime served no user request: the access, runtime-client, ingress + /// and data-plane route paths never dial a `restoring` sandbox's pod, but + /// join its restore or fail retryably, once every routing replica runs + /// them; until then retirement stays disabled. Without the proof, or when the + /// active execution can't be shown + /// to be this claim's (one that predates the claim was reused, not + /// created by it), only the lifecycle reverts and the execution is left as + /// it was. + /// + /// `restore_lease_token` binds the revert to the claim that owns the row: + /// a rollback delayed past the lease TTL must not flip a retry that holds + /// a newer token back to `suspended` (the lease-clear trigger would also + /// drop that retry's lease). `None` is the tokenless evacuation claim. + /// Either way the row must still be `restoring`; a row another writer + /// moved on, including one a node converged to `started`, is left alone. + /// + /// `activation_attempt` is the attempt and generation this claim was + /// assigned, if it got that far. The execution is retired only if it + /// still carries them, because a redrive of the same `restoring` row + /// reuses the execution under a new attempt. A claim with no attempt is + /// identified by its lease token alone, so a tokenless one retires + /// nothing: a delayed evacuation rollback can't tell its execution from a + /// later evacuation redrive's. + /// + /// With `retirement` disabled, only the lifecycle reverts, whatever the + /// proof says: the execution, placement and placement clock stay as the + /// restore left them (see [`AbandonedRestoreRetirement`]). + pub(in crate::controllers::substrate::kubernetes) async fn revert_restore_claim_to_suspended( &self, sandbox_id: SandboxId, - lifecycle: &str, restore_lease_token: Option, - ) -> Result<(), SubstrateError> { - sqlx::query(sql!( - "UPDATE sandboxes - SET lifecycle = $1 - WHERE id = $2 - AND lifecycle NOT IN ('failed', 'quarantined', 'deleted', 'cleanup_required') - AND ($3::uuid IS NULL OR restore_lease_token = $3)" + activation_attempt: Option<(SandboxActivationAttemptId, i64)>, + retirement: crate::controllers::substrate::AbandonedRestoreRetirement, + ) -> Result { + let mut tx = self + .pool + .begin() + .await + .db_context("failed to start restore revert transaction")?; + // Lock first, in its own statement: a later statement in this + // transaction then reads the snapshots and executions as of after the + // lock, so a writer that committed while this waited is seen. + let locked: Option = sqlx::query_scalar(sql!( + "SELECT TRUE + FROM sandboxes + WHERE id = $1 + AND lifecycle = 'restoring' + AND ($2::uuid IS NULL OR restore_lease_token = $2) + FOR UPDATE" )) - .bind(lifecycle) .bind(sandbox_id) .bind(restore_lease_token) - .execute(&self.pool) + .fetch_optional(&mut *tx) .await - .db_context_with(|| format!("failed to revert sandbox lifecycle to {lifecycle}"))?; - Ok(()) + .db_context("failed to lock sandbox for restore revert")?; + if locked.is_none() { + tx.commit() + .await + .db_context("failed to commit restore revert transaction")?; + return Ok(RestoreClaimRevert::NotReverted); + } + + let proof = restore_retry_suspended_revert_snapshot_exists("s"); + // Under the row lock no other claim can replace the execution, and + // `lifecycle_changed_at` still reads this claim's `restoring` + // transition, so an active execution created since, carrying this + // claim's attempt (or, with no attempt, under this claim's lease + // token), is the one it created. + let query = format!( + "SELECT claimed.generation, + claimed.pod_name, + EXISTS ( + SELECT 1 + FROM sandbox_k8s_executions e + WHERE e.sandbox_id = s.id + AND e.active + ), + {proof} + FROM sandboxes s + LEFT JOIN LATERAL ( + SELECT e.generation, e.pod_name + FROM sandbox_k8s_executions e + WHERE e.sandbox_id = s.id + AND e.active + AND e.created_at >= s.lifecycle_changed_at + AND CASE + WHEN $3::uuid IS NOT NULL + THEN e.activation_attempt_id = $3 + AND e.generation = $4 + ELSE $2::uuid IS NOT NULL + END + ) claimed ON TRUE + WHERE s.id = $1" + ); + let (claimed_generation, claimed_pod, any_active, source_proven): ( + Option, + Option, + bool, + bool, + ) = sqlx::query_as(sqlx::AssertSqlSafe(sql!(query.as_str()))) + .bind(sandbox_id) + .bind(restore_lease_token) + .bind(activation_attempt.map(|(attempt, _)| attempt)) + .bind(activation_attempt.map(|(_, generation)| generation)) + .fetch_one(&mut *tx) + .await + .db_context("failed to read restore revert evidence")?; + + let retire = source_proven && retirement.is_enabled(); + let outcome = match (claimed_generation.zip(claimed_pod), retire) { + (Some((generation, pod_name)), true) => { + if !self + .finish_active_execution_generation_in( + &mut tx, sandbox_id, generation, "replaced", + ) + .await? + { + // Not expected under the row lock; fail closed rather + // than revert a row whose execution this claim can't + // retire. + tx.rollback() + .await + .db_context("failed to roll back restore revert transaction")?; + return Ok(RestoreClaimRevert::NotReverted); + } + // `target_node_id` and the artifact marker name the wake + // source and stay; the lifecycle triggers keep the marker on + // `suspended`. `placement_changed_at` is cleared, as the + // markerless restoring recovery in `pod_tracking.rs` does for + // its rows: a + // restore whose pod bound moved it past the proven + // pre-restore source, and both the suspended selector and + // `durably_restorable` take a markerless snapshot only when + // it is newer than the last placement. + sqlx::query(sql!( + "UPDATE sandboxes + SET lifecycle = 'suspended', + placement = 'unplaced', + active_node_id = NULL, + placement_changed_at = NULL + WHERE id = $1" + )) + .bind(sandbox_id) + .execute(&mut *tx) + .await + .db_context("failed to revert sandbox lifecycle to suspended")?; + RestoreClaimRevert::ExecutionRetired { pod_name } + } + (None, true) if !any_active => { + // Proven, and no execution at all: the same placement-clock + // clear as above. An execution the claim reused rather than + // created keeps the clock, since nothing proves its pod + // holds no state of its own. + sqlx::query(sql!( + "UPDATE sandboxes + SET lifecycle = 'suspended', + placement_changed_at = NULL + WHERE id = $1" + )) + .bind(sandbox_id) + .execute(&mut *tx) + .await + .db_context("failed to revert sandbox lifecycle to suspended")?; + RestoreClaimRevert::LifecycleOnly + } + (claimed, _) => { + sqlx::query(sql!( + "UPDATE sandboxes SET lifecycle = 'suspended' WHERE id = $1" + )) + .bind(sandbox_id) + .execute(&mut *tx) + .await + .db_context("failed to revert sandbox lifecycle to suspended")?; + if claimed.is_some() || any_active { + RestoreClaimRevert::ExecutionLeftActive + } else { + RestoreClaimRevert::LifecycleOnly + } + } + }; + tx.commit() + .await + .db_context("failed to commit restore revert transaction")?; + Ok(outcome) } pub(in crate::controllers::substrate::kubernetes) async fn begin_restore_lifecycle( @@ -490,7 +639,7 @@ impl K8sStore { } } - async fn claim_markerless_restoring_restore_lease( + pub(in crate::controllers::substrate::kubernetes) async fn claim_markerless_restoring_restore_lease( &self, sandbox_id: SandboxId, ) -> Result, SubstrateError> { @@ -571,6 +720,53 @@ impl K8sStore { }) } + /// Commit `restoring -> started` for the restore this caller owns, once + /// its wake has succeeded. Returns whether the row moved. + /// + /// This is the cancelled-wake redrive's fenced completion CAS + /// (`wake_completion.rs`): the row must still be `restoring` with a + /// routable active execution behind it. Where the redrive waits for the + /// lease to expire, so that no live attempt is overridden, this requires + /// the lease to be the caller's own: the owner is that live attempt. A + /// row a rollback, the sweeper, a suspend or a delete has moved on matches + /// nothing (leaving `restoring` also clears the token, through the + /// `sandboxes_clear_restore_lease` trigger), and once it is `started` + /// neither the API rollback nor the sweeper's revert (both + /// `restoring`-guarded) can move it back. + /// + /// Without this, a Kubernetes restore driven from an access path, which + /// runs no lifecycle-controller completion, stays `restoring` until the + /// node's published state converges it, and a user request may not dial + /// it until then. + pub(in crate::controllers::substrate::kubernetes) async fn complete_owned_user_restore( + &self, + sandbox_id: SandboxId, + restore_lease_token: Uuid, + ) -> Result { + let routable = + pplx_space_common_svc::sql::sandbox_routability::routable_execution_or_non_k8s( + "sandboxes", + "$3", + ); + let query = format!( + "UPDATE sandboxes + SET lifecycle = 'started', placement = 'on_node' + WHERE id = $1 + AND lifecycle = 'restoring' + AND restore_lease_token = $2 + AND {routable}" + ); + let completed = sqlx::query(sqlx::AssertSqlSafe(sql!(query.as_str()))) + .bind(sandbox_id.as_uuid()) + .bind(restore_lease_token) + .bind(pplx_space_common_core::types::SubstrateKind::Kubernetes) + .execute(&self.pool) + .await + .db_context("failed to complete owned sandbox restore")? + .rows_affected(); + Ok(completed == 1) + } + pub(in crate::controllers::substrate::kubernetes) async fn release_user_restore_lease( &self, sandbox_id: SandboxId, diff --git a/crates/libs/api/src/controllers/substrate/kubernetes/store/started_recovery.rs b/crates/libs/api/src/controllers/substrate/kubernetes/store/started_recovery.rs index 630b24d99..7f0afbafc 100644 --- a/crates/libs/api/src/controllers/substrate/kubernetes/store/started_recovery.rs +++ b/crates/libs/api/src/controllers/substrate/kubernetes/store/started_recovery.rs @@ -556,11 +556,17 @@ impl K8sStore { sandbox_id: SandboxId, generation: i64, ) -> Result { - let row = sqlx::query_as::<_, (i64, String, String, Option)>(sql!( - "SELECT generation, state, pod_name, host(pod_ip) - FROM sandbox_k8s_executions - WHERE sandbox_id = $1 AND generation >= $2 AND active - AND state IN ($3, $4, $5)" + // The sandbox's lifecycle decides what a running execution means: a + // suspend and a restore can land while a follower waits, and the + // generation it then finds running is that restore's. Until the row + // is `started` that restore can still be rolled back, so it is no + // recovery to hand out (see `join_restore_before_dial`). + let row = sqlx::query_as::<_, (i64, String, String, Option, String)>(sql!( + "SELECT e.generation, e.state, e.pod_name, host(e.pod_ip), s.lifecycle + FROM sandbox_k8s_executions e + JOIN sandboxes s ON s.id = e.sandbox_id + WHERE e.sandbox_id = $1 AND e.generation >= $2 AND e.active + AND e.state IN ($3, $4, $5)" )) .bind(sandbox_id.as_uuid()) .bind(generation) @@ -571,7 +577,7 @@ impl K8sStore { .await .db_context("failed to observe started sandbox recovery")?; - let Some((generation, state, pod_name, pod_ip)) = row else { + let Some((generation, state, pod_name, pod_ip, lifecycle)) = row else { return Ok(StartedRecoveryObservation::Failed); }; let state = @@ -581,14 +587,25 @@ impl K8sStore { message: format!("failed to parse started recovery execution state: {err}"), })?; + let lifecycle = + lifecycle + .parse::() + .map_err(|err| SubstrateError::Failed { + message: format!("failed to parse started recovery sandbox lifecycle: {err}"), + })?; Ok(match (state, pod_ip) { - (ActiveExecutionState::Running, Some(pod_ip)) => { + (ActiveExecutionState::Running, Some(pod_ip)) + if lifecycle == SandboxLifecycle::Started => + { StartedRecoveryObservation::Succeeded { generation, pod_name, pod_ip, } } + (ActiveExecutionState::Running, _) if lifecycle == SandboxLifecycle::Restoring => { + StartedRecoveryObservation::Pending + } (ActiveExecutionState::Creating | ActiveExecutionState::Recovering, _) => { StartedRecoveryObservation::Pending } diff --git a/crates/libs/api/src/controllers/substrate/kubernetes/test_substrate.rs b/crates/libs/api/src/controllers/substrate/kubernetes/test_substrate.rs index d9a56f775..55810626c 100644 --- a/crates/libs/api/src/controllers/substrate/kubernetes/test_substrate.rs +++ b/crates/libs/api/src/controllers/substrate/kubernetes/test_substrate.rs @@ -11,6 +11,7 @@ use kube::runtime::{reflector, watcher::Event}; use super::execution::RecoveryJoinBackoffPolicy; use super::*; +use crate::controllers::substrate::AbandonedRestoreRetirement; /// The namespace a substrate built here keeps its sandbox pods and their /// `sandbox_k8s_executions` rows in. @@ -26,8 +27,9 @@ impl KubernetesSubstrate { /// It dials a pod's sandbox proxy on `sandbox_proxy_port`, and a node's /// node-api on the port its node-api pod advertises, or on /// `sandbox_proxy_port` when that pod advertises none. Its create, - /// data-plane, delete and cancel-rate budgets are the ones - /// [`Self::try_from_env`] computes with none of their variables set. The + /// data-plane, delete and cancel-rate budgets, and its abandoned-restore + /// retirement switch (off), are the ones [`Self::try_from_env`] computes + /// with none of their variables set. The /// node-api client still reads its transport and bearer tokens from the /// process environment, as in production. pub fn with_seeded_caches( @@ -40,6 +42,7 @@ impl KubernetesSubstrate { substrate.k8s.create_command_ready_timeout = parse_create_command_ready_timeout(None); substrate.k8s.create_data_plane_ready_timeout = parse_create_data_plane_ready_timeout(None); substrate.k8s.k8s_blocking_delete_timeout = DEFAULT_K8S_BLOCKING_DELETE_TIMEOUT; + substrate.k8s.abandoned_restore_retirement = AbandonedRestoreRetirement::Disabled; substrate.cancel_tracker = CancelRateTracker::with_default_budget(); substrate } @@ -81,8 +84,9 @@ pub(super) fn seeded_substrate( } /// A substrate over `pool` and `client` with the given caches, one-second -/// create and delete budgets, a cancel-rate budget of 64 a second, and the -/// node-group placement gate closed. +/// create and delete budgets, a cancel-rate budget of 64 a second, +/// abandoned-restore retirement enabled, and the node-group placement gate +/// closed. pub(super) fn assemble_substrate( pool: PgPool, client: Client, @@ -103,6 +107,7 @@ pub(super) fn assemble_substrate( create_command_ready_timeout: Duration::from_secs(1), create_data_plane_ready_timeout: Duration::from_secs(1), k8s_blocking_delete_timeout: Duration::from_secs(1), + abandoned_restore_retirement: AbandonedRestoreRetirement::Enabled, caches, }, restore_waiters: RestoreWaiters::new( diff --git a/crates/libs/api/src/controllers/substrate/kubernetes/tests.rs b/crates/libs/api/src/controllers/substrate/kubernetes/tests.rs index 2755ede56..fdf9c801d 100644 --- a/crates/libs/api/src/controllers/substrate/kubernetes/tests.rs +++ b/crates/libs/api/src/controllers/substrate/kubernetes/tests.rs @@ -5441,6 +5441,172 @@ mod kubernetes_tests { Ok(()) } + /// The owner of a user restore commits `restoring -> started` itself + /// before it returns, so the request it serves never dials a `restoring` + /// pod and no node publication has to arrive first. + #[tokio::test(flavor = "current_thread")] + async fn an_owned_restore_commits_started_before_it_returns() -> Result<(), BoxError> { + if !crate::test_support::enter_sim_worker_process() { + return Ok(()); + } + let db = pplx_space_db_test_fixtures::pg_test_db().await?; + let pool = db.pool.clone(); + let sandbox_id = SandboxId::new(); + seed_restoring_sandbox_with_active_lease(&pool, sandbox_id, Duration::from_millis(200)) + .await?; + let sandbox = fetch_sandbox(&pool, sandbox_id) + .await? + .expect("inserted sandbox must be visible"); + let sim_worker = start_sim_worker().await?; + let substrate = substrate_with_store_client_and_proxy_port( + pool.clone(), + running_pod_kubernetes_client(Arc::new(Mutex::new(Vec::new())), "127.0.0.1"), + sim_worker.port(), + ); + + let binding = substrate + .wake_execution_with_metric_identity( + &sandbox, + Duration::from_secs(2), + SandboxApiMetricIdentity::ecw("connect"), + crate::controllers::substrate::RecoveryCharge::Exempt, + ) + .await?; + + assert_eq!(binding.completion, ActivationCompletion::Owned); + let lifecycle: String = + sqlx::query_scalar(sql!("SELECT lifecycle FROM sandboxes WHERE id = $1")) + .bind(sandbox_id) + .fetch_one(&pool) + .await?; + assert_eq!(lifecycle, "started"); + Ok(()) + } + + /// A user request on a `restoring` row whose owner lapsed reclaims the + /// restore under its own claim, drives it to `started` and only then + /// gets a client: it never dials the restoring pod. + #[tokio::test(flavor = "current_thread")] + async fn a_request_reclaims_a_lapsed_restore_and_dials_it_once_started() -> Result<(), BoxError> + { + if !crate::test_support::enter_sim_worker_process() { + return Ok(()); + } + let db = pplx_space_db_test_fixtures::pg_test_db().await?; + let pool = db.pool.clone(); + let sandbox_id = SandboxId::new(); + seed_restoring_sandbox_with_active_lease(&pool, sandbox_id, Duration::from_millis(1)) + .await?; + tokio::time::sleep(Duration::from_millis(50)).await; + let captured_pod_creates = Arc::new(Mutex::new(Vec::new())); + let sim_worker = start_sim_worker().await?; + let substrate = substrate_with_store_client_and_proxy_port( + pool.clone(), + running_pod_kubernetes_client(Arc::clone(&captured_pod_creates), "127.0.0.1"), + sim_worker.port(), + ); + + let client = substrate + .join_restore_before_dial( + sandbox_id, + SandboxApiMetricIdentity::native("restoring_access"), + Duration::from_secs(5), + crate::controllers::substrate::RecoveryCharge::Exempt, + ) + .await; + + assert!(client.is_ok(), "{:?}", client.err()); + let lifecycle: String = + sqlx::query_scalar(sql!("SELECT lifecycle FROM sandboxes WHERE id = $1")) + .bind(sandbox_id) + .fetch_one(&pool) + .await?; + assert_eq!(lifecycle, "started"); + assert_eq!( + captured_pod_creates + .lock() + .expect("capture mutex poisoned") + .len(), + 1, + "the reclaim drives one restore" + ); + Ok(()) + } + + /// A started recovery a suspend superseded redrives the sandbox's + /// restore. If the restore's owner can't commit `started` (here a + /// synthetic database failure on that write), the row stays `restoring` + /// and the access request gets no binding for the restored pod: a later + /// revert may still retire it. + #[tokio::test(flavor = "current_thread")] + async fn a_redriven_restore_hands_out_no_binding_until_it_is_started() -> Result<(), BoxError> { + if !crate::test_support::enter_sim_worker_process() { + return Ok(()); + } + let db = pplx_space_db_test_fixtures::pg_test_db().await?; + let pool = db.pool.clone(); + let sandbox_id = SandboxId::new(); + seed_restoring_sandbox_with_active_lease(&pool, sandbox_id, Duration::from_millis(1)) + .await?; + sqlx::query(sql!( + "UPDATE sandboxes SET lifecycle = 'suspended' WHERE id = $1" + )) + .bind(sandbox_id) + .execute(&pool) + .await?; + sqlx::query(sql!( + "CREATE FUNCTION test_refuse_restored_started() RETURNS trigger AS $$ + BEGIN + IF OLD.lifecycle = 'restoring' AND NEW.lifecycle = 'started' THEN + RAISE EXCEPTION 'synthetic started-commit failure'; + END IF; + RETURN NEW; + END + $$ LANGUAGE plpgsql" + )) + .execute(&pool) + .await?; + sqlx::query(sql!( + "CREATE TRIGGER test_refuse_restored_started BEFORE UPDATE ON sandboxes + FOR EACH ROW EXECUTE FUNCTION test_refuse_restored_started()" + )) + .execute(&pool) + .await?; + let sandbox = fetch_sandbox(&pool, sandbox_id) + .await? + .expect("inserted sandbox must be visible"); + let sim_worker = start_sim_worker().await?; + let substrate = substrate_with_store_client_and_proxy_port( + pool.clone(), + running_pod_kubernetes_client(Arc::new(Mutex::new(Vec::new())), "127.0.0.1"), + sim_worker.port(), + ); + + let binding = substrate + .redrive_suspended_recovery_for_access( + &sandbox, + Some(1), + tokio::time::Instant::now() + Duration::from_secs(3), + SandboxApiMetricIdentity::native("restoring_access"), + crate::controllers::substrate::RecoveryCharge::Exempt, + ) + .await; + + let error = binding.expect_err("a restoring execution must not be handed out"); + assert_eq!( + error.retry_after_cause(), + Some(crate::controllers::ControllerRetryAfterCause::RestoreInFlight), + "{error:?}" + ); + let lifecycle: String = + sqlx::query_scalar(sql!("SELECT lifecycle FROM sandboxes WHERE id = $1")) + .bind(sandbox_id) + .fetch_one(&pool) + .await?; + assert_eq!(lifecycle, "restoring"); + Ok(()) + } + #[tokio::test(flavor = "current_thread")] async fn stale_restore_owner_cannot_materialize_after_lease_reclaim() -> Result<(), BoxError> { // Arrange. diff --git a/crates/libs/api/src/controllers/substrate/kubernetes/tests/native_pod_failover.rs b/crates/libs/api/src/controllers/substrate/kubernetes/tests/native_pod_failover.rs index 320eac2b7..69a299281 100644 --- a/crates/libs/api/src/controllers/substrate/kubernetes/tests/native_pod_failover.rs +++ b/crates/libs/api/src/controllers/substrate/kubernetes/tests/native_pod_failover.rs @@ -52,6 +52,7 @@ fn stale_instance() -> ResolvedPodInstance { ip: PodIp::parse("127.0.0.2").unwrap(), pod_uid: Some(PodUid::parse("space-sbx-old-uid").unwrap()), generation: 1, + sandbox_restoring: false, } } @@ -83,10 +84,10 @@ struct MovedPod { _db: pplx_space_db_test_fixtures::TestDb, } -async fn moved_pod() -> TestResult { +async fn moved_pod(lifecycle: &str) -> TestResult { let db = pplx_space_db_test_fixtures::pg_test_db().await?; let sandbox_id = SandboxId::new(); - insert_test_sandbox_row(&db.pool, sandbox_id, "started").await?; + insert_test_sandbox_row(&db.pool, sandbox_id, lifecycle).await?; let pod_name = "space-sbx-new"; insert_k8s_execution( &db.pool, @@ -131,7 +132,7 @@ async fn a_native_request_follows_a_moved_pod_after_a_one_second_dial() -> TestR if !crate::test_support::enter_sim_worker_process() { return Ok(()); } - let moved = moved_pod().await?; + let moved = moved_pod("started").await?; let started = Instant::now(); let client = native_access::scope( @@ -161,6 +162,30 @@ async fn a_native_request_follows_a_moved_pod_after_a_one_second_dial() -> TestR Ok(()) } +/// A replacement whose sandbox is `restoring` serves no user request: the +/// first dial's error goes back to the caller, whose recovery joins the +/// restore. +#[tokio::test(flavor = "current_thread")] +async fn a_native_request_does_not_follow_a_moved_pod_into_a_restore() -> TestResult { + if !crate::test_support::enter_sim_worker_process() { + return Ok(()); + } + let moved = moved_pod("restoring").await?; + + let result = native_access::scope( + moved + .substrate + .access_kept_worker_client(moved.sandbox_id, &stale_instance()), + ) + .await; + moved.shutdown.cancel(); + + let error = result.expect_err("the restoring replacement is not dialed"); + assert_eq!(error.status(), axum::http::StatusCode::SERVICE_UNAVAILABLE); + assert_eq!(moved.substrate.pod_worker_channels.len(), 0); + Ok(()) +} + #[tokio::test(flavor = "current_thread")] async fn a_native_request_to_a_removed_pod_returns_after_its_first_dial() -> TestResult { if !crate::test_support::enter_sim_worker_process() { @@ -206,7 +231,7 @@ async fn any_other_caller_waits_out_the_full_budget_on_the_old_address() -> Test if !crate::test_support::enter_sim_worker_process() { return Ok(()); } - let moved = moved_pod().await?; + let moved = moved_pod("started").await?; let started = Instant::now(); let result = moved diff --git a/crates/libs/api/src/controllers/substrate/kubernetes/wait_ready.rs b/crates/libs/api/src/controllers/substrate/kubernetes/wait_ready.rs index 7db99edee..b0e744f84 100644 --- a/crates/libs/api/src/controllers/substrate/kubernetes/wait_ready.rs +++ b/crates/libs/api/src/controllers/substrate/kubernetes/wait_ready.rs @@ -94,6 +94,11 @@ impl KubernetesSubstrate { } Err(_) => return Err(first_error), }; + if current.sandbox_restoring { + // A replacement mid-restore isn't dialed for a user request (see + // `join_restore_before_dial`); the caller's recovery joins it. + return Err(first_error); + } if !current.same_instance(pod) { pplx_space_metrics::grpc::client_channel_cache("worker-node-api", "pod_moved"); } diff --git a/crates/libs/api/src/controllers/substrate/mod.rs b/crates/libs/api/src/controllers/substrate/mod.rs index ed10cb989..a1ff3a4ee 100644 --- a/crates/libs/api/src/controllers/substrate/mod.rs +++ b/crates/libs/api/src/controllers/substrate/mod.rs @@ -208,6 +208,52 @@ impl ActivationCompletion { } } +/// Whether a restore rollback retires the abandoned restore's execution, and +/// so makes its pod disposable: the API's rollback of a failed restore claim +/// and the transient-lifecycle sweeper's revert of an aged `restoring` row. +/// +/// Retiring is safe only once no user request can have reached a `restoring` +/// sandbox's pod, which every routing replica (API and internal API) running +/// the restoring gate guarantees. A replica from before that gate still +/// routes to a reachable restore pod, and a retirement would then delete +/// what it served. So retirement ships disabled, behind +/// [`Self::ENV`], and is enabled per deployment once every routing replica +/// runs the gate; rolling back to older routing binaries disables it first. +/// Disabled, both writers revert only the lifecycle, as they did before. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] +pub enum AbandonedRestoreRetirement { + /// Revert only the lifecycle; the restore's execution stays active. + #[default] + Disabled, + /// Retire the abandoned restore's execution in the revert. + Enabled, +} + +impl AbandonedRestoreRetirement { + /// The switch both the API and the cluster controller read + /// ([`Self::from_env`]): `1`, `true`, `yes`, `on` or `enabled`, in any + /// case, turns retirement on; unset or anything else keeps it disabled. + pub const ENV: &'static str = "SPACE_ABANDONED_RESTORE_RETIREMENT_ENABLED"; + + pub fn from_enabled(enabled: bool) -> Self { + if enabled { + Self::Enabled + } else { + Self::Disabled + } + } + + /// The switch as both components read it, so they agree on what turns + /// it on. + pub fn from_env() -> Self { + Self::from_enabled(pplx_space_common_core::env::env_flag_var(Self::ENV, false)) + } + + pub fn is_enabled(self) -> bool { + self == Self::Enabled + } +} + /// Exact operation lease to release after the controller publishes its lifecycle. /// A later owner or execution must never be released by this completion. #[derive(Debug, Clone, Copy)] @@ -1057,6 +1103,13 @@ pub struct SubstrateCapabilities { /// row at completion as a newer, finalized suspend and fence it, and must /// shield that CAS against request cancellation. /// + /// On Kubernetes the owner of a user restore, the wake holding its lease + /// token, also commits `started` itself before it returns, so a user + /// request never dials a `restoring` pod and the access path, which runs + /// no completion CAS, doesn't wait on the node's published state. The + /// caller's CAS then finds the row `started` and completes as before; it + /// still covers every wake whose owner commit didn't land. + /// /// `false` does not mean the substrate's rows are never `restoring`. /// Direct's worker-local wake leaves its pre-wake lifecycle (`suspended`, /// or `restoring` kept by an earlier lease restore) for the completion CAS @@ -1095,6 +1148,15 @@ pub struct SubstrateCapabilities { /// reconcilers unconditionally. The lease client comes from the process /// environment, not from the substrate. pub contends_for_leader_lease: bool, + /// A successful [`SubstrateAdapter::suspend_execution`] has already + /// committed `lifecycle = 'suspended'`, under the fence of the execution + /// it suspended. The caller then writes no lifecycle of its own: an + /// unfenced trailing `suspended` write would land after a wake that + /// claimed the row in between and put a live, routed sandbox back to + /// `suspended` with its execution still active. + /// + /// Direct's suspend leaves the lifecycle to that caller write. + pub suspend_commits_lifecycle: bool, } impl SubstrateCapabilities { @@ -1106,6 +1168,7 @@ impl SubstrateCapabilities { supports_node_pool_placement: false, supports_node_drain: false, contends_for_leader_lease: false, + suspend_commits_lifecycle: false, }; pub const KUBERNETES: Self = Self { @@ -1116,6 +1179,7 @@ impl SubstrateCapabilities { supports_node_pool_placement: true, supports_node_drain: true, contends_for_leader_lease: true, + suspend_commits_lifecycle: true, }; } diff --git a/crates/libs/cluster/controller/server/src/orphaned_restore_pod_gc/classify.rs b/crates/libs/cluster/controller/server/src/orphaned_restore_pod_gc/classify.rs index 6a1ada724..f4e173142 100644 --- a/crates/libs/cluster/controller/server/src/orphaned_restore_pod_gc/classify.rs +++ b/crates/libs/cluster/controller/server/src/orphaned_restore_pod_gc/classify.rs @@ -163,10 +163,17 @@ pub enum SkipClass { /// current execution still materializing its UID, and a same-generation pod /// a restore re-drive could resume and adopt. Pod names embed the /// generation and are unique, so no second same-generation pod can coexist - /// with the current one. A lingering active execution on a `suspended` row - /// also lands here; retiring it is the suspend subsystem's job, not this - /// loop's. + /// with the current one. ActiveRuntime, + /// The pod's generation equals the active execution's on a `suspended` + /// row. A restore revert now retires its claim's execution in the same + /// transaction, so this shape is left by a revert that predates that or + /// couldn't prove a durable restore source, or by a suspend write that + /// raced a wake. Nothing on the row says which, and in the last case the + /// pod holds the only copy of the sandbox's state, so it's never reaped + /// here. Tallied apart from [`Self::ActiveRuntime`] so its count sizes + /// that backlog; the next restore of the sandbox clears it. + SuspendedSameGeneration, /// The owning row exists but isn't Kubernetes-managed, or holds a /// lifecycle value this loop doesn't recognize. It has an owner this loop /// can't reason about, so leave the pod alone. @@ -189,6 +196,7 @@ impl SkipClass { Self::RowRestoring => "row_restoring", Self::RowTransient => "row_transient", Self::ActiveRuntime => "active_runtime", + Self::SuspendedSameGeneration => "suspended_same_generation", Self::UnrecognizedRow => "unrecognized_row", Self::RowQuarantined => "row_quarantined", Self::NoActiveExecution => "no_active_execution", @@ -215,10 +223,11 @@ impl SkipClass { /// generation, so a same-generation pod can still be adopted by a restore that /// resumes that generation — deleting it unlocked would race that adoption and /// could destroy a live or recoverable runtime. Such a pod is the current -/// execution or is still materializing its UID; on a `suspended` row it may be -/// the carrier a failed revert teardown left behind, which the suspend -/// subsystem owns. Its writers clear `active` only once that pod is gone or a -/// durable snapshot exists, which is what makes this loop's +/// execution or is still materializing its UID; on a `suspended` row it is +/// [`SkipClass::SuspendedSameGeneration`]. The writers that clear `active` on +/// a `suspended` row do so only once that pod is gone or restore selection is +/// proven to pick a durable snapshot (a restore revert retires its claim's +/// execution as `replaced` under that proof), which is what makes this loop's /// no-active-execution reap of a `suspended` row safe; a writer that cleared /// `active` with the carrier still holding the only copy would break that. /// @@ -285,6 +294,11 @@ pub fn classify_disposition( Some(active_generation) if pod_generation < active_generation => { Disposition::Orphaned(OrphanReason::RowReverted) } + Some(active_generation) + if lifecycle == L::Suspended && pod_generation == active_generation => + { + Disposition::Skip(SkipClass::SuspendedSameGeneration) + } // At or above the active generation — the current execution, // still materializing, or a same-generation pod a re-drive could // resume and adopt. Not provably superseded, so never reap it diff --git a/crates/libs/cluster/controller/server/src/orphaned_restore_pod_gc/mod.rs b/crates/libs/cluster/controller/server/src/orphaned_restore_pod_gc/mod.rs index 8eac7bcc4..749cb3ffb 100644 --- a/crates/libs/cluster/controller/server/src/orphaned_restore_pod_gc/mod.rs +++ b/crates/libs/cluster/controller/server/src/orphaned_restore_pod_gc/mod.rs @@ -47,14 +47,12 @@ //! registers the capture with `BeginArtifactBackup`, which refuses it either //! way, so a reap can't register a snapshot the sandbox's next restore would //! select. A restore whose unmaterialized claim the request abandoned (the -//! re-drive cancellation guard) retired its execution as `replaced`, and a pod -//! below the active generation has a newer one, so the refusal says the -//! execution was superseded and the node releases the VM and discards the -//! capture. A restore the transient-lifecycle -//! sweeper reverted cleared the row's node columns instead, so the capture is -//! refused for having no source node; nothing tells the node to release, and -//! the pod stays terminating (reported as `teardown_stuck`). A successful -//! DELETE therefore means that +//! re-drive cancellation guard), or that the API's rollback or the +//! transient-lifecycle sweeper reverted with abandoned-restore retirement +//! enabled, retired its execution as `replaced`, +//! and a pod below the active generation has a newer one. Either way the +//! refusal says the execution was superseded, and the node releases the VM and +//! discards the capture. A successful DELETE therefore means that //! teardown has begun (the pod is terminating), not that the VM is gone: it's //! reported as `requested_orphaned_restore_pod_delete`, and the reap is only //! confirmed (`reaped_orphaned_restore_pod`, `cleanup_completed`) once the pod @@ -99,16 +97,21 @@ //! //! ## The one orphan this loop deliberately does NOT handle //! -//! A same-generation pod on a `suspended` row — the revert's `retire_execution` -//! teardown failed before clearing `active` — is left to the suspend subsystem. -//! Reaping it safely means retiring the stale execution first (to defeat the -//! adoption race) *and* proving the pod holds no committed state that exists -//! only locally: a restored sandbox that later idle-auto-suspends presents -//! identically, and its carrier pod may hold the only copy of the suspended VM -//! state. That proof needs suspend-domain snapshot-recency and marker -//! machinery, not a pod GC, and is tracked as a separate follow-up. Until then -//! such a pod is cleaned by the next restore, or reaped here once the -//! generation advances past it. +//! A same-generation pod on a `suspended` row whose execution is still active +//! is left alone. With abandoned-restore retirement enabled +//! (`SPACE_ABANDONED_RESTORE_RETIREMENT_ENABLED`, off by default), both +//! restore reverts (the API's rollback and the transient-lifecycle sweeper's) +//! retire their claim's execution as `replaced` in the same transaction, once +//! restore selection is proven to choose a durable pre-restore source, which +//! turns their leftover pod into the no-active-execution orphan this loop +//! reaps. What remains in this shape is left by a revert with retirement +//! disabled, or that predates it, or couldn't prove the source, or by a +//! suspend write that raced a wake and put a live, routed sandbox back to +//! `suspended`. The row can't tell them apart, and in the last case the pod +//! holds the only copy of the sandbox's state, so this loop only tallies them +//! (`SkipClass::SuspendedSameGeneration`). The next restore of the sandbox +//! clears such a pod, or this loop reaps it once the generation advances past +//! it. //! //! Safeguards, in order: //! - **Restore-only.** Only pods carrying a restore annotation are considered; diff --git a/crates/libs/cluster/controller/server/src/orphaned_restore_pod_gc/reconciler.rs b/crates/libs/cluster/controller/server/src/orphaned_restore_pod_gc/reconciler.rs index 03488ca1f..31d1f83c9 100644 --- a/crates/libs/cluster/controller/server/src/orphaned_restore_pod_gc/reconciler.rs +++ b/crates/libs/cluster/controller/server/src/orphaned_restore_pod_gc/reconciler.rs @@ -1075,6 +1075,11 @@ pub async fn run( delete_failed = report.delete_failed, rate_limited = report.rate_limited, skipped = report.skipped_total(), + suspended_same_generation = report + .skipped + .get(&SkipClass::SuspendedSameGeneration) + .copied() + .unwrap_or(0), terminating = report.terminating, missing_identity = report.missing_identity, deferred = report.deferred, diff --git a/crates/libs/cluster/controller/server/src/orphaned_restore_pod_gc/tests.rs b/crates/libs/cluster/controller/server/src/orphaned_restore_pod_gc/tests.rs index d5439aa9e..f2092e7c2 100644 --- a/crates/libs/cluster/controller/server/src/orphaned_restore_pod_gc/tests.rs +++ b/crates/libs/cluster/controller/server/src/orphaned_restore_pod_gc/tests.rs @@ -525,7 +525,6 @@ fn current_generation_pod_is_skipped() { SandboxLifecycle::Started, SandboxLifecycle::Paused, SandboxLifecycle::Failed, - SandboxLifecycle::Suspended, ] { let s = state(lifecycle, Some(POD_GEN)); assert_eq!( @@ -538,16 +537,16 @@ fn current_generation_pod_is_skipped() { #[test] fn same_generation_pod_is_never_reaped_even_when_row_reverted() { - // A same-generation pod on a reverted/dormant row whose execution was NOT - // retired (a failed teardown left it active) is left alone: a restore - // resuming that generation could still adopt this pod, so deleting it - // unlocked would race that adoption. Retiring the stale active execution is - // the suspended-execution convergence path's job; once it clears `active`, - // this loop reaps the pod as no-active-execution. + // A same-generation pod on a `suspended` row whose execution is still + // active is left alone: a restore resuming that generation could still + // adopt it, and a suspend that raced a wake leaves this same shape with + // the only copy of the sandbox's state in the pod. It's tallied apart from + // the live runtimes so the backlog is visible; once a revert retires the + // execution, this loop reaps the pod as no-active-execution. let s = state(SandboxLifecycle::Suspended, Some(POD_GEN)); assert_eq!( classify_disposition(Some(&s), POD_GEN), - Disposition::Skip(SkipClass::ActiveRuntime) + Disposition::Skip(SkipClass::SuspendedSameGeneration) ); } diff --git a/crates/libs/cluster/controller/server/src/suspended_execution_reaper/mod.rs b/crates/libs/cluster/controller/server/src/suspended_execution_reaper/mod.rs index 5ee4c0987..a323563a9 100644 --- a/crates/libs/cluster/controller/server/src/suspended_execution_reaper/mod.rs +++ b/crates/libs/cluster/controller/server/src/suspended_execution_reaper/mod.rs @@ -12,18 +12,24 @@ //! `placement = 'unplaced', active_node_id = NULL` //! (leaving `target_node_id` pinned as the local-snapshot affinity hint). //! -//! The node-side idle auto-suspend path performs none of that bookkeeping: -//! the node suspends the VM locally and publishes `status = 'suspended'`, -//! and the internal API's published-state convergence promotes -//! `sandboxes.lifecycle` to `'suspended'` — and stops there. The execution -//! row stays `active = TRUE, state = 'running'` with a recorded pod IP, -//! the sandbox stays `placement = 'on_node'` with `active_node_id` set, -//! and the pod's container exits (`Succeeded`) and is later deleted by pod -//! cleanup. The control plane is left claiming a live execution on a pod -//! that no longer exists, indefinitely. The stale bookkeeping feeds the -//! wake/reattach path a long-dead pod name and stale pod IP, and makes -//! forensics/operator queries keyed on `active_node_id` see thousands of -//! "active" sandboxes pinned to long-gone nodes. +//! Node-side idle auto-suspend reaches the same end state through internal +//! API's suspended projections, which retire the active execution in the +//! statement that moves the row to `suspended`. A row can still be left +//! `suspended` with an active execution by a writer that doesn't do that +//! bookkeeping, and nothing else revisits a dormant row. Its pod's container +//! exits (`Succeeded`) and is later deleted by pod cleanup, while the control +//! plane keeps claiming a live execution on a pod that no longer exists. The +//! stale bookkeeping feeds the wake/reattach path a long-dead pod name and +//! stale pod IP, and makes forensics/operator queries keyed on +//! `active_node_id` see "active" sandboxes pinned to long-gone nodes. +//! +//! A *present* pod on such a row is out of scope. A same-generation restore +//! pod there may be an abandoned restore's leftover or a live sandbox a racing +//! suspend write put back to `suspended`, and only the latter's pod holds the +//! sandbox's state. With abandoned-restore retirement enabled, the restore +//! reverts record abandonment durably instead, by retiring their claim's +//! execution as `replaced`, which leaves the pod to the orphaned-restore-pod +//! GC. //! //! ## Safety contract with the wake path //! diff --git a/crates/libs/cluster/controller/server/src/transient_lifecycle_sweeper/lifecycle_writer_tests.rs b/crates/libs/cluster/controller/server/src/transient_lifecycle_sweeper/lifecycle_writer_tests.rs index b6c8fb303..7319538ba 100644 --- a/crates/libs/cluster/controller/server/src/transient_lifecycle_sweeper/lifecycle_writer_tests.rs +++ b/crates/libs/cluster/controller/server/src/transient_lifecycle_sweeper/lifecycle_writer_tests.rs @@ -30,10 +30,15 @@ use std::sync::Arc; use std::time::Duration; -use pplx_space_api::controllers::substrate::Substrate; use pplx_space_api::controllers::substrate::direct::DirectSubstrate; +use pplx_space_api::controllers::substrate::{ + AbandonedRestoreRetirement, PodIdentityFence, RecordedExecution, RetireFence, Substrate, +}; use pplx_space_common_core::sql; -use pplx_space_common_core::types::{NodeId, PositiveDuration, SandboxId, SandboxLifecycle}; +use pplx_space_common_core::types::{ + NodeId, PodName, PodUid, PositiveDuration, SandboxActivationAttemptId, SandboxId, + SandboxLifecycle, +}; use pplx_space_db_test_fixtures::{pg_test_db, pg_test_db_with_max_connections}; use pplx_space_placement::StoredSandboxLifecycle; use pplx_space_placement::test_support::stored_lifecycle_matrix; @@ -43,7 +48,8 @@ use sqlx::PgPool; use super::{SWEEPABLE_LIFECYCLES, SubstrateSweepSink, SweepSink}; use crate::lifecycle_writer_test_support::{ ExecutionSeed, SandboxSeed, bounded, insert_execution, insert_sandbox, insert_snapshot, - kubernetes_substrate, pin_local_artifact, single_backend_pool, wait_until_blocked, + kubernetes_substrate, pin_local_artifact, pod_name, pod_uid, single_backend_pool, + wait_until_blocked, }; fn age() -> PositiveDuration { @@ -58,6 +64,7 @@ fn direct_sink(pool: &PgPool) -> SubstrateSweepSink { pool.clone(), Arc::new(Substrate::from_direct(DirectSubstrate::new(pool.clone()))), ) + .with_abandoned_restore_retirement(AbandonedRestoreRetirement::Enabled) } /// The Postgres `sandboxes` columns a sweeper write moves. @@ -525,7 +532,12 @@ async fn revert_cas_moves_only_restoring_rows() -> TestResult { insert_snapshot(pool, sandbox_id, "ready", -60).await?; let claimed = sink - .revert_to_suspended(sandbox_id, SandboxLifecycle::Restoring, age()) + .revert_to_suspended( + sandbox_id, + SandboxLifecycle::Restoring, + age(), + &RetireFence::NoExecution, + ) .await? .is_some(); @@ -566,7 +578,12 @@ async fn revert_cas_moves_only_restoring_rows() -> TestResult { .await?; } let claimed = sink - .revert_to_suspended(sandbox_id, SandboxLifecycle::Restoring, age()) + .revert_to_suspended( + sandbox_id, + SandboxLifecycle::Restoring, + age(), + &RetireFence::NoExecution, + ) .await?; assert!(claimed.is_none(), "{case}"); assert_eq!( @@ -578,6 +595,386 @@ async fn revert_cas_moves_only_restoring_rows() -> TestResult { Ok(()) } +/// The revert CAS retires the restore's execution as `replaced` in its own +/// transaction, and claims nothing unless the execution still matches every +/// part of the identity the candidate select recorded: a revert that can't +/// record the abandonment on the right execution would strand that execution +/// active on a `suspended` row. A candidate observed with no execution reverts +/// only while none exists, and one that observed nothing never reverts. +/// +/// The execution row sits under the direct sink's `space` row (see the +/// module doc): the retirement matches the execution by sandbox and identity, +/// whatever substrate labels the row. +#[tokio::test(flavor = "current_thread")] +async fn revert_cas_retires_only_the_observed_execution() -> TestResult { + let db = pg_test_db().await?; + let pool = &db.pool; + let sink = direct_sink(pool); + + let observed = |sandbox_id: SandboxId| { + PodIdentityFence::new(PodName::parse(pod_name(sandbox_id, 1)).expect("pod name")) + .with_recorded(RecordedExecution::Read { + pod_uid: Some(PodUid::parse(pod_uid(sandbox_id, 1)).expect("pod uid")), + route_activation_id: None, + }) + .with_generation(1) + }; + struct Case { + name: &'static str, + retires: bool, + fence: fn(PodIdentityFence) -> RetireFence, + /// Whether the execution row records its pod UID. One that records + /// none is what only the unread-fence refusal can tell apart. + records_pod_uid: bool, + } + fn case(name: &'static str, retires: bool, fence: fn(PodIdentityFence) -> RetireFence) -> Case { + Case { + name, + retires, + fence, + records_pod_uid: true, + } + } + let cases = [ + case("the observed execution", true, RetireFence::Pod), + case("another generation", false, |fence| { + RetireFence::Pod(fence.with_generation(2)) + }), + case("another pod", false, |fence| { + let mut fence = fence; + fence.pod_name = PodName::parse("space-sbx-elsewhere-g1").expect("pod name"); + RetireFence::Pod(fence) + }), + case("another pod uid", false, |fence| { + RetireFence::Pod(fence.with_recorded(RecordedExecution::Read { + pod_uid: Some(PodUid::parse("uid-of-a-replacement").expect("pod uid")), + route_activation_id: None, + })) + }), + case("another activation attempt", false, |fence| { + RetireFence::Pod(fence.with_activation_attempt(SandboxActivationAttemptId::new())) + }), + case("another route activation", false, |fence| { + let pod_uid = fence.recorded.pod_uid().cloned(); + RetireFence::Pod(fence.with_recorded(RecordedExecution::Read { + pod_uid, + route_activation_id: Some(SandboxActivationAttemptId::new()), + })) + }), + Case { + records_pod_uid: false, + ..case("an unread row", false, |fence| { + RetireFence::Pod(fence.with_recorded(RecordedExecution::Unread)) + }) + }, + case("no execution observed", false, |_| RetireFence::NoExecution), + case("nothing observed", false, |_| RetireFence::Unobserved), + ]; + + let mut mismatches = Vec::new(); + for Case { + name, + retires, + fence, + records_pod_uid, + } in cases + { + let sandbox_id = SandboxId::new(); + insert_row(pool, sandbox_id, &Row::bare("restoring")).await?; + insert_snapshot(pool, sandbox_id, "ready", -60).await?; + insert_execution(pool, sandbox_id, ExecutionSeed::RUNNING).await?; + sqlx::query(sql!( + "UPDATE sandboxes SET placement = 'on_node' WHERE id = $1" + )) + .bind(sandbox_id) + .execute(pool) + .await?; + if !records_pod_uid { + sqlx::query(sql!( + "UPDATE sandbox_k8s_executions SET pod_uid = NULL WHERE sandbox_id = $1" + )) + .bind(sandbox_id) + .execute(pool) + .await?; + } + + let claimed = sink + .revert_to_suspended( + sandbox_id, + SandboxLifecycle::Restoring, + age(), + &fence(observed(sandbox_id)), + ) + .await? + .is_some(); + + let (lifecycle, placement, active, state): (String, String, bool, String) = + sqlx::query_as(sql!( + "SELECT s.lifecycle, s.placement, e.active, e.state + FROM sandboxes s + JOIN sandbox_k8s_executions e ON e.sandbox_id = s.id + WHERE s.id = $1" + )) + .bind(sandbox_id) + .fetch_one(pool) + .await?; + // A retiring revert also unplaces the row, so it doesn't stay placed + // if the best-effort cleanup that follows never runs. + let expected = if retires { + ("suspended", "unplaced", false, "replaced") + } else { + ("restoring", "on_node", true, "running") + }; + if claimed != retires + || ( + lifecycle.as_str(), + placement.as_str(), + active, + state.as_str(), + ) != expected + { + mismatches.push(format!( + "{name}: claimed {claimed}, row ({lifecycle}, {placement}, {active}, {state})" + )); + } + } + assert!(mismatches.is_empty(), "{mismatches:#?}"); + Ok(()) +} + +/// With abandoned-restore retirement disabled (the default), the revert CAS +/// moves only the lifecycle, as before retirement existed: the restore's +/// execution stays active, the row stays placed and keeps its placement +/// clock, and the cleanup that follows retires the execution under the +/// candidate's fence, which refuses a pod that came up after the check. +#[tokio::test(flavor = "current_thread")] +async fn without_retirement_the_revert_moves_only_the_lifecycle() -> TestResult { + let db = pg_test_db().await?; + let pool = &db.pool; + let sink = + direct_sink(pool).with_abandoned_restore_retirement(AbandonedRestoreRetirement::Disabled); + + let sandbox_id = SandboxId::new(); + insert_row(pool, sandbox_id, &Row::bare("restoring")).await?; + insert_snapshot(pool, sandbox_id, "ready", -60).await?; + insert_execution(pool, sandbox_id, ExecutionSeed::RUNNING).await?; + sqlx::query(sql!( + "UPDATE sandboxes + SET placement = 'on_node', + placement_changed_at = lifecycle_changed_at + INTERVAL '30 seconds' + WHERE id = $1" + )) + .bind(sandbox_id) + .execute(pool) + .await?; + let placement_clock = || async { + sqlx::query_scalar::<_, Option>(sql!( + "SELECT placement_changed_at FROM sandboxes WHERE id = $1" + )) + .bind(sandbox_id) + .fetch_one(pool) + .await + }; + let clock_before = placement_clock().await?; + let fence = RetireFence::Pod( + PodIdentityFence::new(PodName::parse(pod_name(sandbox_id, 1)).expect("pod name")) + .with_recorded(RecordedExecution::Read { + pod_uid: Some(PodUid::parse(pod_uid(sandbox_id, 1)).expect("pod uid")), + route_activation_id: None, + }) + .with_generation(1), + ); + + let reverted = sink + .revert_to_suspended(sandbox_id, SandboxLifecycle::Restoring, age(), &fence) + .await? + .expect("the revert CAS claims"); + + let (lifecycle, placement, active, state): (String, String, bool, String) = + sqlx::query_as(sql!( + "SELECT s.lifecycle, s.placement, e.active, e.state + FROM sandboxes s + JOIN sandbox_k8s_executions e ON e.sandbox_id = s.id + WHERE s.id = $1" + )) + .bind(sandbox_id) + .fetch_one(pool) + .await?; + assert_eq!( + ( + lifecycle.as_str(), + placement.as_str(), + active, + state.as_str() + ), + ("suspended", "on_node", true, "running") + ); + assert_eq!(placement_clock().await?, clock_before); + assert_eq!(reverted.prior_placement_changed_at, clock_before); + Ok(()) +} + +/// A placed-then-reverted restore is durably restorable as soon as the revert +/// lands. Its pod's bind moved `placement_changed_at` past the pre-restore +/// snapshot, and `durably_restorable`, which gates the fenced release of the +/// abandoned pod's runtime, takes a markerless snapshot only when it is newer +/// than the last placement. The revert clears the clock, as the API store's +/// markerless restoring recovery does; left in place, the release would wait +/// for the next suspend and the pod would count against `teardown_stuck`. +#[tokio::test(flavor = "current_thread")] +async fn placed_then_reverted_restore_is_durably_restorable_at_once() -> TestResult { + let db = pg_test_db().await?; + let pool = &db.pool; + let sink = direct_sink(pool); + + let sandbox_id = SandboxId::new(); + insert_row(pool, sandbox_id, &Row::bare("restoring")).await?; + insert_snapshot(pool, sandbox_id, "ready", -60).await?; + // The restore's pod bound after the pre-restore snapshot was taken. + sqlx::query(sql!( + "UPDATE sandboxes + SET placement_changed_at = lifecycle_changed_at + INTERVAL '30 seconds' + WHERE id = $1" + )) + .bind(sandbox_id) + .execute(pool) + .await?; + + let reverted = sink + .revert_to_suspended( + sandbox_id, + SandboxLifecycle::Restoring, + age(), + &RetireFence::NoExecution, + ) + .await? + .expect("the revert CAS claims"); + + assert_eq!(row(pool, sandbox_id).await?.lifecycle, "suspended"); + assert!( + pplx_space_sandbox_store::durably_restorable(pool, sandbox_id).await?, + "a fenced release must not wait for the next suspend" + ); + assert!(reverted.prior_placement_changed_at.is_some()); + Ok(()) +} + +/// A restore's owner commits `restoring -> started` under its own lease as +/// soon as its wake succeeds, before its request dials the runtime (the API's +/// `complete_owned_user_restore`). A revert the sweeper decided on before that +/// commit claims nothing after it, so it can't retire the runtime the owner's +/// request is using, even with the observed execution's exact fence. +#[tokio::test(flavor = "current_thread")] +async fn revert_after_the_owner_commits_started_claims_nothing() -> TestResult { + let db = pg_test_db().await?; + let pool = &db.pool; + let sink = direct_sink(pool); + + let sandbox_id = SandboxId::new(); + insert_row(pool, sandbox_id, &Row::bare("restoring").leased()).await?; + insert_snapshot(pool, sandbox_id, "ready", -60).await?; + insert_execution(pool, sandbox_id, ExecutionSeed::RUNNING).await?; + sqlx::query(sql!( + "UPDATE sandboxes SET placement = 'on_node' WHERE id = $1" + )) + .bind(sandbox_id) + .execute(pool) + .await?; + // The sweep observed the restore's execution before the owner finished. + let fence = RetireFence::Pod( + PodIdentityFence::new(PodName::parse(pod_name(sandbox_id, 1)).expect("pod name")) + .with_recorded(RecordedExecution::Read { + pod_uid: Some(PodUid::parse(pod_uid(sandbox_id, 1)).expect("pod uid")), + route_activation_id: None, + }) + .with_generation(1), + ); + // The owner's commit: under the row's own lease token, from `restoring`. + // `complete_owned_user_restore` is private to the API's Kubernetes store + // (and tested there); what this pins is the sweeper side: a `started` row. + let committed = sqlx::query(sql!( + "UPDATE sandboxes + SET lifecycle = 'started', placement = 'on_node' + WHERE id = $1 + AND lifecycle = 'restoring' + AND restore_lease_token IS NOT NULL" + )) + .bind(sandbox_id) + .execute(pool) + .await? + .rows_affected(); + assert_eq!(committed, 1); + + let claimed = sink + .revert_to_suspended(sandbox_id, SandboxLifecycle::Restoring, age(), &fence) + .await?; + + assert!(claimed.is_none(), "the revert must not claim a started row"); + assert_eq!(row(pool, sandbox_id).await?.lifecycle, "started"); + let (active, state): (bool, String) = sqlx::query_as(sql!( + "SELECT active, state FROM sandbox_k8s_executions WHERE sandbox_id = $1" + )) + .bind(sandbox_id) + .fetch_one(pool) + .await?; + assert_eq!((active, state.as_str()), (true, "running")); + Ok(()) +} + +/// The revert undo puts back the exact `placement_changed_at` the revert +/// cleared, so the `restoring` row's restore retry window reads the same +/// placement clock it did before the sweep. +#[tokio::test(flavor = "current_thread")] +async fn revert_undo_restores_the_placement_clock() -> TestResult { + let db = pg_test_db().await?; + let pool = &db.pool; + let sink = direct_sink(pool); + + let sandbox_id = SandboxId::new(); + insert_row(pool, sandbox_id, &Row::bare("restoring").leased()).await?; + insert_snapshot(pool, sandbox_id, "ready", -60).await?; + sqlx::query(sql!( + "UPDATE sandboxes + SET placement_changed_at = lifecycle_changed_at + INTERVAL '30 seconds' + WHERE id = $1" + )) + .bind(sandbox_id) + .execute(pool) + .await?; + let placement_clock = || async { + sqlx::query_scalar::<_, Option>(sql!( + "SELECT placement_changed_at FROM sandboxes WHERE id = $1" + )) + .bind(sandbox_id) + .fetch_one(pool) + .await + }; + let original = placement_clock().await?; + + let reverted = sink + .revert_to_suspended( + sandbox_id, + SandboxLifecycle::Restoring, + age(), + &RetireFence::NoExecution, + ) + .await? + .expect("the revert CAS claims"); + assert_eq!( + placement_clock().await?, + None, + "the revert clears the clock" + ); + + assert!( + sink.undo_revert_to_suspended(sandbox_id, SandboxLifecycle::Restoring, &reverted, false) + .await? + ); + assert_eq!(row(pool, sandbox_id).await?.lifecycle, "restoring"); + assert_eq!(placement_clock().await?, original); + Ok(()) +} + /// The revert undo moves a row back only out of `suspended` and only while /// the revert's write is the row's last, restoring the lifecycle and the /// fork-preservation flag it is handed but not the lease. @@ -598,7 +995,12 @@ async fn revert_undo_reverts_only_its_own_suspended_write() -> TestResult { .await?; insert_snapshot(pool, sandbox_id, "ready", -60).await?; let token = sink - .revert_to_suspended(sandbox_id, SandboxLifecycle::Restoring, age()) + .revert_to_suspended( + sandbox_id, + SandboxLifecycle::Restoring, + age(), + &RetireFence::NoExecution, + ) .await? .expect("the accepting revert CAS claims"); relabel_keeping_write_token(pool, sandbox_id, stored).await?; diff --git a/crates/libs/cluster/controller/server/src/transient_lifecycle_sweeper/mod.rs b/crates/libs/cluster/controller/server/src/transient_lifecycle_sweeper/mod.rs index 37ee4deed..f6a57af14 100644 --- a/crates/libs/cluster/controller/server/src/transient_lifecycle_sweeper/mod.rs +++ b/crates/libs/cluster/controller/server/src/transient_lifecycle_sweeper/mod.rs @@ -119,7 +119,8 @@ use std::sync::Arc; use std::time::{Duration, Instant}; use pplx_space_api::controllers::substrate::{ - CommandReadyProbe, PodFenceMismatch, RetireExecutionOutcome, RetireFence, Substrate, + AbandonedRestoreRetirement, CommandReadyProbe, PodFenceMismatch, RetireExecutionOutcome, + RetireFence, Substrate, }; use pplx_space_common_core::types::{PositiveDuration, SandboxId, SandboxLifecycle}; use pplx_space_events::{ @@ -682,6 +683,12 @@ pub struct ReconcilerConfig { /// `restoring` row is failed. See /// [`DEFAULT_UNRESTORABLE_RESTORING_AGE_SECS`]. pub unrestorable_restoring_min_placement_age: PositiveDuration, + /// Whether the revert to `suspended` retires the abandoned restore's + /// execution (`SPACE_ABANDONED_RESTORE_RETIREMENT_ENABLED`, default off; + /// see [`AbandonedRestoreRetirement`]). Disabled, the revert only moves + /// the lifecycle and the cleanup retires the execution afterwards, + /// refusing a pod that came up since the candidate was observed. + pub abandoned_restore_retirement: AbandonedRestoreRetirement, } /// Per-lifecycle override resolution. @@ -730,6 +737,7 @@ impl Default for ReconcilerConfig { unrestorable_restoring_min_placement_age: PositiveDuration::new_panic( Duration::from_secs(DEFAULT_UNRESTORABLE_RESTORING_AGE_SECS), ), + abandoned_restore_retirement: AbandonedRestoreRetirement::Disabled, } } } @@ -781,6 +789,7 @@ impl ReconcilerConfig { unrestorable_restoring_enabled: true, unrestorable_restoring_min_placement_age: default .unrestorable_restoring_min_placement_age, + abandoned_restore_retirement: AbandonedRestoreRetirement::from_env(), } } @@ -837,6 +846,15 @@ fn env_lifecycle_age(var: &str) -> LifecycleAge { } } +/// A revert to `suspended` the CAS claimed, with what undoing it needs. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct RevertedToSuspended { + /// Names the exact lifecycle write the revert made. + pub write_token: String, + /// The row's `placement_changed_at` before the revert cleared it. + pub prior_placement_changed_at: Option, +} + /// A `failed` row with an active Kubernetes execution that the /// failed-cleanup pass wants to retire, plus the node to probe first. #[derive(Debug, Clone, PartialEq, Eq)] @@ -937,14 +955,35 @@ pub trait SweepSink: Send + Sync + 'static { /// implementation hard-pins `lifecycle = 'restoring'`: every other /// transient lifecycle's matching backup is a stale prior-cycle copy /// (see `classify::decide`), so the CAS must claim nothing even if - /// classification drifts. Returns the token naming the lifecycle write - /// this call made, or `None` if it claimed nothing. + /// classification drifts. + /// + /// With abandoned-restore retirement enabled, the same transaction + /// retires the restore's execution as `replaced`, matching `fence`'s + /// recorded identity (or, for a candidate observed with no execution, + /// requires that none exists). That retirement is what records the + /// restore as abandoned, so the revert claims nothing unless it lands + /// too. Disabled (the default, see + /// [`ReconcilerConfig::abandoned_restore_retirement`]), the revert moves + /// only the lifecycle, and the cleanup retires the execution afterwards + /// under `fence`. + /// + /// A retiring revert also clears `placement_changed_at`, as the API + /// store's markerless restoring recovery does. A restore whose pod bound + /// moved it past the pre-restore snapshot, and both the suspended restore + /// selector and `durably_restorable` take a markerless snapshot only when + /// it is newer than the last placement: left in place, the proven source isn't + /// selected, and a fenced release of the abandoned pod's runtime is held + /// until the next suspend. + /// + /// Returns what the write needs to be undone, or `None` if it claimed + /// nothing. fn revert_to_suspended( &self, sandbox_id: SandboxId, lifecycle: SandboxLifecycle, age: PositiveDuration, - ) -> impl std::future::Future, sqlx::Error>> + Send; + fence: &RetireFence, + ) -> impl std::future::Future, sqlx::Error>> + Send; /// CAS one provably-unrecoverable row from `lifecycle` to `'failed'`, /// with the same evacuation-exemption guards as the candidate select. @@ -1020,21 +1059,34 @@ pub trait SweepSink: Send + Sync + 'static { fence: RetireFence, ) -> impl std::future::Future> + Send; - /// Undo a revert this sweep just made, when the retire it was supposed to - /// enable turned out to be refused. + /// Undo a revert this sweep just made, when the cleanup it was supposed + /// to enable turned out to be refused. + /// + /// With abandoned-restore retirement disabled (the default), the revert + /// moved only the lifecycle and the cleanup retires the execution under + /// the candidate's fence. A pod that came up after the pre-check, which a + /// Pending pod going Running is enough for, fails that fence and leaves a + /// `suspended` row owning a live execution, which no reconciler revisits; + /// the restore the sweep judged dead is running after all, so putting the + /// lifecycle back is simply correct. + /// + /// With retirement enabled, the revert retired the restore's execution in + /// its own transaction, so the cleanup refuses only when an execution it + /// never observed is active by the time it runs. Putting the lifecycle + /// back lets the next tick judge the row afresh; the retired execution + /// stays `replaced`, since a restore never re-adopts a retired + /// generation. A pod that merely came up after the pre-check isn't a + /// refusal then: the cleanup deletes it as the abandoned restore's + /// leftover (see `candidate_pod_fence_with_observation`). /// - /// The pre-check narrows that window but cannot close it: a pod that - /// recovers between the question and the CAS — a Pending pod going Running - /// is enough, since that alone changes the resourceVersion — leaves a - /// `suspended` row owning a live execution, and no reconciler revisits a - /// suspended row. Putting the lifecycle back is also simply correct in - /// that case: the restore the sweep judged dead is running after all. /// CAS-guarded on the row still being `suspended`, so a genuine suspend - /// that landed since is not clobbered. - /// `write_token` is what the revert CAS returned, naming that exact - /// lifecycle write. Undoing on the lifecycle value alone would reach past - /// this sweep's own transition and drag a later, legitimate suspend back - /// to `restoring`. + /// that landed since is not clobbered. `reverted` is what the revert CAS + /// returned. Its write token names that + /// exact lifecycle write: undoing on the lifecycle value alone would reach + /// past this sweep's own transition and drag a later, legitimate suspend + /// back to `restoring`. Its prior `placement_changed_at` is put back, so + /// the restored `restoring` row keeps the placement clock its restore + /// retry window reads. /// /// `preserve_local_artifact` is the candidate's `fork_restore`, restored /// alongside the lifecycle because leaving `restoring` cleared it. Without @@ -1044,7 +1096,7 @@ pub trait SweepSink: Send + Sync + 'static { &self, sandbox_id: SandboxId, lifecycle: SandboxLifecycle, - write_token: &str, + reverted: &RevertedToSuspended, preserve_local_artifact: bool, ) -> impl std::future::Future> + Send; @@ -1508,10 +1560,11 @@ impl Reconciler { report: &mut TickReport, ) { // Ask before committing (a UID backfill aside, this changes nothing). - // The revert below moves the sandbox to - // `suspended`, which no reconciler revisits, so a fence lost between - // the candidate select and here must be discovered *now* — after the - // CAS, the only remedy is a dormant row owning a live execution. + // The revert below moves the sandbox to `suspended`, which no + // reconciler revisits. Its transaction re-checks the execution row's + // recorded identity, but only this check can see the live pod: a pod + // replaced or moved on since the candidate select must stop the + // revert here. match self .sink .retire_fence_mismatch(candidate.sandbox_id, &candidate.pod_fence) @@ -1541,9 +1594,14 @@ impl Reconciler { } } - let write_token = match self + let reverted = match self .sink - .revert_to_suspended(candidate.sandbox_id, candidate.lifecycle, age) + .revert_to_suspended( + candidate.sandbox_id, + candidate.lifecycle, + age, + &candidate.pod_fence, + ) .await { Ok(claimed) => claimed, @@ -1556,7 +1614,7 @@ impl Reconciler { return; } }; - let Some(write_token) = write_token else { + let Some(reverted) = reverted else { OutcomeEvent::aged_transient( AgedTransientOutcome::CasLost, candidate.lifecycle.as_str(), @@ -1586,18 +1644,16 @@ impl Reconciler { "recoverable_snapshot", ); - // A claimed revert leaves the wedged restore's execution shape - // behind: an active `sandbox_k8s_executions` row, `placement = - // 'placed'`, and possibly a still-Running pod. - // `suspended` is a contract that none of those exist — the normal - // suspend completion clears them all in one transaction — and no - // reconciler collects them on a dormant row (the orphaned-sandbox - // reconciler excludes dormant lifecycles; the failed-cleanup pass - // only lists `failed`). Retire the execution the same way the fail - // path does: it tears down the pod while preserving node-local state, - // finishes the execution row, and unplaces the sandbox. Best-effort: - // the row is already `suspended`, - // so a failure here is logged loudly and leaves only a leak. + // The revert already retired the restore's execution, so what's left + // of the wedged restore is its pod and the row's placement: + // `suspended` is a contract that neither remains, and no reconciler + // collects them on a dormant row (the orphaned-sandbox reconciler + // excludes dormant lifecycles; the failed-cleanup pass only lists + // `failed`). The retire finds no active execution, deletes the pods + // recorded on inactive rows and unplaces the sandbox. Best-effort: a + // pod it misses belongs to no active execution, which the + // orphaned-restore-pod GC and the next restore's lingering-pod sweep + // both reap. emit_revert_cleanup_event( candidate.sandbox_id, SandboxEventType::CleanupStarted, @@ -1648,16 +1704,17 @@ impl Reconciler { ); return; } - // The revert is already durable and the execution it was meant - // to retire is still live, so leaving the row `suspended` - // strands it where nothing looks again. Put the lifecycle back - // and let the next tick judge it afresh. + // The revert retired the restore's execution, so the cleanup + // refused because an execution it never observed is active + // now. Leaving the row `suspended` would strand that one where + // nothing looks again. Put the lifecycle back, if the revert is + // still the row's last write, and let the next tick judge it. match self .sink .undo_revert_to_suspended( candidate.sandbox_id, candidate.lifecycle, - &write_token, + &reverted, candidate.fork_restore, ) .await @@ -1682,8 +1739,8 @@ impl Reconciler { sandbox_id = %candidate.sandbox_id, error = %err, lifecycle = %candidate.lifecycle, - "could not undo a revert whose retire was refused; the row is \ - suspended with a live execution and no reconciler revisits it" + "could not undo a revert whose cleanup was refused; the row is \ + suspended with an active execution and no reconciler revisits it" ), } } @@ -1694,11 +1751,15 @@ impl Reconciler { ) .sandbox_id(candidate.sandbox_id) .error(&err) - .detail( + .detail(if self.config.abandoned_restore_retirement.is_enabled() { + "pod cleanup failed after revert to suspended; the execution is \ + retired, so orphan GC or the next restore removes the pod, but the \ + row keeps its placement, which nothing revisits on a suspended row" + } else { "execution retire failed after revert to suspended; stale execution, \ placement, and pod persist until the node terminates (no reconciler \ - retries them on a suspended row)", - ) + retries them on a suspended row)" + }) .warn(); report.cleanup_failures += 1; emit_revert_cleanup_event( @@ -2592,6 +2653,7 @@ pub async fn run_with_substrate( substrate: Arc, shutdown_token: CancellationToken, ) { - let sink = SubstrateSweepSink::new(pool, substrate); + let sink = SubstrateSweepSink::new(pool, substrate) + .with_abandoned_restore_retirement(config.abandoned_restore_retirement); run(config, sink, shutdown_token).await; } diff --git a/crates/libs/cluster/controller/server/src/transient_lifecycle_sweeper/store.rs b/crates/libs/cluster/controller/server/src/transient_lifecycle_sweeper/store.rs index 20f111e12..6733e7be4 100644 --- a/crates/libs/cluster/controller/server/src/transient_lifecycle_sweeper/store.rs +++ b/crates/libs/cluster/controller/server/src/transient_lifecycle_sweeper/store.rs @@ -26,7 +26,8 @@ use sqlx::PgPool; use super::classify::{ExecutionRoutability, ProbeObservation}; use super::probe::{ProbeNodeError, SweepCandidate}; -use super::{CommandReadyPendingCandidate, FailedCleanupCandidate, SweepSink}; +use super::{CommandReadyPendingCandidate, FailedCleanupCandidate, RevertedToSuspended, SweepSink}; +use pplx_space_api::controllers::substrate::AbandonedRestoreRetirement; /// Max preserved `command_ready_pending` rows resolved per tick. Each costs one /// non-recovering guest probe (bounded by the configured probe timeout), so the @@ -169,9 +170,11 @@ const LIFECYCLE_WRITE_TOKEN: &str = "to_char(lifecycle_changed_at, 'YYYY-MM-DD H /// Put a reverted row back, bound to the revert's own write. /// -/// Restores `preserve_local_artifact_on_restore_failure` alongside the -/// lifecycle: leaving `restoring` fired `sandboxes_clear_restore_lease`, which -/// clears it, and a fork restore that came back without its preservation +/// Restores the `placement_changed_at` a retiring revert cleared (a clock +/// still set, by a lifecycle-only revert or a pod bind since, is kept), and +/// `preserve_local_artifact_on_restore_failure` alongside the lifecycle: +/// leaving `restoring` fired `sandboxes_clear_restore_lease`, which clears +/// it, and a fork restore that came back without its preservation /// contract could discard the local artifact it was supposed to keep. The /// lease itself is deliberately *not* restored — another actor may hold one by /// now. @@ -179,7 +182,8 @@ pub(crate) fn undo_revert_to_suspended_query() -> String { format!( "UPDATE sandboxes SET lifecycle = $3, - preserve_local_artifact_on_restore_failure = $5 + preserve_local_artifact_on_restore_failure = $5, + placement_changed_at = COALESCE(placement_changed_at, $6) WHERE id = $1 AND managed_by = $2 AND lifecycle = 'suspended' @@ -329,10 +333,26 @@ pub(crate) struct AgedTransientRow { /// upgraded with what the substrate currently sees. /// /// The persisted UID cannot tell a pod that *moved*: a Pending pod becoming -/// Running keeps its UID, and that is precisely the recovery this sweep must -/// not destroy — its evidence is a node probe, and a pod coming up after that -/// probe invalidates it. A resourceVersion observed here, before the probe and -/// the CAS, is what the substrate re-checks at teardown to notice. +/// Running keeps its UID, and the sweep's evidence that the sandbox has no +/// live runtime is a node probe, which a pod coming up after it invalidates. +/// A resourceVersion observed here, before the probe and the CAS, is what the +/// substrate re-checks to notice. +/// +/// The revert to `suspended` re-checks it once, before its CAS +/// (`retire_fence_mismatch`). With abandoned-restore retirement disabled (the +/// default), the cleanup then retires the still-active execution under this +/// fence and refuses a pod that came up since, and the revert is undone. +/// Enabled, the revert retires the execution in the CAS's own transaction, +/// and a restore pod that binds after that check is the abandoned +/// restore's leftover: the restore's lease has expired, its row aged past the +/// sweep's window, and the revert's snapshot proof says restore selection +/// picks a durable pre-restore source. So the cleanup deletes it, the row +/// stays `suspended`, and the next wake restores from that source. The row +/// never reached `started`, and no user request reached its VM: the API's +/// access, runtime-client, ingress and data-plane route paths never dial a +/// `restoring` sandbox's pod, so nothing it holds is newer than the source. +/// That holds only once every routing replica runs those paths, which is why +/// retirement stays disabled until then (see `AbandonedRestoreRetirement`). /// /// The observation may only *confirm* the row's persisted UID, never replace /// it. The row is read by one query and the pods by a later one, so a row that @@ -603,7 +623,32 @@ pub(crate) fn converge_restoring_query() -> String { /// * the suspended-revert-safe restore snapshot predicate, so a late artifact /// published by a failed restore branch can't become the snapshot a later /// suspended restore selects. +/// +/// It also clears `placement_changed_at` (see +/// [`SweepSink::revert_to_suspended`]). pub(crate) fn revert_to_suspended_query() -> String { + let ready_snapshot = restore_retry_suspended_revert_snapshot_exists("sandboxes"); + format!( + "UPDATE sandboxes + SET lifecycle = 'suspended', + placement_changed_at = NULL + WHERE managed_by = $1 + AND lifecycle = $2 + AND lifecycle = 'restoring' + AND lifecycle_changed_at < NOW() - make_interval(secs => $3::BIGINT) + AND (restore_lease_expires_at IS NULL + OR restore_lease_expires_at < NOW() - make_interval(secs => $3::BIGINT)) + AND ({ready_snapshot}) + AND id = $4 + RETURNING {LIFECYCLE_WRITE_TOKEN}" + ) +} + +/// [`revert_to_suspended_query`] with abandoned-restore retirement disabled: +/// the same guards, moving only the lifecycle. The execution, placement and +/// placement clock stay, and the cleanup that follows retires the execution +/// under the candidate's fence, refusing a pod that came up since. +pub(crate) fn revert_to_suspended_lifecycle_only_query() -> String { let ready_snapshot = restore_retry_suspended_revert_snapshot_exists("sandboxes"); format!( "UPDATE sandboxes @@ -620,6 +665,80 @@ pub(crate) fn revert_to_suspended_query() -> String { ) } +/// Retire the reverted restore's execution as `replaced` inside the revert +/// CAS's transaction, matching the identity the candidate select recorded +/// (see [`SweepSink::revert_to_suspended`]). A crash, error or refusal +/// between a revert and a separate retire would leave a `suspended` row with +/// its same-generation execution still active, which nothing revisits. +/// +/// Returns `false`, and the caller rolls the revert back, when the active +/// execution is no longer the one the candidate observed. +async fn retire_reverted_restore_execution( + tx: &mut sqlx::Transaction<'_, sqlx::Postgres>, + sandbox_id: SandboxId, + fence: &PodIdentityFence, +) -> Result { + if fence.recorded == RecordedExecution::Unread { + return Ok(false); + } + let retired = sqlx::query(sql!( + "UPDATE sandbox_k8s_executions + SET active = FALSE, + state = 'replaced', + pod_ip = NULL, + terminated_at = COALESCE(terminated_at, NOW()), + updated_at = NOW() + WHERE sandbox_id = $1 + AND active + AND pod_name = $2 + AND generation IS NOT DISTINCT FROM $3 + AND activation_attempt_id IS NOT DISTINCT FROM $4 + AND NULLIF(BTRIM(pod_uid), '') IS NOT DISTINCT FROM $5::TEXT + AND route_activation_id IS NOT DISTINCT FROM $6" + )) + .bind(sandbox_id) + .bind(fence.pod_name.as_str()) + .bind(fence.generation) + .bind(fence.activation_attempt_id) + .bind(fence.recorded.pod_uid().map(PodUid::as_str)) + .bind(fence.recorded.route_activation_id()) + .execute(&mut **tx) + .await? + .rows_affected() + == 1; + if retired { + // The retired execution no longer places the row. The cleanup that + // follows unplaces it too, but it is best-effort, and a `suspended` + // row left reading as placed is one nothing revisits. + sqlx::query(sql!( + "UPDATE sandboxes + SET placement = 'unplaced', + active_node_id = NULL + WHERE id = $1" + )) + .bind(sandbox_id) + .execute(&mut **tx) + .await?; + // The node state the open exposures described died with the runtime. + sqlx::query(sql!( + "UPDATE sandbox_port_exposures + SET apply_state = 'pending', + last_applied_node_id = NULL, + last_error = NULL, + last_bridge_status = NULL, + last_applied_bridge_generation = NULL, + last_observed_at = NULL, + updated_at = NOW() + WHERE sandbox_id = $1 + AND desired_state = 'open'" + )) + .bind(sandbox_id) + .execute(&mut **tx) + .await?; + } + Ok(retired) +} + /// Shared predicate for the unrestorable-`restoring` sweep: a markerless /// `restoring` row that the restore planner **provably cannot restore from /// anywhere** — the exact wedge the aged-transient sweep above cannot reach, @@ -737,6 +856,8 @@ pub struct SubstrateSweepSink { /// replaced node-api pod gets a fresh connection. Probing is sequential, /// so the `tokio::sync::Mutex` is uncontended. probe_clients: tokio::sync::Mutex>, + /// See [`super::ReconcilerConfig::abandoned_restore_retirement`]. + abandoned_restore_retirement: AbandonedRestoreRetirement, } #[derive(Clone)] @@ -768,9 +889,20 @@ impl SubstrateSweepSink { pool, substrate, probe_clients: tokio::sync::Mutex::new(HashMap::new()), + abandoned_restore_retirement: AbandonedRestoreRetirement::Disabled, } } + /// Whether [`SweepSink::revert_to_suspended`] retires the abandoned + /// restore's execution. Disabled by default. + pub fn with_abandoned_restore_retirement( + mut self, + retirement: AbandonedRestoreRetirement, + ) -> Self { + self.abandoned_restore_retirement = retirement; + self + } + async fn probe_client( &self, node_name: &str, @@ -1061,7 +1193,7 @@ impl SweepSink for SubstrateSweepSink { &self, sandbox_id: SandboxId, lifecycle: SandboxLifecycle, - write_token: &str, + reverted: &RevertedToSuspended, preserve_local_artifact: bool, ) -> Result { let managed_by = self.substrate.managed_by(); @@ -1075,8 +1207,9 @@ impl SweepSink for SubstrateSweepSink { .bind(sandbox_id) .bind(managed_by) .bind(lifecycle.to_string()) - .bind(write_token) + .bind(&reverted.write_token) .bind(preserve_local_artifact) + .bind(reverted.prior_placement_changed_at) .fetch_optional(&self.pool) .await?; Ok(restored.is_some()) @@ -1087,17 +1220,84 @@ impl SweepSink for SubstrateSweepSink { sandbox_id: SandboxId, lifecycle: SandboxLifecycle, age: PositiveDuration, - ) -> Result, sqlx::Error> { + fence: &RetireFence, + ) -> Result, sqlx::Error> { let managed_by = self.substrate.managed_by(); - let claimed = - sqlx::query_scalar::<_, String>(sqlx::AssertSqlSafe(sql!(revert_to_suspended_query()))) - .bind(managed_by) - .bind(lifecycle.to_string()) - .bind(Self::age_secs_bind(age)) + let mut tx = self.pool.begin().await?; + // Take the sandbox row lock that every execution claim also takes, so + // the execution check below can't race a new claim, and read the + // placement clock the CAS clears while holding it. + let prior_placement_changed_at: Option> = + sqlx::query_scalar(sql!( + "SELECT placement_changed_at + FROM sandboxes + WHERE id = $1 + AND managed_by = $2 + FOR UPDATE" + )) + .bind(sandbox_id) + .bind(managed_by) + .fetch_optional(&mut *tx) + .await?; + let Some(prior_placement_changed_at) = prior_placement_changed_at else { + tx.rollback().await?; + return Ok(None); + }; + let retire = self.abandoned_restore_retirement.is_enabled(); + let query = if retire { + revert_to_suspended_query() + } else { + revert_to_suspended_lifecycle_only_query() + }; + let claimed = sqlx::query_scalar::<_, String>(sqlx::AssertSqlSafe(sql!(query.as_str()))) + .bind(managed_by) + .bind(lifecycle.to_string()) + .bind(Self::age_secs_bind(age)) + .bind(sandbox_id) + .fetch_optional(&mut *tx) + .await?; + let Some(write_token) = claimed else { + tx.rollback().await?; + return Ok(None); + }; + if !retire { + // The cleanup retires the execution afterwards, under the fence. + tx.commit().await?; + return Ok(Some(RevertedToSuspended { + write_token, + prior_placement_changed_at, + })); + } + let execution_settled = match fence { + RetireFence::Pod(pod_fence) => { + retire_reverted_restore_execution(&mut tx, sandbox_id, pod_fence).await? + } + RetireFence::NoExecution => { + !sqlx::query_scalar::<_, bool>(sql!( + "SELECT EXISTS ( + SELECT 1 + FROM sandbox_k8s_executions + WHERE sandbox_id = $1 + AND active + )" + )) .bind(sandbox_id) - .fetch_optional(&self.pool) - .await?; - Ok(claimed) + .fetch_one(&mut *tx) + .await? + } + // The candidate select always observes one of the two above; a + // caller that observed nothing can't name the execution to retire. + RetireFence::Unobserved => false, + }; + if !execution_settled { + tx.rollback().await?; + return Ok(None); + } + tx.commit().await?; + Ok(Some(RevertedToSuspended { + write_token, + prior_placement_changed_at, + })) } async fn fail_aged_candidate( diff --git a/crates/libs/cluster/controller/server/src/transient_lifecycle_sweeper/tests.rs b/crates/libs/cluster/controller/server/src/transient_lifecycle_sweeper/tests.rs index b4ec774b3..0d7d99e30 100644 --- a/crates/libs/cluster/controller/server/src/transient_lifecycle_sweeper/tests.rs +++ b/crates/libs/cluster/controller/server/src/transient_lifecycle_sweeper/tests.rs @@ -511,7 +511,7 @@ impl SweepSink for FakeSink { &self, sandbox_id: SandboxId, _lifecycle: SandboxLifecycle, - _write_token: &str, + _reverted: &RevertedToSuspended, _preserve_local_artifact: bool, ) -> Result { let mut state = self.0.lock().unwrap(); @@ -524,10 +524,16 @@ impl SweepSink for FakeSink { sandbox_id: SandboxId, _lifecycle: SandboxLifecycle, _age: PositiveDuration, - ) -> Result, sqlx::Error> { + _fence: &RetireFence, + ) -> Result, sqlx::Error> { let mut state = self.0.lock().unwrap(); state.revert_calls.push(sandbox_id); - Ok((!state.cas_unclaimed.contains(&sandbox_id)).then(|| "revert-write".to_string())) + Ok( + (!state.cas_unclaimed.contains(&sandbox_id)).then(|| RevertedToSuspended { + write_token: "revert-write".to_string(), + prior_placement_changed_at: None, + }), + ) } async fn fail_aged_candidate( @@ -829,6 +835,7 @@ fn config_with_all_overrides(age: PositiveDuration) -> ReconcilerConfig { .unwrap(), unrestorable_restoring_enabled: true, unrestorable_restoring_min_placement_age: age, + abandoned_restore_retirement: Default::default(), } } @@ -4116,6 +4123,7 @@ mod postgres_backed { .bind(SandboxLifecycle::Restoring.to_string()) .bind(token) .bind(false) + .bind(None::) .fetch_optional(pool) .await }; @@ -4189,6 +4197,7 @@ mod postgres_backed { .bind(SandboxLifecycle::Restoring.to_string()) .bind(write_token) .bind(true) + .bind(None::) .fetch_optional(pool) .await?; assert!(restored.is_some(), "the undo must claim its own write"); diff --git a/crates/libs/internal/api/server/src/data_plane_sessions.rs b/crates/libs/internal/api/server/src/data_plane_sessions.rs index 24da5b395..5f29b7eb2 100644 --- a/crates/libs/internal/api/server/src/data_plane_sessions.rs +++ b/crates/libs/internal/api/server/src/data_plane_sessions.rs @@ -95,6 +95,23 @@ fn activation_may_repair(ingress: bool, error: &Status) -> bool { ) } +/// No data-plane route resolves to a sandbox that is `restoring`: its +/// restore can still fail and be rolled back, and the rollback retires the +/// execution and deletes its pod, discarding whatever it served. The +/// resolvers turn this refusal into a wait on the restore through the +/// activation owner, then retry, and return it (retryable `Unavailable`) +/// when the restore didn't reach `started` in their budget. +fn refuse_restoring_route(lifecycle: SandboxLifecycle) -> Result<(), Status> { + if lifecycle == SandboxLifecycle::Restoring { + Err(sandbox_service_route_not_ready_status( + Code::Unavailable, + "sandbox is restoring; retry once the restore completes", + )) + } else { + Ok(()) + } +} + fn require_started_ingress(lifecycle: SandboxLifecycle) -> Result<(), Status> { if lifecycle == SandboxLifecycle::Started { Ok(()) diff --git a/crates/libs/internal/api/server/src/data_plane_sessions/route_rows.rs b/crates/libs/internal/api/server/src/data_plane_sessions/route_rows.rs index b5f09a458..3b106fbbe 100644 --- a/crates/libs/internal/api/server/src/data_plane_sessions/route_rows.rs +++ b/crates/libs/internal/api/server/src/data_plane_sessions/route_rows.rs @@ -74,7 +74,13 @@ pub(super) async fn update_route_row( backend_endpoint: &str, backend_transport: &str, ) -> Result { - sqlx::query_as::<_, SessionRouteRow>(sql!( + // The update re-reads the sandbox, and is rolled back if it refuses + // what it read, so a refused refresh changes no route. + let mut tx = pool + .begin() + .await + .map_err(|error| Status::internal(format!("update data-plane session route: {error}")))?; + let row = sqlx::query_as::<_, SessionRouteRow>(sql!( "UPDATE data_plane_sessions d SET current_node_id = $2, backend_endpoint = $3, @@ -138,10 +144,18 @@ pub(super) async fn update_route_row( .bind(current_node_id) .bind(backend_endpoint) .bind(backend_transport) - .fetch_optional(pool) + .fetch_optional(&mut *tx) .await .map_err(|error| Status::internal(format!("update data-plane session route: {error}")))? - .ok_or_else(|| Status::not_found("data-plane session not found")) + .ok_or_else(|| Status::not_found("data-plane session not found"))?; + // A suspend and a restore may have committed since the caller's first + // read, and the refreshed identity is then the restoring replacement's. + // Dropping the transaction rolls the route change back. + refuse_restoring_route(row.sandbox_lifecycle)?; + tx.commit() + .await + .map_err(|error| Status::internal(format!("update data-plane session route: {error}")))?; + Ok(row) } /// Explicit registered endpoints win; database INET host values need IPv6 brackets. diff --git a/crates/libs/internal/api/server/src/data_plane_sessions/service_route.rs b/crates/libs/internal/api/server/src/data_plane_sessions/service_route.rs index a9c8e6778..002a19cb7 100644 --- a/crates/libs/internal/api/server/src/data_plane_sessions/service_route.rs +++ b/crates/libs/internal/api/server/src/data_plane_sessions/service_route.rs @@ -67,11 +67,16 @@ pub(crate) async fn unstable_resolve_sandbox_service_route( None }; let ingress = transport == SandboxServiceRouteTransport::ServicePortIngress; + let lifecycle: Result = row + .lifecycle + .parse() + .map_err(|error| Status::internal(format!("invalid sandbox lifecycle: {error}"))); + let restoring = matches!(lifecycle, Ok(SandboxLifecycle::Restoring)); let result = async { + let lifecycle = lifecycle?; + refuse_restoring_route(lifecycle)?; if transport == SandboxServiceRouteTransport::ServicePortIngress { - require_started_ingress(row.lifecycle.parse().map_err(|error| { - Status::internal(format!("invalid sandbox lifecycle: {error}")) - })?)?; + require_started_ingress(lifecycle)?; } let current_node_id = row.active_node_id.ok_or_else(|| { sandbox_service_route_not_ready_status( @@ -197,17 +202,23 @@ pub(crate) async fn unstable_resolve_sandbox_service_route( Ok(response) => return Ok(response), Err(error) => error, }; - let Some(owner) = - activation.filter(|_| !activated && activation_may_repair(ingress, &error)) + let Some(owner) = activation + .filter(|_| !activated && (restoring || activation_may_repair(ingress, &error))) else { return Err(error); }; - owner + // A restoring row waits on its restore here; if that fails, the + // caller still gets the retryable refusal rather than the waiter's + // error. + if let Err(prepare_error) = owner .prepare( sandbox_id, deadline.saturating_duration_since(tokio::time::Instant::now()), ) - .await?; + .await + { + return Err(if restoring { error } else { prepare_error }); + } activated = true; // Re-read identity and authorization after activation; never replay bytes. } diff --git a/crates/libs/internal/api/server/src/data_plane_sessions/session_route.rs b/crates/libs/internal/api/server/src/data_plane_sessions/session_route.rs index b624359bf..7a0d59ffe 100644 --- a/crates/libs/internal/api/server/src/data_plane_sessions/session_route.rs +++ b/crates/libs/internal/api/server/src/data_plane_sessions/session_route.rs @@ -78,7 +78,9 @@ pub(crate) async fn resolve_session_route( Ok(SessionKind::Port | SessionKind::Protocol) ); let sandbox_id = row.sandbox_id; + let restoring = row.sandbox_lifecycle == SandboxLifecycle::Restoring; let result = async { + refuse_restoring_route(row.sandbox_lifecycle)?; if matches!( row.kind.parse::(), Ok(SessionKind::Port | SessionKind::Protocol) @@ -207,17 +209,23 @@ pub(crate) async fn resolve_session_route( Ok(response) => return Ok(response), Err(error) => error, }; - let Some(owner) = - activation.filter(|_| !activated && activation_may_repair(ingress, &error)) + let Some(owner) = activation + .filter(|_| !activated && (restoring || activation_may_repair(ingress, &error))) else { return Err(error); }; - owner + // A restoring row waits on its restore here; if that fails, the + // caller still gets the retryable refusal rather than the waiter's + // error. + if let Err(prepare_error) = owner .prepare( sandbox_id, deadline.saturating_duration_since(tokio::time::Instant::now()), ) - .await?; + .await + { + return Err(if restoring { error } else { prepare_error }); + } activated = true; // Re-read identity and authorization after activation; never replay bytes. } diff --git a/crates/libs/internal/api/server/src/data_plane_sessions/tests.rs b/crates/libs/internal/api/server/src/data_plane_sessions/tests.rs index 15d391a57..f195e355e 100644 --- a/crates/libs/internal/api/server/src/data_plane_sessions/tests.rs +++ b/crates/libs/internal/api/server/src/data_plane_sessions/tests.rs @@ -170,3 +170,53 @@ async fn stalled_current_worker_leaves_time_for_delegated_recovery() { .await .unwrap(); } + +/// A session whose stored route is stale is re-pointed by `update_route_row`, +/// which re-reads the sandbox. A suspend and a restore can commit between the +/// resolver's first read, which saw the sandbox `started`, and that update; the +/// refreshed row then names the restoring replacement, and must be refused +/// retryably rather than signed and returned. +#[tokio::test(flavor = "current_thread")] +async fn a_route_refresh_refuses_a_sandbox_that_started_restoring() +-> pplx_space_test_env::TestResult { + let Some(db) = pplx_space_db_test_fixtures::try_pg_test_db().await? else { + return Ok(()); + }; + let pool = &db.pool; + let node_id = NodeId::new(); + let sandbox_id = SandboxId::new(); + let session_id = format!("term_{}", uuid::Uuid::now_v7()); + crate::tests::insert_data_plane_worker_node(pool, node_id, "10.0.0.45").await?; + crate::tests::insert_data_plane_active_sandbox(pool, sandbox_id, node_id).await?; + crate::tests::insert_active_execution(pool, sandbox_id, "10.80.0.45", 2).await?; + crate::tests::insert_data_plane_session(pool, &session_id, sandbox_id).await?; + let first = route_rows::fetch_route_row(pool, &session_id, None).await?; + assert_eq!(first.sandbox_lifecycle, SandboxLifecycle::Started); + // A same-node suspend and restore commit after that read. + sqlx::query("UPDATE sandboxes SET lifecycle = 'restoring' WHERE id = $1") + .bind(sandbox_id) + .execute(pool) + .await?; + + let refreshed = + route_rows::update_route_row(pool, &session_id, node_id, "10.0.0.45:50051", "grpc").await; + + let error = refreshed.expect_err("a restoring sandbox must not be routed to"); + assert_eq!(error.code(), tonic::Code::Unavailable, "{error:?}"); + assert!(error.message().contains("restoring"), "{error:?}"); + // The refused refresh changed no route. + let after = route_rows::fetch_route_row(pool, &session_id, None).await?; + assert_eq!( + ( + after.route_generation, + after.current_node_id, + after.backend_endpoint + ), + ( + first.route_generation, + first.current_node_id, + first.backend_endpoint + ) + ); + Ok(()) +} diff --git a/crates/libs/internal/api/server/src/tests.rs b/crates/libs/internal/api/server/src/tests.rs index 4e05a50a4..8891d0da4 100644 --- a/crates/libs/internal/api/server/src/tests.rs +++ b/crates/libs/internal/api/server/src/tests.rs @@ -3017,7 +3017,7 @@ fn route_credential_proof( } } -async fn insert_data_plane_worker_node( +pub(crate) async fn insert_data_plane_worker_node( pool: &sqlx::PgPool, node_id: NodeId, address: &str, @@ -3076,7 +3076,7 @@ async fn insert_data_plane_k8s_worker_node_without_address( Ok(()) } -async fn insert_data_plane_active_sandbox( +pub(crate) async fn insert_data_plane_active_sandbox( pool: &sqlx::PgPool, sandbox_id: SandboxId, node_id: NodeId, @@ -3116,7 +3116,7 @@ async fn mark_sandbox_command_ready_pending( Ok(()) } -async fn insert_active_execution( +pub(crate) async fn insert_active_execution( pool: &sqlx::PgPool, sandbox_id: SandboxId, pod_ip: &str, @@ -3200,7 +3200,7 @@ fn sha256_hex(value: &str) -> String { hex::encode(hasher.finalize()) } -async fn insert_data_plane_session( +pub(crate) async fn insert_data_plane_session( pool: &sqlx::PgPool, session_id: &str, sandbox_id: SandboxId, @@ -7263,6 +7263,7 @@ async fn a_missing_templates_table_degrades_end_to_end() -> TestResult { mod backup_generation_fence; mod publication; +mod restoring_routes; #[tokio::test(flavor = "current_thread")] async fn ecw_route_resolve_enforces_key_surface_and_organization() -> TestResult { diff --git a/crates/libs/internal/api/server/src/tests/restoring_routes.rs b/crates/libs/internal/api/server/src/tests/restoring_routes.rs new file mode 100644 index 000000000..4606c93be --- /dev/null +++ b/crates/libs/internal/api/server/src/tests/restoring_routes.rs @@ -0,0 +1,207 @@ +//! No data-plane route resolves to a sandbox that is `restoring`, on any +//! transport: the resolver waits on the restore through the activation owner +//! and resolves once the sandbox is `started`, and otherwise refuses with a +//! retryable `Unavailable`, never the backend endpoint of the restoring pod. + +use super::*; + +/// An activation owner whose restore completes while it is waited on. +struct CompletingActivation { + pool: sqlx::PgPool, + calls: std::sync::atomic::AtomicUsize, +} + +#[tonic::async_trait] +impl crate::ingress_activation::IngressActivation for CompletingActivation { + async fn prepare(&self, sandbox_id: SandboxId, _: std::time::Duration) -> Result<(), Status> { + self.calls.fetch_add(1, std::sync::atomic::Ordering::SeqCst); + sqlx::query("UPDATE sandboxes SET lifecycle = 'started' WHERE id = $1") + .bind(sandbox_id) + .execute(&self.pool) + .await + .map_err(|error| Status::internal(error.to_string()))?; + Ok(()) + } +} + +#[derive(Debug, Clone, Copy)] +enum Owner { + /// No activation owner is configured. + Absent, + /// The owner can't finish the restore. + Unavailable, + /// The owner finishes the restore. + Completes, +} + +fn restoring_route_request( + sandbox_id: SandboxId, + api_key_hash: String, +) -> Request { + Request::new(UnstableResolveSandboxServiceRouteRequest { + sandbox_id: sandbox_id.to_string(), + port: 49_983, + route_kind: SandboxServiceRouteKind::Agent as i32, + transport: SandboxServiceRouteTransport::Direct as i32, + credential_proofs: vec![route_credential_proof( + SandboxServiceRouteCredentialKind::OrgApiKey, + api_key_hash, + )], + }) +} + +/// A direct (ECW envd) route to a restoring sandbox resolves only once its +/// restore completes, and is refused retryably when no owner can finish it. +#[tokio::test(flavor = "current_thread")] +async fn direct_route_to_a_restoring_sandbox_waits_for_its_restore() -> TestResult { + if !pplx_space_test_env::enter_test_process(&[ + ( + "SPACE_INTERNAL_RPC_INSECURE_DEV", + Some(std::ffi::OsStr::new("true")), + ), + ( + "SPACE_INTERNAL_TOKEN", + Some(std::ffi::OsStr::new("restoring-route-test-secret")), + ), + ]) { + return Ok(()); + } + + for owner in [Owner::Absent, Owner::Unavailable, Owner::Completes] { + let Some(mut fixture) = cluster_fixture().await? else { + return Ok(()); + }; + let pool = fixture.service.pool.clone(); + let completing = std::sync::Arc::new(CompletingActivation { + pool: pool.clone(), + calls: std::sync::atomic::AtomicUsize::new(0), + }); + let unavailable = std::sync::Arc::new(UnavailableIngressActivation( + std::sync::atomic::AtomicUsize::new(0), + )); + fixture.service.ingress_activation = match owner { + Owner::Absent => None, + Owner::Unavailable => Some(unavailable.clone()), + Owner::Completes => Some(completing.clone()), + }; + let node_id = NodeId::new(); + let sandbox_id = SandboxId::new(); + let api_key_hash = sha256_hex("restoring-route-api-key"); + insert_data_plane_worker_node(&pool, node_id, "10.0.0.43").await?; + let node_api = sim_worker_node_api(node_id).await?; + insert_data_plane_direct_node_endpoint(&pool, node_id, node_api.endpoint()).await?; + insert_data_plane_active_sandbox(&pool, sandbox_id, node_id).await?; + insert_active_execution(&pool, sandbox_id, "10.90.0.43", 1).await?; + insert_e2b_api_key(&pool, api_key_hash.as_str()).await?; + sqlx::query("UPDATE sandboxes SET lifecycle = 'restoring' WHERE id = $1") + .bind(sandbox_id) + .execute(&pool) + .await?; + + let resolved = fixture + .service + .unstable_resolve_sandbox_service_route(restoring_route_request( + sandbox_id, + api_key_hash, + )) + .await; + + match owner { + Owner::Absent | Owner::Unavailable => { + let error = resolved.expect_err("a restoring sandbox must not be routed to"); + assert_eq!(error.code(), Code::Unavailable, "{owner:?}: {error:?}"); + assert!( + error.message().contains("restoring"), + "{owner:?}: the refusal must be the restoring one, not the owner's: {error:?}" + ); + assert_eq!( + unavailable.0.load(std::sync::atomic::Ordering::SeqCst), + usize::from(matches!(owner, Owner::Unavailable)), + "{owner:?}: a configured owner is asked to finish the restore first" + ); + } + Owner::Completes => { + let route = resolved?.into_inner(); + assert_eq!(route.lifecycle, "started"); + assert_eq!( + completing.calls.load(std::sync::atomic::Ordering::SeqCst), + 1 + ); + } + } + } + Ok(()) +} + +/// A terminal session route to a restoring sandbox, which no ingress check +/// covers, resolves only once its restore completes, and is refused +/// retryably when no owner can finish it. +#[tokio::test(flavor = "current_thread")] +async fn session_route_to_a_restoring_sandbox_waits_for_its_restore() -> TestResult { + for owner in [Owner::Absent, Owner::Unavailable, Owner::Completes] { + let Some(mut fixture) = cluster_fixture().await? else { + return Ok(()); + }; + let pool = fixture.service.pool.clone(); + let completing = std::sync::Arc::new(CompletingActivation { + pool: pool.clone(), + calls: std::sync::atomic::AtomicUsize::new(0), + }); + let unavailable = std::sync::Arc::new(UnavailableIngressActivation( + std::sync::atomic::AtomicUsize::new(0), + )); + fixture.service.ingress_activation = match owner { + Owner::Absent => None, + Owner::Unavailable => Some(unavailable.clone()), + Owner::Completes => Some(completing.clone()), + }; + let node_id = NodeId::new(); + let sandbox_id = SandboxId::new(); + let session_id = format!("term_{}", Uuid::now_v7()); + insert_data_plane_worker_node(&pool, node_id, "10.0.0.44").await?; + insert_data_plane_active_sandbox(&pool, sandbox_id, node_id).await?; + insert_active_execution(&pool, sandbox_id, "10.80.0.44", 44).await?; + insert_data_plane_session(&pool, &session_id, sandbox_id).await?; + sqlx::query("UPDATE sandboxes SET lifecycle = 'restoring' WHERE id = $1") + .bind(sandbox_id) + .execute(&pool) + .await?; + + let resolved = fixture + .service + .resolve_data_plane_session_route(data_plane_request( + ResolveDataPlaneSessionRouteRequest { + session_id: session_id.clone(), + protocol_session_id: None, + token_sha256: None, + required_scope: None, + requested_transport: None, + }, + )) + .await; + + match owner { + Owner::Completes => { + assert_eq!(resolved?.into_inner().session_id, session_id); + assert_eq!( + completing.calls.load(std::sync::atomic::Ordering::SeqCst), + 1 + ); + } + Owner::Absent | Owner::Unavailable => { + let error = resolved.expect_err("a restoring sandbox must not be routed to"); + assert_eq!(error.code(), Code::Unavailable, "{owner:?}: {error:?}"); + assert!( + error.message().contains("restoring"), + "{owner:?}: {error:?}" + ); + assert_eq!( + unavailable.0.load(std::sync::atomic::Ordering::SeqCst), + usize::from(matches!(owner, Owner::Unavailable)), + "{owner:?}: a configured owner is asked to finish the restore first" + ); + } + } + } + Ok(()) +} diff --git a/crates/libs/metrics/src/sandbox/restore.rs b/crates/libs/metrics/src/sandbox/restore.rs index 35aad0e65..8630855ec 100644 --- a/crates/libs/metrics/src/sandbox/restore.rs +++ b/crates/libs/metrics/src/sandbox/restore.rs @@ -20,6 +20,11 @@ //! request emits a placement sample, so this counter is that request's only //! record of the re-drive. //! +//! [`claim_reverted`] fires once per failed fresh restore claim the API +//! rolls back toward `suspended`, tagged with what happened to the +//! execution the claim created. `execution_left_active` is the case that +//! still leaves an adoptable same-generation pod on a `suspended` row. +//! //! End-to-end restore duration stays on the existing //! `snapshot.restore.duration_ms` distribution (node side); this module owns //! only the placement decision and its re-drive. @@ -203,3 +208,44 @@ pub fn cross_node_redrive(path: RedrivePath, outcome: RedriveOutcome) { let tags = [tag("path", path.as_str()), tag("outcome", outcome.as_str())]; counter("sandbox.restore.redrive", 1, &tags); } + +/// What a failed fresh restore claim's rollback did. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum RevertOutcome { + /// Reverted to `suspended`, and the claim's execution was retired as + /// abandoned. + ExecutionRetired, + /// Reverted to `suspended` with no active execution left. + LifecycleOnly, + /// Reverted to `suspended` with an execution still active: the claim's + /// own, unproven, or one the claim reused rather than created. + ExecutionLeftActive, + /// The row had moved on from the claim; nothing was reverted. + NotReverted, +} + +impl RevertOutcome { + pub const fn as_str(self) -> &'static str { + match self { + Self::ExecutionRetired => "execution_retired", + Self::LifecycleOnly => "lifecycle_only", + Self::ExecutionLeftActive => "execution_left_active", + Self::NotReverted => "not_reverted", + } + } +} + +impl std::fmt::Display for RevertOutcome { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(self.as_str()) + } +} + +/// Emit one failed fresh restore claim's rollback outcome. +pub fn claim_reverted(outcome: RevertOutcome) { + counter( + "sandbox.restore.claim_revert", + 1, + &[tag("outcome", outcome.as_str())], + ); +} diff --git a/docs/configuration.md b/docs/configuration.md index 751848261..5b62829db 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -50,6 +50,7 @@ because disabling it by mistake would leave every create queued forever. | `SPACE_TRANSIENT_LIFECYCLE_SWEEPER_TICK_SECS` | 60 | | | `SPACE_TRANSIENT_LIFECYCLE_SWEEPER_DEFAULT_AGE_SECS` | 600 | Age before a row in a transient lifecycle state is retired, unless a per-state age below applies. | | `SPACE_TRANSIENT_LIFECYCLE_SWEEPER_RESTORING_AGE_SECS`, `SPACE_TRANSIENT_LIFECYCLE_SWEEPER_SUSPENDING_AGE_SECS`, `SPACE_TRANSIENT_LIFECYCLE_SWEEPER_CHECKPOINTING_AGE_SECS` | default age | Per-state override; `0` disables that state. | +| `SPACE_ABANDONED_RESTORE_RETIREMENT_ENABLED` | off | API and cluster controller: whether a failed restore's rollback (API) and the transient-lifecycle sweeper's revert of an aged `restoring` row retire the abandoned restore's execution and delete its pod. Off, both revert only the lifecycle and the sweeper's cleanup refuses a restore pod that came up after its check. Turn it on only once every API and internal-API replica refuses to route to a `restoring` sandbox, and turn it off before rolling back to replicas that don't. `1`, `true`, `yes`, `on` or `enabled` turns it on; anything else keeps it off. | | `SPACE_STUCK_SNAPSHOT_REAPER_ENABLED` | off | | | `SPACE_SUSPENDED_EXECUTION_REAPER_ENABLED` | off | Runs dry by default. | | `SPACE_STUCK_CONTAINER_CREATING_GC_ENABLED` | off | | diff --git a/docs/space/diagrams/orphaned-restore-pod-gc.mmd b/docs/space/diagrams/orphaned-restore-pod-gc.mmd deleted file mode 100644 index d19c9752c..000000000 --- a/docs/space/diagrams/orphaned-restore-pod-gc.mmd +++ /dev/null @@ -1,51 +0,0 @@ -flowchart TD - classDef race fill:#fde2e4,stroke:#c0392b,color:#000 - classDef scan fill:#dbeafe,stroke:#1d4ed8,color:#000 - classDef skip fill:#e5e7eb,stroke:#374151,color:#000 - classDef reap fill:#dcfce7,stroke:#15803d,color:#000 - classDef gate fill:#fef3c7,stroke:#b45309,color:#000 - - subgraph race["The orphan"] - r1["Pinned restore pod
Unschedulable on source shortlist"]:::race - r2["Restore interrupted
(crash / abandoned request)"]:::race - r3["Row settles away from restoring;
execution retired or advanced"]:::race - r4["Source frees capacity:
stale pod binds → unrouted VM"]:::race - r1 --> r2 --> r3 --> r4 - end - - tick["Tick (every 60s, if ENABLED)"]:::scan - cache["Reflector cache:
restore-annotated pods only"]:::scan - filt{"terminating or
missing uid / sandbox id / generation?"}:::gate - classify["Batched Postgres classify
ANY($1): lifecycle + active generation
(excluding deleted tombstones)"]:::scan - - disp{"Supersession fence"}:::gate - skipT["Skip: row restoring, other transient,
quarantined, or unrecognized"]:::skip - skipA["Skip: pod gen ≥ active gen
(live runtime, re-drive adoptable,
same-gen orphan → suspend subsystem),
or no active execution outside suspended/failed"]:::skip - orphan["Orphan: row deleted,
suspended/failed with no active execution,
or pod gen < active gen"]:::reap - - clock{"continuously orphaned
≥ threshold (5m)?
(clock resets on cold cache / DB outage / restart)"}:::gate - budget{"rate limit + tick budget
(fairness cursor)"}:::gate - verify["Verify: cache-bypassing pod GET
(same UID, restore marker, sandbox, generation)
+ authoritative Postgres recheck"]:::scan - dry{"DRY_RUN?"}:::gate - would["Metric mode:dry_run + log, once per pod
no delete, no sandbox event"]:::skip - del["cleanup_requested → DELETE
preconditions: UID + resourceVersion
no DB lock held
→ requested_orphaned_restore_pod_delete"]:::reap - pend{"later ticks: pod still
in the cache?"}:::gate - done["gone: reaped_orphaned_restore_pod
+ cleanup_completed"]:::reap - stuck["terminating ≥ 5m and confirmed by a fresh GET:
teardown_stuck + cleanup_failed
(re-read until it settles)"]:::race - fail["error / timeout / 409:
cleanup_failed, retry next tick"]:::skip - - r4 -.-> cache - tick --> cache --> filt - filt -->|no| classify --> disp - disp --> skipT - disp --> skipA - disp --> orphan --> clock - clock -->|yes| budget --> verify - verify -->|still orphan| dry - verify -->|not orphan / gone / changed| stop["Skip this tick"]:::skip - dry -->|yes| would - dry -->|no| del - del -->|accepted| pend - del -->|not accepted| fail - pend -->|no| done - pend -->|yes, too long| stuck diff --git a/docs/space/diagrams/orphaned-restore-pod-gc.png b/docs/space/diagrams/orphaned-restore-pod-gc.png deleted file mode 100644 index 9d83ecf9d91738302c69543667bc0d49e341cbc8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 389798 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

t1Vg< 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

mZ`SQk&yo^Zhi=7- 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 MN=dTh)tjLI153WWZ2$lO diff --git a/docs/space/diagrams/orphaned-restore-pod-gc.svg b/docs/space/diagrams/orphaned-restore-pod-gc.svg deleted file mode 100644 index 693f21636..000000000 --- a/docs/space/diagrams/orphaned-restore-pod-gc.svg +++ /dev/null @@ -1 +0,0 @@ -

The orphan

no

yes

still orphan

not orphan / gone / changed

yes

no

accepted

not accepted

no

yes, too long

Pinned restore pod
Unschedulable on source shortlist

Restore interrupted
(crash / abandoned request)

Row settles away from restoring;
execution retired or advanced

Source frees capacity:
stale pod binds → unrouted VM

Tick (every 60s, if ENABLED)

Reflector cache:
restore-annotated pods only

terminating or
missing uid / sandbox id / generation?

Batched Postgres classify
ANY($1): lifecycle + active generation
(excluding deleted tombstones)

Supersession fence

Skip: row restoring, other transient,
quarantined, or unrecognized

Skip: pod gen ≥ active gen
(live runtime, re-drive adoptable,
same-gen orphan → suspend subsystem),
or no active execution outside suspended/failed

Orphan: row deleted,
suspended/failed with no active execution,
or pod gen < active gen

continuously orphaned
≥ threshold (5m)?
(clock resets on cold cache / DB outage / restart)

rate limit + tick budget
(fairness cursor)

Verify: cache-bypassing pod GET
(same UID, restore marker, sandbox, generation)
+ authoritative Postgres recheck

DRY_RUN?

Metric mode:dry_run + log, once per pod
no delete, no sandbox event

cleanup_requested → DELETE
preconditions: UID + resourceVersion
no DB lock held
→ requested_orphaned_restore_pod_delete

later ticks: pod still
in the cache?

gone: reaped_orphaned_restore_pod
+ cleanup_completed

terminating ≥ 5m and confirmed by a fresh GET:
teardown_stuck + cleanup_failed
(re-read until it settles)

error / timeout / 409:
cleanup_failed, retry next tick

Skip this tick

\ No newline at end of file