Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update User Agent methods and format #225

Merged
merged 16 commits into from Jun 24, 2020
Merged

Update User Agent methods and format #225

merged 16 commits into from Jun 24, 2020

Conversation

shubha-rajan
Copy link
Contributor

Change Description

Updated the SocketFactory classes to set the user agent in CoreSocketFactory so that it is specific to the connector. Also updated the logic in CoreSocketFactory.setApplicationName to append to the existing user agent string if it has already been set, allowing third party tools like spring-cloud-gcp to add custom names.

Checklist

  • Make sure to open an issue as a
    bug/issue
    before writing your code! That way we can discuss the change, evaluate
    designs, and agree on the general idea.
  • Ensure the tests and linter pass
  • Appropriate documentation is updated (if necessary)

Relevant issues:

@shubha-rajan
Copy link
Contributor Author

Update: currently working for the postgres connector. This will be ready for review once I test the other connectors.
image
image

@shubha-rajan
Copy link
Contributor Author

Confirmed that it's working for all connectors (screenshots from Stackdriver Trace)
image
image
image

@shubha-rajan shubha-rajan marked this pull request as ready for review June 16, 2020 21:06
@@ -31,8 +31,24 @@

private Socket socket;

private static final String APPLICATION_NAME = "mysql-socket-factory-connector-j-5";

