Skip to content

The priority queue does more work than its contract requires #1

Open

Description

@cblanquera

Timing: Follow-up after trial/3 merges into main; defer implementation until then.

Review baseline: Local trial/3 at a094418baf6388e3eeca23821df481a7f0a8751c. This branch is not yet on the remote. Source locations below refer to that snapshot and should be rechecked after merge.

Problem

ItemQueue::add sorts the entire collection after every insertion. pop removes index zero from a Vec, shifting the remaining entries. Fully draining n queued items therefore entails O(n²) element movement. This is a scaling concern; no current-workload latency regression has been measured.

Evidence

  • crates/lib/src/queue.rs:80-103: append followed by full sorting.
  • crates/lib/src/queue.rs:163-169: entries.remove(0).

Acceptance criteria

  • Choose storage/scheduling that avoids repeated full sorting and front-shifting, justified against expected queue sizes.
  • Preserve descending priority, FIFO ties, checked sequence/priority arithmetic, retained high/low watermarks, and cooperative execution behavior.
  • Verify ordering, mixed insertion/removal, and front/back insertion after draining; record the resulting complexity or focused performance evidence.

Code examples and suggested direction

The current queue must preserve entries() -> &[Item<T>] in execution order. A heap alone does not provide that view. One candidate is a sorted VecDeque kept contiguous after every insertion:

// Sketch inside add(), after all fallible arithmetic checks.
// New entries go after existing entries with the same priority.
let index = self.entries
    .iter()
    .position(|queued| queued.priority < entry.priority)
    .unwrap_or(self.entries.len());
self.entries.insert(index, entry);
self.entries.make_contiguous();

// pop() can then avoid shifting every remaining entry:
self.entries.pop_front().map(|entry| entry.item)

This candidate still has O(n) insertion; the improvement is removing repeated full sorting and making a complete drain linear. It is a suggestion to evaluate, not an accepted storage change.

Activity

  1. cblanquera commented on Oct 26, 2026

    @cblanquera
    MemberAuthor

    Suggested investigation and fix notes

    Deferred until trial/3 is merged into main. Recheck the merged source and tests first. The snippets are examples/proposals, not an approved patch or a change to frozen contracts.

    1. Read ItemQueue, TaskQueue::run, and stable_item_queue_ties_extremes_and_drain in crates/lib/tests/event_contracts.rs. Inventory the sorted borrowed entries() contract before selecting storage.
    2. Compare the sorted-contiguous deque candidate in the description with other compatible approaches. A plain BinaryHeap is insufficient: its slice/iteration is heap order, and requiring T: Ord or Clone would narrow the API.
    3. If using the deque, establish contiguity after every mutator. Popping the front preserves that invariant. Keep priority/sequence exhaustion checks before any mutation, and retain historical watermarks after draining.
    4. Extend ordering checks with insertion after partial drain, many equal-priority values, and payloads that implement neither Clone nor Ord. Preserve before/action/after abort and error behavior.
    5. Record focused insert/drain measurements across increasing sizes; keep noisy wall-clock thresholds out of unit tests.

    Illustrative entries() implementation only if every mutator establishes the invariant:

    pub fn entries(&self) -> &[Item<T>] {
        let (ordered, wrapped) = self.entries.as_slices();
        debug_assert!(wrapped.is_empty());
        ordered
    }

    Start verification with cargo test -p stackpress-lib --test event_contracts --locked. See the standard BinaryHeap contract when comparing alternatives.

    Implementation workflow: After the merge, follow .agents/workflows/rust-programming.md: prove the affected behavior, complete the three test-verified review rounds, and record relevant formatting/lints/docs, feature/platform scope, and applicable development coverage. Preserve unrelated changes.

  2. cblanquera commented on Oct 26, 2026

    @cblanquera
    MemberAuthor

    Fixed on trial/3 in 644795f (merged as 64a5b70, task 00010 lane 1; not yet on main, so leaving open).

    • ItemQueue now keeps a sorted VecDeque: binary upper-bound insertion after equal priorities (FIFO ties), kept contiguous so entries() -> &[Item<T>] is unchanged.
    • Complexity: O(log n) comparisons + O(n) worst-case insertion movement, O(1) pop, O(n) full drain (was O(n²)). Not claiming heap-like insertion.
    • Tests: descending priority, FIFO ties, 400-operation mixed insert/remove with slice comparisons, front/back insertion after a full drain, retained watermarks, checked arithmetic and sequence exhaustion.

    Coordinator-verified: fmt, Clippy -D warnings, Rustdoc -D warnings, cargo +1.96.0 check, 454 workspace tests, 856 doctests pass; lib production coverage 93.64%.

  3. added a commit that references this issue on Oct 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions