Skip to content

Add SqlClient performance test pipeline with baseline comparison, noise reduction, and ADX ingestion#4473

Open
cheenamalhotra wants to merge 27 commits into
mainfrom
dev/cheena/dev-automation-perf-test-pipeline
Open

Add SqlClient performance test pipeline with baseline comparison, noise reduction, and ADX ingestion#4473
cheenamalhotra wants to merge 27 commits into
mainfrom
dev/cheena/dev-automation-perf-test-pipeline

Conversation

@cheenamalhotra

@cheenamalhotra cheenamalhotra commented Jul 23, 2026

Copy link
Copy Markdown
Member

Summary

Adds a performance-test pipeline for Microsoft.Data.SqlClient under eng/pipelines/perf/. It runs the BenchmarkDotNet perf suite on a dedicated performance test lab (Azure Dedicated Hosts), compares the branch under test against a released NuGet baseline, applies noise-reduction controls, and optionally ingests results into an Azure Data Explorer (Kusto) database.

What's included

  • Pipeline (sqlclient-perf-pipeline.yml) — extends the reusable Perf.Test.Job template; provisions a VM, runs benchmarks over SSH, collects results, and runs post-test translation/ingestion on the agent.
  • On-VM harness (scripts/run-perf-tests.sh / .ps1) — installs the SDK, creates the perf DB, injects the VM connection string, pins the client to a reserved CPU set, and runs the benchmarks.
  • Two-pass build model — current build via ProjectReference (branch under test) vs. baseline via PackageReference (released MDS from NuGet.org), pinned with VersionOverride under Central Package Management.
  • Noise reduction — interleaved per-unit baseline↔candidate runs + best-of-N confirmation (interleave_perf.py), client CPU pinning, warm-up, allocator/network tuning on Linux, fail-loud guards, and diagnostics capture.
  • Comparison (compare_perf.py) — matches benchmarks by (Type, Method, Parameters), emits comparison.md/comparison.json, and can gate the run on confirmed regressions.
  • Kusto ingestion (perf_to_kusto.py, ingest_kusto.py) — translates BenchmarkDotNet JSON into PerfRun + PerfBenchmarkResult NDJSON and performs a queued ingestion. ADX cluster/database/service-connection are sourced from a pipeline library variable group (ADX Cluster Variables), not hard-coded.
  • Runner harness changesPerformanceTests/Program.cs gains PERF_LIST_BENCHMARKS / PERF_BENCHMARK modes to support single-unit interleaving; BenchmarkConfig.cs enables the JSON "full" exporter.

Notes

  • The pipeline runs on internal perf-lab infrastructure and consumes an internal extends template; it is manual/scheduled only (pr: none, trigger: none) and does not run on public PRs or commits.
  • Ingestion is optional and gated at runtime — the pipeline still runs and compares when the ADX variables are empty, publishing the translated NDJSON as an artifact for manual/backfill ingestion.

TODOs for later:

  • Add support for running benchmarks against .NET Franework 4.8 on Windows.

Checklist

  • Tests added or updated (perf harness/runner)
  • Public API changes documented (none — no public API changes)
  • Ensure no breaking changes introduced
  • Perf Pipeline setup and verified.

cheenamalhotra and others added 14 commits July 21, 2026 17:17
Adds a new Azure DevOps pipeline that runs the Microsoft.Data.SqlClient
BenchmarkDotNet performance tests on the internal "Perf Test Lab" by
consuming the reusable extends template v1/Perf.Test.Job.yml from the
InternalDriverTools/PerfTest repository (per wiki page 284).

Files:
- eng/pipelines/perf/sqlclient-perf-pipeline.yml: extends the perf template.
- eng/pipelines/perf/scripts/run-perf-tests.sh: Linux on-VM entry point.
- eng/pipelines/perf/scripts/run-perf-tests.ps1: Windows on-VM entry point.

The on-VM scripts install the pinned .NET SDK (global.json), create the
perf database, inject the VM SQL Server connection string into the benchmark
runner config, pin the client to the reserved CPU set, run the benchmarks,
and collect BenchmarkDotNet artifacts for the template to publish.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 4a21e2a3-6efa-45c7-9bfd-81e7f3d24a89
Extend the SqlClient performance pipeline to:
- Run benchmarks against a released NuGet baseline (default 7.0.2,
  overridable at queue time) and against the branch under test built
  from source, both CPU-pinned, in two passes on the VM.
- Compare the two passes and emit a per-benchmark delta (markdown +
  json) surfaced as the run summary.
- Translate BenchmarkDotNet "full" JSON into the perf-results Kusto
  schema (PerfRun + PerfBenchmarkResult, wiki 270) and optionally ingest
  via an ADO service connection (AzureCLI@2 + queued ingestion).

Enables JsonExporter.Full and adds a CPM VersionOverride switch
(MdsPackageVersion) so the baseline pass can pin a released MDS version
restored from NuGet.org through a dedicated single-source config.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 4a21e2a3-6efa-45c7-9bfd-81e7f3d24a89
Pre-fill the Kusto parameters so ingestion runs without queue-time input:
- kustoClusterUri: https://sqldrivers.westus2.kusto.windows.net
- kustoDatabase: PerfResultsTestDB
- kustoServiceConnection: PerfLab Infra Deployments

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 4a21e2a3-6efa-45c7-9bfd-81e7f3d24a89
…Python

The 'Ingest results into Kusto' step ran a system-wide pip install, which
Ubuntu agents (Python 3.12, PEP 668) reject with 'externally-managed-environment',
failing the step. Install azure-kusto-data/azure-kusto-ingest into an isolated
virtualenv under Agent.TempDirectory and run ingest_kusto.py with the venv
interpreter instead.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 4a21e2a3-6efa-45c7-9bfd-81e7f3d24a89
The agent lacks python3-venv/ensurepip, so 'python3 -m venv' fails too. pip
itself is present, so install azure-kusto-data/azure-kusto-ingest into the
per-user site and bypass the PEP 668 externally-managed marker with
--break-system-packages, then run ingest_kusto.py with python3.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 4a21e2a3-6efa-45c7-9bfd-81e7f3d24a89
Each benchmark pass runs from an empty perf-run-<label> working directory. The
perf app loads datatypes.json from DATATYPES_CONFIG (falling back to the CWD),
but the scripts only exported RUNNER_CONFIG, so datatypes.json was looked up in
the empty run dir and threw FileNotFoundException. Export DATATYPES_CONFIG
pointing at the checked-in file in the PerformanceTests project (needs no
per-run modification), matching the RUNNER_CONFIG pattern, in both the bash and
PowerShell VM runners.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 4a21e2a3-6efa-45c7-9bfd-81e7f3d24a89
The ingest step used fire-and-forget queued ingestion, which reports success at
queue time. When the JSON mappings were missing from the database, every
ingestion failed asynchronously (BadRequest_MappingReferenceWasNotFound) yet the
pipeline step stayed green and the DB stayed empty, with no error in the log.

- Set IngestionProperties.flush_immediately so tiny perf payloads seal at once
  instead of waiting out the ~5 min batching window.
- After queuing, poll PerfRun/PerfBenchmarkResult for the current PipelineRunId
  until the expected row counts appear (configurable --verify-timeout, default
  300s). On timeout, dump '.show ingestion failures' to the build log and fail
  the step so real errors are visible. Verification is best-effort when the
  principal lacks query rights (warns, does not fail).

Verified end-to-end against the live cluster: rows landed in ~40s and the
verifier confirmed success.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 4a21e2a3-6efa-45c7-9bfd-81e7f3d24a89
Apply the harness-owned noise-reduction controls from InternalDriverTools
wiki 339 (Reducing Noise in Performance Tests):

- Fail loud (2.10): preflight SELECT 1 before passes, plus a post-pass guard
  that fails the run when a pass produced zero benchmark results, so an empty
  comparison can never be reported green.
- Warm-up (2.5): touch the target DB in preflight to prime buffer pool/plan cache.
- Allocator tuning (2.8, Linux): export MALLOC_MMAP_THRESHOLD_ / MALLOC_TRIM_THRESHOLD_.
- Network tuning (2.9, Linux): best-effort sysctl for ephemeral ports + tcp_tw_reuse.
- Diagnostics (2.11): capture SQL instance config, CPU topology, and per-pass
  CPU-clock/thermal telemetry into results/diagnostics/.
- Regression gate (3): add failOnRegression pipeline param (default false) that
  threads --fail-on-regression to compare_perf.py.

Mirrored across run-perf-tests.sh and run-perf-tests.ps1; documented in README
(including the proposed larger follow-ups: interleaving, small bench binaries,
best-of-N confirmation).

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 4a21e2a3-6efa-45c7-9bfd-81e7f3d24a89
…/§2.6)

Replace the two-full-sequential-pass model with per-unit interleaving plus
best-of-N regression confirmation, the noise-reduction controls from
InternalDriverTools wiki 339 that require a structural change.

- Program.cs: add a BenchmarkUnit registry and env-driven modes
  PERF_LIST_BENCHMARKS (enumerate enabled units) and PERF_BENCHMARK=<unit>
  (run a single unit). No env still runs all enabled units, so the default
  behaviour is unchanged.
- interleave_perf.py: new orchestrator. Builds both variants once into
  distinct output dirs, runs each unit baseline-then-candidate back-to-back,
  re-runs only flagged units N times, and confirms a regression only on a
  strict majority. Emits the same results/{baseline,current,comparison}
  + summary.md layout as the sequential path (Kusto ingest unchanged); the
  gate fails only on CONFIRMED regressions.
- run-perf-tests.sh/.ps1: add --run-mode/-RunMode (interleaved default) and
  --confirmation-runs/-ConfirmationRuns (3); build each variant to
  perf-build-{baseline,current} and dispatch to the orchestrator, keeping the
  legacy sequential compare path as a fallback.
- pipeline yml: add benchmarkRunMode + confirmationRuns params, threaded to
  the VM script args.
- README: document the interleaving/best-of-N run model and the new params;
  move §2.2/§2.3/§2.6 from proposed to implemented.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 4a21e2a3-6efa-45c7-9bfd-81e7f3d24a89
