diff --git a/pageserver/src/http/routes.rs b/pageserver/src/http/routes.rs index 1eb24c1507..b4c6a21f71 100644 --- a/pageserver/src/http/routes.rs +++ b/pageserver/src/http/routes.rs @@ -916,6 +916,7 @@ pub fn make_router( "/v1/tenant/:tenant_id/timeline/:timeline_id/download_remote_layers", timeline_download_remote_layers_handler_post, ) + .post("/add_forced_now", handle_add_forced_now) .get( "/v1/tenant/:tenant_id/timeline/:timeline_id/download_remote_layers", timeline_download_remote_layers_handler_get, @@ -926,3 +927,14 @@ pub fn make_router( ) .any(handler_404)) } + +async fn handle_add_forced_now(req: Request) -> Result, ApiError> { + let now = get_query_param(&req, "now")?; + + let now = chrono::DateTime::parse_from_rfc3339(&now).unwrap(); + let now = now.with_timezone(&chrono::Utc); + + crate::tenant::timeline::Timeline::force_next_now(now.into()); + + json_response(StatusCode::OK, ()) +} diff --git a/pageserver/src/tenant.rs b/pageserver/src/tenant.rs index 8b370f7421..0cc5cbb540 100644 --- a/pageserver/src/tenant.rs +++ b/pageserver/src/tenant.rs @@ -90,7 +90,7 @@ pub mod mgr; pub mod tasks; pub mod upload_queue; -mod timeline; +pub mod timeline; pub mod size; diff --git a/pageserver/src/tenant/timeline.rs b/pageserver/src/tenant/timeline.rs index 77f8934d4e..dda11e46f6 100644 --- a/pageserver/src/tenant/timeline.rs +++ b/pageserver/src/tenant/timeline.rs @@ -15,7 +15,7 @@ use tokio_util::sync::CancellationToken; use tracing::*; use std::cmp::{max, min, Ordering}; -use std::collections::HashMap; +use std::collections::{HashMap, VecDeque}; use std::fs; use std::ops::{Deref, Range}; use std::path::{Path, PathBuf}; @@ -75,6 +75,9 @@ enum FlushLoopState { Exited, } +pub static PENDING_NOWS: once_cell::sync::Lazy>> = + once_cell::sync::Lazy::new(|| Default::default()); + pub struct Timeline { conf: &'static PageServerConf, tenant_conf: Arc>, @@ -2627,6 +2630,10 @@ impl Timeline { Ok(()) } + pub fn force_next_now(next: SystemTime) { + PENDING_NOWS.lock().unwrap().push_back(next) + } + /// Update information about which layer files need to be retained on /// garbage collection. This is separate from actually performing the GC, /// and is updated more frequently, so that compaction can remove obsolete @@ -2674,10 +2681,28 @@ impl Timeline { // work, so avoid calling it altogether if time-based retention is not // configured. It would be pointless anyway. let pitr_cutoff = if pitr != Duration::ZERO { - let now = SystemTime::now(); + let now = PENDING_NOWS.lock().unwrap().pop_front(); + let now = if let Some(now) = now { + let dt = chrono::DateTime::::from(now); + let dt = dt.to_rfc3339_opts(chrono::SecondsFormat::Micros, true); + tracing::warn!(now = dt, "using forced now"); + now + } else { + SystemTime::now() + }; + if let Some(pitr_cutoff_timestamp) = now.checked_sub(pitr) { let pitr_timestamp = to_pg_timestamp(pitr_cutoff_timestamp); + { + let dt = chrono::DateTime::::from(now); + let dt = dt.to_rfc3339_opts(chrono::SecondsFormat::Micros, true); + info!( + ?pitr, + pitr_cutoff_timestamp = dt, + "searching lsn for timestamp" + ); + } match self.find_lsn_for_timestamp(pitr_timestamp).await? { LsnForTimestamp::Present(lsn) => lsn, LsnForTimestamp::Future(lsn) => {