Phase 7f: claw-cargo auto-records fingerprint → (repo, git_ref)
Build with clawstor cache / Cargo build (clawstor-cached) (pull_request) Failing after 3s

Closes the ref-tracking loop. claw-cargo build now records the
producing (repo, git_ref) alongside every cache-put fingerprint,
so cluster-ref-sweep can identify stale entries later without
operator bookkeeping.

New BuildArgs flags:
* --repo <owner/name> (env CLAWSTOR_REPO, or GITEA_REPOSITORY /
  GITHUB_REPOSITORY when the CI runner sets them via that name
  in workflow env)
* --git-ref <branch-or-tag> (env CLAWSTOR_GIT_REF)
* --ref-tracking-dir <path> (env CLAWSTOR_DATA_DIR, typically
  /var/lib/claw-store/data — same root as cluster.blob_store_root)

Semantics:
* All three unset → silently skipped. Existing cache flows are
  unchanged.
* dir doesn't exist or open() fails → logs warn, cache still valid.
* record() call fails → logs warn, cache still valid.

The tracking store is co-located with the daemon's data dir so
cluster-ref-sweep on that host sees the annotations. Runners
mount /var/lib/claw-store/data via bind-mount today.

No new tests here — the primitive (RefTracking::record) already
has full coverage. This is thin glue.
This commit is contained in:
Omar Sobh
2026-07-14 11:01:15 -07:00
parent 7bc5ba987c
commit 3af6390316
+56
View File
@@ -279,6 +279,24 @@ struct BuildArgs {
/// (WAN, tunneled links). /// (WAN, tunneled links).
#[arg(long, default_value_t = 1)] #[arg(long, default_value_t = 1)]
parallel_restore: usize, parallel_restore: usize,
/// Phase 7f (2026-07-14): on cache-put, record `(fingerprint,
/// repo, git_ref)` locally so a later cluster-ref-sweep can
/// reap cache entries whose refs are gone upstream. Defaults
/// from CI env: `GITEA_REPOSITORY` / `GITHUB_REPOSITORY`.
/// Silently skipped when unset — cache still works without it.
#[arg(long, env = "CLAWSTOR_REPO")]
repo: Option<String>,
/// Phase 7f (2026-07-14): git ref (branch or tag name)
/// associated with this cache-put. Defaults from CI env:
/// `GITEA_REF_NAME` / `GITHUB_REF_NAME`. Silently skipped
/// when unset.
#[arg(long, env = "CLAWSTOR_GIT_REF")]
git_ref: Option<String>,
/// Path to the daemon's cluster.blob_store_root — needed to
/// write ref-tracking annotations. Defaults to
/// `/var/lib/claw-store/data`. Skipped if not writable.
#[arg(long, env = "CLAWSTOR_DATA_DIR")]
ref_tracking_dir: Option<std::path::PathBuf>,
/// Extra args passed verbatim to `cargo build` (after `--`). /// Extra args passed verbatim to `cargo build` (after `--`).
#[arg(last = true)] #[arg(last = true)]
cargo_args: Vec<String>, cargo_args: Vec<String>,
@@ -784,6 +802,44 @@ async fn cmd_build(args: BuildArgs) -> Result<()> {
blob_id, blob_id,
uploaded_bytes: capture_bytes, uploaded_bytes: capture_bytes,
}; };
// Phase 7f: annotate this fingerprint with its producing
// (repo, git_ref) so a later cluster-ref-sweep can
// identify it as stale when the ref disappears
// upstream. Silently skipped when any required piece
// is missing — the cache still works without tracking.
if let (Some(repo), Some(git_ref), Some(dir)) = (
args.repo.as_deref(),
args.git_ref.as_deref(),
args.ref_tracking_dir.as_ref(),
) {
if dir.is_dir() {
match crate::cluster::ref_tracking::RefTracking::open(dir.clone()) {
Ok(rt) => {
let now = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map(|d| d.as_secs())
.unwrap_or(0);
if let Err(e) = rt
.record(*fp.as_bytes(), repo, git_ref, now)
.await
{
tracing::warn!(
fingerprint = %fp,
repo,
git_ref,
error = %e,
"ref-tracking record failed; cache still valid"
);
}
}
Err(e) => tracing::warn!(
dir = %dir.display(),
error = %e,
"opening ref-tracking store failed; skipping annotation"
),
}
}
}
// Tempfile drops when we leave scope, unlinking automatically. // Tempfile drops when we leave scope, unlinking automatically.
} }
} }