summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/remake/api.rs35
-rw-r--r--src/remake/local.rs6
-rw-r--r--src/remake/records.rs18
3 files changed, 40 insertions, 19 deletions
diff --git a/src/remake/api.rs b/src/remake/api.rs
index 41d72ad..f5063e0 100644
--- a/src/remake/api.rs
+++ b/src/remake/api.rs
@@ -19,11 +19,12 @@
//! Request body for POST (all optional; `baseHeads` not for `compact`, whose base is
//! fixed when it commits):
//! `{ "kind": "compact", "spec": null, "baseHeads": ["<hex>", …],
-//! "replayWindow": {"epochs": 1, "seconds": 604800},
-//! "archiveRetention": {"epochs": 2, "seconds": 2592000},
+//! "replayWindow": {"epochs": 1, "age": "7d"},
+//! "archiveRetention": {"epochs": 2, "age": "30d"},
//! "settle": "8m", "switchDelay": "1m" }`
//!
-//! `settle` and `switchDelay` are durations (`units`: seconds, or e.g. `500ms`, `8m`).
+//! `settle`, `switchDelay` and the retentions' `age` are durations (`units`: seconds,
+//! or e.g. `500ms`, `8m`, `7d`).
//!
//! `?force=` overrides checks: `true` all of them, or a comma-separated list of
//! * `switchDelay`: accept one below a keepalive interval (for tests and debugging,
@@ -164,8 +165,8 @@ struct StartRequest {
/// them are carried over. The current heads if absent. For a remake derived from
/// a read, use the heads that read returned (`GET /store?heads=true`).
base_heads: Option<Vec<String>>,
- replay_window: Option<Retention>,
- archive_retention: Option<Retention>,
+ replay_window: Option<RetentionRequest>,
+ archive_retention: Option<RetentionRequest>,
/// How long votes are collected; twice the dormancy threshold if absent.
#[serde(default, with = "crate::units::optional_duration")]
settle: Option<Duration>,
@@ -175,6 +176,26 @@ struct StartRequest {
switch_delay: Option<Duration>,
}
+/// A retention as a request gives it: the age as a duration.
+#[derive(Debug, Default, Deserialize)]
+#[serde(rename_all = "camelCase", deny_unknown_fields)]
+struct RetentionRequest {
+ epochs: Option<u64>,
+ #[serde(default, with = "crate::units::optional_duration")]
+ age: Option<Duration>,
+}
+
+impl From<RetentionRequest> for Retention {
+ fn from(request: RetentionRequest) -> Self {
+ Retention {
+ epochs: request.epochs,
+ age_ms: request
+ .age
+ .map(|age| u64::try_from(age.as_millis()).unwrap_or(u64::MAX)),
+ }
+ }
+}
+
/// What `?force=` may override on a proposal (`force`).
const FORCE: &[&str] = &["switchDelay", "supersede", "coordinatorActive"];
/// What `?force=` may override on an abort.
@@ -488,10 +509,10 @@ async fn start_kind<H: Host>(
policy: Policy {
replay_window: request
.replay_window
- .unwrap_or_else(Policy::default_replay_window),
+ .map_or_else(Policy::default_replay_window, Retention::from),
archive_retention: request
.archive_retention
- .unwrap_or_else(Policy::default_archive_retention),
+ .map_or_else(Policy::default_archive_retention, Retention::from),
},
settle_ms,
proposed_at: now,
diff --git a/src/remake/local.rs b/src/remake/local.rs
index 8e9d8ba..c6a4c18 100644
--- a/src/remake/local.rs
+++ b/src/remake/local.rs
@@ -399,7 +399,7 @@ mod tests {
AutoCommit::new(),
Retention {
epochs: Some(1),
- seconds: None,
+ age_ms: None,
},
1,
now,
@@ -414,7 +414,7 @@ mod tests {
AutoCommit::new(),
Retention {
epochs: Some(0),
- seconds: Some(3600),
+ age_ms: Some(3_600_000),
},
5,
now,
@@ -427,7 +427,7 @@ mod tests {
AutoCommit::new(),
Retention {
epochs: None,
- seconds: None,
+ age_ms: None,
},
3,
now,
diff --git a/src/remake/records.rs b/src/remake/records.rs
index 119ff0c..9e2b14e 100644
--- a/src/remake/records.rs
+++ b/src/remake/records.rs
@@ -40,7 +40,7 @@ use crate::document::replica::Epoch;
use crate::document::store::{self, StoreError};
/// One day, for retention defaults.
-const DAY_SECS: u64 = 24 * 60 * 60;
+const DAY_MS: u64 = 24 * 60 * 60 * 1000;
// --- record types ------------------------------------------------------------
@@ -71,13 +71,15 @@ impl EpochRecord {
}
/// How long something is kept. A value is kept until every given limit has passed.
+/// Requests give the age as a duration (`api`, `RetentionRequest`); records hold it in
+/// milliseconds.
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct Retention {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub(crate) epochs: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
- pub(crate) seconds: Option<u64>,
+ pub(crate) age_ms: Option<u64>,
}
impl Retention {
@@ -85,9 +87,7 @@ impl Retention {
/// `age_ms` milliseconds after the fact.
pub(crate) fn covers(&self, epochs: u64, age_ms: u64) -> bool {
self.epochs.is_some_and(|limit| epochs <= limit)
- || self
- .seconds
- .is_some_and(|limit| age_ms <= limit.saturating_mul(1000))
+ || self.age_ms.is_some_and(|limit| age_ms <= limit)
}
}
@@ -107,14 +107,14 @@ impl Policy {
pub(crate) fn default_replay_window() -> Retention {
Retention {
epochs: Some(1),
- seconds: Some(7 * DAY_SECS),
+ age_ms: Some(7 * DAY_MS),
}
}
pub(crate) fn default_archive_retention() -> Retention {
Retention {
epochs: Some(2),
- seconds: Some(30 * DAY_SECS),
+ age_ms: Some(30 * DAY_MS),
}
}
}
@@ -515,14 +515,14 @@ mod tests {
fn retention_covers_until_every_limit_passed() {
let both = Retention {
epochs: Some(1),
- seconds: Some(10),
+ age_ms: Some(10_000),
};
assert!(both.covers(1, 60_000)); // epochs still within
assert!(both.covers(5, 5_000)); // time still within
assert!(!both.covers(2, 10_001));
let none = Retention {
epochs: None,
- seconds: None,
+ age_ms: None,
};
assert!(!none.covers(0, 0));
}