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) => {