diff options
Diffstat (limited to 'src/remake/api.rs')
| -rw-r--r-- | src/remake/api.rs | 57 |
1 files changed, 55 insertions, 2 deletions
diff --git a/src/remake/api.rs b/src/remake/api.rs index 5c10b66..5e99d77 100644 --- a/src/remake/api.rs +++ b/src/remake/api.rs @@ -16,7 +16,8 @@ //! is in progress, without having to know its ID. //! //! Request body for POST (all optional): -//! `{ "replayWindow": {"epochs": 1, "seconds": 604800}, +//! `{ "kind": "compact", "spec": null, +//! "replayWindow": {"epochs": 1, "seconds": 604800}, //! "archiveRetention": {"epochs": 2, "seconds": 2592000}, //! "settleSecs": 480 }` @@ -35,12 +36,12 @@ use tokio::sync::broadcast; use tracing::{error, info, warn}; use uuid::Uuid; -use super::Host; use super::coordinator; use super::local::LocalStatus; use super::records::{ self, AbortRequest, EpochRecord, Phase, Policy, Proposal, Record, Retention, unix_millis, }; +use super::{Host, kind}; use crate::document::store::StoreError; #[derive(Debug, Default, Deserialize)] @@ -61,6 +62,10 @@ pub(crate) struct StartQuery { #[derive(Debug, Default, Deserialize)] #[serde(rename_all = "camelCase", deny_unknown_fields)] struct StartRequest { + /// The remake kind; `compact` if absent. + kind: Option<String>, + /// Kind-specific part of the request. + spec: Option<JsonValue>, replay_window: Option<Retention>, archive_retention: Option<Retention>, settle_secs: Option<u64>, @@ -154,11 +159,30 @@ async fn wait_until_final<H: Host>(host: &H, id: &str) -> Option<Report> { } } +/// `POST /admin/remakes`. pub(crate) async fn start<H: Host>( State(host): State<H>, Query(query): Query<StartQuery>, body: Bytes, ) -> Response { + start_kind(host, query, body, None).await +} + +/// `POST /admin/compaction`: the original endpoint, for compacting remakes only. +pub(crate) async fn start_compaction<H: Host>( + State(host): State<H>, + Query(query): Query<StartQuery>, + body: Bytes, +) -> Response { + start_kind(host, query, body, Some(kind::DEFAULT_KIND)).await +} + +async fn start_kind<H: Host>( + host: H, + query: StartQuery, + body: Bytes, + only: Option<&'static str>, +) -> Response { if !host.is_node_ready().await { return error_response(StatusCode::SERVICE_UNAVAILABLE, "node is not ready", None); } @@ -178,6 +202,32 @@ pub(crate) async fn start<H: Host>( } }; + let kind_name = request.kind.as_deref().unwrap_or(kind::DEFAULT_KIND); + if only.is_some_and(|only| only != kind_name) { + return error_response( + StatusCode::BAD_REQUEST, + format!("this endpoint only starts {} remakes", kind::DEFAULT_KIND), + None, + ); + } + let Some(kind) = kind::by_name(kind_name) else { + return error_response( + StatusCode::BAD_REQUEST, + format!("unknown remake kind {kind_name:?}"), + None, + ); + }; + let prepared = match kind.propose(request.spec.as_ref().unwrap_or(&JsonValue::Null)) { + Ok(prepared) => prepared, + Err(reason) => { + return error_response( + StatusCode::BAD_REQUEST, + format!("invalid {kind_name} spec: {reason}"), + None, + ); + } + }; + let dormant_after = host.dormant_after(); let settle = request .settle_secs @@ -239,6 +289,9 @@ pub(crate) async fn start<H: Host>( proposed_at: now, settle_until: now + settle_ms, peers_considered, + kind: kind.name().to_owned(), + content: prepared.content, + translation: prepared.translation, }; // From here on, nothing depends on the client: writing the proposal and starting the |
