pub enum SweepScope<'a> {
Boot,
Live(&'a HashSet<String>),
}Expand description
Delete session snapshots (and their journals) beyond the retention caps.
Nothing pruned this directory before car#1310, so <id>.json and the
larger <id>.events.jsonl beside it accumulated for the life of the
installation — and coder.list pays a read, a serde_json parse and a
stat for every one of them on every call. car#1262 bounded the in-memory
registry and explicitly left this alone; this is the disk arm.
Two exemptions, both about not destroying the only record of something that still exists:
- A session that is not terminal is never
collected. Same rule as
RunStore’s in-progress exemption. Note this coversNeedsApproval, which is deliberately non-terminal — a snapshot waiting on a human is not garbage however old it is. - A session whose
workspace_pathis still a directory is never collected. The snapshot is the only thing that names that worktree; deleting it turns a directory an operator kept (keep_workspace_on_failure) or a preservedneeds_approvalorphan into an unattributable leak.
The count cap ranks only COLLECTABLE sessions, matching RunStore’s
completed_rank: an exempt session neither dies nor consumes a keeper slot.
So max_sessions is a bound on what retention manages, NOT on the size of
the directory — an install that sets keep_workspace_on_failure holds every
failed session’s snapshot, journal AND worktree on top of the cap, by the
operator’s own request. Restart-orphaned worktrees are also retained: a
crash must not erase unfinished edits merely to satisfy history caps.
Once a retained worktree is explicitly removed, its history is collectable.
The age cap reads updated_at, which is stamped on every transition, so it
measures time since the session last did anything rather than since it
started.
The journal is unlinked BEFORE the snapshot, and the collection is counted
on the snapshot. Only *.json is enumerated, so a journal whose snapshot is
already gone is invisible to the candidate pass — removing the snapshot
first would strand the larger file permanently on any unlink error. (The
pass at the end sweeps journals already stranded that way, including by a
coder.start that created the sink and died before its first persist.)
Best-effort: an unreadable snapshot is skipped, and a failed unlink is
logged rather than propagated — this runs at boot and must not block it.
Returns the number of sessions collected.
Which process state a gc_sessions sweep is running in.
Not an Option<&HashSet>: that carries two orthogonal bits in one type and
only one of them is about the set. None would have to mean BOTH “filter
nothing” AND “sweep orphan journals” — so a caller with no live set to hand
over, the natural reading of None, would silently re-enable a deletion
pass that is safe only where no live sink can own a journal.
Variants§
Boot
Daemon construction, where the session registry is provably empty. Also sweeps orphan journals, which is safe only here.
Live(&'a HashSet<String>)
Mid-lifetime, carrying the ids the in-process registry still holds.
Auto Trait Implementations§
impl<'a> Freeze for SweepScope<'a>
impl<'a> RefUnwindSafe for SweepScope<'a>
impl<'a> Send for SweepScope<'a>
impl<'a> Sync for SweepScope<'a>
impl<'a> Unpin for SweepScope<'a>
impl<'a> UnsafeUnpin for SweepScope<'a>
impl<'a> UnwindSafe for SweepScope<'a>
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
impl<S, T> Duplex<S> for Twhere
T: FromSample<S> + ToSample<S>,
impl<T> ErasedDestructor for Twhere
T: 'static,
Source§impl<S> FromSample<S> for S
impl<S> FromSample<S> for S
fn from_sample_(s: S) -> S
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§fn in_current_span(self) -> Instrumented<Self> ⓘ
fn in_current_span(self) -> Instrumented<Self> ⓘ
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more