Conversation
…esultSet PR #1163 refactored telemetry from singleton to per-connection, but introduced per-row overhead in DatabricksResultSet.next() by traversing the parentStatement → connection → context → ConcurrentHashMap chain on every row (~6.5M times for a 1025MB query). This added ~2.3us/row, compounding to ~15s of overhead. Fix: resolve the TelemetryCollector once at construction time and store it in a final field. The next() method calls the cached collector directly, eliminating per-row object traversal, type casts, UUID hashing, and ConcurrentHashMap lookups. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
NEXT_CHANGELOG.md
Outdated
| - **Enhanced `enableMultipleCatalogSupport` behavior**: When this parameter is disabled (`enableMultipleCatalogSupport=0`), metadata operations (such as `getSchemas()`, `getTables()`, `getColumns()`, etc.) now return results only when the catalog parameter is either `null` or matches the current catalog. For any other catalog name, an empty result set is returned. This ensures metadata queries are restricted to the current catalog context. When enabled (`enableMultipleCatalogSupport=1`), metadata operations continue to work across all accessible catalogs. | ||
|
|
||
| ### Fixed | ||
| - Optimized telemetry collection during result set iteration by caching the TelemetryCollector at construction time, eliminating per-row lookup overhead for large result sets. |
Collaborator
There was a problem hiding this comment.
as this was not released, do we need a changelog? wondering if that would confuse readers
Collaborator
Author
There was a problem hiding this comment.
fair point, will remove
samikshya-db
approved these changes
Feb 21, 2026
Collaborator
samikshya-db
left a comment
There was a problem hiding this comment.
Thanks for fixing this!
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
| boolean hasNext = this.executionResult.next(); | ||
| TelemetryHelper.recordResultSetIteration( | ||
| parentStatement, statementId, resultSetMetaData.getChunkCount(), hasNext); | ||
| if (cachedTelemetryCollector != null) { |
Collaborator
There was a problem hiding this comment.
why do we have to do this with every next? this is super wasteful and anti-pattern with no benefit. Please fix this to have it on fetch request boundary.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
TelemetryCollectorreference atDatabricksResultSetconstruction time instead of resolving it on everynext()callProblem
PR #1163 refactored telemetry collection from a singleton pattern to per-connection, but inadvertently introduced per-row overhead in
DatabricksResultSet.next(). On every row iteration (~6.5M times for a 1025MB query), the code now traverses:This adds ~2.3 microseconds per row, compounding to ~15 seconds of overhead over 6.5M rows.
Fix
Resolve the
TelemetryCollectoronce in each constructor via aresolveTelemetryCollector()helper and store it in afinalfield. Thenext()method calls the cached collector directly, bypassing the entire per-row resolution chain. The connection-to-collector mapping is stable for the lifetime of a result set, so caching at construction time is safe.Before:
After:
Benchmark Results
Regression Bisection (1025MB query, ~6.5M rows, Arrow cloud-fetch, Thrift mode, warm iteration)
0e8b1ad9c9ffe28a2f33347235a1eaa3f64Post-Fix Segment Latency (500K rows per segment, warm iteration)
The fix restores the expected bursty pattern (fast in-memory iteration alternating with chunk-download waits):
vs the unfixed v3.1.2 which showed flat ~1,450ms per segment (dominated by uniform per-row overhead).
Comprehensive Comparison (1025MB, warm iteration)
NoArrow mode is unaffected by the regression (~145-163s for both versions) because the per-row telemetry overhead (~2.3us) is negligible compared to the ~25us/row cost of non-Arrow iteration.
Test plan
mvn test -Dtest=DatabricksResultSetTest— all 48 tests pass (43 existing + 5 new)🤖 Generated with Claude Code
NO_CHANGELOG=true
OVERRIDE_FREEZE=true