Polish: cluster-ref-sweep --apply #85

Merged
osobh merged 1 commits from polish-ref-sweep-apply into main 2026-07-14 20:11:10 +00:00
Showing only changes of commit 1c5d3c08bd - Show all commits
+38 -2
View File
@@ -230,6 +230,13 @@ enum Cmd {
/// reaped before someone can rebuild against them. /// reaped before someone can rebuild against them.
#[arg(long, default_value_t = 14)] #[arg(long, default_value_t = 14)]
retention_days: u64, retention_days: u64,
/// Polish (2026-07-14): actually forget the stale
/// ref-tracking records. Without this flag the command is
/// dry-run only. Blob data is untouched — eviction happens
/// on the next `cluster-gc` when the fingerprint's tag pin
/// disappears (nobody pins it any more).
#[arg(long)]
apply: bool,
}, },
/// Phase 7d (2026-07-14): take a point-in-time snapshot of every /// Phase 7d (2026-07-14): take a point-in-time snapshot of every
/// blob currently in the local store. Snapshots are cheap /// blob currently in the local store. Snapshots are cheap
@@ -371,7 +378,8 @@ async fn main() -> Result<()> {
gitea_url, gitea_url,
gitea_token, gitea_token,
retention_days, retention_days,
} => cmd_cluster_ref_sweep(&cfg, &gitea_url, gitea_token, retention_days).await?, apply,
} => cmd_cluster_ref_sweep(&cfg, &gitea_url, gitea_token, retention_days, apply).await?,
Cmd::ClusterSnapshotCreate { name } => cmd_cluster_snapshot_create(&cfg, &name).await?, Cmd::ClusterSnapshotCreate { name } => cmd_cluster_snapshot_create(&cfg, &name).await?,
Cmd::ClusterSnapshotList => cmd_cluster_snapshot_list(&cfg).await?, Cmd::ClusterSnapshotList => cmd_cluster_snapshot_list(&cfg).await?,
Cmd::ClusterSnapshotShow { name } => cmd_cluster_snapshot_show(&cfg, &name).await?, Cmd::ClusterSnapshotShow { name } => cmd_cluster_snapshot_show(&cfg, &name).await?,
@@ -777,6 +785,7 @@ async fn cmd_cluster_ref_sweep(
gitea_url: &str, gitea_url: &str,
gitea_token: Option<String>, gitea_token: Option<String>,
retention_days: u64, retention_days: u64,
apply: bool,
) -> Result<()> { ) -> Result<()> {
use cluster::gitea::GiteaClient; use cluster::gitea::GiteaClient;
use cluster::ref_tracking::RefTracking; use cluster::ref_tracking::RefTracking;
@@ -872,13 +881,40 @@ async fn cmd_cluster_ref_sweep(
} }
} }
println!(); println!();
println!("dry-run: no records modified. Use `forget` per fp to prune records"); if apply {
// Polish (2026-07-14): actually forget the stale records.
// Blob data untouched — the next cluster-gc reclaims disk
// once the fp's associated tag pins drop off.
let mut forgotten = 0usize;
let mut errors = 0usize;
for fp in &stale {
match rt.forget(fp).await {
Ok(true) => forgotten += 1,
Ok(false) => {} // already gone
Err(e) => {
errors += 1;
eprintln!("warn: forget({}) failed: {e}", hex_bytes(fp));
}
}
}
println!("applied: forgot {forgotten} ref-tracking records ({errors} errors)");
} else {
println!("dry-run: no records modified. Re-run with --apply to prune.");
}
println!("(blob eviction on next cluster-gc handles the actual disk reclaim)."); println!("(blob eviction on next cluster-gc handles the actual disk reclaim).");
println!("total elapsed: {:?}", started.elapsed()); println!("total elapsed: {:?}", started.elapsed());
println!("────────────────────────────────────────────────────"); println!("────────────────────────────────────────────────────");
Ok(()) Ok(())
} }
fn hex_bytes(bytes: &[u8; 32]) -> String {
let mut s = String::with_capacity(64);
for b in bytes {
s.push_str(&format!("{b:02x}"));
}
s
}
async fn cmd_cluster_peer_status( async fn cmd_cluster_peer_status(
peer: &str, peer: &str,
rpc_addr: SocketAddr, rpc_addr: SocketAddr,