private static String getUserAgentString() {
Copy link
Contributor

Choose a reason for hiding this comment

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

Rather than run this each time connect is called, can we use a static block so it's only called once? Otherwise this method has to run on each connect.

It would also be convient to try to keep as much of this logic in the core factory as possible, so it doesn't need to be repeated.

static {
    SocketFactory.setArtifactNameOrSomething("mysql-socket-factory-connector-j-5/" + VERSION );
}

try {
Properties packageInfo = new Properties();
packageInfo
.load(SocketFactory.class.getClassLoader().getResourceAsStream("project.properties"));
Copy link
Contributor

Choose a reason for hiding this comment

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

The gcp-spring folks are getting the artifact version without having to read from a file (example). Can we do something similar here?

Looks like there is a getImplementationtitle that might be helpful as well.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I tried that earlier but kept getting null values for the version. From looking it up, I gathered it was because Maven jar builds don't include that information in the jar manifest by default. I added a config option to the parent pom.xml which added that metadata to the jar, but it still showed up as null when getting the value.

@@ -290,7 +296,8 @@ private static SQLAdmin createAdminApiClient(HttpRequestInitializer requestIniti
JsonFactory jsonFactory = JacksonFactory.getDefaultInstance();
SQLAdmin.Builder adminApiBuilder =
new Builder(httpTransport, jsonFactory, requestInitializer)
.setApplicationName(getApplicationName());
.setApplicationName(
String.format("%s %s", getDefaultUserAgent(), getApplicationName()));
Copy link
Contributor

Choose a reason for hiding this comment

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

What if this is null?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

so right now, getDefaultUserAgent defaults to "cloud-sql-java-connector" and getApplicationName defaults to an empty string. So there shouldn't be any cases of either of those returning null.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'll probably get rid of the getDefaultUserAgent method in the next commit though, since we don't need to set a system property. In that case I'll handle if the connector name is null.

@@ -333,6 +340,24 @@ private static KeyPair generateRsaKeyPair() {
return generator.generateKeyPair();
}

/** Sets the default string which is appended to the SQLAdmin API client User-Agent header. */
private static void setDefaultUserAgent(String userAgent) {
System.setProperty(APPLICATION_NAME_PROPERTY, userAgent);
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think we need to store this in a system property - is only done for getApplicationName() for legacy reasons.

@jsimonweb
Copy link

For the postgres connector, the /version seems to be getting logged as /null instead of the expected /1.0.17

@shubha-rajan
Copy link
Contributor Author

For the postgres connector, the /version seems to be getting logged as /null instead of the expected /1.0.17

This might have been because of early testing. I tried using another method to get the version and it kept returning null. There should also be some that log the version correctly

@jsimonweb
Copy link

Got it. LGTM. Logged format postgres-socket-factory/1.0.17-SNAPSHOT

Copy link
Contributor

@kurtisvg kurtisvg left a comment

Choose a reason for hiding this comment

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

Left a few comments, but apparently didn't attach them to a review correctly (sorry!)

@shubha-rajan
Copy link
Contributor Author

  1. Can we use load packageInfo in as a static private field as well?
  2. What happens if the user uses both MySQL and Postgres? Maybe we should maintain a list of UserAgent's that should be used?
  3. I also think we may need to consider to take care with the name - maybe addUserAgent, to avoid conflicting with setApplicationName? thoughts?

I think addUserAgent would make sense if we're maintaining a list of user agents. So then when any of the connectors are loaded into memory, the user agent would be added to the list of user agents. I made some changes to incorporate that in the last commit.

I do have a few questions:

  1. If the user is using both MySQL and Postgres, the user agents are added as soon as the connector classes are loaded because of the static block, right? so then any call to connect would have both user agents, regardless of which connector is making the call. Is this what we want?
  2. Since CoreSocketFactory (and the SQLAdminApi client) is instantiated after the first connect call, is it possible that a user using both MySQL and Postgres would instantiate the SQLAdminAPI before both classes are loaded into memory and both user agents are added?
  3. What's the behavior we want for setApplicationName? This is used by external apps to add their own user agent. Would it make more sense for this string to be stored in a separate variable (so that multiple calls would overwrite it) or should it be appended to the userAgents list?

@kurtisvg
Copy link
Contributor

  1. If the user is using both MySQL and Postgres, the user agents are added as soon as the connector classes are loaded because of the static block, right? so then any call to connect would have both user agents, regardless of which connector is making the call. Is this what we want?

Yes - raw number of API calls for each db isn't (IMO) a useful metric, but number of users is.

  1. Since CoreSocketFactory (and the SQLAdminApi client) is instantiated after the first connect call, is it possible that a user using both MySQL and Postgres would instantiate the SQLAdminAPI before both classes are loaded into memory and both user agents are added?

It should always set at least one, but I'm open to suggestions on improving to ensure both are entered. I'm not too terribly worried since this is an edge case.

  1. What's the behavior we want for setApplicationName? This is used by external apps to add their own user agent. Would it make more sense for this string to be stored in a separate variable (so that multiple calls would overwrite it) or should it be appended to the userAgents list?

I think leaving setApplicationName alone for now to preserve compatibility, and just manually appending whatever the property is on client initialization is fine.

private static final String DEFAULT_APPLICATION_NAME = "mysql-socket-factory-connector-j-5";

static {
CoreSocketFactory.addUserAgent(DEFAULT_APPLICATION_NAME);
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: I think at this point you should just inline these.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fixed


static {
try {
Properties packageInfo = new Properties();
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we move this logic into a function and just call it here so it's a bit more contained? This way we can return "unknown" over null on exception (you may want to check with @jsimonweb if we need to use anything special instead of "unknown")

Copy link
Contributor Author

Choose a reason for hiding this comment

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

moved to separate function in latest commit

/** Sets the default string which is appended to the SQLAdmin API client User-Agent header. */
public static void addUserAgent(String artifactId) {
if (version != null) {
userAgents.add(artifactId + "/" + version);
Copy link
Contributor

Choose a reason for hiding this comment

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

we should avoid letting version be null and just default it to some safe value (even if empty string). Also, we discussed adding a quick check to avoid repeats.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

it's defaulted to "unknown" in the latest commit

@@ -0,0 +1 @@
version=${project.version}
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: should the path just be com.google.cloud.sql since we're using the same version for all of the artifacts?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fixed

@@ -333,6 +350,28 @@ private static KeyPair generateRsaKeyPair() {
return generator.generateKeyPair();
}

/** Sets the default string which is appended to the SQLAdmin API client User-Agent header. */
public static void addUserAgent(String artifactId) {
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: Should this be addArtifactId since we're only setting a portion of the intended UserAgent?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fixed

}

/** Returns the default string which is appended to the SQLAdmin API client User-Agent header. */
public static String getUserAgentString() {
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
public static String getUserAgentString() {
private static String getUserAgents() {


/** Returns the default string which is appended to the SQLAdmin API client User-Agent header. */
public static String getUserAgentString() {
return String.join(" ", userAgents);
Copy link
Contributor

Choose a reason for hiding this comment

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

Should this add in USER_TOKEN_PROPERTY_NAME if set?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Not sure. Right now I'm inlining combining the two strings when creating the SQLAdmin API client.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Never mind, changed this to append the application name in the latest set of commits

Properties packageInfo = new Properties();
packageInfo.load(CoreSocketFactory.class.getClassLoader().getResourceAsStream(
"com.google.cloud.sql/project.properties"));
return packageInfo.getProperty("version");
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: Should this have a default?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fixed

@shubha-rajan shubha-rajan merged commit 6b67a1a into master Jun 24, 2020
@shubha-rajan shubha-rajan deleted the update-user-agent branch June 24, 2020 18:52
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.

Update UserAgent format for usage metrics
3 participants