Inline '${{ if }}' directives are not allowed inside a quoted string value.
Append the --fail-on-regression flag via a key-level if/else conditional
(the same pattern already used for testScript) so the whole value is a
plain string with only simple parameter substitutions.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 4a21e2a3-6efa-45c7-9bfd-81e7f3d24a89
Drop the kusto/ table-creation KQL scripts (PerfRun.kql,
PerfBenchmarkResult.kql) from the perf folder.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 4a21e2a3-6efa-45c7-9bfd-81e7f3d24a89
Genericize the perf pipeline README ahead of a public repo PR:
remove internal wiki page/section citations, the concrete Kusto
cluster URI / database / service-connection names, internal lab
naming, the ADO pipeline name, and the now-removed kusto/*.kql
file references. Kusto (Azure Data Explorer) documentation is kept
generic since the ingestion scripts remain.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 4a21e2a3-6efa-45c7-9bfd-81e7f3d24a89
Remove the hard-coded Kusto cluster URI, database, and ARM service
connection from the perf pipeline. These now come from a pipeline
library variable group 'ADX Cluster Variables' (KustoClusterUri,
KustoDatabase, KustoServiceConnection), so no infrastructure
identifiers are committed to the public repo.

The compile-time ingestion gate is converted to a runtime condition
on KustoClusterUri/KustoServiceConnection, and the README is updated
to document the variable group instead of the removed parameters.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 4a21e2a3-6efa-45c7-9bfd-81e7f3d24a89
Copilot AI review requested due to automatic review settings July 23, 2026 02:59
@github-project-automation github-project-automation Bot moved this to To triage in SqlClient Board Jul 23, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Adds an internal Azure DevOps performance-testing pipeline for Microsoft.Data.SqlClient, including on-VM harness scripts to run BenchmarkDotNet in a noise-reduced baseline-vs-candidate model and (optionally) translate/ingest results into ADX (Kusto). This fits the repo’s existing eng/pipelines/ infrastructure and extends the existing perf test project to support single-unit execution needed for interleaving.

Changes:

  • Adds a new perf pipeline (eng/pipelines/perf/sqlclient-perf-pipeline.yml) that provisions a perf-lab VM, runs benchmarks, publishes results, and optionally ingests translated NDJSON into Kusto.
  • Adds Linux/Windows VM harness scripts plus Python utilities for interleaving (best-of-N confirmation), baseline comparison, and Kusto translation/ingestion.
  • Updates the perf test runner to support enumerating/running individual benchmark “units”, and ensures BenchmarkDotNet “full” JSON reports are emitted.

Reviewed changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
src/Microsoft.Data.SqlClient/tests/PerformanceTests/Program.cs Adds PERF_LIST_BENCHMARKS / PERF_BENCHMARK modes to enable interleaved per-unit runs.
src/Microsoft.Data.SqlClient/tests/PerformanceTests/Microsoft.Data.SqlClient.PerformanceTests.csproj Enables baseline pinning in Package mode via MdsPackageVersion + VersionOverride under CPM.
src/Microsoft.Data.SqlClient/tests/PerformanceTests/Config/BenchmarkConfig.cs Enables BenchmarkDotNet JSON “full” exporter needed for comparison/translation.
eng/pipelines/perf/sqlclient-perf-pipeline.yml New ADO perf pipeline extending internal perf-lab template; publishes artifacts and optionally ingests to Kusto.
eng/pipelines/perf/scripts/run-perf-tests.sh Linux on-VM harness: install SDK, create DB, run interleaved/sequential passes, compare results.
eng/pipelines/perf/scripts/run-perf-tests.ps1 Windows on-VM harness counterpart (affinity pinning via ProcessorAffinity).
eng/pipelines/perf/scripts/interleave_perf.py Interleaved + best-of-N orchestrator invoking single-unit runner mode and producing comparison outputs.
eng/pipelines/perf/scripts/compare_perf.py Baseline vs current comparison over *-report-full.json with markdown/JSON outputs.
eng/pipelines/perf/scripts/perf_to_kusto.py Translates BenchmarkDotNet “full” JSON into Kusto PerfRun/PerfBenchmarkResult NDJSON.
eng/pipelines/perf/scripts/ingest_kusto.py Queued ingestion into ADX using az-cli auth + post-ingest verification polling.
eng/pipelines/perf/README.md Documents pipeline architecture, parameters, noise-reduction controls, and ingestion setup.

Comment thread eng/pipelines/perf/sqlclient-perf-pipeline.yml Outdated
Comment thread eng/pipelines/perf/scripts/run-perf-tests.sh
Comment thread eng/pipelines/perf/scripts/run-perf-tests.sh Outdated
Comment thread eng/pipelines/perf/scripts/run-perf-tests.ps1
Comment thread eng/pipelines/perf/scripts/run-perf-tests.ps1 Outdated
Remove Build Configuration, Self checkout folder name, and Driver Name
as queue-time parameters. They are invariant for the SqlClient perf
pipeline, so inline them as fixed constants (Release, dotnet-sqlclient,
Microsoft.Data.SqlClient) instead of exposing them as parameters or
variables. README parameter table updated accordingly.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 4a21e2a3-6efa-45c7-9bfd-81e7f3d24a89
Copilot AI review requested due to automatic review settings July 23, 2026 03:03

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 11 out of 11 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (2)

eng/pipelines/perf/scripts/run-perf-tests.sh:167

  • The “reuse pre-installed SDK” check doesn’t actually verify the pinned SDK version from global.json. It only checks for any 10.0.* SDK, which could pick an incompatible feature band/patch and cause builds/restores to fail (global.json pins 10.0.300). Also, DOTNET_ROOT is derived from the dotnet shim path; if dotnet is a symlink in /usr/bin this can point to the wrong root.
# Reuse a pre-installed SDK only if it already satisfies global.json; otherwise install locally.
if command -v dotnet >/dev/null 2>&1 && dotnet --list-sdks 2>/dev/null | grep -q '10\.0\.'; then
    echo "Using pre-installed dotnet: $(command -v dotnet)"
    export DOTNET_ROOT="$(dirname -- "$(command -v dotnet)")"
else

eng/pipelines/perf/scripts/run-perf-tests.ps1:127

  • The pre-installed dotnet detection checks only for any 10.0.* SDK, but global.json pins a specific SDK version (10.0.300). If the VM image has a different 10.0 SDK, builds can fail despite this check passing. Prefer verifying the exact pinned version (or at least the pinned feature band) before skipping Install-DotNet.
$hasNet10Sdk = $false
if (Get-Command dotnet -ErrorAction SilentlyContinue) {
    if ((dotnet --list-sdks) -match '^10\.0\.') { $hasNet10Sdk = $true }
}
if ($hasNet10Sdk) {
    Write-Host "Using pre-installed dotnet: $((Get-Command dotnet).Source)"
} else {
    Install-DotNet
}

Comment thread eng/pipelines/perf/scripts/compare_perf.py
…Type

The PerfRun schema added three required columns. Emit them from
perf_to_kusto.py:

  * OperatingSystem - Windows/Linux (from the pipeline platform, with a
    fallback to the benchmark host OsVersion).
  * Architecture    - x64/x86 (from the benchmark host Architecture,
    overridable via --architecture).
  * RunType         - Sequential/Interweaved (from the harness run mode).

Adds --operating-system/--architecture/--run-type args with schema-value
normalization, wires platform + benchmarkRunMode into both translation
invocations, and documents the new columns in the README.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 4a21e2a3-6efa-45c7-9bfd-81e7f3d24a89
Copilot AI review requested due to automatic review settings July 23, 2026 03:32

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 11 out of 11 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (2)

eng/pipelines/perf/scripts/run-perf-tests.sh:167

  • The preinstalled-SDK check is too permissive and can skip installing the SDK required by global.json (e.g., a host with 10.0.1xx will satisfy grep '10\.0\.' but dotnet build will later fail because global.json requires 10.0.3xx). Also, setting DOTNET_ROOT to the directory containing the dotnet shim (often /usr/bin) is incorrect and can break host resolution.

Consider checking for a compatible feature band derived from global.json and avoiding DOTNET_ROOT overrides when using the system installation.

# Reuse a pre-installed SDK only if it already satisfies global.json; otherwise install locally.
if command -v dotnet >/dev/null 2>&1 && dotnet --list-sdks 2>/dev/null | grep -q '10\.0\.'; then
    echo "Using pre-installed dotnet: $(command -v dotnet)"
    export DOTNET_ROOT="$(dirname -- "$(command -v dotnet)")"
else

eng/pipelines/perf/scripts/run-perf-tests.ps1:127

  • The script decides whether to install the SDK based on a generic ^10\.0\. check, which can accept a preinstalled SDK that doesn't satisfy global.json (e.g., 10.0.1xx vs required 10.0.3xx). This can cause the subsequent build to fail.

Consider deriving the required SDK feature band from global.json (rollForward=patch ⇒ same feature band) and checking dotnet --list-sdks against that instead.

$hasNet10Sdk = $false
if (Get-Command dotnet -ErrorAction SilentlyContinue) {
    if ((dotnet --list-sdks) -match '^10\.0\.') { $hasNet10Sdk = $true }
}
if ($hasNet10Sdk) {
    Write-Host "Using pre-installed dotnet: $((Get-Command dotnet).Source)"
} else {
    Install-DotNet
}

Comment thread eng/pipelines/perf/sqlclient-perf-pipeline.yml
Copilot AI review requested due to automatic review settings July 23, 2026 03:38
- Pipeline: omit --baseline-version entirely when no baseline is requested,
  via nested key-level conditionals, so an empty value can't be consumed as
  the --regression-threshold argument and corrupt arg parsing.
- run-perf-tests.sh: gate SDK reuse on `dotnet --version` from the repo root
  (honours global.json rollForward) instead of a hard-coded 10.0.* match; and
  resolve the dotnet symlink so DOTNET_ROOT points at the real install root.
- run-perf-tests.sh: export DB_NAME so the inline Python config-rewrite reads
  the intended database name instead of its own default.
- run-perf-tests.ps1: gate SDK reuse on `dotnet --version` from the repo root;
  correct the DB-create comment to match the sqlcmd-required (throw) behavior.
- compare_perf.py: compute allocation delta with `is not None` checks so a
  valid 0-byte baseline is not skipped; keep the percentage undefined for a
  0 baseline but surface the raw 0 -> X byte transition in the report.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 4a21e2a3-6efa-45c7-9bfd-81e7f3d24a89
…Path)

The build tag value is placed in the tagging endpoint's URL path, and a colon
trips ADO's "potentially dangerous Request.Path value was detected (:)" filter,
failing the tag step. Use "Baseline <version>" (no colon) instead.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 4a21e2a3-6efa-45c7-9bfd-81e7f3d24a89
Copilot AI review requested due to automatic review settings July 23, 2026 17:30

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 11 out of 11 changed files in this pull request and generated 1 comment.

Comment thread eng/pipelines/perf/scripts/run-perf-tests.sh
…g escaping, unused param)

Resolve the open Copilot review threads on PR #4473:

- interleave_perf.py: parse_cpus() now treats a malformed CPU spec as non-fatal
  (warns and disables pinning) instead of raising and failing the whole run.
- interleave_perf.py: drop the unused fail_on_regression parameter from
  orchestrate(); the gate is already applied in main() from the confirmed set.
- run-perf-tests.sh: validate --run-mode and --confirmation-runs right after
  parsing so bad queue-time inputs fail fast instead of silently falling back.
- run-perf-tests.sh: use 'sudo -n' for the best-effort sysctl network tuning so
  a VM without passwordless sudo fails immediately instead of blocking on a prompt.
- run-perf-tests.sh / run-perf-tests.ps1: wrap the SQL password in double quotes
  (doubling embedded quotes) when building the connection string so a password
  containing ';', '=', spaces or quotes can no longer corrupt it.
- sqlclient-perf-pipeline.yml: gate the ingestion task on the enableKustoIngestion
  boolean at compile time (${{ if eq(..., true) }}) instead of a fragile
  stringified 'True' comparison inside the runtime condition.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 4a21e2a3-6efa-45c7-9bfd-81e7f3d24a89
Copilot AI review requested due to automatic review settings July 23, 2026 18:23

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 11 out of 11 changed files in this pull request and generated no new comments.

@cheenamalhotra cheenamalhotra added this to the 7.1.0-preview3 milestone Jul 23, 2026
@cheenamalhotra cheenamalhotra moved this from To triage to In review in SqlClient Board Jul 23, 2026
@cheenamalhotra
cheenamalhotra marked this pull request as ready for review July 23, 2026 18:28
@cheenamalhotra
cheenamalhotra requested a review from a team as a code owner July 23, 2026 18:28
…ests.ps1

The Windows perf VM runs run-perf-tests.ps1, which binds -PascalCase named
parameters, but testScriptArgs always emitted bash-style --kebab-case flags.
PowerShell could not bind '--results-subdir', so 'perf-results' fell through
positionally onto -RunMode and tripped its ValidateSet("interleaved","sequential"),
aborting the script before any results were written. The template's result copy
then failed with 'C:/perf-tests/perf-results/.: No such file or directory'.

Make testScriptArgs platform-aware: emit '-PascalCase' args for the Windows
PowerShell entry point and keep '--kebab-case' args for the Linux bash entry
point. This is a bug in the pipeline YAML, not the extends template.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 4a21e2a3-6efa-45c7-9bfd-81e7f3d24a89
Copilot AI review requested due to automatic review settings July 24, 2026 21:01

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 11 out of 11 changed files in this pull request and generated 2 comments.

Comments suppressed due to low confidence (1)

eng/pipelines/perf/scripts/run-perf-tests.ps1:267

  • Get-AffinityMask builds a 64-bit affinity bitmask via 1 -shl $c. If PERF_CLIENT_CPUS ever includes a CPU index >= 64 (common on large Windows VMs), the shifts wrap/truncate and produce an incorrect mask, potentially pinning to the wrong cores. Since ProcessorAffinity/SetProcessAffinityMask don’t support processor groups, it’s safer to detect this and disable pinning with a warning.
    [long]$mask = 0
    foreach ($part in $cpuSpec.Split(",")) {
        if ($part -match '^\s*(\d+)\s*-\s*(\d+)\s*$') {
            for ($c = [int]$Matches[1]; $c -le [int]$Matches[2]; $c++) { $mask = $mask -bor ([long]1 -shl $c) }
        } elseif ($part -match '^\s*(\d+)\s*$') {

Comment on lines +36 to +39
[ValidateSet("interleaved", "sequential")]
[string]$RunMode = "interleaved",
# Best-of-N: total interleaved passes for a flagged unit before a regression is confirmed.
[int]$ConfirmationRuns = 3
Comment on lines +79 to +88
if os.name == "nt": # Windows
import ctypes

mask = 0
for c in cpus:
mask |= (1 << c)
handle = int(proc._handle) # noqa: SLF001 (Popen exposes the OS handle here)
if ctypes.windll.kernel32.SetProcessAffinityMask(handle, ctypes.c_size_t(mask)) == 0:
print(f"WARNING: SetProcessAffinityMask failed for pid {proc.pid}.", file=sys.stderr)
except Exception as exc: # noqa: BLE001
The Windows perf VM runs Windows PowerShell 5.1, where $ErrorActionPreference =
'Stop' promotes ANY native-command stderr write into a terminating error even
on a zero exit code. When the pinned .NET SDK was not yet present, 'dotnet'
wrote SDK-resolution diagnostics ("The command could not be loaded ... No .NET
SDKs were found") to stderr, so run-perf-tests.ps1 aborted in ~1s at the first
unguarded dotnet call - before it could install the SDK - and the template's
result copy then failed with 'perf-results: No such file or directory'.

Add an Invoke-Native helper that runs native tools (dotnet/python) with the
preference relaxed and judges success solely by the process exit code, and set
$PSNativeCommandUseErrorActionPreference = $false on PowerShell 7.3+. Route all
native invocations through it (dotnet build x2, interleave/compare python), make
'dotnet --info' informational/non-fatal, and relax the preference inline around
the calls that intentionally tolerate a non-zero exit (SDK detection, benchmark
result count) or need a bespoke failure message (sqlcmd db-create, preflight).

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 4a21e2a3-6efa-45c7-9bfd-81e7f3d24a89
Copilot AI review requested due to automatic review settings July 24, 2026 21:55

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 11 out of 11 changed files in this pull request and generated 1 comment.

Comment on lines 63 to +67
_config = Config.Load();
SetupConfigurations();

Run_SqlConnectionBenchmark();
Run_SqlCommandBenchmark();
Run_SqlBulkCopyBenchmark();
Run_DataTypeReaderBenchmark();
Run_DataTypeReaderAsyncBenchmark();
Run_AsyncLargeDataReadBenchmark();
Run_MarsOverheadBenchmark();
Run_ParallelAsyncConnectionBenchmark();
Run_CancellationTokenReadAsyncBenchmark();
Run_SequentialXmlReadBenchmark();
Run_JsonVsVarcharReadBenchmark();
Run_BeginTransactionBenchmark();
Run_ConnectionPoolStressBenchmark();

// Interleaving support for the perf pipeline (InternalDriverTools wiki 339 §2.2/§2.3):
// the harness can enumerate the enabled units and run one at a time, so a candidate and
Record the benchmark runner's top-level boolean feature flags (currently
UseManagedSniOnWindows, UseOptimizedAsyncBehaviour, WaitForProfiler,
UseNativeMemoryAndETWProfiler) into the new PerfRun.Config dynamic column so
each run's SqlClient behaviour is queryable alongside its results.

- perf_to_kusto.py: add _load_runner_config() that reads runnerconfig.jsonc,
  strips // comments, and returns all top-level bool flags except
  ConnectionString and Benchmarks; new --runner-config arg wires it into the
  PerfRun row as "Config". Extraction is dynamic, so future bool flags are
  picked up automatically.
- sqlclient-perf-pipeline.yml: pass --runner-config (PerformanceTests/
  runnerconfig.jsonc) to both the current and baseline translation calls.

The inline JSON ingestion mapping is built from the payload's own keys, so the
new dynamic column ingests without any ingest_kusto.py changes.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 4a21e2a3-6efa-45c7-9bfd-81e7f3d24a89
Copilot AI review requested due to automatic review settings July 24, 2026 22:18

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 11 out of 11 changed files in this pull request and generated 1 comment.

Comment on lines +119 to +123
: "${SQL_SERVER:=localhost}"
if [[ -z "${SQL_PASSWORD:-}" ]]; then
echo "ERROR: SQL_PASSWORD environment variable is not set (expected from the perf template)." >&2
exit 1
fi
…sage

The baseline variant build failed on the Windows perf VM (exit 1) in the
#if WINDOWS block of BenchmarkConfig.cs, which had never been compiled off-VM:

  * CS0103/CS0246: NativeMemoryProfiler / EtwProfiler were imported from the
    ...Diagnostics.Windows.Configs namespace (which holds the *attributes*),
    but the diagnoser types live in BenchmarkDotNet.Diagnostics.Windows.
  * CS0117: NativeMemoryProfiler has no static .Default; it must be constructed
    with new NativeMemoryProfiler().

The real errors were hidden because Build-Variant let the dotnet build output
flow onto its success stream, so '$dir = Build-Variant ...' swallowed the whole
build log into the return value (also corrupting the exe-dir path handed to the
interleaved orchestrator). Route build output through Out-Host so failures are
visible in the transcript and only the path is returned.

Verified by compiling the PerformanceTests project with the Windows code path
forced on (-p:OS=Windows_NT) against the 7.0.2 baseline package: builds clean;
the default non-Windows build is unaffected.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 4a21e2a3-6efa-45c7-9bfd-81e7f3d24a89
Copilot AI review requested due to automatic review settings July 24, 2026 22:26

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 11 out of 11 changed files in this pull request and generated 1 comment.

Comment on lines +277 to +281
if [ -f "$resultsDir/runinfo.env" ]; then
# shellcheck disable=SC1091
. "$resultsDir/runinfo.env" 2>/dev/null || true
machineName="${MACHINE_NAME:-$machineName}"
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: In review

Development

Successfully merging this pull request may close these issues.

4 participants