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
|
/// or — on dedup — the existing entry's id with the new reason
|
||||||
/// appended).
|
/// appended).
|
||||||
///
|
///
|
||||||
/// Dedup rule: a `Queued` entry with the same `(kind, agent)` swallows
|
/// Dedup rule:
|
||||||
/// the new request and returns its existing id. Running and terminal
|
/// - `Rebuild` / `Spawn` / `Destroy`: a `Queued` entry with the same
|
||||||
/// entries do not dedup — operators are free to re-queue a rebuild
|
/// `(kind, agent)` swallows the new request.
|
||||||
/// that's currently running (something changed since it started) or
|
/// - `MetaUpdate`: dedup ALSO requires the `inputs` field to match —
|
||||||
/// re-run one that just finished.
|
/// 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(
|
pub fn enqueue(
|
||||||
&self,
|
&self,
|
||||||
kind: QueueKind,
|
kind: QueueKind,
|
||||||
|
|
@ -238,7 +245,9 @@ impl RebuildQueue {
|
||||||
|
|
||||||
/// Same as `enqueue` but carries an `inputs` payload — used by
|
/// Same as `enqueue` but carries an `inputs` payload — used by
|
||||||
/// `MetaUpdate` enqueues to tell the worker which meta-flake
|
/// `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(
|
pub fn enqueue_with_inputs(
|
||||||
&self,
|
&self,
|
||||||
kind: QueueKind,
|
kind: QueueKind,
|
||||||
|
|
@ -249,9 +258,15 @@ impl RebuildQueue {
|
||||||
inputs: Vec<String>,
|
inputs: Vec<String>,
|
||||||
) -> u64 {
|
) -> u64 {
|
||||||
let mut inner = self.inner.lock().expect("rebuild_queue mutex poisoned");
|
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() {
|
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) {
|
if !entry.reason.contains(&reason) {
|
||||||
entry.reason.push_str(&format!("\nalso requested by: {reason}"));
|
entry.reason.push_str(&format!("\nalso requested by: {reason}"));
|
||||||
}
|
}
|
||||||
|
|
@ -619,6 +634,62 @@ mod tests {
|
||||||
assert!(snap[0].reason.contains("auto sweep"));
|
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]
|
#[test]
|
||||||
fn dedup_does_not_apply_across_kinds_or_agents() {
|
fn dedup_does_not_apply_across_kinds_or_agents() {
|
||||||
let q = RebuildQueue::new();
|
let q = RebuildQueue::new();
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue