microsoft / microsoft/mssql-rs

mssql-odbc: fetch path is significantly slower than msodbcsql18

Open
#185 0 comments 0 reactions 0 assignees View on GitHub
performance
Dominant language
Rust
Stars
53
Forks
14
Avg merge
1d 15h
Merged PRs (30d)
137

Description

## Description

Benchmarking `mssql-odbc` against the native `msodbcsql18` driver with a C++ Google Benchmark harness (`mssql-odbc/tests/perf`) against a local SQL Server 2022 shows the fetch path is substantially slower.

Baseline measurements (median, 10k rows, 7 reps):

| Benchmark | mssql-odbc | msodbcsql18 | Ratio |
|---|---|---|---|
| `BM_Fetch_NarrowRows/10000` | 17.34 ms | 4.84 ms | 3.6× slower |
| `BM_Fetch_WideRows/10000` | 49.8 ms | 42.4 ms | 1.17× slower |
| `BM_Type_VarcharMax` | — | — | ~1049× slower |
| `BM_ExecDirect_SelectOne` | 218 µs | 119 µs | 1.8× slower |

## Analysis

The row path constructs and polls a fresh async state machine per row. `#[async_trait]` boxes every trait method call, so both the size and the frequency of that construction cost real time per row. Each `SQLFetch` pays:

- a boxed `#[async_trait]` future for the transport read
- a cancellation wrapper and a timeout wrapper
- two `Instant::now()` calls
- several `Arc` clones for metadata / parser context / decryptor
- a full ODBC mutex + DBC handle handoff

...all for a single row.

Additionally, every variable-length column allocates a fresh `Vec` that is freed when the row buffer is recycled, so wide rows pay an allocate/free pair per column.

`SQLGetData` on `varchar(max)` re-materialized and re-converted the entire payload on every chunked call, giving quadratic behavior.

## Scope

- [x] Batch row prefetch so the per-row fixed costs are amortized across a rowset
- [x] Recycle column byte buffers across batched rows
- [x] Fix the `varchar(max)` chunked-read quadratic behavior
- [ ] Investigate the execute path (`BM_ExecDirect_SelectOne`, `BM_PreparedExecute`), still ~1.5–1.8× slower

## Repro

```powershell
cd mssql-odbc\tests\perf
.\run_perf.ps1 -Uid -Pwd -MinTime 1.0 -Repetitions 7
```

Contributor guide

Open the contributing guide

Research direction

Start in mssql-odbc/tests/perf and run run_perf.ps1 with the documented SQL Server credentials and benchmark settings. Compare BM_ExecDirect_SelectOne and BM_PreparedExecute against msodbcsql18, then identify and address the remaining execute-path overhead and rerun the benchmarks to verify the gap.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp, rust, sql
Domain
backend, databases, performance
Issue type
Refactor
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
38/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.