-
Notifications
You must be signed in to change notification settings - Fork 131
feat: support option for setting client_id while creating spanner database client #3832
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
base: main
Are you sure you want to change the base?
Conversation
this.dbClient = spanner.getDatabaseClient(options.getDatabaseId()); | ||
DatabaseClient tempDbClient = null; | ||
final DatabaseId databaseId = options.getDatabaseId(); | ||
try { | ||
Optional<String> clientIdOpt = extractClientIdOptional(options); | ||
if (clientIdOpt.isPresent() && !clientIdOpt.get().isEmpty()) { | ||
if (this.spanner instanceof ExtendedSpanner) { | ||
ExtendedSpanner extendedSpanner = (ExtendedSpanner) this.spanner; | ||
tempDbClient = extendedSpanner.getDatabaseClient(databaseId, clientIdOpt.get()); | ||
} | ||
} | ||
} catch (Exception e) { | ||
System.err.println( | ||
"WARNING: Failed during DatabaseClient initialization (possibly getting specific ID), falling back to default. Error: " | ||
+ e.getMessage()); | ||
} | ||
if (tempDbClient == null) { | ||
tempDbClient = spanner.getDatabaseClient(databaseId); | ||
} | ||
this.dbClient = tempDbClient; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can simplify this entire block into one line like this:
this.dbClient = spanner.getDatabaseClient(
databaseId,
options.getInitialConnectionPropertyValue(ConnectionProperties.CLIENT_ID));
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But to do this, you need to make sure that:
getDatabaseClient(DatabaseId, String)
accepts a null value for the second argument (which it should, as that is the default when calling the overload without a client id)- Change the return type of
SpannerPool#getSpanner(..)
to an interface or class that supports the additional client-id argument. That means that you get an instance that you can use directly on line 315.
private Optional<String> extractClientIdOptional(ConnectionOptions options) { | ||
return Optional.ofNullable(options.getInitialConnectionPropertyValues()) | ||
.map(props -> props.get(CLIENT_ID)) | ||
.map(ConnectionPropertyValue::getValue) | ||
.map(Object::toString) | ||
.filter(id -> !id.isEmpty()); | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove, see above
@@ -411,7 +439,7 @@ static Attributes createOpenTelemetryAttributes(DatabaseId databaseId) { | |||
} | |||
|
|||
@VisibleForTesting | |||
ConnectionState.Type getConnectionStateType() { | |||
Type getConnectionStateType() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove the unrelated changes in this PR
null, | ||
StringValueConverter.INSTANCE, | ||
Context.STARTUP); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: remove empty line
|
||
DatabaseClientImpl databaseClient = | ||
(DatabaseClientImpl) impl.getDatabaseClient(db, "clientId-1"); | ||
assertThat(databaseClient.clientId).isEqualTo("clientId-1"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: prefer the use of junit
assertions, so in this case assertEquals(expected, actual)
. This should also be the case if the test class that you are editing contains assertThat(..)
style assertions.
@@ -157,6 +159,7 @@ class ConnectionImpl implements Connection { | |||
private static final ParsedStatement RELEASE_STATEMENT = | |||
AbstractStatementParser.getInstance(Dialect.GOOGLE_STANDARD_SQL) | |||
.parse(Statement.of("RELEASE s1")); | |||
private static final String CLIENT_ID = "client_id"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: remove (see below)
} | ||
|
||
@Override | ||
public DatabaseClient getDatabaseClient(DatabaseId db, String clientId) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- What happens if you call this method twice with the same db and clientId? Does it return the same instance, both with the correct clientId? Add a test for that.
- What happens if you call this method twice with the same db, but different clientIds? Does it return the same instance? Or two different instances with the correct clientId? Add a test for that.
- What happens if you call this method twice with different dbs, but the same clientIds? Add a test.
// Getting a new database client for an invalidated database should use the same client id. | ||
databaseClient.pool.setResourceNotFoundException( | ||
new DatabaseNotFoundException(DoNotConstructDirectly.ALLOWED, "not found", null, null)); | ||
DatabaseClientImpl revalidated = (DatabaseClientImpl) impl.getDatabaseClient(db, "clientId-1"); | ||
assertThat(revalidated).isNotSameInstanceAs(databaseClient); | ||
assertThat(revalidated.clientId).isEqualTo(databaseClient.clientId); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems like a copy-pasted test from something else that is not really testing the new feature that is being added here.
|
||
package com.google.cloud.spanner; | ||
|
||
public interface ExtendedSpanner extends Spanner { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that we should:
- Move this interface into the package
com.google.cloud.spanner.connection
- Rename it to
InternalSpanner
- Add the annotation
@InternalApi
- Add a javadoc saying '/** This interface is used internally to create (JDBC) connections for Spanner and is only intended for internal usage. */`
/** | ||
* Returns a {@code DatabaseClient} for the given database and given client id. It uses a pool of | ||
* sessions to talk to the database. | ||
* <!--SNIPPET get_db_client--> | ||
* | ||
* <pre>{@code | ||
* SpannerOptions options = SpannerOptions.newBuilder().build(); | ||
* Spanner spanner = options.getService(); | ||
* final String project = "test-project"; | ||
* final String instance = "test-instance"; | ||
* final String database = "example-db"; | ||
* final String client_id = "client_id" | ||
* DatabaseId db = | ||
* DatabaseId.of(project, instance, database); | ||
* | ||
* DatabaseClient dbClient = spanner.getDatabaseClient(db, client_id); | ||
* }</pre> | ||
* | ||
* <!--SNIPPET get_db_client--> | ||
*/ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove this documentation, as this is intended for internal usage. Also add an @InternalApi
annotation to the method.
Fixes googleapis/java-spanner-jdbc#1856.
Summary:
This PR adds support for specifying an optional
client_Id
when creating a SpannerDatabaseClient
. This allows client applications (including JDBC connections using theclient_id
property) to identify themselves to the Spanner service, primarily for enhanced monitoring and logging capabilities.Implementation:
getDatabaseClient(DatabaseId db, String clientId)
overload to theSpanner
interface.clientId
when obtaining theDatabaseClient
.Testing:
mvn test
).