rebuild_queue: dedup MetaUpdate on (kind, agent, inputs) (closes #365)
This commit is contained in:
parent
0b03d5bcfb
commit
3fa12bf363
1 changed files with 79 additions and 8 deletions
|
|
@ -220,11 +220,18 @@ impl RebuildQueue {
|
|||
/// or — on dedup — the existing entry's id with the new reason
|
||||
/// appended).
|
||||
///
|
||||
/// Dedup rule: a `Queued` entry with the same `(kind, agent)` swallows
|
||||
/// the new request and returns its existing id. Running and terminal
|
||||
/// entries do not dedup — operators are free to re-queue a rebuild
|
||||
/// that's currently running (something changed since it started) or
|
||||
/// re-run one that just finished.
|
||||
/// Dedup rule:
|
||||
/// - `Rebuild` / `Spawn` / `Destroy`: a `Queued` entry with the same
|
||||
/// `(kind, agent)` swallows the new request.
|
||||
/// - `MetaUpdate`: dedup ALSO requires the `inputs` field to match —
|
||||
/// two meta-updates with different input lists are distinct work
|
||||
/// and must queue separately (closes #365: previously the second
|
||||
/// meta-update collapsed into the first whenever it was still
|
||||
/// `Queued`, losing the second's input set).
|
||||
///
|
||||
/// Running and terminal entries never dedup — operators are free
|
||||
/// to re-queue a rebuild that's currently running (something
|
||||
/// changed since it started) or re-run one that just finished.
|
||||
pub fn enqueue(
|
||||
&self,
|
||||
kind: QueueKind,
|
||||
|
|
@ -238,7 +245,9 @@ impl RebuildQueue {
|
|||
|
||||
/// Same as `enqueue` but carries an `inputs` payload — used by
|
||||
/// `MetaUpdate` enqueues to tell the worker which meta-flake
|
||||
/// inputs to bump.
|
||||
/// inputs to bump. For `MetaUpdate` the `inputs` value is part of
|
||||
/// the dedup key (two meta-updates with different inputs are
|
||||
/// distinct operations, see #365).
|
||||
pub fn enqueue_with_inputs(
|
||||
&self,
|
||||
kind: QueueKind,
|
||||
|
|
@ -249,9 +258,15 @@ impl RebuildQueue {
|
|||
inputs: Vec<String>,
|
||||
) -> u64 {
|
||||
let mut inner = self.inner.lock().expect("rebuild_queue mutex poisoned");
|
||||
// Dedup against a pending entry with the same (kind, agent).
|
||||
// Dedup against a pending entry with the same (kind, agent) —
|
||||
// and, for MetaUpdate, the same `inputs` list (see method
|
||||
// docstring + #365 for why).
|
||||
for entry in inner.entries.iter_mut() {
|
||||
if entry.state == QueueState::Queued && entry.kind == kind && entry.agent == agent {
|
||||
if entry.state == QueueState::Queued
|
||||
&& entry.kind == kind
|
||||
&& entry.agent == agent
|
||||
&& (kind != QueueKind::MetaUpdate || entry.inputs == inputs)
|
||||
{
|
||||
if !entry.reason.contains(&reason) {
|
||||
entry.reason.push_str(&format!("\nalso requested by: {reason}"));
|
||||
}
|
||||
|
|
@ -619,6 +634,62 @@ mod tests {
|
|||
assert!(snap[0].reason.contains("auto sweep"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn meta_update_dedup_matches_inputs() {
|
||||
// Two MetaUpdate enqueues with identical inputs → dedup (#365).
|
||||
let q = RebuildQueue::new();
|
||||
let a = q.enqueue_with_inputs(
|
||||
QueueKind::MetaUpdate,
|
||||
"hyperhive".to_owned(),
|
||||
QueueSource::Manual,
|
||||
"first".to_owned(),
|
||||
None,
|
||||
vec!["nixpkgs".to_owned()],
|
||||
);
|
||||
let b = q.enqueue_with_inputs(
|
||||
QueueKind::MetaUpdate,
|
||||
"hyperhive".to_owned(),
|
||||
QueueSource::Manual,
|
||||
"duplicate click".to_owned(),
|
||||
None,
|
||||
vec!["nixpkgs".to_owned()],
|
||||
);
|
||||
assert_eq!(a, b, "identical-inputs meta-updates should dedup");
|
||||
assert_eq!(q.snapshot().len(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn meta_update_dedup_separates_distinct_inputs() {
|
||||
// Two MetaUpdate enqueues with DIFFERENT inputs → distinct
|
||||
// entries, not deduped (the actual #365 bug).
|
||||
let q = RebuildQueue::new();
|
||||
let a = q.enqueue_with_inputs(
|
||||
QueueKind::MetaUpdate,
|
||||
"hyperhive".to_owned(),
|
||||
QueueSource::Manual,
|
||||
"bump nixpkgs".to_owned(),
|
||||
None,
|
||||
vec!["nixpkgs".to_owned()],
|
||||
);
|
||||
let b = q.enqueue_with_inputs(
|
||||
QueueKind::MetaUpdate,
|
||||
"hyperhive".to_owned(),
|
||||
QueueSource::Manual,
|
||||
"bump bitburner-agent".to_owned(),
|
||||
None,
|
||||
vec!["agent-bitburner/bitburner-agent".to_owned()],
|
||||
);
|
||||
assert_ne!(a, b, "different-inputs meta-updates must NOT dedup");
|
||||
let snap = q.snapshot();
|
||||
assert_eq!(snap.len(), 2);
|
||||
// Both inputs lists are preserved.
|
||||
let inputs: Vec<&[String]> = snap.iter().map(|e| e.inputs.as_slice()).collect();
|
||||
assert!(inputs.iter().any(|i| *i == ["nixpkgs"]));
|
||||
assert!(inputs
|
||||
.iter()
|
||||
.any(|i| *i == ["agent-bitburner/bitburner-agent"]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dedup_does_not_apply_across_kinds_or_agents() {
|
||||
let q = RebuildQueue::new();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue