Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,7 @@ public interface HostMonitorService {
HostMonitorConnectionContext startMonitoring(
Connection connectionToAbort,
HostSpec hostSpec,
Properties properties,
int failureDetectionTimeMillis,
int failureDetectionIntervalMillis,
int failureDetectionCount) throws SQLException;
Properties properties) throws SQLException;

/**
* Stop monitoring for a connection represented by the given {@link HostMonitorConnectionContext}.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@

package software.amazon.jdbc.plugin.efm2;

import static software.amazon.jdbc.plugin.efm2.HostMonitoringConnectionPlugin.FAILURE_DETECTION_COUNT;
import static software.amazon.jdbc.plugin.efm2.HostMonitoringConnectionPlugin.FAILURE_DETECTION_INTERVAL;
import static software.amazon.jdbc.plugin.efm2.HostMonitoringConnectionPlugin.FAILURE_DETECTION_TIME;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;
Expand Down Expand Up @@ -54,14 +58,23 @@ public class HostMonitorServiceImpl implements HostMonitorService {
protected final MonitorService coreMonitorService;
protected final TelemetryFactory telemetryFactory;
protected final TelemetryCounter abortedConnectionsCounter;
protected final int failureDetectionTimeMillis;
protected final int failureDetectionIntervalMillis;
protected final int failureDetectionCount;

protected HostMonitorKey monitorKey;

public HostMonitorServiceImpl(final @NonNull FullServicesContainer serviceContainer, Properties props) {
public HostMonitorServiceImpl(final @NonNull FullServicesContainer serviceContainer, final Properties props) {
this.serviceContainer = serviceContainer;
this.coreMonitorService = serviceContainer.getMonitorService();
this.pluginService = serviceContainer.getPluginService();
this.telemetryFactory = serviceContainer.getTelemetryFactory();
this.abortedConnectionsCounter = telemetryFactory.createCounter("efm2.connections.aborted");

this.failureDetectionTimeMillis = FAILURE_DETECTION_TIME.getInteger(props);
this.failureDetectionIntervalMillis = FAILURE_DETECTION_INTERVAL.getInteger(props);
this.failureDetectionCount = FAILURE_DETECTION_COUNT.getInteger(props);

this.coreMonitorService.registerMonitorTypeIfAbsent(
HostMonitorImpl.class,
TimeUnit.MILLISECONDS.toNanos(MONITOR_DISPOSAL_TIME_MS.getLong(props)),
Expand All @@ -78,21 +91,10 @@ public static void closeAllMonitors() {
public HostMonitorConnectionContext startMonitoring(
final Connection connectionToAbort,
final HostSpec hostSpec,
final Properties properties,
final int failureDetectionTimeMillis,
final int failureDetectionIntervalMillis,
final int failureDetectionCount) throws SQLException {

final HostMonitor monitor = this.getMonitor(
hostSpec,
properties,
failureDetectionTimeMillis,
failureDetectionIntervalMillis,
failureDetectionCount);

final Properties properties) throws SQLException {
final HostMonitor monitor = this.getMonitor(hostSpec, properties);
final HostMonitorConnectionContext context = new HostMonitorConnectionContext(connectionToAbort);
monitor.startMonitoring(context);

return context;
}

Expand Down Expand Up @@ -131,37 +133,55 @@ public void releaseResources() {
*
* @param hostSpec Information such as hostname of the server.
* @param properties The user configuration for the current connection.
* @param failureDetectionTimeMillis A failure detection time in millis.
* @param failureDetectionIntervalMillis A failure detection interval in millis.
* @param failureDetectionCount A failure detection count.
* @return A {@link HostMonitorImpl} object associated with a specific server.
* @throws SQLException if there's errors getting or creating a monitor
*/
protected HostMonitor getMonitor(
final HostSpec hostSpec,
final Properties properties,
final int failureDetectionTimeMillis,
final int failureDetectionIntervalMillis,
final int failureDetectionCount) throws SQLException {

final String monitorKey = String.format("%d:%d:%d:%s",
failureDetectionTimeMillis,
failureDetectionIntervalMillis,
failureDetectionCount,
hostSpec.getUrl());
final Properties properties) throws SQLException {
String hostUrl = hostSpec.getUrl();
if (this.monitorKey == null || !hostUrl.equals(this.monitorKey.getUrl())) {
// The URL being monitored has changed, so we need to recalculate the monitor key.
this.monitorKey = new HostMonitorKey(
hostUrl,
Copy link
Contributor

Choose a reason for hiding this comment

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

Why are we storing the URL 2x here ? Looking at the code, the only thing we are interested in is the port and the host, seems to me that we could store those and just compare them?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The first time is so that we can check the passed in URL against the URL of the current key. These URLs could be different because of failover, read/write splitting, or the initial connection plugin selecting a new host. If a new URL appears, we need a new monitor.

The second value is the cache key, which consists of the URL as well as the efm2 settings. We include the settings in the key so that users can have multiple monitors for the same URL but with different settings. This may be useful if some of their connections require aggressive failure detection while other connections are fine with more relaxed settings.

String.format("%d:%d:%d:%s",
this.failureDetectionTimeMillis,
this.failureDetectionIntervalMillis,
this.failureDetectionCount,
hostUrl)
);
}

return this.coreMonitorService.runIfAbsent(
HostMonitorImpl.class,
monitorKey,
this.monitorKey,
this.serviceContainer,
this.pluginService.getProperties(),
(servicesContainer) -> new HostMonitorImpl(
servicesContainer,
hostSpec,
properties,
failureDetectionTimeMillis,
failureDetectionIntervalMillis,
failureDetectionCount,
this.failureDetectionTimeMillis,
this.failureDetectionIntervalMillis,
this.failureDetectionCount,
this.abortedConnectionsCounter));
}

protected static class HostMonitorKey {
private final String url;
private final String keyValue;

public HostMonitorKey(String url, String keyValue) {
this.url = url;
this.keyValue = keyValue;
}

public String getUrl() {
return url;
}

public String getKeyValue() {
return keyValue;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -148,11 +148,6 @@ public <T, E extends Exception> T execute(
return jdbcMethodFunc.call();
}

final int failureDetectionTimeMillis = FAILURE_DETECTION_TIME.getInteger(this.properties);
final int failureDetectionIntervalMillis =
FAILURE_DETECTION_INTERVAL.getInteger(this.properties);
final int failureDetectionCount = FAILURE_DETECTION_COUNT.getInteger(this.properties);

initMonitorService();

T result;
Expand All @@ -167,14 +162,10 @@ public <T, E extends Exception> T execute(
final HostSpec monitoringHostSpec = this.getMonitoringHostSpec();

try {
monitorContext =
this.monitorService.startMonitoring(
this.pluginService.getCurrentConnection(), // abort this connection if needed
monitoringHostSpec,
this.properties,
failureDetectionTimeMillis,
failureDetectionIntervalMillis,
failureDetectionCount);
monitorContext = this.monitorService.startMonitoring(
this.pluginService.getCurrentConnection(), // abort this connection if needed
monitoringHostSpec,
this.properties);
} catch (SQLException e) {
throw WrapperUtils.wrapExceptionIfNeeded(exceptionClass, e);
}
Expand Down
Loading