Skip to content

Add priority preemption to BuildIngestionService - #17

Merged
jaredpar merged 1 commit into
mainfrom
jaredpar-iazdoclient-interface
Jun 22, 2026
Merged

Add priority preemption to BuildIngestionService#17
jaredpar merged 1 commit into
mainfrom
jaredpar-iazdoclient-interface

Conversation

@jaredpar

Copy link
Copy Markdown
Owner

Summary

Fixes priority queue starvation where navigating to the builds menu would queue priority builds that had to wait behind already-running normal builds.

Changes

Priority preemption mechanism:

  • When PrioritizeBuild() is called, in-flight non-priority tasks are cancelled via per-task CancellationTokenSource
  • Cancelled tasks are reset to pending state (no attempt increment) so they'll be retried
  • Worker loop uses Task.WhenAny with a SemaphoreSlim priority signal to detect incoming priority work

Cancellation safety (compile-time guarantees):

  • All DB write functions (InsertTestsData, InsertTimelineData, InsertPrInfoData, MarkComplete, MarkFailed, etc.) are static local functions that structurally cannot capture the CancellationToken
  • All fetch functions (FetchTestsDataAsync, FetchTimelineDataAsync, FetchPrInfoDataAsync) are static and explicitly receive CancellationToken
  • CancellationToken threaded through AzdoClient and HelixClient HTTP calls for proper cooperative cancellation

Testing infrastructure:

  • DelegateHandler pattern for HttpMessageHandler injection (no subclassing per test)
  • AzdoClient.Create(HttpMessageHandler) and HelixClient.Create(HttpMessageHandler) factories
  • MaxParallelism configurable via constructor for deterministic test control
  • 3 preemption tests covering tests/timeline/helix task types, each verifying:
    • Priority build completes successfully
    • Preempted builds are reset to pending state

Test results

All 174 tests pass, 0 warnings.

When priority builds arrive via PrioritizeBuild(), cancel in-flight
non-priority tasks to free worker slots immediately. This prevents
priority builds from starving behind long-running normal ingestion.

Key changes:
- Configurable max parallelism via constructor (for testability)
- Per-task CancellationTokenSource linked to the global CTS
- Priority signal (SemaphoreSlim) wakes worker loop on PrioritizeBuild
- PreemptNonPriorityTasks cancels non-priority CTSes, drains them
- WaitOrCancel<T> extension races tasks against cancellation token
- Preempted tasks reset to pending (no attempt increment)
- AzdoClient.Create(HttpMessageHandler) factory for test injection

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@jaredpar
jaredpar merged commit e0b8075 into main Jun 22, 2026
1 check passed
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.

1 participant