feat: support option for setting client_id while creating spanner database client#3832
feat: support option for setting client_id while creating spanner database client#3832aakashanandg wants to merge 9 commits intogoogleapis:mainfrom
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.
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.
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()); | ||
| } | ||
|
|
|
|
||
| @VisibleForTesting | ||
| ConnectionState.Type getConnectionStateType() { | ||
| Type getConnectionStateType() { |
There was a problem hiding this comment.
Remove the unrelated changes in this PR
| null, | ||
| StringValueConverter.INSTANCE, | ||
| Context.STARTUP); | ||
|
|
|
|
||
| DatabaseClientImpl databaseClient = | ||
| (DatabaseClientImpl) impl.getDatabaseClient(db, "clientId-1"); | ||
| assertThat(databaseClient.clientId).isEqualTo("clientId-1"); |
There was a problem hiding this comment.
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.
| 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.
nit: remove (see below)
| } | ||
|
|
||
| @Override | ||
| public DatabaseClient getDatabaseClient(DatabaseId db, String clientId) { |
There was a problem hiding this comment.
- 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.
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.
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.
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_Idwhen creating a SpannerDatabaseClient. This allows client applications (including JDBC connections using theclient_idproperty) to identify themselves to the Spanner service, primarily for enhanced monitoring and logging capabilities.Implementation:
getDatabaseClient(DatabaseId db, String clientId)overload to theSpannerinterface.clientIdwhen obtaining theDatabaseClient.Testing:
mvn test).