Skip to content

[PECOBLR-435] added application name to track BiTool#837

Merged
shivam2680 merged 12 commits into
mainfrom
shivam2680/application-name-url-param
Jun 3, 2025
Merged

[PECOBLR-435] added application name to track BiTool#837
shivam2680 merged 12 commits into
mainfrom
shivam2680/application-name-url-param

Conversation

@shivam2680

@shivam2680 shivam2680 commented May 22, 2025

Copy link
Copy Markdown
Collaborator

Description

Added application name url param. This will be used to set client app name in telemetry. Earlier, it was set to null.
Now we follow fallback mechanism to set user agent and client app in telemetry

  1. UserAgentEntry url param
  2. ApplicationName url param
  3. Connection.setClientInfo("ApplicationName", "xyz")
  4. System property app.name

Testing

Manual
Script:

String jdbcUrl =
        "jdbc:databricks://e2-dogfood.staging.cloud.databricks.com:443/default;transportMode=http;ssl=1;httpPath=;AuthMech=3;applicationname=test-abcd";
    Connection con =
        DriverManager.getConnection(jdbcUrl, "token", "xx");
    System.out.println("Connection established......");
    con.setClientInfo("applicationname", "test-shivam");
    System.out.println("updated config");
    System.out.println(getDriverSystemConfiguration().toString());

Additional Notes to the Reviewer

NO_CHANGELOG=true

@shivam2680 shivam2680 marked this pull request as ready for review May 22, 2025 17:09

@samikshya-db samikshya-db left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor comments, LGTM otherwise. Thanks for this!

DatabricksConnectionContextFactory.create(url, info);
DriverUtil.setUpLogging(connectionContext);
UserAgentManager.setUserAgent(connectionContext);
updateClientAppName(connectionContext.getApplicationName());

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you call this function at the layer that builds the telemetry connection config itself? This can be abstracted out of here.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you mean under exportFailureLog(ctx) functions? or when we are constructing DRIVER_SYSTEM_CONFIGURATION

DatabricksConnectionContextFactory.create(url, info);
DriverUtil.setUpLogging(connectionContext);
UserAgentManager.setUserAgent(connectionContext);
updateClientAppName(connectionContext.getApplicationName());

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can set this in following order:

  1. set from UserAgentEntry connection property
  2. set from ApplicationName connection property
  3. set from ApplicationName client info property

@shivam2680 shivam2680 requested a review from gopalldb May 23, 2025 13:04
Comment thread src/main/java/com/databricks/jdbc/common/util/UserAgentHelper.java Outdated
Comment thread src/main/java/com/databricks/jdbc/common/util/UserAgentHelper.java Outdated
Comment thread src/main/java/com/databricks/jdbc/common/util/UserAgentHelper.java Outdated
Comment thread src/test/java/com/databricks/jdbc/common/util/UserAgentHelperTest.java Outdated
IDatabricksConnectionContext connectionContext =
DatabricksConnectionContextFactory.create(url, info);
DriverUtil.setUpLogging(connectionContext);
// This initializes both user agent headers and telemetry

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we remove this comment? This seems misleading for someone having telemetry = OFF.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

import com.databricks.sdk.core.UserAgent;

/** Helper class for determining and setting user agent and client app name. */
public class UserAgentHelper {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any reason we split the existing UserAgentManager helper into a different UserAgent helper? (looks like an unnecessary split in my opinion.)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just wanted the fallback mechanism utility to be separate. But, I agree, merging makes sense.

Comment on lines +149 to +154
// Preserve existing custom headers (like User-Agent) and add/update auth headers
Map<String, String> newHeaders = new HashMap<>(customHeaders);
if (refreshedHeaders != null) {
newHeaders.putAll(refreshedHeaders);
}
customHeaders = newHeaders;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor improved version :

Suggested change
// Preserve existing custom headers (like User-Agent) and add/update auth headers
Map<String, String> newHeaders = new HashMap<>(customHeaders);
if (refreshedHeaders != null) {
newHeaders.putAll(refreshedHeaders);
}
customHeaders = newHeaders;
if (refreshed != null) {
customHeaders = new HashMap<>(customHeaders);
customHeaders.putAll(refreshed);
}

Comment on lines +166 to +168
Map<String, String> headers = new HashMap<>(customHeaders);
headers.put("User-Agent", UserAgentManager.getUserAgentString());
customHeaders = headers;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Map<String, String> headers = new HashMap<>(customHeaders);
headers.put("User-Agent", UserAgentManager.getUserAgentString());
customHeaders = headers;
customHeaders = new HashMap<>(customHeaders);
customHeaders.put("User-Agent", UserAgentManager.getUserAgentString());

customHeaders =
refreshedHeaders != null ? new HashMap<>(refreshedHeaders) : Collections.emptyMap();
// Preserve existing custom headers (like User-Agent) and add/update auth headers
Map<String, String> newHeaders = new HashMap<>(customHeaders);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does that mean we don't add user-Agent info in our current thrift flow? Do we pass it from DatabricksHttpClient? I see that DatabricksHttpClient needs change too.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not really.
we do add user agent info in current thrift flow. thrift client is constructed from databrickshttpclient. While constructing, we do set userAgent in makeClosableHttpClient.
I've added logic to update user agent in thrift client because we want to update it when setClientInfo property as well.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Based on offline discussion) : SDK and thrift are not exhaustive set of places where this would be required. Any other call using HTTPClient will have the incorrect userAgent

void resetAccessToken(String newAccessToken);

/** Update the user agent headers when application name changes */
void updateUserAgent();

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we are going to remove this, right?

}
try {
String userAgentString = UserAgentManager.getUserAgentString();
if (!isNullOrEmpty(userAgentString) && !request.containsHeader("User-Agent")) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we need to do

if (!isNullOrEmpty(userAgentString) || !ObjectEquals(request.containsHeader("User-Agent"), userAgentString) {

inorder to replace the potentially stale userAgent (i.e., before customer sets it via session? )

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I discussed this also with @gopalldb , the plan is to introduce client info property and applicationname url param as fallback for userAgentEntry url param. UserAgentEntry always takes precedence.

@samikshya-db samikshya-db left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor comment, looks good otherwise! Thanks for the clean code!

@jayantsing-db

Copy link
Copy Markdown
Collaborator

UserAgentEntry url param
ApplicationName url param

what is the rationale for requiring two separate params to trace user activity?

@samikshya-db

Copy link
Copy Markdown
Collaborator

@jayantsing-db - this is in hope to reduce the number of unknown BI tool entries on the backend.

Apparently, applicationName is a standard way to set the BI tool. More context here : https://databricks.slack.com/archives/C08AS8EFRNC/p1747302484928519?thread_ts=1747287041.406849&cid=C08AS8EFRNC

@shivam2680 shivam2680 merged commit 3957211 into databricks:main Jun 3, 2025
15 of 16 checks passed
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.

4 participants