refactor(#2286): make transient set/clear private — RAII guard is the only door
This commit is contained in:
parent
9c3884e031
commit
05c91245c7
1 changed files with 14 additions and 8 deletions
|
|
@ -338,6 +338,9 @@ pub struct TransientState {
|
|||
/// drop — including drop-via-cancellation, the path that bare
|
||||
/// `set_transient` / `clear_transient` pairs leaked through. Holds an
|
||||
/// `Arc<Coordinator>` so the guard is freely returnable / movable.
|
||||
#[must_use = "the guard clears the transient when dropped; bind it for the operation's \
|
||||
duration (`let _guard = coord.transient_guard(...)`). An unbound call drops \
|
||||
it immediately and un-sets the transient at once — the exact footgun this guards against."]
|
||||
pub struct TransientGuard {
|
||||
coord: Arc<Coordinator>,
|
||||
name: String,
|
||||
|
|
@ -1158,13 +1161,13 @@ impl Coordinator {
|
|||
|
||||
/// Mark an agent as in-progress (only one state per agent for now).
|
||||
///
|
||||
/// Prefer `transient_guard` when possible — it auto-clears on drop
|
||||
/// even if the surrounding future is cancelled (HTTP request
|
||||
/// aborted, runtime shutdown mid-rebuild, panic between set and
|
||||
/// clear). The bare `set_transient` / `clear_transient` pair leaks
|
||||
/// the transient on any of those paths and the dashboard then
|
||||
/// shows the agent stuck in "rebuilding…" forever.
|
||||
pub fn set_transient(&self, name: &str, kind: TransientKind) {
|
||||
/// Private on purpose: the RAII [`TransientGuard`] (via
|
||||
/// [`Coordinator::transient_guard`]) is the only door, so the paired
|
||||
/// `clear_transient` always runs on drop even if the surrounding future
|
||||
/// is cancelled (HTTP request aborted, runtime shutdown mid-rebuild,
|
||||
/// panic). A bare set with no guaranteed clear would leak the transient
|
||||
/// and leave the dashboard stuck in "rebuilding…" forever.
|
||||
fn set_transient(&self, name: &str, kind: TransientKind) {
|
||||
self.transient.lock().unwrap().insert(
|
||||
name.to_owned(),
|
||||
TransientState {
|
||||
|
|
@ -1188,7 +1191,10 @@ impl Coordinator {
|
|||
});
|
||||
}
|
||||
|
||||
pub fn clear_transient(&self, name: &str) {
|
||||
/// Clear an agent's transient state. Private: only reachable through
|
||||
/// [`TransientGuard`]'s `Drop`, which guarantees it runs (see
|
||||
/// [`Coordinator::set_transient`]).
|
||||
fn clear_transient(&self, name: &str) {
|
||||
let removed = self.transient.lock().unwrap().remove(name);
|
||||
if let Some(state) = removed {
|
||||
// Stamp the tombstone so the crash watcher can still see
|
||||
|
|
|
|||
Loading…
Reference in a new issue