Skip to content

Commit

Permalink
refactor: Refactor RefreshAheadConnectionInfoCache. (#1997)
Browse files Browse the repository at this point in the history
This makes a number of refactoring changes to align the Java connector with other implementations.

- Introduce a ConnectionInfoCache interface
- Rename DefaultConnectionInfoCache to RefreshAheadConnectionInfoCache
- Update and simplify instantiation logic of RefreshAheadConnectionInfoCache

Part of #992
  • Loading branch information
hessjcg authored May 29, 2024
1 parent 966a45d commit d97a93b
Show file tree
Hide file tree
Showing 10 changed files with 229 additions and 202 deletions.
26 changes: 26 additions & 0 deletions core/src/main/java/com/google/cloud/sql/core/ConnectionInfo.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import com.google.cloud.sql.IpType;
import java.time.Instant;
import java.util.Map;
import java.util.stream.Collectors;
import javax.net.ssl.SSLContext;

/** Represents the results of a certificate and metadata refresh operation. */
Expand Down Expand Up @@ -51,4 +52,29 @@ Map<IpType, String> getIpAddrs() {
SslData getSslData() {
return sslData;
}

ConnectionMetadata toConnectionMetadata(
ConnectionConfig config, CloudSqlInstanceName instanceName) {
String preferredIp = null;

for (IpType ipType : config.getIpTypes()) {
preferredIp = getIpAddrs().get(ipType);
if (preferredIp != null) {
break;
}
}
if (preferredIp == null) {
throw new IllegalArgumentException(
String.format(
"[%s] Cloud SQL instance does not have any IP addresses matching preferences (%s)",
instanceName.getConnectionName(),
config.getIpTypes().stream().map(IpType::toString).collect(Collectors.joining(","))));
}

return new ConnectionMetadata(
preferredIp,
sslData.getKeyManagerFactory(),
sslData.getTrustManagerFactory(),
sslData.getSslContext());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* Copyright 2024 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.google.cloud.sql.core;

/** ConnectionInfoCache is the contract for a caching strategy for ConnectionInfo. */
interface ConnectionInfoCache {

/**
* Returns metadata needed to create a connection to the instance.
*
* @return returns ConnectionMetadata containing the preferred IP and SSL connection data.
* @throws IllegalArgumentException If the instance has no IP addresses matching the provided
* preferences.
*/
ConnectionMetadata getConnectionMetadata(long timeoutMs);

void forceRefresh();

void refreshIfExpired();

void close();
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package com.google.cloud.sql.core;

import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManagerFactory;

/**
Expand All @@ -27,16 +28,19 @@ public class ConnectionMetadata {
private final String preferredIpAddress;
private final KeyManagerFactory keyManagerFactory;
private final TrustManagerFactory trustManagerFactory;
private final SSLContext sslContext;

/** Construct an immutable ConnectionMetadata. */
public ConnectionMetadata(
String preferredIpAddress,
KeyManagerFactory keyManagerFactory,
TrustManagerFactory trustManagerFactory) {
TrustManagerFactory trustManagerFactory,
SSLContext sslContext) {

this.preferredIpAddress = preferredIpAddress;
this.keyManagerFactory = keyManagerFactory;
this.trustManagerFactory = trustManagerFactory;
this.sslContext = sslContext;
}

public String getPreferredIpAddress() {
Expand All @@ -50,4 +54,8 @@ public KeyManagerFactory getKeyManagerFactory() {
public TrustManagerFactory getTrustManagerFactory() {
return trustManagerFactory;
}

public SSLContext getSslContext() {
return sslContext;
}
}
19 changes: 10 additions & 9 deletions core/src/main/java/com/google/cloud/sql/core/Connector.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ class Connector {
private final ListenableFuture<KeyPair> localKeyPair;
private final long minRefreshDelayMs;

private final ConcurrentHashMap<ConnectionConfig, DefaultConnectionInfoCache> instances =
private final ConcurrentHashMap<ConnectionConfig, ConnectionInfoCache> instances =
new ConcurrentHashMap<>();
private final int serverProxyPort;
private final ConnectorConfig config;
Expand Down Expand Up @@ -107,13 +107,13 @@ Socket connect(ConnectionConfig config, long timeoutMs) throws IOException {
return UnixSocketChannel.open(socketAddress).socket();
}

DefaultConnectionInfoCache instance = getConnection(config);
ConnectionInfoCache instance = getConnection(config);
try {

String instanceIp = instance.getConnectionMetadata(timeoutMs).getPreferredIpAddress();
ConnectionMetadata metadata = instance.getConnectionMetadata(timeoutMs);
String instanceIp = metadata.getPreferredIpAddress();
logger.debug(String.format("[%s] Connecting to instance.", instanceIp));

SSLSocket socket = instance.createSslSocket(timeoutMs);
SSLSocket socket = (SSLSocket) metadata.getSslContext().getSocketFactory().createSocket();
socket.setKeepAlive(true);
socket.setTcpNoDelay(true);
socket.connect(new InetSocketAddress(instanceIp, serverProxyPort));
Expand All @@ -137,8 +137,8 @@ Socket connect(ConnectionConfig config, long timeoutMs) throws IOException {
}
}

DefaultConnectionInfoCache getConnection(ConnectionConfig config) {
DefaultConnectionInfoCache instance =
ConnectionInfoCache getConnection(ConnectionConfig config) {
ConnectionInfoCache instance =
instances.computeIfAbsent(config, k -> createConnectionInfo(config));

// If the client certificate has expired (as when the computer goes to
Expand All @@ -151,10 +151,11 @@ DefaultConnectionInfoCache getConnection(ConnectionConfig config) {
return instance;
}

private DefaultConnectionInfoCache createConnectionInfo(ConnectionConfig config) {
private ConnectionInfoCache createConnectionInfo(ConnectionConfig config) {
logger.debug(
String.format("[%s] Connection info added to cache.", config.getCloudSqlInstance()));
return new DefaultConnectionInfoCache(

return new RefreshAheadConnectionInfoCache(
config, adminApi, instanceCredentialFactory, executor, localKeyPair, minRefreshDelayMs);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

import com.google.auth.oauth2.AccessToken;
import com.google.auth.oauth2.GoogleCredentials;
import com.google.cloud.sql.AuthType;
import com.google.cloud.sql.CredentialFactory;
import java.io.IOException;
import java.time.Duration;
Expand All @@ -43,6 +44,14 @@ class DefaultAccessTokenSupplier implements AccessTokenSupplier {
private final int retryCount;
private final Duration retryDuration;

static AccessTokenSupplier newInstance(AuthType authType, CredentialFactory tokenSourceFactory) {
if (authType == AuthType.IAM) {
return new DefaultAccessTokenSupplier(tokenSourceFactory);
} else {
return Optional::empty;
}
}

/**
* Creates an instance with default retry settings.
*
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,12 @@ public DefaultConnectionInfoRepositoryFactory(String userAgents) {
@Override
public DefaultConnectionInfoRepository create(
HttpRequestInitializer requestInitializer, ConnectorConfig config) {
SQLAdmin adminApiBuilder = getApiBuilder(requestInitializer, config);
return new DefaultConnectionInfoRepository(adminApiBuilder);
}

private SQLAdmin getApiBuilder(
HttpRequestInitializer requestInitializer, ConnectorConfig config) {
HttpTransport httpTransport;
try {
httpTransport = GoogleNetHttpTransport.newTrustedTransport();
Expand Down Expand Up @@ -71,6 +77,6 @@ public DefaultConnectionInfoRepository create(
if (config.getUniverseDomain() != null) {
adminApiBuilder.setUniverseDomain(config.getUniverseDomain());
}
return new DefaultConnectionInfoRepository(adminApiBuilder.build());
return adminApiBuilder.build();
}
}
Loading

0 comments on commit d97a93b

Please sign in to comment.