microsoft / microsoft/mssql-rs
mssql-odbc: single-row bound fetch regressed ~17% against the pinned perf baseline
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 53
- Forks
- 14
- Avg merge
- 1d 15h
- Merged PRs (30d)
- 137
Description
Describe the bug
The ODBC perf pipeline (pipeline 2325) flagged a confirmed regression against the pinned mssql-odbc baseline in run 20260905.1 (buildId 172669). The regression is isolated to rowset size 1: the fixed per-call setup cost of SQLFetchScroll grew, and at rowset 1 that cost is paid once per row instead of being amortized across 64 or 1000 rows.
Three changes landed between the last green run (49f2ccb4, run 171863) and the regressed run, and each added fixed work to every fetch:
- Descriptor-backed bindings (AB#47437) made every
SQLFetchScroll,SQLBindCol, andSQLFreeStmt(SQL_UNBIND)callhandles::live_type(ard), which takes the process-globalLIVE_HANDLESmutex and does a hash lookup. Thebind_cycleworkload pays this 17 times per row. - Deferred
SQL_C_DEFAULTresolution (#460) acquires the ENV mutex and heap-allocates aVec<SqlSmallInt>of per-column SQL types on every fetch, whether or not any binding actually usesSQL_C_DEFAULT. - The PLP metadata cache added in
e8c4ffa4was lost in the revert at49f2ccb4, sofill_rowsetre-locks the STMT and rebuilds per-column PLP encoding info on every call.
Steps to reproduce
- Build
mssql-odbcat5c412918(or any commit at or after the merge of #460). - Run the perf-lab benchmark suite against it with the pinned baseline commit from
mssql-odbc-bench/perf-lab/baseline-commit.txt:mssql-odbc-bench/perf-lab/run-benchmarks.sh(orrun-benchmarks.ps1on Windows)
- Compare the
fetch/rowset_100k_c15_fixed/bound_rowset_1andfetch/rowset_20k_c15_fixed/bind_cycle_rowset_1medians against the baseline.
Expected behavior
Single-row fetch wall time stays at or below the pinned baseline. Run 171863 had it 17.71% faster than baseline on bound_rowset_1 and 15.55% faster on bind_cycle_rowset_1, so that is the level the driver had already reached.
Actual behavior
Run 172669 measured, as medians over 5 repetitions:
| Benchmark | Baseline | Candidate | Delta |
|---|---|---|---|
fetch/rowset_100k_c15_fixed/bound_rowset_1 |
141.13 ms | 163.56 ms | +16.7% |
fetch/rowset_20k_c15_fixed/bind_cycle_rowset_1 |
109.06 ms | 123.22 ms | +13.2% |
Rowset 64 and 1000 variants were flat, and the reported execute_ms and metadata_bind_ms phases were unchanged, which places the cost inside the fetch loop's per-call setup rather than in execution or metadata binding.
Version
commit 5c412918
Affected crate
mssql-odbc
Environment
- Perf pipeline 2325, run 20260905.1 (buildId 172669)
- Compared against the baseline commit pinned in
mssql-odbc-bench/perf-lab/baseline-commit.txt
Additional context
The pipeline's regression gate fires at 5% higher wall time, and both benchmarks reproduced in the confirmation rounds, so this is not run-to-run noise.
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 mssql-odbc's fetch loop and the reported per-call setup paths, then run perf-lab/run-benchmarks.sh or run-benchmarks.ps1 using the commit in perf-lab/baseline-commit.txt. Compare bound_rowset_1 and bind_cycle_rowset_1 against the pinned baseline, while checking that rowset 64 and 1000 remain flat. Done means the single-row medians no longer exceed the baseline regression threshold.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust, sql
- Domain
- backend, databases, performance
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 52/100