Skip to content

fix: drain entire queue per flush to avoid streaming tail lag#424

Open
heyitsaamir wants to merge 1 commit intomainfrom
fix/drain-entire-stream-queue-per-flush
Open

fix: drain entire queue per flush to avoid streaming tail lag#424
heyitsaamir wants to merge 1 commit intomainfrom
fix/drain-entire-stream-queue-per-flush

Conversation

@heyitsaamir
Copy link
Copy Markdown
Collaborator

Summary

  • Removes the 10-item batch cap in Stream.Flush() so each flush cycle drains the entire queue
  • Prevents a long streaming tail when the LLM finishes generating faster than chunks are sent to Teams
  • Port of microsoft/teams.py#384 / microsoft/teams.ts#520

Test plan

  • Existing AspNetCorePluginStreamTests pass (15/15 on both net8.0 and net10.0)
  • Build succeeds with no warnings

🤖 Generated with Claude Code

Remove the 10-item batch cap in Stream.Flush() so each cycle
processes all queued items, preventing a long streaming tail when
the LLM finishes faster than chunks are sent to Teams.

Port of microsoft/teams.py#384 / microsoft/teams.ts#520.

Co-Authored-By: Claude <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

Removes the fixed 10-item cap in Stream.Flush() so each flush cycle drains the entire pending activity queue, reducing “tail lag” when generation outpaces outbound sending.

Changes:

  • Remove the 10-item batch limit in Flush() and drain _queue fully per flush
  • Replace the “no work done” check (i == 0) with a queue-count-based early return guard
Comments suppressed due to low confidence (1)

Libraries/Microsoft.Teams.Plugins/Microsoft.Teams.Plugins.AspNetCore/AspNetCorePlugin.Stream.cs:1

  • startCount is computed from _queue.Count, which is not a reliable indicator of whether this flush actually dequeued items in concurrent scenarios. If _queue.Count is 0 at line 138 but items are enqueued immediately after and then dequeued in the loop, startCount == 0 will still be true and the method will return early, potentially dropping/defering sends even though activities were drained and _count was incremented. Prefer tracking whether anything was actually dequeued (e.g., var dequeued = 0; increment inside the loop and if (dequeued == 0) return;), which matches the previous i == 0 behavior and avoids reliance on _queue.Count.
// Copyright (c) Microsoft Corporation. All rights reserved.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

}

if (i == 0) return;
if (startCount == 0) return;
Copy link

Copilot AI Apr 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

startCount is computed from _queue.Count, which is not a reliable indicator of whether this flush actually dequeued items in concurrent scenarios. If _queue.Count is 0 at line 138 but items are enqueued immediately after and then dequeued in the loop, startCount == 0 will still be true and the method will return early, potentially dropping/defering sends even though activities were drained and _count was incremented. Prefer tracking whether anything was actually dequeued (e.g., var dequeued = 0; increment inside the loop and if (dequeued == 0) return;), which matches the previous i == 0 behavior and avoids reliance on _queue.Count.

Copilot uses AI. Check for mistakes.
var i = 0;

Queue<TypingActivity> informativeUpdates = new();
var startCount = _queue.Count;
Copy link

Copilot AI Apr 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ConcurrentQueue<T>.Count can be O(n) and is an imprecise snapshot under concurrency. Since this value is only used to decide whether to return, it’s more efficient and accurate to avoid Count entirely and instead track a local dequeued counter (or a boolean flag) while draining.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants