Skip to content

Deduplicate UserAgent other-info entries - #939

Closed
vuanhphung wants to merge 2 commits into
databricks:mainfrom
vuanhphung:dedup-useragent-other-info
Closed

vuanhphung wants to merge 2 commits into
databricks:mainfrom
vuanhphung:dedup-useragent-other-info

Conversation

@vuanhphung

@vuanhphung vuanhphung commented Oct 5, 2026 •

Copy link
Copy Markdown

Summary

Deduplicates UserAgent.withOtherInfo entries and reads them in asString() without a lock, so registering the same entry repeatedly no longer grows User-Agent state or per-request cost.

Why

withOtherInfo appended to a static list on every call, and asString() formatted the whole list under a lock on every request. The Databricks JDBC driver registers its entries on every Driver.connect(), so in a long-running JVM that opens a connection per query, request latency and lock contention grew with every connection the JVM had opened. In a reproduction against a SQL warehouse (databricks/databricks-jdbc#1714), the list reached 7,280 entries after ~3.7k connections, and 70% of worker CPU samples were in UserAgent.asString. Fixing it in the SDK covers every caller, not just JDBC.

What changed

Interface changes

None.

Behavioral changes

Repeated withOtherInfo/withPartner calls with the same key and value are no-ops. The rendered User-Agent is unchanged, since asString() already removed duplicates.

Internal changes

otherInfo is a ConcurrentHashMap.newKeySet(), and Info implements equals/hashCode. The field is package-private so tests can check its size.

How is this tested?

  • UserAgentTest.testUserAgentWithOtherInfoDeduplicated: 1,000 identical registrations add one entry; the same key with a new value adds another.
  • UserAgentLoadTest now asserts that 200 concurrent registrations of one entry add it once.
  • Both tests fail when Info.equals is made identity-only.
  • Full databricks-sdk-java unit suite passes locally (1,502 tests).

This pull request and its description were written by Isaac.

UserAgent.withOtherInfo appended to a static list on every call, and
asString formatted the whole list under a lock on every request. Callers
that register entries per connection, such as the JDBC driver, made
request latency and lock contention grow with every connection a JVM had
opened.

Entries are now stored once in a CopyOnWriteArrayList, so repeated
registrations are no-ops and asString reads without locking. The rendered
header is unchanged.

Co-authored-by: Isaac <no-reply@databricks.com>
Signed-off-by: Vu Anh Phung <vu.phung@databricks.com>
@vuanhphung
vuanhphung deployed to test-trigger-is October 5, 2026 20:16 — with GitHub Actions Active
ConcurrentHashMap.newKeySet() is the standard concurrent set and adds new
entries in constant time, which matters for callers that register many
distinct values.

Co-authored-by: Isaac <no-reply@databricks.com>
Signed-off-by: Vu Anh Phung <vu.phung@databricks.com>
@vuanhphung
vuanhphung deployed to test-trigger-is October 5, 2026 20:25 — with GitHub Actions Active
@github-actions

github-actions Bot commented Oct 5, 2026

Copy link
Copy Markdown
Contributor

If integration tests don't run automatically, an authorized user can run them manually by following the instructions below:

Trigger:
go/deco-tests-run/sdk-java

Inputs:

  • PR number: 939
  • Commit SHA: d4cd916e5893b4918c79b84c59b1d62b297bafd1

Checks will be approved automatically on success.

@vuanhphung

Copy link
Copy Markdown
Author

Closing: the Java SDK source now lives in the monorepo, so this fix is moving there.

@vuanhphung vuanhphung closed this Oct 6, 2026

This branch was successfully deployed

1 active deployment
test-trigger-is — d4cd916e Deployed Oct 5, 2026 by vuanhphung via Check secrets access #1663
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant