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 | } |