summaryrefslogtreecommitdiffstats
path: root/src/remake/quarantine.rs
diff options
context:
space:
mode:
authortv <tv@krebsco.de>2026-10-21 04:08:26 +0200
committertv <tv@krebsco.de>2026-10-21 04:08:26 +0200
commitc9559af92ba2e3d27188d0a8efd1861180613840 (patch)
treeccbe7d41607cebff29002b9080d448549b100a38 /src/remake/quarantine.rs
parent812eb0b3b714a16584c058de4b6a1de84a8e8dd7 (diff)
turn compaction into a kind of remake
Diffstat (limited to 'src/remake/quarantine.rs')
-rw-r--r--src/remake/quarantine.rs46
1 files changed, 44 insertions, 2 deletions
diff --git a/src/remake/quarantine.rs b/src/remake/quarantine.rs
index 5643549..deed8cd 100644
--- a/src/remake/quarantine.rs
+++ b/src/remake/quarantine.rs
@@ -14,6 +14,7 @@
use std::convert::Infallible;
+use automerge::AutoCommit;
use axum::{
extract::{Path, State},
http::{header, StatusCode},
@@ -22,12 +23,16 @@ use axum::{
};
use serde::Serialize;
use serde_json::{json, Value as JsonValue};
-use tracing::info;
+use tracing::{info, warn};
+use uuid::Uuid;
use super::api::error_response;
use super::delta;
+use super::kind::Translator;
use super::local::Quarantined;
+use super::records::unix_millis;
use super::Host;
+use crate::document::replica::Epoch;
use crate::document::store;
#[derive(Debug, Serialize)]
@@ -58,6 +63,41 @@ fn not_found() -> Response {
error_response(StatusCode::NOT_FOUND, "no such quarantine entry", None)
}
+/// Puts writes a remake had no place for into quarantine, with `document` (the old
+/// epoch's) to read them from. Returns the entry's ID, or None if there is nothing.
+pub(super) fn add<H: Host>(
+ host: &H,
+ epoch: Epoch,
+ target: Epoch,
+ document: &AutoCommit,
+ quarantined: Vec<(Vec<String>, String)>,
+) -> Option<String> {
+ if quarantined.is_empty() {
+ return None;
+ }
+ let mut reasons: Vec<&str> = quarantined
+ .iter()
+ .map(|(_, reason)| reason.as_str())
+ .collect();
+ reasons.sort_unstable();
+ reasons.dedup();
+ let reason = reasons.join("; ");
+ let paths: Vec<Vec<String>> = quarantined.into_iter().map(|(path, _)| path).collect();
+
+ let id = Uuid::new_v4().to_string();
+ warn!(quarantine = %id, epoch = %epoch, paths = paths.len(), reason = %reason, "Quarantined writes the remake has no place for");
+ host.remake().add_quarantine(Quarantined {
+ id: id.clone(),
+ epoch,
+ target,
+ created_at: unix_millis(),
+ reason,
+ paths,
+ document: document.clone(),
+ });
+ Some(id)
+}
+
pub(crate) async fn list<H: Host>(State(host): State<H>) -> Response {
Json(host.remake().list_quarantine(summary)).into_response()
}
@@ -129,7 +169,9 @@ pub(crate) async fn replay<H: Host>(State(host): State<H>, Path(id): Path<String
let (stats, entry) = host
.commit_doc(move |doc| {
- let stats = delta::replay_paths(&entry.document, doc, &entry.paths);
+ // As they are: the operator decided they belong at their old paths.
+ let stats =
+ delta::replay_paths(&entry.document, doc, &entry.paths, &Translator::default());
Ok::<_, Infallible>((stats, entry))
})
.await