Skip to content

Commit 8a75c58

Browse files
authored
Improve handling of connections through unix domain sockets. (#210)
1 parent d86a454 commit 8a75c58

File tree

6 files changed

+60
-40
lines changed

6 files changed

+60
-40
lines changed

README.md

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -160,9 +160,16 @@ this purpose.
160160

161161
### Connection via Unix Sockets
162162

163-
The library will automatically detect when it is running on GAE Standard, and will connect via the
164-
provided unix socket for reduced latency.
163+
To connect using a Unix domain socket (such as the one created by the Cloud SQL
164+
proxy), you can use the `unixSocketPath` property to specify a path to a local
165+
file instead of connecting directly over TCP.
165166

166-
To force the library to connect to a unix socket (typically created by the Cloud SQL proxy) when
167-
running outside of the GAE-Standard environment, set the environment variable
168-
`CLOUD_SQL_FORCE_UNIX_SOCKET` to any value.
167+
Example using MySQL:
168+
```
169+
jdbc:mysql:///<DATABASE_NAME>?unixSocketPath=</PATH/TO/UNIX/SOCKET>&cloudSqlInstance=<INSTANCE_CONNECTION_NAME>&socketFactory=com.google.cloud.sql.mysql.SocketFactory&user=<MYSQL_USER_NAME>&password=<MYSQL_USER_PASSWORD>
170+
```
171+
172+
Example using PostgreSQL:
173+
```
174+
jdbc:postgresql:///<DATABASE_NAME>?unixSocketPath=</PATH/TO/UNIX/SOCKET>&cloudSqlInstance=<INSTANCE_CONNECTION_NAME>&socketFactory=com.google.cloud.sql.postgres.SocketFactory&user=<POSTGRESQL_USER_NAME>&password=<POSTGRESQL_USER_PASSWORD>
175+
```

connector-j-5/src/main/java/com/google/cloud/sql/mysql/SocketFactory.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ public class SocketFactory implements com.mysql.jdbc.SocketFactory {
3333

3434
@Override
3535
public Socket connect(String hostname, int portNumber, Properties props) throws IOException {
36-
socket = CoreSocketFactory.connect(props, CoreSocketFactory.MYSQL_SOCKET_FILE_FORMAT);
36+
socket = CoreSocketFactory.connect(props);
3737
return socket;
3838
}
3939

connector-j-6/src/main/java/com/google/cloud/sql/mysql/SocketFactory.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ public class SocketFactory implements com.mysql.cj.api.io.SocketFactory {
3434
@Override
3535
public Socket connect(String host, int portNumber, Properties props, int loginTimeout)
3636
throws IOException {
37-
socket = CoreSocketFactory.connect(props, CoreSocketFactory.MYSQL_SOCKET_FILE_FORMAT);
37+
socket = CoreSocketFactory.connect(props);
3838
return socket;
3939
}
4040

connector-j-8/src/main/java/com/google/cloud/sql/mysql/SocketFactory.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ public <T extends Closeable> T connect(
4545
public <T extends Closeable> T connect(
4646
String host, int portNumber, Properties props, int loginTimeout) throws IOException {
4747
@SuppressWarnings("unchecked")
48-
T socket = (T) CoreSocketFactory.connect(props, CoreSocketFactory.MYSQL_SOCKET_FILE_FORMAT);
48+
T socket = (T) CoreSocketFactory.connect(props);
4949
return socket;
5050
}
5151

core/src/main/java/com/google/cloud/sql/core/CoreSocketFactory.java

Lines changed: 42 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,7 @@
6363
*/
6464
public final class CoreSocketFactory {
6565
public static final String CLOUD_SQL_INSTANCE_PROPERTY = "cloudSqlInstance";
66-
public static final String MYSQL_SOCKET_FILE_FORMAT = "/cloudsql/%s";
67-
public static final String POSTGRES_SOCKET_FILE_FORMAT = "/cloudsql/%s/.s.PGSQL.5432";
66+
private static final String UNIX_SOCKET_PROPERTY = "unixSocketPath";
6867

6968
/**
7069
* Property used to set the application name for the underlying SQLAdmin client.
@@ -149,62 +148,74 @@ static ListeningScheduledExecutorService getDefaultExecutor() {
149148
MoreExecutors.getExitingScheduledExecutorService(executor));
150149
}
151150

151+
/** Extracts the Unix socket argument from specified properties object. If unset, returns null. */
152+
private static String getUnixSocketArg(Properties props) {
153+
String unixSocketPath = props.getProperty(UNIX_SOCKET_PROPERTY);
154+
if (unixSocketPath != null) {
155+
// Get the Unix socket file path from the properties object
156+
return unixSocketPath;
157+
} else if (System.getenv("CLOUD_SQL_FORCE_UNIX_SOCKET") != null) {
158+
// If the deprecated env var is set, warn and use `/cloudsql/INSTANCE_CONNECTION_NAME`
159+
// A socket factory is provided at this path for GAE, GCF, and Cloud Run
160+
logger.warning(
161+
String.format(
162+
"\"CLOUD_SQL_FORCE_UNIX_SOCKET\" env var has been deprecated. Please use"
163+
+ " '%s=\"/cloudsql/INSTANCE_CONNECTION_NAME\"' property in your JDBC url"
164+
+ " instead.",
165+
UNIX_SOCKET_PROPERTY));
166+
return "/cloudsql/" + props.getProperty(CLOUD_SQL_INSTANCE_PROPERTY);
167+
}
168+
return null; // if unset, default to null
169+
}
170+
171+
/**
172+
* Creates a socket representing a connection to a Cloud SQL instance.
173+
*/
174+
public static Socket connect(Properties props) throws IOException {
175+
return connect(props, null);
176+
}
177+
152178
/**
153179
* Creates a socket representing a connection to a Cloud SQL instance.
154180
*
155-
* <p>Depending on the environment, it may return either a SSL Socket or a Unix Socket.
181+
* <p>Depending on the given properties, it may return either a SSL Socket or a Unix Socket.
156182
*
157183
* @param props Properties used to configure the connection.
184+
* @param unixPathSuffix suffix to add the the Unix socket path. Unused if null.
158185
* @return the newly created Socket.
159186
* @throws IOException if error occurs during socket creation.
160187
*/
161-
public static Socket connect(Properties props, String socketPathFormat) throws IOException {
188+
public static Socket connect(Properties props, String unixPathSuffix) throws IOException {
162189
// Gather parameters
163190
final String csqlInstanceName = props.getProperty(CLOUD_SQL_INSTANCE_PROPERTY);
164-
final List<String> ipTypes = listIpTypes(props.getProperty("ipTypes", DEFAULT_IP_TYPES));
165-
final boolean forceUnixSocket = System.getenv("CLOUD_SQL_FORCE_UNIX_SOCKET") != null;
166191

167192
// Validate parameters
168193
Preconditions.checkArgument(
169194
csqlInstanceName != null,
170195
"cloudSqlInstance property not set. Please specify this property in the JDBC URL or the "
171196
+ "connection Properties with value in form \"project:region:instance\"");
172197

173-
// GAE Standard + GCF provide a connection path at "/cloudsql/<CONNECTION_NAME>"
174-
if (forceUnixSocket || runningOnGaeStandard() || runningOnGoogleCloudFunctions()) {
198+
// Connect using the specified Unix socket
199+
String unixSocket = getUnixSocketArg(props);
200+
if (unixSocket != null) {
201+
// Verify it ends with the correct suffix
202+
if (unixPathSuffix != null && !unixSocket.endsWith(unixPathSuffix)) {
203+
unixSocket = unixSocket + unixPathSuffix;
204+
}
175205
logger.info(
176206
String.format(
177-
"Connecting to Cloud SQL instance [%s] via unix socket.", csqlInstanceName));
178-
UnixSocketAddress socketAddress =
179-
new UnixSocketAddress(new File(String.format(socketPathFormat, csqlInstanceName)));
207+
"Connecting to Cloud SQL instance [%s] via unix socket at %s.",
208+
csqlInstanceName, unixSocket));
209+
UnixSocketAddress socketAddress = new UnixSocketAddress(new File(unixSocket));
180210
return UnixSocketChannel.open(socketAddress).socket();
181211
}
182212

213+
final List<String> ipTypes = listIpTypes(props.getProperty("ipTypes", DEFAULT_IP_TYPES));
183214
logger.info(
184215
String.format("Connecting to Cloud SQL instance [%s] via SSL socket.", csqlInstanceName));
185216
return getInstance().createSslSocket(csqlInstanceName, ipTypes);
186217
}
187218

188-
/** Returns {@code true} if running in a Google App Engine Standard runtime. */
189-
private static boolean runningOnGaeStandard() {
190-
// gaeEnv="standard" indicates standard instances
191-
String gaeEnv = System.getenv("GAE_ENV");
192-
// runEnv="Production" requires to rule out Java 8 emulated environments
193-
String runEnv = System.getProperty("com.google.appengine.runtime.environment");
194-
// gaeRuntime="java11" in Java 11 environments (no emulated environments)
195-
String gaeRuntime = System.getenv("GAE_RUNTIME");
196-
197-
return "standard".equals(gaeEnv)
198-
&& ("Production".equals(runEnv) || "java11".equals(gaeRuntime));
199-
}
200-
201-
/** Returns {@code true} if running in a Google Cloud Functions runtime. */
202-
private static boolean runningOnGoogleCloudFunctions() {
203-
// Functions automatically sets a few variables we can use to guess the env:
204-
// See https://cloud.google.com/functions/docs/env-var#nodejs_10_and_subsequent_runtimes
205-
return System.getenv("K_SERVICE") != null && System.getenv("K_REVISION") != null;
206-
}
207-
208219
/**
209220
* Creates a secure socket representing a connection to a Cloud SQL instance.
210221
*

postgres/src/main/java/com/google/cloud/sql/postgres/SocketFactory.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ public class SocketFactory extends javax.net.SocketFactory {
3434
private static final Logger logger = Logger.getLogger(SocketFactory.class.getName());
3535

3636
private static final String DEPRECATED_SOCKET_ARG = "SocketFactoryArg";
37+
private static final String POSTGRES_SUFFIX = "/.s.PGSQL.5432";
3738

3839
private Properties props;
3940

@@ -51,6 +52,7 @@ public SocketFactory(Properties info) {
5152
DEPRECATED_SOCKET_ARG, CoreSocketFactory.CLOUD_SQL_INSTANCE_PROPERTY));
5253
info.setProperty(CoreSocketFactory.CLOUD_SQL_INSTANCE_PROPERTY, oldInstanceKey);
5354
}
55+
5456
this.props = info;
5557
}
5658

@@ -68,7 +70,7 @@ private static Properties createDefaultProperties(String instanceName) {
6870

6971
@Override
7072
public Socket createSocket() throws IOException {
71-
return CoreSocketFactory.connect(props, CoreSocketFactory.POSTGRES_SOCKET_FILE_FORMAT);
73+
return CoreSocketFactory.connect(props, POSTGRES_SUFFIX);
7274
}
7375

7476
@Override

0 commit comments

Comments
 (0)