NVIDIA-NeMo / NVIDIA-NeMo/DataDesigner

RequestFairQueue selection cost grows with historical group activations

Open
#945 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug
Dominant language
Python
Stars
2.2k
Forks
211
Avg merge
2d 6h
Merged PRs (30d)
40

Description

Priority Level

High (Major functionality broken)

Describe the bug

RequestFairQueue retains one heap entry for every historical group activation. _activate_group() appends a new tuple to _heap, while commit() removes only the corresponding entry from _active_heap_entries. The stale tuple remains in _heap permanently.

select_next() copies and heapifies the full historical heap on every selection, then pops stale tuples until it reaches a current active entry. Selection cost and retained state therefore grow with total requests processed rather than the number of active request groups.

This creates increasing event-loop CPU overhead during long asynchronous generation runs without changing the number of active groups.

Steps/Code to reproduce bug
from data_designer.engine.models.request_admission.queue import RequestFairQueue, RequestWaiter
from data_designer.engine.models.request_admission.resources import (
    RequestAdmissionItem,
    RequestDomain,
    RequestGroupSpec,
    RequestResourceKey,
)

resource = RequestResourceKey("provider", "model", RequestDomain.CHAT)
group = RequestGroupSpec(resource)
queue = RequestFairQueue()

for index in range(1_000):
    waiter = RequestWaiter(
        waiter_id=str(index),
        item=RequestAdmissionItem(resource=resource, group=group),
        enqueued_at=float(index),
    )
    assert queue.enqueue(waiter)
    selection = queue.select_next(lambda _waiter, _view: True)
    assert selection is not None
    assert queue.commit(selection) is waiter

assert not queue.has_waiters
print(len(queue._heap), len(queue._active_heap_entries))

The queue has no waiters or active groups, but _heap still contains approximately 1,000 historical entries. Repeating the loop continues to grow the heap.

Expected behavior

Request selection state and cost should be bounded by currently active request groups. Historical activations should either be removed or compacted, or select_next() should construct its temporary heap from _active_heap_entries only.

Weighted fair ordering and transaction semantics should remain unchanged.

Agent Diagnostic / Prior Investigation

The request-admission design documents describe RequestFairQueue as the canonical weighted-fair queue, but no existing issue was found for historical heap growth.

A GIL-only profile of a long asynchronous generation run attributed 18.42% of samples to request-admission queue selection. A minimal active-only heap change reduced that share to 0.51%, a 97.2% relative reduction. In the same controlled workload, response and completed-record throughput improved by approximately 22-25%.

A focused regression test can repeatedly activate and drain one group, then count heap pops during the next public select_next() call. The fixed implementation requires one pop regardless of the number of historical activations.

Additional context

The affected queue is internal, so the fix should not require a public API change. This is related to the request-admission work tracked by #645.

Checklist
  • I reproduced this issue or provided a minimal example
  • I searched the docs/issues myself, or had my agent do so
  • If I used an agent, I included its diagnostics above

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Read data_designer/engine/models/request_admission/queue.py, especially RequestFairQueue._activate_group(), select_next(), and commit(); use the reproduction to inspect heap state after repeated activation and draining. Add a focused regression test that counts heap pops during a later select_next(), while confirming weighted-fair ordering and transaction semantics remain unchanged.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
backend, performance
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
76/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.