OXYGENHUB

The nightly gc remembers its day across restarts dbcb0b813caa

It kept the day it last ran in memory, so every restart during gc_hour
(a deploy between 03:00 and 03:59 UTC) ran the whole pass again: on
2026-10-17 two deploys did, metalgear2's repack in GCS took 87 s each
time and its requests queued behind it for up to 74 s. The day is now
kept in <data>/gc-last-day, written as the run starts.

Refs #228
Author
Andy McCall @andymccall <[email protected]>
via
agent Claude Code <[email protected]> · delegate for changes, until 2026-11-13 19:00Z · key ea5f78160088
When
· 2026-10-17 03:44 UTC
Signature
✔ signed by Claude Code
Hash
dbcb0b813caacaa4c918b57fbeb64879c39ea338fba2a042ab5159213ec7f77e
Depends on
8a3334b7a364
Depended on by
439ecd8708a1
In bookmarks
api-unknown-fields cancel-landed-runs diffusion-pin-bookmark draft-ready main oh-comment-body-file proposal-list-fast
In tags
v0.6.0 v0.6.1 v0.7.0 v0.8.0 v0.9.0 v0.9.1 v0.9.11 v0.9.15 v0.9.16 v0.9.19 v0.9.2 v0.9.20 v0.9.21 v0.9.22 v0.9.23 v0.9.24 v0.9.3 v0.9.4 v0.9.5 v0.9.6 v0.9.7 v0.9.8 v0.9.9
Edits
6 atoms, 0 tree operations, 1 file

Browse the tree at this change

modified crates/hub/src/maintenance.rs +35 −3
…
67 unchanged lines
68 68
    done
69 69
}
70 70
71 +
/// The day the nightly run last ran, kept in the data directory: a restart
72 +
/// within `gc_hour` (a deploy) does not run it again. On 2026-10-17 one did,
73 +
/// and metalgear2's requests waited 74 s behind its repack in GCS.
74 +
fn last_run_path(app: &Shared) -> std::path::PathBuf {
75 +
    app.config.data.join("gc-last-day")
76 +
}
77 +
78 +
fn last_run(app: &Shared) -> Option<String> {
79 +
    Some(std::fs::read_to_string(last_run_path(app)).ok()?.trim().to_string())
80 +
}
81 +
82 +
fn record_run(app: &Shared, day: &str) {
83 +
    if let Err(e) = std::fs::write(last_run_path(app), day) {
84 +
        tracing::warn!("gc: recording the nightly run: {e}");
85 +
    }
86 +
}
87 +
88 +
/// A day as the record keeps it: `2026-10-17`.
89 +
fn day_text(day: time::Date) -> String {
90 +
    format!("{:04}-{:02}-{:02}", day.year(), u8::from(day.month()), day.day())
91 +
}
92 +
71 93
/// Run [`gc_all`] once a day at `retention.gc_hour` UTC.
72 94
pub async fn nightly(app: Shared) {
73 95
    let hour = app.config.retention.gc_hour;
…
1 unchanged lines
75 97
        tracing::info!("gc: the nightly run is off (retention.gc_hour = {hour})");
76 98
        return;
77 99
    }
78 -
    let mut last_day = None;
100 +
    let mut last_day = last_run(&app);
79 101
    loop {
80 102
        tokio::time::sleep(std::time::Duration::from_secs(60)).await;
81 103
        let now = time::OffsetDateTime::now_utc();
82 104
        if u32::from(now.hour()) != hour {
83 105
            continue;
84 106
        }
85 -
        let day = now.date();
86 -
        if last_day == Some(day) {
107 +
        let day = day_text(now.date());
108 +
        if last_day.as_deref() == Some(day.as_str()) {
87 109
            continue;
88 110
        }
111 +
        record_run(&app, &day);
89 112
        last_day = Some(day);
90 113
        let done = gc_all(&app).await;
91 114
        tracing::info!("gc: nightly run over {} repositories, {} failed", done.len(), done.iter().filter(|d| d.failed).count());
115 +
    }
116 +
}
117 +
118 +
#[cfg(test)]
119 +
mod tests {
120 +
    #[test]
121 +
    fn a_day_is_written_as_the_record_keeps_it() {
122 +
        let day = time::Date::from_calendar_date(2026, time::Month::October, 7).unwrap();
123 +
        assert_eq!(super::day_text(day), "2026-10-07");
92 124
    }
93 125
}