microsoft / microsoft/duroxide
Add tryDequeueEvent(queueName) to orchestration context
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 217
- Forks
- 61
- Avg merge
- 2d 22h
- Merged PRs (30d)
- 3
Description
Problem
Duroxide currently exposes only blocking dequeueEvent(queueName) for orchestration event queues.
Some orchestrations need to greedily drain already-queued events into their own replay-safe buffer before processing. With the current API, there is no way to ask "is the queue empty right now?" or "pop one event only if something is already queued".
The only available workaround is heuristic:
- repeatedly race
dequeueEvent("messages")against a short timer such as 3s - if a message wins, buffer it and race again
- if the timer wins, assume the queue is drained enough for this pass
That works, but it is not exact:
- it adds arbitrary latency
- it depends on a magic timeout
- one burst can get split across multiple rounds
- the orchestration cannot distinguish a truly empty queue from a temporarily quiet queue
Concrete use case
PilotSwarm has parent orchestrations that receive a mixed stream of child updates, user prompts, and management messages on the same "messages" queue.
The parent wants to:
- drain already-queued events into a replay-safe KV work buffer
- process that buffered work one item at a time
- avoid
continue_as_new()with a large unmatched-event backlog, because carry-forward overflow can drop messages
Without a non-blocking dequeue primitive, we have to simulate drain completion with a quiet-window timer. That makes correctness and latency depend on the timeout instead of actual queue state.
Proposal
Add a native orchestration primitive such as:
const evt = yield* ctx.tryDequeueEvent("messages");
// evt is null if the queue is currently empty
Semantics:
- returns the next already-queued event if one exists
- returns
nullimmediately if the named queue is empty - does not block waiting for future messages
- preserves normal queue ordering
Why this matters
- enables exact drain-until-empty logic
- removes heuristic quiet timers such as 3s races
- lowers latency for bursty workloads
- simplifies queue-to-KV buffering patterns
- makes orchestration behavior more predictable under load
Alternative
If tryDequeueEvent() is not desirable, exposing queue depth / queue-empty state as an orchestration property would also enable this pattern in user code. But a native tryDequeueEvent() would be simpler and safer.
Summary
Today the workaround is: race(dequeueEvent(queue), shortTimer) and assume the queue is drained when the timer wins.
A native tryDequeueEvent() would replace that heuristic with exact semantics.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the orchestration context's existing dequeueEvent(queueName) implementation and trace how queued events are selected and replayed. Define the non-blocking empty-queue behavior while preserving ordering, then add coverage for draining queued events and returning immediately when the queue is empty.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- backend-api-design, distributed-systems
- Issue type
- Feature
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100