Implement weighted test batching using live ADO Analytics API - #57798
Implement weighted test batching using live ADO Analytics API#57798m-nash wants to merge 5 commits into
Conversation
There was a problem hiding this comment.
Pull request overview
This PR improves PR test batching for the .NET SDK repo by replacing count/alphabetical bucketing with weighted LPT bin-packing, using live test duration estimates from the Azure DevOps Analytics OData API to better balance CI batch runtimes (especially for macOS).
Changes:
- Added a new PowerShell script to query ADO Analytics for per-test-assembly wall-clock runtimes and emit per-package weights.
- Added
Split-ArrayByWeighthelper implementing LPT (Longest Processing Time) bin-packing based on weights and a target bucket time. - Wired the weights file + target time through PR matrix generation templates and CI job parameters (PR builds only for weight collection).
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| eng/scripts/Get-TestAssemblyWeights.ps1 | New script to scan sdk/ test projects, query ADO Analytics OData, and write a package→seconds weights JSON. |
| eng/common/scripts/Helpers/Package-Helpers.ps1 | Adds Split-ArrayByWeight LPT batching helper. |
| eng/common/scripts/job-matrix/Create-PrJobMatrix.ps1 | Loads optional weights file and switches batching strategy to weighted LPT when available. |
| eng/pipelines/templates/steps/pr-matrix-presteps.yml | Adds PR-only pre-step to generate the weights file using System.AccessToken. |
| eng/common/pipelines/templates/jobs/generate-job-matrix.yml | Passes -WeightsFile and -TargetBatchTimeSeconds through to PR matrix generation. |
| eng/pipelines/templates/jobs/ci.yml | Configures PR matrix generation to use the weights file and a 30-minute target bucket time (1800s). |
|
Hi m-nash. Thank you for your interest in helping to improve the Azure SDK experience and for your contribution. We've noticed that there hasn't been recent engagement on this pull request. If this is still an active work stream, please let us know by pushing some changes or leaving a comment. Otherwise, we'll close this out in 7 days. |
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: e5e0d1d6-f002-4bd1-9156-55ec649d89cf
461261f to
b941898
Compare
There was a problem hiding this comment.
Review details
Suppressed comments (4)
eng/scripts/Get-TestAssemblyWeights.ps1:1
Get-TestAssemblyWeights.ps1usesForEach-Object -Parallel, which requires PowerShell 7+. Without a#Requires -Version 7.0guard, running this script under Windows PowerShell 5.1 will fail immediately with a parameter-binding error.
Consider adding a version requirement at the top of the script to fail fast with a clear message.
<#
eng/pipelines/templates/steps/pr-matrix-presteps.yml:106
- The fallback warning message hard-codes "(10 packages per job)", but the actual count-based batching size is controlled elsewhere (e.g.,
PRJobBatchSize) and may not be 10 in all pipelines that reuse this template. This can make the warning misleading when someone changes the batch size.
Consider removing the hard-coded number (or wiring it through as a parameter) so the log remains accurate.
$reason = $_.Exception.Message -replace "`r?`n", " "
Write-Host "##vso[task.logissue type=warning]Runtime-weighted test batching could not be applied: $reason Falling back to count-based test batching (10 packages per job)."
Remove-Item $weightsFile -Force -ErrorAction SilentlyContinue
eng/pipelines/templates/steps/pr-matrix-presteps.yml:120
- The runtime-weighted batching step applies
Apply-WeightedBatching.ps1 -Target 240, butApply-WeightedBatching.ps1documents a defaultTargetof 1800 (seconds) and the PR description states batches target ~30 minutes. With weights expressed in seconds (seeGet-TestAssemblyWeights.ps1),240is a 4-minute bucket target, which seems inconsistent with the stated 30-minute goal unless this is intentionally a smaller "sub-bucket" size.
Can you clarify the intended unit/target here and align the value (or update the docs/description) so the configured target matches the desired per-job runtime behavior?
eng/scripts/Apply-WeightedBatching.ps1 `
-PackageInfoFolder "$(Build.ArtifactStagingDirectory)/PackageInfo" `
-WeightsFile $weightsFile `
-Target 240
eng/pipelines/templates/jobs/ci.yml:230
- PR metadata/description says the implementation adds
Split-ArrayByWeightand newCreate-PrJobMatrix.ps1parameters like-WeightsFile/-TargetBatchTimeSeconds, but the actual flow in this branch useseng/scripts/Apply-WeightedBatching.ps1+PRWeightedBucketsPerJob/PRWeightedBatchingAppliedingenerate-job-matrix.ymlinstead. This mismatch makes it hard to validate the PR against the description/design.
Please update the PR description (and/or the "Changes" table) to reflect the current approach and the files/parameters that were actually modified.
# Runtime weighting first creates small balanced buckets. Linux and Windows
# combine three buckets while slower macOS keeps one bucket per job. If
# runtime weighting fails, the default preserves count-based batching.
PRJobBatchSize: 10
PRWeightedBucketsPerJob:
Linux: 3
Windows: 3
Mac: 1
PRWeightedBatchingApplied: $(TestRuntimeWeightingApplied)
- Files reviewed: 7/7 changed files
- Comments generated: 0 new
- Review effort level: Lite
|
Azure Pipelines: Successfully started running 2 pipeline(s). 9 pipeline(s) were filtered out due to trigger conditions. There may be pipelines that require an authorized user to comment /azp run to run. |
b6ebbb0 to
4d4e97d
Compare
|
Azure Pipelines: Successfully started running 2 pipeline(s). 10 pipeline(s) were filtered out due to trigger conditions. There may be pipelines that require an authorized user to comment /azp run to run. |
Design doc
Full background and experiment handoff: https://gist.github.com/m-nash/b2429de2a9f6abcce018abd9c23aac49
Problem
The current PR test batching splits packages alphabetically into fixed-size buckets with no awareness of test runtime. This creates too many jobs, wastes agent time, and can place several expensive test suites in the same batch.
Example from build 6102688 (BatchSize=20):
Reducing the fixed package count avoids some timeouts, but increases job count and setup overhead instead of balancing the work.
At larger scales, expanding every package directly into the test matrix can also exceed Azure Pipelines YAML limits. The 100-package baseline hit this limit and abandoned 123 test jobs.
Solution
Replace fixed-size splitting with LPT (Longest Processing Time) bin packing balanced by test runtimes queried live from the Azure DevOps Analytics API.
How it works
Get-TestAssemblyWeights.ps1resolves the test assemblies required by the current PRApply-WeightedBatching.ps1uses deterministic LPT bin packing to distribute expensive packages across balanced bucketsFailure handling
Test results
20-package comparison
50-package comparison
100-package comparison
The weighted run completed with:
The current-batching baseline exceeded Azure Pipelines YAML limits during test package expansion, causing 123 test jobs to be abandoned. Consolidating weighted packages before matrix expansion resolves this scaling failure.
Query performance
Missing assemblies used the one-second fallback.
Conclusion
Adopt runtime-weighted test batching.
The complete comparisons show:
This reduces CI cost and agent-pool pressure while preserving substantial timeout headroom and improving scalability.
Comparison builds
Follow-up: Azure.Storage.Blobs 30-minute bottleneck
After weighted batching,
Azure.Storage.Blobsremains a roughly 30-minute macOS critical-path job because it is already isolated in its own bucket and cannot be subdivided by package-level scheduling.In the isolated net10.0 run, the complete job took 33.2 minutes: 20.9 minutes in
Build & Test—approximately 10.3 minutes restoring/building and 10.5 minutes executing/discovering tests—plus 12.3 minutes of pipeline setup and result-handling overhead. A follow-up analysis documents the measurements and possible options for addressing work within this package.Fixes: #58028
Related: #57679