Skip to content

perf(spanner): store buffered_values in a VecDeque#6037

Merged
olavloite merged 2 commits into
googleapis:mainfrom
fornwall:fix/spanner-partial-result-set-quadratic-drain
Jul 17, 2026
Merged

perf(spanner): store buffered_values in a VecDeque#6037
olavloite merged 2 commits into
googleapis:mainfrom
fornwall:fix/spanner-partial-result-set-quadratic-drain

Conversation

@fornwall

@fornwall fornwall commented Jul 12, 2026

Copy link
Copy Markdown
Contributor

process_partial_result_set() drained values off the front of a Vec once per complete row, causing a performance issue (being an order of magnitude slower than e.g. the python spanner client) to due excessive memmove on larger result sets.

Fix this by changing buffered_values to a VecDeque.

See https://github.com/fornwall/spanner-client-perf-issue for a benchmark that contains before and after flamegraphs and concrete numbers.

Additionally, as a related minor enhancement, introduce a capacity reservation on ready_rows to pre-allocate memory based on the buffered values and column count.

@fornwall
fornwall requested review from a team as code owners July 12, 2026 11:06
@product-auto-label product-auto-label Bot added the api: spanner Issues related to the Spanner API. label Jul 12, 2026

@gemini-code-assist gemini-code-assist Bot 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.

Code Review

This pull request updates ResultSet in src/spanner/src/result_set.rs by changing the type of buffered_values from Vec to VecDeque to optimize queue operations. Additionally, it introduces a capacity reservation on ready_rows to pre-allocate memory based on the buffered values and column count. There are no review comments, and I have no feedback to provide.

fornwall added a commit to fornwall/adbc-spanner that referenced this pull request Jul 14, 2026
… googleapis main (#277)

Repoint all eight google-cloud-* git dependencies (spanner, auth, lro, wkt,
gax, both admin crates, spanner-grpc-mock) from
fornwall/google-cloud-rust @ 3872d288 to
googleapis/google-cloud-rust @ a69f6d5771ea454cadb6aace92a0f114b1b27e50
(current upstream main, a strict 36-commit fast-forward — the old pin was
itself an upstream commit, so the fork never diverged at the pin).

The O(n^2) row-drain perf fix (fork PR fornwall/google-cloud-rust#2, closed)
was never in the old pin and is now pending upstream as
googleapis/google-cloud-rust#6037; this bump is perf-neutral.

No driver code changes were needed. Also update deny.toml allow-git, the
CLAUDE.md revert-checklist rev, and the benchmarks README PR reference.


Claude-Session: https://claude.ai/code/session_01Ao7MRWDTdb3rdgN9kWBf3e

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
@fornwall fornwall changed the title perf(spanner): store PartialResultSet values in a VecDeque perf(spanner): store buffered_values values in a VecDeque Jul 14, 2026
@fornwall
fornwall force-pushed the fix/spanner-partial-result-set-quadratic-drain branch from 262f334 to 7ac6cb6 Compare July 14, 2026 13:44
@fornwall fornwall changed the title perf(spanner): store buffered_values values in a VecDeque perf(spanner): store buffered_values in a VecDeque Jul 14, 2026
process_partial_result_set drained values off the front of a `Vec` once per
complete row, causing a performance issue to due excessive `memmove`,
especially on larger result sets.

Fix this by changing `buffered_values` to a `VecDeque`.

Signed-off-by: Fredrik Fornwall <fredrik@fornwall.net>
@fornwall
fornwall force-pushed the fix/spanner-partial-result-set-quadratic-drain branch from 7ac6cb6 to fe188ee Compare July 14, 2026 14:02
@olavloite

Copy link
Copy Markdown
Contributor

/gcbrun

@codecov

codecov Bot commented Jul 16, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 97.01%. Comparing base (e0ead5f) to head (6721dc9).

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #6037      +/-   ##
==========================================
- Coverage   97.02%   97.01%   -0.01%     
==========================================
  Files         253      253              
  Lines       63547    63549       +2     
==========================================
- Hits        61655    61654       -1     
- Misses       1892     1895       +3     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@olavloite

Copy link
Copy Markdown
Contributor

@fornwall Thanks for this fix. This does indeed make a significant difference.

being an order of magnitude slower than e.g. the python spanner client

I was not able to reproduce this, though. At least not with real Spanner. It could be that the Emulator uses a different chunking strategy that real Spanner, making the comparison different. What I saw using real Spanner was this:

Benchmark Python Rust Before Rust After
200,000 rows - 2 cols 5.75s 2.26s 0.11s
100,000 rows - 12 cols 2.62s 1.07s 0.35s

@olavloite
olavloite merged commit 7ef1510 into googleapis:main Jul 17, 2026
41 of 42 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

api: spanner Issues related to the Spanner API.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants